diff --git a/.gitignore b/.gitignore index 979e8d65..244a1879 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /build +/build-no-security +/build32 /build64 -/third_party \ No newline at end of file +/third_party +/src/framework/tests/proxy/proxy.json diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..26943a0d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "third_party/cmake-build-system"] + path = third_party/cmake-build-system + url = https://github.com/securesocketfunneling/cmake-build-system.git +[submodule "third_party/http-parser/http-parser"] + path = third_party/http-parser/http-parser + url = https://github.com/nodejs/http-parser.git diff --git a/CMakeLists.txt b/CMakeLists.txt index d2f70208..8be45a1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,11 +2,21 @@ cmake_minimum_required(VERSION 2.8) set(project_name "SSF") project(${project_name}) -set(SSF_VERSION_MAJOR 1) -set(SSF_VERSION_MINOR 0) -set(SSF_VERSION_BOUNCE 1) +set(SSF_VERSION_MAJOR 2) +set(SSF_VERSION_MINOR 1) +set(SSF_VERSION_FIX 0) +set(SSF_VERSION_BOUNCE 2) set(SSF_VERSION_TRANSPORT 1) +set(SSF_VERSION "${SSF_VERSION_MAJOR}.${SSF_VERSION_MINOR}.${SSF_VERSION_FIX}") + +message("** SSF_VERSION ${SSF_VERSION}") + +# --- Options +option(BUILD_UNIT_TESTS "Build SSF unit tests" ON) +option(BUILD_FRAMEWORK_UNIT_TESTS "Build SSF framework unit tests" OFF) +option(SSF_SECURITY "Activate TLS layer over TCP (FORCE_TCP_ONLY or STANDARD)" "STANDARD") + # --- Set client/server security if(SSF_SECURITY STREQUAL "FORCE_TCP_ONLY") message("** SSF_SECURITY is FORCE_TCP_ONLY") @@ -15,164 +25,39 @@ if(SSF_SECURITY STREQUAL "FORCE_TCP_ONLY") else() message("** SSF_SECURITY is STANDARD") add_definitions(-DTLS_OVER_TCP_LINK) - set(SSF_VERSION_SECURITY 1) + set(SSF_VERSION_SECURITY 2) endif() -# --- Include CMake build components and CMake common files -add_subdirectory(./cmake) -include(HelpersIdeTarget) - # --- Project variables set(project_BINARY_DIR "${${project_name}_BINARY_DIR}") set(project_ROOT_DIR "${${project_name}_SOURCE_DIR}") set(project_SRC_DIR "${project_ROOT_DIR}/src") set(project_IMG_DIR "${project_ROOT_DIR}/img") -set(project_LIB_DIR "${project_SRC_DIR}/lib") set(project_THIRDPARTY_DIR "${project_ROOT_DIR}/third_party") -# --- Project files declaration -set(VERSIONS_FILES - "${project_BINARY_DIR}/versions.h") - -FILE(GLOB_RECURSE COMMON_BOOST_FIBER_FILES - "${project_SRC_DIR}/common/boost/fiber/*.hpp" - "${project_SRC_DIR}/common/boost/fiber/*.cpp") - -FILE(GLOB_RECURSE COMMON_CONFIG_FILES - "${project_SRC_DIR}/common/config/*.h" - "${project_SRC_DIR}/common/config/*.cpp") - -FILE(GLOB_RECURSE COMMON_ERROR_FILES - "${project_SRC_DIR}/common/error/*.h" - "${project_SRC_DIR}/common/error/*.cpp") - -FILE(GLOB_RECURSE COMMON_NETWORK_FILES - "${project_SRC_DIR}/common/network/*.h" - "${project_SRC_DIR}/common/network/*.cpp") - -FILE(GLOB_RECURSE COMMON_UTILS_FILES - "${project_SRC_DIR}/common/utils/*.h" - "${project_SRC_DIR}/common/utils/*.cpp" - "${project_SRC_DIR}/common/utils/*.ipp") - -FILE(GLOB CORE_COMMAND_LINE_STANDARD_FILES - "${project_SRC_DIR}/core/command_line/standard/*.h" - "${project_SRC_DIR}/core/command_line/standard/*.cpp") - -FILE(GLOB CORE_COMMAND_LINE_COPY_FILES - "${project_SRC_DIR}/core/command_line/copy/*.h" - "${project_SRC_DIR}/core/command_line/copy/*.cpp") - -FILE(GLOB_RECURSE CORE_TRANSPORT_POLICIES_FILES - "${project_SRC_DIR}/core/transport_virtual_layer_policies/*.h" - "${project_SRC_DIR}/core/transport_virtual_layer_policies/*.cpp" - "${project_SRC_DIR}/core/transport_virtual_layer_policies/*.ipp") - -FILE(GLOB_RECURSE CORE_NETWORK_VIRTUAL_LAYER_POLICIES_FILES - "${project_SRC_DIR}/core/network_virtual_layer_policies/*.h" - "${project_SRC_DIR}/core/network_virtual_layer_policies/*.cpp" - "${project_SRC_DIR}/core/network_virtual_layer_policies/*.ipp") - -FILE(GLOB_RECURSE CORE_FACTORIES_FILES - "${project_SRC_DIR}/core/factories/*.h" - "${project_SRC_DIR}/core/factories/*.cpp" - "${project_SRC_DIR}/core/factories/*.ipp" - "${project_SRC_DIR}/core/factory_manager/*.h" - "${project_SRC_DIR}/core/factory_manager/*.cpp" - "${project_SRC_DIR}/core/factory_manager/*.ipp" - "${project_SRC_DIR}/core/service_manager/*.h" - "${project_SRC_DIR}/core/service_manager/*.cpp" - "${project_SRC_DIR}/core/service_manager/*.ipp") - -FILE(GLOB BASE_SERVICE_FILES - "${project_SRC_DIR}/services/*.h" - "${project_SRC_DIR}/services/*.cpp" - "${project_SRC_DIR}/services/*.ipp") - -FILE(GLOB_RECURSE ADMIN_SERVICE_FILES - "${project_SRC_DIR}/services/admin/*.h" - "${project_SRC_DIR}/services/admin/*.cpp" - "${project_SRC_DIR}/services/admin/*.ipp") - -FILE(GLOB_RECURSE DATAGRAMS_TO_FIBERS_SERVICE_FILES - "${project_SRC_DIR}/services/datagrams_to_fibers/*.h" - "${project_SRC_DIR}/services/datagrams_to_fibers/*.cpp" - "${project_SRC_DIR}/services/datagrams_to_fibers/*.ipp") - -FILE(GLOB_RECURSE FIBERS_TO_DATAGRAMS_SERVICE_FILES - "${project_SRC_DIR}/services/fibers_to_datagrams/*.h" - "${project_SRC_DIR}/services/fibers_to_datagrams/*.cpp" - "${project_SRC_DIR}/services/fibers_to_datagrams/*.ipp") - -FILE(GLOB_RECURSE SOCKETS_TO_FIBERS_SERVICE_FILES - "${project_SRC_DIR}/services/datagrams_to_fibers/*.h" - "${project_SRC_DIR}/services/datagrams_to_fibers/*.cpp" - "${project_SRC_DIR}/services/datagrams_to_fibers/*.ipp") - -FILE(GLOB_RECURSE FIBERS_TO_SOCKETS_SERVICE_FILES - "${project_SRC_DIR}/services/fibers_to_datagrams/*.h" - "${project_SRC_DIR}/services/fibers_to_datagrams/*.cpp" - "${project_SRC_DIR}/services/fibers_to_datagrams/*.ipp") - -FILE(GLOB_RECURSE SOCKS_SERVICE_FILES - "${project_SRC_DIR}/services/socks/*.h" - "${project_SRC_DIR}/services/socks/*.cpp" - "${project_SRC_DIR}/services/socks/*.ipp") - -FILE(GLOB COPY_FILE_SERVICE_FILES - "${project_SRC_DIR}/services/copy_file/*.h" - "${project_SRC_DIR}/services/copy_file/*.cpp" - "${project_SRC_DIR}/services/copy_file/*.ipp" - "${project_SRC_DIR}/services/copy_file/fiber_to_file/*.h" - "${project_SRC_DIR}/services/copy_file/fiber_to_file/*.cpp" - "${project_SRC_DIR}/services/copy_file/fiber_to_file/*.ipp" - "${project_SRC_DIR}/services/copy_file/file_to_fiber/*.h" - "${project_SRC_DIR}/services/copy_file/file_to_fiber/*.cpp" - "${project_SRC_DIR}/services/copy_file/file_to_fiber/*.ipp" - "${project_SRC_DIR}/services/copy_file/file_enquirer/*.h" - "${project_SRC_DIR}/services/copy_file/file_enquirer/*.cpp" - "${project_SRC_DIR}/services/copy_file/file_enquirer/*.ipp" - "${project_SRC_DIR}/services/copy_file/packet/*.h" - "${project_SRC_DIR}/services/copy_file/packet/*.cpp" - "${project_SRC_DIR}/services/copy_file/packet/*.ipp" - "${project_SRC_DIR}/services/copy_file/filesystem/*.h" - "${project_SRC_DIR}/services/copy_file/filesystem/*.cpp") - -FILE(GLOB_RECURSE USER_SERVICE_FILES - "${project_SRC_DIR}/services/user_services/*.h" - "${project_SRC_DIR}/services/user_services/*.cpp" - "${project_SRC_DIR}/services/user_services/*.ipp") +set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR}) +set(project_EXEC_DIR_NAME "ssf-${SSF_VERSION}") +set(project_EXEC_DIR "${project_BINARY_DIR}/ssf/${project_EXEC_DIR_NAME}") -set(PARSER_FILES - "${project_SRC_DIR}/core/parser/bounce_parser.h" - "${project_SRC_DIR}/core/parser/bounce_parser.cpp") - -FILE(GLOB_RECURSE CLIENT_FILES - "${project_SRC_DIR}/core/client/*.h" - "${project_SRC_DIR}/core/client/*.ipp") - -FILE(GLOB_RECURSE SERVER_FILES - "${project_SRC_DIR}/core/server/*.h" - "${project_SRC_DIR}/core/server/*.ipp") +# --- Include CMake build system scripts +add_subdirectory("${project_THIRDPARTY_DIR}/cmake-build-system") +include(HelpersIdeTarget) -set(BOOST_ROOT - "${project_THIRDPARTY_DIR}/boost" CACHE PATH "Path of boost library") +set(BOOST_ROOT "${project_THIRDPARTY_DIR}/boost" CACHE PATH "Path of boost library") message("** BOOST_ROOT: ${BOOST_ROOT}") -set(OPENSSL_ROOT_DIR - "${project_THIRDPARTY_DIR}/openssl" CACHE PATH "Path of openssl library") +set(OPENSSL_ROOT_DIR "${project_THIRDPARTY_DIR}/openssl" CACHE PATH "Path of openssl library") message("** OPENSSL_ROOT_DIR: ${OPENSSL_ROOT_DIR}") -# --- Include platform specific CMakeList - -# ---- Icon parameters -set(ICON_RC "") -set(EXEC_FLAG "") # ---- Third party parameters set(BOOST_PLATFORM_FLAGS "") set(BOOST_PLATFORM_COMPONENTS "") set(OPENSSL_PLATFORM_COMPONENTS "") +# --- Include platform specific CMakeList +set(ICON_RC "") +set(EXEC_FLAG "") + if (WIN32) include(./cmake-ms/CMakeLists.txt) elseif (UNIX) @@ -191,8 +76,8 @@ set(Boost_USE_STATIC_RUNTIME ON) # --- Boost components find_package( - Boost REQUIRED - FLAGS + Boost REQUIRED + FLAGS STATIC ${BOOST_PLATFORM_FLAGS} WITH_COMPONENTS @@ -201,11 +86,11 @@ find_package( program_options date_time filesystem - log regex thread chrono - ${BOOST_PLATFORM_COMPONENTS} + context + coroutine ) # --- OpenSSL components @@ -216,63 +101,47 @@ find_package( ${OPENSSL_PLATFORM_FLAGS} ) +# --- http-parser components +add_subdirectory(third_party/http-parser) + # --- Setup version.h file set(SSF_VERSION_BOOST ${Boost_VERSION}) set(SSF_VERSION_OPENSSL ${OpenSSL_VERSION}) configure_file(${project_SRC_DIR}/versions.h.in ${project_BINARY_DIR}/versions.h) include_directories(${project_BINARY_DIR}) - -# --- Include source directory -include_directories(${project_SRC_DIR}) +# --- Test certs +set(SSF_CERT_TEST_ROOT_FILES + "${project_ROOT_DIR}/certs/certificate.crt" + "${project_ROOT_DIR}/certs/dh4096.pem" + "${project_ROOT_DIR}/certs/private.key") +set(SSF_CERT_TEST_TRUSTED_FILES + "${project_ROOT_DIR}/certs/trusted/ca.crt" +) -# --- Set macro SSF service files -set(SERVICES_FILES - ${BASE_SERVICE_FILES} - ${ADMIN_SERVICE_FILES} - ${DATAGRAMS_TO_FIBERS_SERVICE_FILES} - ${FIBERS_TO_DATAGRAMS_SERVICE_FILES} - ${SOCKETS_TO_FIBERS_SERVICE_FILES} - ${FIBERS_TO_SOCKETS_SERVICE_FILES} - ${SOCKS_SERVICE_FILES} - ${USER_SERVICE_FILES} - ${COPY_FILE_SERVICE_FILES}) +# --- Copy test certs in ssf install dir +set(project_EXEC_CERTS_DIR "${project_EXEC_DIR}/certs") +file(MAKE_DIRECTORY ${project_EXEC_CERTS_DIR}) +file(MAKE_DIRECTORY ${project_EXEC_CERTS_DIR}/trusted) -# --- Set macro SSF source files -set(SSF_SOURCES - ${VERSIONS_FILES} - ${COMMON_BOOST_FIBER_FILES} - ${COMMON_CONFIG_FILES} - ${COMMON_ERROR_FILES} - ${COMMON_NETWORK_FILES} - ${COMMON_UTILS_FILES} - ${CORE_TRANSPORT_POLICIES_FILES} - ${CORE_NETWORK_VIRTUAL_LAYER_POLICIES_FILES} - ${CORE_FACTORIES_FILES} - ${SERVICES_FILES} - ${PARSER_FILES} - ${CLIENT_FILES} - ${SERVER_FILES} -) +file(COPY ${SSF_CERT_TEST_ROOT_FILES} DESTINATION ${project_EXEC_CERTS_DIR}) +file(COPY ${SSF_CERT_TEST_TRUSTED_FILES} DESTINATION "${project_EXEC_CERTS_DIR}/trusted") -set (BUILD_UNIT_TESTS ON) -if (BUILD_UNIT_TESTS) +# --- Unit tests +if (BUILD_UNIT_TESTS OR BUILD_FRAMEWORK_UNIT_TESTS) include(GTest) - set(GTEST_ROOT_DIR - "${project_THIRDPARTY_DIR}/gtest" CACHE PATH "Path of gtest library") + set(GTEST_ROOT_DIR "${project_THIRDPARTY_DIR}/gtest" CACHE PATH "Path of gtest library") # --- Extract GTest archive in build directory gtest_unpack_archive() enable_testing() - # --- Add GTest project + # --- Add GTest project add_subdirectory(${GTEST_ROOT_DIR}) +endif (BUILD_UNIT_TESTS OR BUILD_FRAMEWORK_UNIT_TESTS) - # --- Add src test - add_subdirectory("${project_SRC_DIR}/tests") -endif (BUILD_UNIT_TESTS) - -add_subdirectory("${project_SRC_DIR}/core") +# --- Add sources +add_subdirectory(src) diff --git a/LICENSE.txt b/LICENSE.txt index de5ec05c..f80368de 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -5,6 +5,7 @@ This software is using: * OpenSSL project released with OpenSSL License and SSLeay License * Google Test project released with BSD 3-Clause license * CMake modules project released with Boost Software License 1.0 + * HTTP Parser project released with MIT license Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index a3267b5d..104740b3 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,14 @@ ### Requirements * Winrar >= 5.2.1 (Third party builds on windows) - * Boost >= 1.56.0 + * Boost >= 1.61.0 * OpenSSL >= 1.0.2 - * Google Test >= 1.7.0 + * Google Test = 1.7.0 * CMake >= 2.8.11 * nasm (openssl build on windows) * Perl | Active Perl >= 5.20 (openssl build on windows) * C++11 compiler (Visual Studio 2013, Clang, g++, etc.) + * libkrb5-dev or equivalent (gssapi on linux) SSF_SECURITY: @@ -42,7 +43,7 @@ If you are using *openssl-1.0.2a*, you need to fix the file ``crypto/x509v3/v3_s Copy [the diff from OpenSSL Github](https://github.com/openssl/openssl/commit/77b1f87214224689a84db21d2eb54e9497186d93.diff) (ignore the 2 first lines) and put it in ``PROJECT_PATH/third_party/openssl/patches``. The build script will then patch the sources. -* Copy [GTest archive](http://code.google.com/p/googletest/downloads/list) in ``third_party/gtest`` +* Copy [GTest archive](https://github.com/google/googletest/archive/release-1.7.0.zip) in ``third_party/gtest`` ```bash cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest @@ -50,18 +51,19 @@ Copy [the diff from OpenSSL Github](https://github.com/openssl/openssl/commit/77 * Generate project - ```bash - mkdir PROJECT_PATH/build - cd PROJECT_PATH/build - cmake -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ - ``` +```bash +git submodule update --init --recursive +mkdir PROJECT_PATH/build +cd PROJECT_PATH/build +cmake -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ +``` - * Build project +* Build project - ```bash - cd PROJECT_PATH/build - cmake --build `pwd` --config Debug|Release - ``` +```bash +cd PROJECT_PATH/build +cmake --build . --config Debug|Release +``` ### Build SSF on Linux @@ -83,7 +85,7 @@ cp boost_1_XX_Y.tar.bz2 PROJECT_PATH/third_party/boost cp openssl-1.0.XY.tar.gz PROJECT_PATH/third_party/openssl ``` -* Copy [GTest archive](http://code.google.com/p/googletest/downloads/list) in ``third_party/gtest`` +* Copy [GTest archive](https://github.com/google/googletest/archive/release-1.7.0.zip) in ``third_party/gtest`` ```bash cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest @@ -92,6 +94,7 @@ cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest * Generate project ```bash +git submodule update --init --recursive mkdir PROJECT_PATH/build cd PROJECT_PATH/build cmake -DCMAKE_BUILD_TYPE=Release|Debug -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ @@ -100,7 +103,8 @@ cmake -DCMAKE_BUILD_TYPE=Release|Debug -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP * Build project ```bash -cmake --build PROJECT_PATH/build -- -j +cd PROJECT_PATH/build +cmake --build . -- -j ``` ### Build SSF on Mac OS X @@ -123,7 +127,7 @@ cp boost_1_XX_Y.tar.bz2 PROJECT_PATH/third_party/boost cp openssl-1.0.XY.tar.gz PROJECT_PATH/third_party/openssl ``` -* Copy [GTest archive](http://code.google.com/p/googletest/downloads/list) in ``third_party/gtest`` +* Copy [GTest archive](https://github.com/google/googletest/archive/release-1.7.0.zip) in ``third_party/gtest`` ```bash cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest @@ -132,6 +136,7 @@ cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest * Generate project ```bash +git submodule update --init --recursive mkdir PROJECT_PATH/build cd PROJECT_PATH/build cmake -DCMAKE_BUILD_TYPE=Release|Debug -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ @@ -140,10 +145,10 @@ cmake -DCMAKE_BUILD_TYPE=Release|Debug -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP * Build project ```bash -cmake --build PROJECT_PATH/build -- -j +cd PROJECT_PATH/build +cmake --build . ``` - ## How to configure ### Generating certificates for TLS connections @@ -230,19 +235,46 @@ The chain will be CLIENT -> SERVER1:PORT1 -> SERVER2:PORT2 -> SERVER3:PORT3 -> T ### Standard command line ```plaintext -ssf[.exe] [-h] [-L loc:ip:dest] [-R rem:ip:dest] [-D port] [-F port] [-U loc:ip:dest] [-V rem:ip:dest] [-b bounce_file] [-c config_file] [-p port] [host] +ssf[.exe] [-h] [-v verb_level] [-q] [-L loc:ip:dest] [-R rem:ip:dest] [-D port] [-F port] [-U loc:ip:dest] [-V rem:ip:dest] [-X port] [-Y port] [-b bounce_file] [-c config_file] [-p port] [host] ``` -* host : the IP address or the name of the remote server to connect to. -* -p : *port* is the port on which to listen (for the server) or to connect (for the client). The default value is 8011. +* -v : Verbosity level (critical, error, warning, info, debug, trace), default is info +* -q : Quiet mode (no log) * -L : TCP port forwarding with *loc* as the local TCP port, *ip* and *dest* as destination toward which the forward should be done from the server. * -R : TCP remote port forwarding with *rem* as the TCP port to forward from the remote host, *ip* and *dest* as destination toward which the forward should be done from the client. * -D : open a port (*port*) on the client to connect to a SOCKS server on the server from the client. * -F : open a port (*port*) on the server to connect to a SOCKS server on the client from the server. * -U : UDP port forwarding with *loc* as the UDP port to forward from the client, *ip* and *dest* as destination toward which the forward should be done from the server. * -V : UDP remote port forwarding with *rem* as the UDP port to forward from the server, *ip* and *dest* as destination toward which the forward should be done from the client. +* -X : open a port (*port*) on the client side, each connection to that port creates a process with I/O forwarded to/from the server side (the binary used can be set with the config file) +* -Y : open a port (*port*) on the server side, each connection to that port creates a process with I/O forwarded to/from the client side (the binary used can be set with the config file) * -b : *bounce_file* is the file containing the list of relays to use. * -c : *config_file* is the config file containing configuration for SSF (TLS configuration). +* -p : *port* is the port on which to listen (for the server) or to connect (for the client). The default value is 8011. +* host : the IP address or the name of the remote server to connect to. + +#### Server example + +Server will listen on all network interfaces on port **8011** + +```plaintext +ssfs[.exe] +``` + +Server will listen on **192.168.0.1:9000** + +```plaintext +ssfs[.exe] -p 9000 192.168.0.1 +``` + +#### Client example + +Client will open port 9000 locally and wait SOCKS requests to be transferred to +server **192.168.0.1:8000** + +```plaintext +ssfc[.exe] -D 9000 -b bounce.txt -c config.json -p 8000 192.168.0.1 +``` ### Copy command line @@ -300,13 +332,39 @@ ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] remote_host@path/to/file "key_path": "./certs/private.key", "dh_path": "./certs/dh4096.pem", "cipher_alg": "DHE-RSA-AES256-GCM-SHA384" + }, + "http_proxy": { + "host": "proxy.example.com", + "port": "3128", + "credentials": { + "username": "user", + "password": "password", + "domain": "EXAMPLE.COM", + "reuse_ntlm": "true", + "reuse_kerb": "true" + } + }, + "services": { + "shell": { + "path": "/bin/bash", + "args": "" + } } } } ``` -* *tls.ca_cert_path* : relative or absolute path to the CA certificate file -* *tls.cert_path* : relative or absolute path to the instance certificate file -* *tls.key_path* : relative or absolute path to the private key file -* *tls.dh_path* : relative or absolute path to the Diffie-Hellman file -* *tls.cipher_alg* : cypher algorithm +* *tls.ca_cert_path* : relative or absolute path to the CA certificate file +* *tls.cert_path* : relative or absolute path to the instance certificate file +* *tls.key_path* : relative or absolute path to the private key file +* *tls.dh_path* : relative or absolute path to the Diffie-Hellman file +* *tls.cipher_alg* : cipher algorithm +* *http_proxy.host* : HTTP proxy host +* *http_proxy.port* : HTTP proxy port +* *http_proxy.credentials.username* : proxy username credentials (all platform: Basic or Digest, Windows: NTLM and Negotiate if reuse = false) +* *http_proxy.credentials.password* : proxy password credentials (all platform: Basic or Digest, Windows: NTLM and Negotiate if reuse = false) +* *http_proxy.credentials.domain* : user domain (NTLM and Negotiate auth on Windows only) +* *http_proxy.credentials.reuse_ntlm* : reuse current computer user credentials to authenticate with proxy NTLM auth (SSO) +* *http_proxy.credentials.reuse_kerb* : reuse current computer user credentials (Kerberos ticket) to authenticate with proxy Negotiate auth (SSO) +* *services.shell.path* : binary path used for shell creation (optional) +* *services.shell.args* : binary arguments used for shell creation (optional) diff --git a/cmake-ms/CMakeLists.txt b/cmake-ms/CMakeLists.txt index 87185f2a..cf71cd0b 100644 --- a/cmake-ms/CMakeLists.txt +++ b/cmake-ms/CMakeLists.txt @@ -5,34 +5,21 @@ if (WIN32) include(MSVCStaticRuntime) include(HelpersIdeTarget) - # --- Include windows impl - - # --- Filesystem globbing - FILE(GLOB_RECURSE COPY_FILE_SERVICE_WINDOWS_FILES - "${project_SRC_DIR}/services/copy_file/filesystem/windows/*.h" - "${project_SRC_DIR}/services/copy_file/filesystem/windows/*.cpp") - - set(COPY_FILE_SERVICE_FILES - ${COPY_FILE_SERVICE_FILES} - ${COPY_FILE_SERVICE_WINDOWS_FILES}) - # --- Icon path set(ICON_RC "${project_IMG_DIR}/icon.rc") - set(EXEC_FLAG "") + set(EXEC_FLAG "RUNTIME_STATIC") # --- Boost platform requirements list(APPEND BOOST_PLATFORM_FLAGS "RUNTIME_STATIC") - list(APPEND BOOST_PLATFORM_COMPONENTS "context" "coroutine") - + # --- OpenSSL platform requirements list(APPEND OPENSSL_PLATFORM_FLAGS "RUNTIME_STATIC") - + # --- Flags for compilation add_definitions(-D_WIN32_WINNT=0x0501) if (MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /wd4503") add_definitions(-D_SCL_SECURE_NO_WARNINGS) add_definitions(-D_CRT_SECURE_NO_WARNINGS) endif(MSVC) endif (WIN32) - diff --git a/cmake-unix/CMakeLists.txt b/cmake-unix/CMakeLists.txt index 834c603d..624a16eb 100644 --- a/cmake-unix/CMakeLists.txt +++ b/cmake-unix/CMakeLists.txt @@ -2,37 +2,42 @@ cmake_minimum_required(VERSION 2.8) # --- Special settings to Unix platform if (UNIX) - # --- Icon path - if (APPLE) - set(ICON_NAME "icon.icns") - set(ICON_RC "${project_IMG_DIR}/${ICON_NAME}") - set(MACOSX_BUNDLE_ICON_FILE "${project_IMG_DIR}/${ICON_NAME}") - set(EXEC_FLAG "MACOSX_BUNDLE") - endif (APPLE) - - - # --- Include linux/unix impl - - # --- Filesystem globbing - FILE(GLOB_RECURSE COPY_FILE_SERVICE_LINUX_FILES - "${project_SRC_DIR}/services/copy_file/filesystem/linux/*.h" - "${project_SRC_DIR}/services/copy_file/filesystem/linux/*.cpp") - - set(COPY_FILE_SERVICE_FILES - ${COPY_FILE_SERVICE_FILES} - ${COPY_FILE_SERVICE_LINUX_FILES}) - + # --- Flags for compilation if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") + set(CLANG_NO_BOOST_WARNINGS "-Wno-unneeded-internal-declaration -Wno-unused-variable") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -std=c++11 -stdlib=libc++ ${CLANG_NO_BOOST_WARNINGS}") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -Wall -ggdb -std=c++11 -stdlib=libc++ ${CLANG_NO_BOOST_WARNINGS}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ggdb") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") + set(PLATFORM_SPECIFIC_LIB_DEP "pthread") + else() + + set(GCC_STATIC_LINK_FLAGS "-static-libstdc++ -static-libgcc") + set(GCC_NO_SYMBOLS_FLAGS "-Wl,-s") set(GCC_NO_BOOST_WARNINGS "-Wno-long-long -Wno-unused-value -Wno-unused-local-typedefs") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -std=c++11 ${GCC_NO_BOOST_WARNINGS}") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -Wall -ggdb -std=c++0x ${GCC_NO_BOOST_WARNINGS}") - set(PLATFORM_SPECIFIC_LIB_DEP "pthread" "rt" ${CMAKE_DL_LIBS}) + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -pedantic -std=c++11 ${GCC_NO_BOOST_WARNINGS} ${GCC_STATIC_LINK_FLAGS}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -ggdb") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 ${GCC_NO_SYMBOLS_FLAGS}") + + set(PLATFORM_SPECIFIC_LIB_DEP "pthread" "rt" ${CMAKE_DL_LIBS}) + + if (${CMAKE_SIZEOF_VOID_P} EQUAL "8") + # Downgrade version of libc function for 64 bits executables + # Force libc version function and wrap it with linker + # * memcpy 2.14 -> 2.2.5 + + add_library(linux_libc_funcs_version_downgrade + "${project_SRC_DIR}/linux_compatibility/memcpy.cpp" + ) + + list(APPEND PLATFORM_SPECIFIC_LIB_DEP "linux_libc_funcs_version_downgrade") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--wrap=memcpy") + endif() + endif () -endif(UNIX) +endif(UNIX) diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt deleted file mode 100644 index 510d0c0b..00000000 --- a/cmake/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -# ------------------------------------------------------------------------------ -# This script makes all helpers for external packages available from caller. -# -# An external package is a package normally found through 'find_package()' -# CMake's function. The helpers, here, are to allow possibility to embed these -# external packages in the source tree of a project. -# -# The idea will be to store the source code of the (handled) external projects -# in the source of the main project and then to call the CMake's function -# 'find_package()' with the corresponding package's name. -# -# ------------------------------------------------------------------------------ - -# -# Update parent's search path for CMake Modules -#list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}") -file(GLOB _dirs_list *) -foreach(_dir ${_dirs_list}) - if(IS_DIRECTORY "${_dir}") - if(EXISTS "${_dir}/CMakeLists.txt") - add_subdirectory("${_dir}") - else() - list(APPEND CMAKE_MODULE_PATH "${_dir}") - endif() - endif() -endforeach() -set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE) diff --git a/cmake/cmake-modules/LICENSE_1_0.txt b/cmake/cmake-modules/LICENSE_1_0.txt deleted file mode 100644 index 36b7cd93..00000000 --- a/cmake/cmake-modules/LICENSE_1_0.txt +++ /dev/null @@ -1,23 +0,0 @@ -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/cmake/cmake-modules/ListCombinations.cmake b/cmake/cmake-modules/ListCombinations.cmake deleted file mode 100644 index 4321f624..00000000 --- a/cmake/cmake-modules/ListCombinations.cmake +++ /dev/null @@ -1,53 +0,0 @@ -# - Combine lists of prefixes and suffixes in all combinations -# -# list_combinations(var PREFIXES listitems... SUFFIXES listitems...) - -# where var is the name of your desired output variable and PREFIXES -# and SUFFIXES are special arguments that indicate the start of your -# list of prefixes or suffixes respectively. -# -# Original Author: -# 2009-2010 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright Iowa State University 2009-2010. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -if(__list_combinations) - return() -endif() -set(__list_combinations YES) - -function(list_combinations var) - # Parse arguments - set(_prefixes) - set(_suffixes) - set(_nowhere) - set(_curdest _nowhere) - foreach(_element ${ARGN}) - if("${_element}" STREQUAL "PREFIXES") - set(_curdest _prefixes) - elseif("${_element}" STREQUAL "SUFFIXES") - set(_curdest _suffixes) - else() - list(APPEND ${_curdest} "${_element}") - endif() - endforeach() - if(_nowhere) - message(STATUS "_prefixes ${_prefixes}") - message(STATUS "_prefixes ${_suffixes}") - message(STATUS "_prefixes ${_nowhere}") - message(FATAL_ERROR - "Syntax error in use of ${CMAKE_CURRENT_LIST_FILE}") - endif() - - foreach(_prefix ${_prefixes}) - foreach(_suffix ${_suffixes}) - list(APPEND _out "${_prefix}${_suffix}") - endforeach() - endforeach() - - set(${var} "${_out}" PARENT_SCOPE) -endfunction() diff --git a/cmake/cmake-modules/MSVCStaticRuntime.cmake b/cmake/cmake-modules/MSVCStaticRuntime.cmake deleted file mode 100644 index e4ceea6f..00000000 --- a/cmake/cmake-modules/MSVCStaticRuntime.cmake +++ /dev/null @@ -1,33 +0,0 @@ -# - Modify compile flags to use the static runtimes of MSVC -# -# include(MSVCStaticRuntime) -# -# Requires these CMake modules: -# ListCombinations.cmake -# -# Original Author: -# 2009-2010 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright Iowa State University 2009-2010. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -if(MSVC) - string(TOUPPER "${CMAKE_CONFIGURATION_TYPES}" _conftypesUC) - include(ListCombinations) - list_combinations(_varnames - PREFIXES - CMAKE_C_FLAGS_ - CMAKE_CXX_FLAGS_ - SUFFIXES - ${_conftypesUC}) - foreach(_var ${_varnames}) - string(REPLACE "/MDd" "/MTd" ${_var} "${${_var}}") - string(REPLACE "/MD" "/MT" ${_var} "${${_var}}") - endforeach() -endif() - -set(Boost_USE_STATIC_LIBS ON) diff --git a/cmake/cmake-modules/README.markdown b/cmake/cmake-modules/README.markdown deleted file mode 100644 index 81ec9b22..00000000 --- a/cmake/cmake-modules/README.markdown +++ /dev/null @@ -1,107 +0,0 @@ -Ryan's CMake Modules -==================== - -Ryan A. Pavlik, Ph.D. - - - - -Introduction ------------- - -This is a collection of CMake modules that I've produced during the course -of a variety of software development. There are a number of find modules, -especially for virtual reality and physical simulation packages, some utility -modules of more general interest, and some patches or workarounds for -CMake itself. - -Each module is generally documented, and depending on how busy I was -when I created it, the documentation can be fairly complete. - -By now, it also includes contributions both from open-source projects I work on, -as well as friendly strangers on the Internet contributing their modules. I am -very grateful for improvements/fixes/pull requests! - -How to Integrate ----------------- - -These modules are probably best placed wholesale into a `cmake` subdirectory -of your project source. - -If you use Git, try installing [git-subtree][1] (included by default on -Git for Windows and perhaps for your Linux distro, especially post-1.9.1), so -you can easily use this repository for subtree merges, updating simply. - -For the initial checkout: - - cd projectdir - - git subtree add --squash --prefix=cmake https://github.com/rpavlik/cmake-modules.git master - -For updates: - - cd projectdir - - git subtree pull --squash --prefix=cmake https://github.com/rpavlik/cmake-modules.git master - -If you originally installed this by just copying the files, you'll sadly have -to delete the directory, commit that, then do the `git subtree add`. Annoying, -but I don't know a workaround. - -If you use some other version control, you can export a copy of this directory -without the git metadata by calling: - - ./export-to-directory.sh yourprojectdir/cmake - -You might also consider exporting to a temp directory and merging changes, since -this will not overwrite by default. You can pass -f to overwrite existing files. - -How to Use ----------- - -At the minimum, all you have to do is add a line like this near the top -of your root CMakeLists.txt file (but not before your `project()` call): - - list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - -You might also want the extra automatic features/fixes included with the -modules, for that, just add another line following the first one: - - include(UseBackportedModules) - -Look at `module-help.html`/`.txt` (generated by `update-help.sh` on a unix-like shell with a pre-3.0 version of CMake.) -either in this directory or online at -for more information on individual modules. Since it requires an older CMake for generation, -the docs might get out of date, sorry - but you can always look at the files themselves. - - -Licenses --------- - -The modules that I wrote myself are all subject to this license: - -> Copyright Iowa State University 2009-2014, -> or Copyright Sensics, Inc. 2014-2015, -> or Copyright Ryan A. Pavlik 2009-2015 -> -> Distributed under the Boost Software License, Version 1.0. -> -> (See accompanying file `LICENSE_1_0.txt` or copy at -> ) - -Modules based on those included with CMake are under the OSI-approved -BSD license, which is included in each of those modules. A few other modules -are modified from other sources - when in doubt, look at the `.cmake`. - -If you'd like to contribute, that would be great! Just make sure to include -the license boilerplate in your module, and send a pull request. - -Important License Note! ------------------------ - -If you find this file inside of another project, rather at the top-level -directory, you're in a separate project that is making use of these modules. -That separate project can (and probably does) have its own license specifics. - - -[1]: http://github.com/apenwarr/git-subtree "Git Subtree master" diff --git a/cmake/common/EnhancedList.cmake b/cmake/common/EnhancedList.cmake deleted file mode 100644 index 6db9503e..00000000 --- a/cmake/common/EnhancedList.cmake +++ /dev/null @@ -1,29 +0,0 @@ -# -# Define some enhancement to list object -# -if(__H_ENHANCED_LIST_INCLUDED) - return() -endif() -set(__H_ENHANCED_LIST_INCLUDED TRUE) - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== - -# ------------------------------------------------------------------------------ -# list_join( -# # Name of the list -# # String to use as separator for each list items -# # Name of the variable that will host the resulting string -# ) -# -# This function joins all items of a list into a string. Each items stored in -# the destination string will be separated with a given string separator. -macro(list_join list_name separator string_name) - string(REGEX REPLACE - "([^\\\\]|^);" - "\\1${separator}" - ${string_name} - "${${list_name}}" - ) -endmacro() diff --git a/cmake/common/FileEdit.cmake b/cmake/common/FileEdit.cmake deleted file mode 100644 index 9a3959a2..00000000 --- a/cmake/common/FileEdit.cmake +++ /dev/null @@ -1,223 +0,0 @@ -# Implementation of tools to edit text files -# -if(__H_FILE_EDIT_INCLUDED) - return() -endif() -set(__H_FILE_EDIT_INCLUDED TRUE) - -include(HelpersArguments) - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== - -# ------------------------------------------------------------------------------ -# file_create_unique( -# # Name of variable where to store file's name -# [PREFIX ] # Prefix to use for file's name -# [SUFFIX ] # Suffix to add to file's name -# [CONTENT ] # Optional content to write in created file -# [PATH ] # Path to where the file is to be created -# ) -# -# This function creates a file using a unique name. If the content is provided, -# it is used to fill the file. -# -function(file_create_unique var_name) - # Parse arguments - parse_arguments("FILE" - "" - "PREFIX;SUFFIX;CONTENT;PATH" - "" - "" - ${ARGN} - ) - - if(NOT FILE_PATH) - set(FILE_PATH "${CMAKE_CURRENT_BINARY_DIR}") - endif() - - # Prepare meta information to create unique file name - string(TIMESTAMP _tmp_file "%I%M%S" ) - string(RANDOM - LENGTH 16 - ALPHABET "0123456789ABCDEF" - RANDOM_SEED "${_tmp_file}" - _alea - ) - - # Create file name - set(_tmp_file "${_tmp_file}-${_alea}${FILE_SUFFIX}") - set(_tmp_file "${FILE_PATH}/${FILE_PREFIX}${_tmp_file}") - - # Copy given input in the file - if(FILE_CONTENT) - file(WRITE "${_tmp_file}" "${FILE_CONTENT}") - else() - file(WRITE "${_tmp_file}" "") - endif() - - # Return created file's name to parent - set(${var_name} "${_tmp_file}" PARENT_SCOPE) -endfunction() - - -# ------------------------------------------------------------------------------ -# file_load( -# FILE # Path to the file to load -# OUTPUT # Name of the parent's variable where to store -# # file's content -# MERGE_SPLIT # Ask to reassemble split lines -# ) -# -# This function loads a file's content and store it in a destination list -# variable owned by the caller (i.e. parent) -# -# N.B.: The MERGE_SPLIT option is to force file's split lines to be -# reassembled into a single line. -# A split line is a line ending with the character '\' followed with -# a EOL character (i.e. '\n' special character) -# -# example: -# file_load( -# FILE my_file -# OUTPUT lines -# MERGE_SPLIT -# ) -# message("'${lines}'") -# -# With the following content for 'my_file': -# -# Hello World -# I \ -# love \ -# football -# -# The displayed message will be a list with 2 elements: -# 'Hello World;I love football' -# -- -function(file_load) - parse_arguments("FILE_LOAD" - "MERGE_SPLIT" - "FILE;OUTPUT" - "" - "" - ${ARGN} - ) - - if(NOT FILE_LOAD_FILE OR NOT FILE_LOAD_OUTPUT) - return() - endif() - - # Read file's lines but ensure its ';' characters will not conflict - file(STRINGS "${FILE_LOAD_FILE}" _lines_list NEWLINE_CONSUME) - if(FILE_LOAD_MERGE_SPLIT) - string(REGEX REPLACE "\\\\\n" "" _lines_list "${_lines_list}") - else() - string(REGEX REPLACE "(\\\\\n)" "\\\\\\1" _lines_list "${_lines_list}") - endif() - string(REGEX REPLACE "\n$" "" _lines_list "${_lines_list}") - string(REGEX REPLACE "\n" ";" _lines_list "${_lines_list}") - - set(${FILE_LOAD_OUTPUT} "${_lines_list}" PARENT_SCOPE) -endfunction() - -# ------------------------------------------------------------------------------ -# file_edit( -# FILE # of files to edit -# RULE # Edit rule to apply to given files -# # Filter for lines the rule has to be applied to -# # Regular expression to line's part to be edited -# # Regular expression for modification to apply -# MERGE_SPLIT # Ask to reassemble split lines -# ) -# -# This function is used to edit the input files using the given rules -# A rule use the following syntax -# -# -# N.B.: To provide more than one RULE, just add a new RULE line in your call -# -# example: -# file_edit( -# FILE my_file -# RULE "" "o" "O" -# RULE "fOOtball" "ll" "L2" -# ) -# -# With the following content for 'my_file': -# -# Hello World -# I love football -# -# The resulting file's content will be: -# -# HellO WOrld -# I lOve fOOtbaL2 -# --- -function(file_edit) - parse_arguments("FILE_EDIT" - "MERGE_SPLIT" - "" - "FILE" - "RULE" - ${ARGN} - ) - - if(NOT FILE_EDIT_RULE OR NOT FILE_EDIT_FILE) - return() - endif() - - if(FILE_EDIT_MERGE_SPLIT) - set(FILE_EDIT_MERGE_SPLIT "MERGE_SPLIT") - endif() - - # Aggregate all rules filter to speed up line validation - unset(_line_filter) - foreach(_rule IN LISTS FILE_EDIT_RULE) - list(GET _rule 0 _rule) - if(_rule) - list(APPEND _line_filter "${_rule}") - endif() - endforeach() - string(REPLACE ";" "|" _line_filter "(${_line_filter})") - - foreach(_file IN LISTS FILE_EDIT_FILE) - # Check file exists - if(NOT EXISTS "${_file}") - message(FATAL_ERROR "Cannot access to file '${_file}'") - endif() - - # Load the current file's content - file_load(FILE "${_file}" OUTPUT _lines_list ${FILE_EDIT_MERGE_SPLIT}) - - # Patch and save file's line - set(_tmp_file "${_file}.new") - file(WRITE "${_tmp_file}") - foreach(_line IN LISTS _lines_list) - if(_line AND _line MATCHES "${_line_filter}") - foreach(_rule IN LISTS FILE_EDIT_RULE) - list(GET _rule 0 _filter) - if(NOT _filter OR _line MATCHES "${_filter}") - list(GET _rule 1 _find) - list(LENGTH _rule _replace) - if(_replace GREATER 2) - list(GET _rule 2 _replace) - else() - unset(_replace) - endif() - string(REGEX REPLACE "${_find}" "${_replace}" _line "${_line}") - endif() - endforeach() - endif() - if(NOT FILE_EDIT_MERGE_SPLIT) - string(REGEX REPLACE "\\\\\\\\$" "\\\\" _line "${_line}") - endif() - file(APPEND "${_tmp_file}" "${_line}\n") - endforeach() - - # Replace file with its patched version - file(RENAME "${_file}" "${_file}.sav") - file(RENAME "${_tmp_file}" "${_file}") - endforeach() -endfunction() diff --git a/cmake/common/HelpersArguments.cmake b/cmake/common/HelpersArguments.cmake deleted file mode 100644 index a54f743c..00000000 --- a/cmake/common/HelpersArguments.cmake +++ /dev/null @@ -1,107 +0,0 @@ -# -# Define some helpers to handle function/macro arguments -# -if(__H_HELPER_ARGUMENTS_INCLUDED) - return() -endif() -set(__H_HELPER_ARGUMENTS_INCLUDED TRUE) - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== -# ------------------------------------------------------------------------------ -# parse_arguments( -# # Prefix that will be added to created variable name -# # List of boolean options -# # List of single value options -# # List of multiple values options -# # List of complex values (single and nested multiple) options -# # Arguments to match against provided options -# ) -# -# Parse the given arguments and dispatches them among the specified fields -# -# All parsed arguments will be stored in their respective created variable -# of the form _ where will be the name of the associated -# option. -# N.B.: All the unparsed arguments will be stored in created variable named -# _ARGN. -# -- -function(parse_arguments prefix options singles lists maps) - # Prepare returned values - set(fields_list ${options} ${singles} ${lists} ${maps}) - foreach(field ARGN ${fields_list}) - unset(${prefix}_${field}) - endforeach() - - # Parse each arguments - unset(current_field) - foreach(arg ${ARGN}) - # Test if current argument is a field's keyword - list(FIND fields_list "${arg}" field_index) - if(field_index GREATER -1) - # Before changing field, store value of previous one - if(current_field AND tmp_list) - if(field_type STREQUAL MAP) - string(REPLACE ";" "\\;" tmp_list "${tmp_list}") - endif() - list(APPEND ${prefix}_${current_field} "${tmp_list}") - unset(tmp_list) - endif() - - # Test if new field is an option - list(FIND options "${arg}" field_index) - if(NOT field_index EQUAL -1) - # This field need do not depend on other args. No need for buffering - # Just set its value to ON - set(${prefix}_${arg} ON) - unset(current_field) - else() - # Setup the name of field to populate - set(current_field ${arg}) - - # Test if new field is a single value - list(FIND singles "${arg}" field_index) - if(NOT field_index EQUAL -1) - unset(field_type) - else() - # Test if new field is a multiple value - list(FIND lists "${arg}" field_index) - if(field_index GREATER -1) - set(field_type LIST) - else() - # Last choice, field is a map (i.e. multiple list) - set(field_type MAP) - endif() - endif() - endif() - else() - # Store current argument in its destination - if(current_field) - if(field_type) - list(APPEND tmp_list ${arg}) - else() - set(${prefix}_${current_field} ${arg}) - unset(current_field) - endif() - else() - # No field currently selected. Default destination is ARGN - list(APPEND ${prefix}_ARGN ${arg}) - endif() - endif() - endforeach() - - # Flush parse cache ... if any - if(current_field AND tmp_list) - if(field_type STREQUAL MAP) - string(REPLACE ";" "\\;" tmp_list "${tmp_list}") - endif() - list(APPEND ${prefix}_${current_field} "${tmp_list}") - endif() - - # Export created variables to parent's scope - foreach(field ARGN ${fields_list}) - set(${prefix}_${field} "${${prefix}_${field}}" PARENT_SCOPE) - endforeach() - -endfunction() diff --git a/cmake/common/HelpersIdeTarget.cmake b/cmake/common/HelpersIdeTarget.cmake deleted file mode 100644 index e08c3a99..00000000 --- a/cmake/common/HelpersIdeTarget.cmake +++ /dev/null @@ -1,348 +0,0 @@ -# -# Define some helpers for IDE's Groups management -# -if(__H_HELPER_IDE_GROUP_INCLUDED) - return() -endif() -set(__H_HELPER_IDE_GROUP_INCLUDED TRUE) - -# === -# === Include some external files -# === -include(CMakeParseArguments) - -# === -# === Set up some parameters -# === - -# --- Enable IDE to display projects' and files' groups -set_property(GLOBAL PROPERTY USE_FOLDERS ON) - -# === -# === Define helper functions -# === - -# --- -# --- add_target( -# --- TYPE [] (EXECUTABLE or LIBRARY followed by optional flags [WIN32|MACOSX_BUNDLE|SHARED|MODULE|STATIC|EXCLUDE_FROM_ALL]) -# --- FILES -# --- [PREFIX_SKIP ] (default to '(\./)?src') -# --- [SOURCE_GROUP_NAME ] (default to 'Source Files') -# --- [HEADER_GROUP_NAME ] (default to 'Header Files') -# --- [RESOURCE_GROUP_NAME ] (default to 'Resource Files') -# --- [SOURCE_FILTER ] (default to '\.(c(\+\+|xx|pp|c)?|C|M|mm)') -# --- [HEADER_FILTER ] (default to '\.h(h|m|pp|\+\+)?') -# --- [GROUP ] (default to 'Executable' or 'Libraries') -# --- [LABEL ] -# --- [LINKS ] -# --- [DEPENDS ] -# --- [DEFINITIONS ] -# --- ) -# --- -# --- N.B.: is the name of the Target to create -# --- is a set of flags specific to the Target (cf documentation of 'add_executable' and 'add_library' fro more precisions) -# --- -# --- Creates a target (executable or library) whose files will be grouped into -# --- IDE's virtual folders (sources, headers and resources) -# --- -# --- Example: -# --- add_target(myTargetLib -# --- TYPE -# --- LIBRARY STATIC -# --- FILES -# --- "src/files/gui/main.cpp" -# --- "src/files/gui/dialog.cpp" -# --- "src/files/tools/convert.cpp" -# --- "src/files/gui/dialog.h" -# --- "src/files/tools/convert.h" -# --- PREFIX_SKIP -# --- "src/files" -# --- DEFINITIONS -# --- MY_DEF1=VAL1 -# --- MY_DEF2=VAL2 -# --- MY_NO_VAL_DEF -# --- ) -# --- -# --- will create an target for an executable named "myTarget". For the IDE, the -# --- target will be composed of virtual groups tree that hold the files: -# --- myTargetLib -# --- |- Source Files -# --- | |- gui -# --- | | |- main.cpp -# --- | | \- dialog.cpp -# --- | | -# --- | \- tools -# --- | \- convert.cpp -# --- | -# --- \- HeaderFiles -# --- |- gui -# --- | \- dialog.h -# --- | -# --- \- tools -# --- \- convert.h -# --- -# --- N.B.: If the PREFIX_SKIP parameter is omitted, then the above result would -# --- be: -# --- myTargetLib -# --- |- Source Files -# --- | \- files -# --- | |- gui -# --- | | |- main.cpp -# --- | | \- dialog.cpp -# --- | | -# --- | \- tools -# --- | \- convert.cpp -# --- | -# --- \- HeaderFiles -# --- \- files -# --- |- gui -# --- | \- dialog.h -# --- | -# --- \- tools -# --- \- convert.h -function(add_target _target_name) - # --- Parse arguments - set(exec_options "WIN32;MACOSX_BUNDLE") - set(lib_options "STATIC;SHARED;MODULE") - - CMake_Parse_Arguments("_ATG" - "${exec_options};${lib_options};EXCLUDE_FROM_ALL" - "TYPE;LABEL;GROUP;PREFIX_SKIP;SOURCE_GROUP_NAME;HEADER_GROUP_NAME;RESOURCE_GROUP_NAME;SOURCE_FILTER;HEADER_FILTER" - "FILES;LINKS;DEPENDS;DEFINITIONS" - ${ARGN}) - - list(APPEND _ATG_FILES "${_ATG_UNPARSED_ARGUMENTS}") - - # --- Check parsed arguments - if(NOT _target_name) - message(FATAL_ERROR "No target specified") - endif() - - if(NOT _ATG_TYPE) - message(FATAL_ERROR "No type has been specified to target '${_target_name}'") - endif() - string(TOUPPER "${_ATG_TYPE}" _ATG_TYPE) - - if((NOT _ATG_TYPE STREQUAL "EXECUTABLE") AND (NOT _ATG_TYPE STREQUAL "LIBRARY")) - message(FATAL_ERROR "Invalid type '${_ATG_TYPE}' specified to target '${_target_name}'") - endif() - - if(NOT _ATG_FILES) - message(FATAL_ERROR "No files specified for target '${_target_name}'") - endif() - - if(NOT _ATG_PREFIX_SKIP) - set(_ATG_PREFIX_SKIP "(\\./)?src") - endif() - - if(NOT _ATG_SOURCE_GROUP_NAME) - set(_ATG_SOURCE_GROUP_NAME "Source Files") - endif() - - if(NOT _ATG_HEADER_GROUP_NAME) - set(_ATG_HEADER_GROUP_NAME "Header Files") - endif() - - if(NOT _ATG_RESOURCE_GROUP_NAME) - set(_ATG_RESOURCE_GROUP_NAME "Resource Files") - endif() - - if(NOT _ATG_SOURCE_FILTER) - set(_ATG_SOURCE_FILTER "\\.(c(\\+\\+|xx|pp|c)?|C|M|mm)") - endif() - - if(NOT _ATG_HEADER_FILTER) - set(_ATG_HEADER_FILTER "\\.h(h|m|pp|\\+\\+)?") - endif() - - set(_ATG_TARGET_OPTIONS) - foreach(_option ${exec_options}) - if(_ATG_${_option}) - if(NOT _ATG_TYPE STREQUAL "EXECUTABLE") - message(FATAL_ERROR "Invalid option '${_option}' for '${_ATG_TYPE}' target '${_target_name}'") - endif() - list(APPEND _ATG_TARGET_OPTIONS ${_option}) - endif() - endforeach() - - foreach(_option ${lib_options}) - if(_ATG_${_option}) - if(NOT _ATG_TYPE STREQUAL "LIBRARY") - message(FATAL_ERROR "Invalid option '${_option}' for '${_ATG_TYPE}' target '${_target_name}'") - endif() - list(APPEND _ATG_TARGET_OPTIONS ${_option}) - endif() - endforeach() - - if(_ATG_EXCLUDE_FROM_ALL) - list(APPEND _ATG_TARGET_OPTIONS EXCLUDE_FROM_ALL) - endif() - - # --- Link each files to a filter/group - foreach(_file_path ${_ATG_FILES}) - get_filename_component(_sub_group "${_file_path}" PATH) - if (_ATG_PREFIX_SKIP) - string(REGEX REPLACE "^${_ATG_PREFIX_SKIP}/*" "" _sub_group "${_sub_group}") - endif() - - if(_file_path MATCHES "^.*${_ATG_SOURCE_FILTER}$") - set(_sub_group "${_ATG_SOURCE_GROUP_NAME}/${_sub_group}") - elseif(_file_path MATCHES "^.*${_ATG_HEADER_FILTER}$") - set(_sub_group "${_ATG_HEADER_GROUP_NAME}/${_sub_group}") - else() - set(_sub_group "${_ATG_RESOURCE_GROUP_NAME}/${_sub_group}") - endif() - - if(MSVC) - string(REPLACE "/" "\\" _sub_group "${_sub_group}") - endif() - source_group("${_sub_group}" FILES "${_file_path}") - endforeach() - - # --- Create Target - if(_ATG_TYPE STREQUAL "EXECUTABLE") - add_executable(${_target_name} ${_ATG_TARGET_OPTIONS} - ${_ATG_FILES} - ) - if(NOT _ATG_GROUP) - set(_ATG_GROUP "Executables") - endif() - else() - add_library(${_target_name} ${_ATG_TARGET_OPTIONS} - ${_ATG_FILES} - ) - if(NOT _ATG_GROUP) - set(_ATG_GROUP "Libraries") - endif() - endif() - - # --- Declare target's dependencies - if(_ATG_DEPENDS) - add_dependencies(${_target_name} ${_ATG_DEPENDS}) - endif() - - # --- Declare target's links - if(_ATG_LINKS) - target_link_libraries(${_target_name} ${_ATG_LINKS}) - endif() - - # -- Setup some target's properties - set_property(TARGET "${_target_name}" PROPERTY FOLDER "${_ATG_GROUP}") - - if(_ATG_LABEL) - set_property(TARGET "${_target_name}" PROPERTY PROJECT_LABEL "${_ATG_LABEL}") - endif() - - if(_ATG_DEFINITIONS) - get_property(_target_definitions TARGET "${_target_name}" PROPERTY COMPILE_DEFINITIONS) - set_property(TARGET "${_target_name}" PROPERTY COMPILE_DEFINITIONS ${_target_definitions} ${_ATG_DEFINITIONS}) - endif() - -endfunction(add_target) - -# --- -# --- project_group( [ | TARGET LABEL ]) -# --- -# --- Set target(s) as being hold by a specific project group -# --- -# --- A Target associated to a tree will appear under the associated project group in the IDE. -# --- -# --- Example: -# --- project_group( -# --- "tools/commandlines" -# --- TARGET "myTarget" LABEL "theTool" -# --- -# --- ) -# --- -# --- will create a virtual project group tree to hold the target: -# --- tools \ -# --- | | -> Create group tree -# --- \- commandlines / -# --- | -# --- \- theTool | -> Displayed name for the target -# --- -function(project_group _group) - CMake_Parse_Arguments("_PG" "" "TARGET;LABEL" "" ${ARGN}) - if(NOT _group) - message(FATAL_ERROR "No group name/path specified") - endif() - - if(_PG_TARGET AND _PG_LABEL AND (NOT _PG_UNPARSED_ARGUMENTS)) - if (NOT TARGET ${_PG_TARGET}) - message(FATAL_ERROR "Invalid target's name '${_PG_TARGET}' added to project group '${_group}'") - endif() - set_property(TARGET "${_PG_TARGET}" PROPERTY FOLDER "${_group}") - set_property(TARGET "${_PG_TARGET}" PROPERTY PROJECT_LABEL "${_PG_LABEL}") - elseif((NOT _PG_TARGET) AND (NOT _PG_LABEL) AND _PG_UNPARSED_ARGUMENTS) - foreach(_tgt ${_PG_UNPARSED_ARGUMENTS}) - if (NOT TARGET ${_tgt}) - message(FATAL_ERROR "Invalid target's name '${_tgt}' added to project group '${_group}'") - endif() - set_property(TARGET "${_tgt}" PROPERTY FOLDER "${_group}") - endforeach() - else() - message(FATAL_ERROR "Invalid target parameters '${ARGN}' while added to project group '${_group}'") - endif() - -endfunction(project_group) - -# --- -# --- file_group( -# --- FILES -# --- [PREFIX_SKIP ] (default to '(\./)?src') -# --- -# --- Associates a list of files to a virtual group tree -# --- -# --- Example: -# --- file_group("Sources" -# --- PREFIX_SKIP "\\.\\./src" -# --- -# --- FILES -# --- ../src/gui/main.cpp -# --- ../src/gui/dialog.cpp -# --- ../src/tools/convert.cpp -# --- ) -# --- -# --- will create a virtual group tree to hold the files: -# --- Source -# --- |- gui -# --- | |- main.cpp -# --- | \- dialog.cpp -# --- | -# --- \- tools -# --- \- convert.cpp -# --- -# --- N.B.: If the PREFIX_SKIP parameter is omitted, then the above result would had been: -# --- Source -# --- \- .. -# --- \- src -# --- |- gui -# --- | |- main.cpp -# --- | \- dialog.cpp -# --- | -# --- \- tools -# --- \- convert.cpp -# --- -function(file_group _group) - CMake_Parse_Arguments("_FG" "" "PREFIX_SKIP" "FILES" ${ARGN}) - list(APPEND _FG_FILES "${_FG_UNPARSED_ARGUMENTS}") - if(NOT _FG_PREFIX_SKIP) - set(_FG_PREFIX_SKIP "(\\./)?src") - endif() - - foreach(_file_path ${_FG_FILES}) - get_filename_component(_sub_group "${_file_path}" PATH) - if (_FG_PREFIX_SKIP) - string(REGEX REPLACE "^${_FG_PREFIX_SKIP}/*" "" _sub_group "${_sub_group}") - endif() - - if (_group) - set(_sub_group "${_group}/${_sub_group}") - endif() - - if(MSVC) - string(REPLACE "/" "\\" _sub_group "${_sub_group}") - endif() - source_group("${_sub_group}" FILES "${_file_path}") - endforeach() -endfunction(file_group) \ No newline at end of file diff --git a/cmake/common/MultiList.cmake b/cmake/common/MultiList.cmake deleted file mode 100644 index 64c28d63..00000000 --- a/cmake/common/MultiList.cmake +++ /dev/null @@ -1,67 +0,0 @@ -# Implementation of a multi list behavior -# -# The main difference versus a legacy list is that all provided elements will be -# aggregated and then insert as a single element which is, at the end, a list -# -if(__H_MULTI_LIST_INCLUDED) - return() -endif() -set(__H_MULTI_LIST_INCLUDED TRUE) - -# ------------------------------------------------------------------------------ -# multi_list( -# # All the legacy list's command are accepted in addition to -# # APPEND_AT which is used to add new items to an item list -# # present in the multi-list -# # The syntax of the APPEND_AT is: -# # APPEND_AT ... -# -# # The name of the multi-list the command has to be applied to -# -# # The arguments associated to the specified command -# ) -# --- -function(multi_list command name) - string(TOUPPER "${command}" command) - if(command MATCHES "^(INSERT|APPEND_AT)$") - # Extract the destination index from arguments - list(GET ARGN 0 _index) - list(REMOVE_AT ARGN 0) - string(REPLACE ";" "\\;" ARGN "${ARGN}") - - if(${name}) - unset(_tmp_list) - foreach(_item ${${name}}) - string(REPLACE ";" "\\;" _item "${_item}") - - # Check if index reached 0 to add the value - if(_index GREATER -1) - if(_index EQUAL 0) - if(command STREQUAL "INSERT") - list(APPEND _tmp_list "${ARGN}") - else() - set(_item "${_item}\\;${ARGN}") - endif() - endif() - math(EXPR _index "${_index} - 1") - endif() - - # Update destination list with the aggregation of current item's elements - list(APPEND _tmp_list "${_item}") - endforeach() - set(${name} "${_tmp_list}" PARENT_SCOPE) - return() - else() - # Add to underneath list the aggregation of all input elements - list(APPEND ${name} "${ARGN}") - endif() - elseif(command STREQUAL "APPEND") - # Add to underneath list the aggregation of all input elements - string(REPLACE ";" "\\;" ARGN "${ARGN}") - list(${command} ${name} "${ARGN}") - else() - # Forward command to system's list - list(${command} ${name} ${ARGN}) - endif() - set(${name} "${${name}}" PARENT_SCOPE) -endfunction() diff --git a/cmake/common/SystemTools.cmake b/cmake/common/SystemTools.cmake deleted file mode 100644 index 9819c970..00000000 --- a/cmake/common/SystemTools.cmake +++ /dev/null @@ -1,226 +0,0 @@ -# Implementation of tools to perform some system actions -# -if(__H_SYSTEM_TOOLS_INCLUDED) - return() -endif() -set(__H_SYSTEM_TOOLS_INCLUDED TRUE) - -include(HelpersArguments) -include(FileEdit) - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== - -# ------------------------------------------------------------------------------ -# system_run( -# [EXEC [args]] # Command line to execute -# [EVAL [...]] # List of instructions to evaluate -# ) -# -# This function runs (i.e. executes or evaluates) the given commands -function(system_run) - parse_arguments("RUN" - "DEBUG;NO_LOG" - "WORKING_DIR" - "EXEC;EVAL" - "" - ${ARGN} - ) - - # Ensure that NO unparsed arguments has been found - if(RUN_ARGN) - message(FATAL_ERROR "Unparsed arguments found:\n${RUN_ARGN}") - endif() - - # Check a EXEC or an EVAL has been provided - if(NOT RUN_EXEC AND NOT RUN_EVAL) - message(FATAL_ERROR "Neither explicit EXEC nor EVAL specified:\t${ARGN}" - ) - elseif(RUN_EXEC AND RUN_EVAL) - message(FATAL_ERROR "EXEC and EVAL cannot be both specified") - endif() - - if(RUN_EVAL) - # Proceed the EVAL (if any) - system_eval(${RUN_EVAL}) - else() - # Proceed the EXEC - unset(_flags) - foreach(_flag DEBUG NO_LOG) - if(RUN_${_flag}) - list(APPEND _flags ${_flag}) - endif() - endforeach() - - system_execute(${_flags} - WORKING_DIR "${RUN_WORKING_DIR}" - ${RUN_EXEC} - ) - endif() -endfunction() - -# ------------------------------------------------------------------------------ -# system_eval( -# [...] # List of instructions to evaluate -# ) -# -# This function evaluates the given list of cmake's script lines. -function(system_eval) - # Create file unique name - file_create_unique(_tmp_file SUFFIX ".cmake.tmp") - - # Copy given input in the file - file(WRITE "${_tmp_file}" "# Auto generated file for 'eval' emulation\n") - foreach(_line ${ARGN}) - file(APPEND "${_tmp_file}" "${_line}\n") - endforeach() - - # Execute the file and release it - include("${_tmp_file}") - file(REMOVE "${_tmp_file}") -endfunction() - -# ------------------------------------------------------------------------------ -# system_execute( -# NO_LOG # Disable LOG feature. Outputs are not redirected. -# NO_LOG_DUMP # Disable automatic dump of log in case of error -# LOG # Path to the file that will host the execution log -# LOG_VAR # Name of variable that will receive execution log -# DEBUG # Enable debug mode -# WORKING_DIR # Working directory execution should be performed -# COMMAND # Path to the command to execute -# ARGS [ # Options to path through to system_execute(...) function -# ) -# -# This function is a wrapper to SystemTools's system_execute(...) function. -# Its purpose is to wrap the given command, and its specified arguments, -# with a platform's specific build environment where compile tools are all -# available. -# -function(external_execute) - if(NOT (MSVC AND "${CMAKE_GENERATOR}" MATCHES "^Visual Studio [0-9]+")) - system_execute(${ARGN}) - return() - endif() - - # Parse arguments - parse_arguments("WRAP" - "NO_LOG;NO_LOG_DUMP;DEBUG" - "WORKING_DIR;LOG;LOG_VAR;COMMAND" - "ARGS;ENV" - "" - ${ARGN} - ) - - unset(_script_path) - - # Prepare wrapped command - unset(_command_flags) - foreach(_arg NO_LOG NO_LOG_DUMP DEBUG WORKING_DIR LOG LOG_VAR ENV ARGN) - if(WRAP_${_arg}) - list(APPEND _command_flags "${_arg}" "${WRAP_${_arg}}") - endif() - endforeach() - - # Determine Visual Studio's script path - if(CMAKE_CXX_COMPILER) - get_filename_component(_vc_path "${CMAKE_CXX_COMPILER}" PATH) - else() - get_filename_component(_vc_path "${CMAKE_C_COMPILER}" PATH) - endif() - - # Get Visual Studio profile - get_filename_component(_vc_profile "${_vc_path}" NAME) - if("${_vc_profile}" STREQUAL "bin") - set(_vc_profile "32") - endif() - - # Build BAT script to execute input command - set(_debug OFF) - if(WRAP_DEBUG) - set(_debug ON) - endif() - - file_create_unique(_script_path - PREFIX "wrap_" - SUFFIX ".bat" - CONTENT - "@REM Auto generated BAT file - @echo ${_debug} - call \"${_vc_path}/vcvars${_vc_profile}.bat\" && goto :next - exit /b %ERRORLEVEL% - goto :eof - :next - @echo ${_debug} - \"${WRAP_COMMAND}\"" - ) - foreach(_arg IN LISTS WRAP_ARGS) - file(APPEND "${_script_path}" " \"${_arg}\"") - endforeach() - file(APPEND "${_script_path}" "\n") - - if(NOT (_script_path AND EXISTS "${_script_path}")) - external_error("Cannot create wrapper script") - endif() - - if(WRAP_DEBUG) - message(STATUS "*ENVWRAP SCRIPT: ${_script_path}") - endif() - - system_execute( - ${_command_flags} - COMMAND "$ENV{COMSPEC}" - ARGS "/c" "${_script_path}" - ) - - if(NOT WRAP_DEBUG) - file(REMOVE "${_script_path}") - endif() -endfunction() - -# ------------------------------------------------------------------------------ -# external_parse_arguments( -# # Name of the package -# # List of flag directives to parse out -# # List of single value directives to parse out -# # List of multiple values directives to parse out -# # List of maps directives to parse out -# ) -# -# This function parses the packages' arguments with the given list of -# directives' name. -# The packages' arguments are retrieved from the directive FLAGS present (if -# any) in the global variable _FIND_COMPONENTS. -# -# Once executed, the following variables will be available in the scope of the -# caller: -# - _FIND_COMPONENTS: -# It is updated to contain ONLY components list -# -# - _: -# Contains the value(s) of the found directive -# -# - _NO_COMPONENTS: -# If no components have been specified and NO_COMPONENT directive is found -# from _FIND_COMPONENTS variable, then this variable will be -# specified to inform caller that no components must be compiled. -# -macro(external_parse_arguments _name _flags _singles _lists _maps) - # Parse package's FIND_COMPONENTS global variable to filter out - # package's compilation flags - string(TOUPPER "${_name}" _name_upper) - parse_arguments("${_name_upper}" - "NO_COMPONENTS" - "" - "FLAGS;WITH_COMPONENTS" - "" - ${${_name}_FIND_COMPONENTS} - ) - - if(${_name_upper}_WITH_COMPONENTS) - list(APPEND ${_name_upper}_ARGN "${${_name_upper}_WITH_COMPONENTS}") - unset(${_name_upper}_WITH_COMPONENTS) - endif() - - # Reset package's FIND_COMPONENTS variable - set(${_name}_FIND_COMPONENTS ${${_name_upper}_ARGN}) - set(${_name}_FIND_COMPONENTS ${${_name_upper}_ARGN} PARENT_SCOPE) - - # Parse package's (real) arguments - if(${_name_upper}_FLAGS) - parse_arguments("${_name_upper}" - "${_flags}" - "${_singles}" - "${_lists}" - "${_maps}" - ${${_name_upper}_FLAGS} - ) - - if(${${_name_upper}_ARGN}) - external_error("Unexpected flags found: '${${_name_upper}_ARGN}'") - endif() - endif() - - if(${_name}_DEBUG) - external_debug("external_parse_arguments") - foreach(_var NO_COMPONENTS FLAGS WITH_COMPONENTS ${_name}_FIND_COMPONENTS - ${_flags} ${_singles} ${_lists} ${_maps}) - set(_var "${_name_upper}_${_var}") - external_debug("${_var} = ${${_var}}") - endforeach() - endif() - - unset(_name_upper) -endmacro() - -# ------------------------------------------------------------------------------ -# external_error( -# # The list of error messages to display -# ) -# -function(external_error) - if(NOT PKG_NAME AND EXTERNALS_PKG_NAME) - set(PKG_NAME "${EXTERNALS_PKG_NAME}") - endif() - - message(FATAL_ERROR "[${PKG_NAME}] # " ${ARGN}) -endfunction() - -# ------------------------------------------------------------------------------ -# external_debug( -# # The list of error messages to display -# ) -# -function(external_debug) - if(NOT PKG_NAME AND EXTERNALS_PKG_NAME) - set(PKG_NAME "${EXTERNALS_PKG_NAME}") - endif() - - if(NOT ${PKG_NAME}_DEBUG) - return() - endif() - - if(PKG_NAME) - set(PKG_NAME "[${PKG_NAME}] ") - endif() - - message("${PKG_NAME}* " ${ARGN}) -endfunction() - -# ------------------------------------------------------------------------------ -# external_log( -# # The list of log messages linked to package -# ) -# -function(external_log) - if(NOT PKG_NAME AND EXTERNALS_PKG_NAME) - set(PKG_NAME "${EXTERNALS_PKG_NAME}") - endif() - - if(NOT PKG_NAME OR ${PKG_NAME}_DEBUG OR NOT ${PKG_NAME}_FIND_QUIETLY) - if(PKG_NAME) - set(PKG_NAME "[${PKG_NAME}] ") - endif() - - message("${_EXTERNALS_LOG}" "${PKG_NAME}" ${ARGN}) - endif() -endfunction() - -# ------------------------------------------------------------------------------ -# externals_relative_path( -# # Destination variable where to store subdirectory relative path -# # Path to directory that has to be converted into a relative -# # subdirectory path -# ) -# -# This function transform the given directory path into a relative path anchored -# to value of CMAKE_SOURCE_DIR. -# -function(externals_relative_path var_out dir_path) - # Reset destination variable - set(${var_out} "" PARENT_SCOPE) - # Remove CMAKE_SOURCE_DIR from given path - string(REPLACE "${CMAKE_SOURCE_DIR}" "" _tmp "${dir_path}") - if(_tmp AND NOT "${_tmp}" STREQUAL "${dir_path}") - # Remove any trailing '/' characters before returning result - string(REGEX REPLACE "^/*([^/].*[^/])/*$" "\\1" _tmp "${_tmp}") - set(${var_out} "${_tmp}" PARENT_SCOPE) - endif() -endfunction() - -# ------------------------------------------------------------------------------ -# external_find_file( -# # Output variable where to store full path of -# # found file -# NAMES # List of reg-ex patterns of file to found -# PATHS # List of paths where search must be performed -# [PATH_SUFFIXES # List of suffixes to append to each search -# # paths -# ) -# -# This function searches for a file matching given patterns in the specified -# folders. -# -function(external_find_file var_out) - # Reset output variable - set(${var_out} "NOTFOUND" PARENT_SCOPE) - - # Parse arguments - parse_arguments("PKG_FILE" - "" - "" - "NAMES;PATHS;PATH_SUFFIXES" - "" - ${ARGN} - ) - - if(NOT PKG_FILE_NAMES) - external_error("No file names to search for") - endif() - - if(NOT PKG_FILE_PATHS) - external_error("No file paths to search from") - endif() - - # Build list of matching path - unset(_paths_list) - foreach(_path IN LISTS PKG_FILE_PATHS) - if(_path) - list(APPEND _paths_list "${_path}") - foreach(_suffix IN LISTS PKG_FILE_PATH_SUFFIXES) - list(APPEND _paths_list "${_path}/${_suffix}") - endforeach() - endif() - endforeach() - - # Search for the specified files - list_join(PKG_FILE_NAMES "|" _files_pattern) - foreach(_path IN LISTS _paths_list) - file(GLOB _files_list RELATIVE "${_path}" "${_path}/*") - if("${_files_list}" MATCHES "^(.*;|)(${_files_pattern})(;.*|)$") - set(${var_out} "${_path}/${CMAKE_MATCH_2}" PARENT_SCOPE) - return() - endif() - endforeach() -endfunction() - -# ------------------------------------------------------------------------------ -# external_teardown_build_context( -# [NAME] # Name of the targeted package -# [CACHE_SUFFIX ] # Suffix to use to identify the cache of already -# ) -# -# This function stores the list of components and extra flags to the package's -# specific cache. This cache is later used by 'external_setup_build_context()' -# to retrieve list of already available components and extra flags. -# -# N.B.: The components list and the extra flags list is retrieved from caller's -# scope. -# -function(external_teardown_build_context) - # Parse arguments - parse_arguments("PKG" - "" - "NAME;CACHE_SUFFIX" - "" - "" - ${ARGN} - ) - - # Check arguments - if(NOT PKG_NAME) - if(NOT PKG_ARGN) - external_error("No name provided for external package") - endif() - list(GET PKG_ARGN 0 PKG_NAME) - list(REMOVE_AT PKG_ARGN 0) - endif() - string(TOLOWER "${PKG_NAME}" PKG_NAME_LOWER) - string(TOUPPER "${PKG_NAME}" PKG_NAME_UPPER) - - if(NOT PKG_CACHE_SUFFIX AND PKG_ARGN) - list(GET PKG_ARGN 0 PKG_CACHE_SUFFIX) - list(REMOVE_AT PKG_ARGN 0) - endif() - - set(PKG_CACHE_VAR "${PKG_NAME_UPPER}_CACHE_COMPONENTS_${PKG_CACHE_SUFFIX}") - set(PKG_CACHE_FLAGS_VAR "${PKG_NAME_UPPER}_CACHE_FLAGS_${PKG_CACHE_SUFFIX}") - - # Store extra flags cache - if(${PKG_NAME_UPPER}_EXTRA_FLAGS) - set(${PKG_CACHE_FLAGS_VAR} "${${PKG_NAME_UPPER}_EXTRA_FLAGS}" - CACHE INTERNAL "List of ${PKG_NAME}'s extra flags to build components" - ) - endif() - - # Store components cache - set(_cache_info "List of ${PKG_NAME}'s already built components") - if(${PKG_NAME_UPPER}_COMPONENTS) - set(${PKG_CACHE_VAR} "${${PKG_NAME_UPPER}_COMPONENTS}" - CACHE INTERNAL "${_cache_info}" - ) - elseif(NOT ${PKG_NAME_UPPER}_NO_COMPONENTS) - set(${PKG_CACHE_VAR} "-ALL-" CACHE INTERNAL "${_cache_info}") - endif() - - if(${PKG_NAME}_DEBUG) - external_debug("external_set_cache_components:") - external_debug(" PKG_CACHE_VAR : ${PKG_CACHE_VAR}") - external_debug(" ${PKG_CACHE_VAR} : ${${PKG_CACHE_VAR}}") - external_debug(" PKG_CACHE_FLAGS_VAR : ${PKG_CACHE_FLAGS_VAR}") - external_debug(" ${PKG_CACHE_FLAGS_VAR}: ${${PKG_CACHE_FLAGS_VAR}}") - endif() -endfunction() - -# ------------------------------------------------------------------------------ -# external_setup_build_context( -# [NAME] # Name of the targeted package -# [CACHE_SUFFIX ] # Suffix to use to identify the cache of already -# # built components -# ) -# -# This function sets up the package's environment for the build process. -# It will create the following variables in the caller's scope: -# - _EXTRA_FLAGS: -# The complete list of extra (i.e. raw) flags to pass-through to package's -# low level build scripts -# - _COMPONENTS: -# The complete list of components that are requested for this build process -# of package -# - _NO_COMPONENTS: -# Boolean flag indicating that package must be build WITHOUT any components. -# - _NEED_BUILD: -# Boolean flag indicating if a build is really needed (i.e. new components -# have been requested or some new extra flags have been defined). -# -function(external_setup_build_context) - # Parse arguments - parse_arguments("PKG" - "" - "NAME;CACHE_SUFFIX" - "" - "" - ${ARGN} - ) - - # Check arguments - if(NOT PKG_NAME) - if(NOT PKG_ARGN) - external_error("No name provided for external package") - endif() - list(GET PKG_ARGN 0 PKG_NAME) - list(REMOVE_AT PKG_ARGN 0) - endif() - string(TOLOWER "${PKG_NAME}" PKG_NAME_LOWER) - string(TOUPPER "${PKG_NAME}" PKG_NAME_UPPER) - - if(NOT PKG_CACHE_SUFFIX AND PKG_ARGN) - list(GET PKG_ARGN 0 PKG_CACHE_SUFFIX) - list(REMOVE_AT PKG_ARGN 0) - endif() - - set(PKG_CACHE_VAR "${PKG_NAME_UPPER}_CACHE_COMPONENTS_${PKG_CACHE_SUFFIX}") - set(PKG_CACHE_FLAGS_VAR "${PKG_NAME_UPPER}_CACHE_FLAGS_${PKG_CACHE_SUFFIX}") - - if(${PKG_NAME}_DEBUG) - external_debug("external_setup_build_context:") - external_debug(" PKG_CACHE_VAR : ${PKG_CACHE_VAR}") - external_debug(" ${PKG_CACHE_VAR} : ${${PKG_CACHE_VAR}}") - external_debug(" PKG_CACHE_FLAGS_VAR : ${PKG_CACHE_FLAGS_VAR}") - external_debug(" ${PKG_CACHE_FLAGS_VAR}: ${${PKG_CACHE_FLAGS_VAR}}") - foreach(_var COMPONENTS NO_COMPONENTS EXTRA_FLAGS) - set(_var "${PKG_NAME_UPPER}_${_var}") - external_debug(" ${_var} : ${${_var}}") - endforeach() - endif() - - # Assume no additional build is needed - set(_need_build OFF) - - # Check no new extra flags have been provided - if(${PKG_CACHE_FLAGS_VAR} OR ${PKG_NAME_UPPER}_EXTRA_FLAGS) - set(_flags_to_use "${${PKG_CACHE_FLAGS_VAR}}") - if(${PKG_NAME_UPPER}_EXTRA_FLAGS) - string(REPLACE "${${PKG_NAME_UPPER}_EXTRA_FLAGS}" "" _tmp - "${${PKG_CACHE_FLAGS_VAR}}" - ) - if("${_tmp}" STREQUAL "${${PKG_CACHE_FLAGS_VAR}}") - list(APPEND _flags_to_use "${${PKG_NAME_UPPER}_EXTRA_FLAGS}") - list_join(_flags_to_use " " _tmp) - external_log("Extra Flags: ${_tmp}") - set(_need_build ON) - endif() - endif() - - set(${PKG_NAME_UPPER}_EXTRA_FLAGS "${_flags_to_use}" PARENT_SCOPE) - endif() - - # Check if all package's components have already been built - if(${PKG_NAME_UPPER}_NO_COMPONENTS) - set(_need_build ON) - set(${PKG_NAME_UPPER}_COMPONENTS "" PARENT_SCOPE) - elseif(NOT "${PKG_CACHE_VAR}" STREQUAL "-ALL-") - # Extract newly specified components from given list - unset(_components_to_build) - if(${PKG_NAME_UPPER}_COMPONENTS) - # Create the list of components to build - list(REMOVE_DUPLICATES ${PKG_NAME_UPPER}_COMPONENTS) - list(APPEND _components_to_build "${${PKG_NAME_UPPER}_COMPONENTS}") - if(${PKG_CACHE_VAR}) - list(REMOVE_ITEM _components_to_build ${${PKG_CACHE_VAR}}) - endif() - endif() - - # Check if new components have to be built - set(_build_message "Available components: ") - if(_components_to_build OR _need_build) - # Create reg-ex filter for the components to build - list_join(_components_to_build "|" _components_filter) - - # Create the full list of components to be present in to build libraries - list(APPEND _components_to_build ${${PKG_CACHE_VAR}}) - list(SORT _components_to_build) - - # Create composed message with new and already available components - if(_components_filter) - string(REGEX REPLACE - "(${_components_filter})" - "*\\1" - _displayed_components_list - "${_components_to_build}" - ) - else() - set(_displayed_components_list "${_components_to_build}") - endif() - list_join(_displayed_components_list ", " _displayed_components_list) - - # Add new message in list - list(APPEND _build_message "${_displayed_components_list}") - elseif(NOT ${PKG_NAME_UPPER}_COMPONENTS) - list(APPEND _build_message "-ALL-") - endif() - - if(_components_to_build OR NOT ${PKG_NAME_UPPER}_COMPONENTS) - external_log(${_build_message}) - set(_need_build ON) - endif() - - set(${PKG_NAME_UPPER}_COMPONENTS "${_components_to_build}" PARENT_SCOPE) - endif() - - - set(${PKG_NAME_UPPER}_NEED_BUILD "${_need_build}" PARENT_SCOPE) -endfunction() - - -# ------------------------------------------------------------------------------ -# external_unpack_archive( -# SOURCE # Path to archive to expand -# DESTINATION # Path to destination directory where to -# # expand archive -# ) -# -# This function expands the given archive in the specified destination -# -function(external_unpack_archive) - # Parse arguments - parse_arguments("PACK" - "" - "SOURCE;DESTINATION" - "" - "" - ${ARGN} - ) - - if(NOT (PACK_SOURCE AND EXISTS "${PACK_SOURCE}")) - external_error("Invalid source") - endif() - - if(NOT (PACK_DESTINATION AND IS_DIRECTORY "${PACK_DESTINATION}")) - external_error("Invalid destination") - endif() - - # Find tools needed for unpacking archives - unset(_search_paths) - unset(_unpacker_name) - unset(_unpacker CACHE) - if(CMAKE_HOST_WIN32) - set(_unpacker_name "winrar") - else() - if("${PACK_SOURCE}" MATCHES "^.*\\.zip$") - set(_unpacker_name "unzip") - else() - set(_unpacker_name "tar") - endif() - endif() - - find_program(_unpacker "${_unpacker_name}" "${_search_paths}") - if(NOT _unpacker) - external_error("No '${_unpacker_name}' tool found") - endif() - - # Compute arguments to extract package's source tree from archive - unset(_unpacker_args) - if(CMAKE_HOST_WIN32) - list(APPEND _unpacker_args - "x" # Extract files from archive - "-inul" # Do not prompt dialogue box in case of errors - "-o+" # Always overwrite destination files - "-ibck" # Run in background (i.e. no progress window displayed) - "${PACK_SOURCE}" - ) - else() - list(APPEND _unpacker_args - "x" # Extract files from archive - "-f" # Unpack following file - "${PACK_SOURCE}" - ) - - if("${PACK_SOURCE}" MATCHES "^.*\\.(tgz|tar\\.gz)$") - list(INSERT _unpacker_args 1 "-z") - elseif("${PACK_SOURCE}" MATCHES "^.*\\.(tbz|tar\\.bz2)$") - list(INSERT _unpacker_args 1 "-j") - elseif("${PACK_SOURCE}" MATCHES "^.*\\.(tar\\.Z)$") - list(INSERT _unpacker_args 1 "-Z") - elseif("${PACK_SOURCE}" MATCHES "^.*\\.zip$") - unset(_unpacker_args) - list(APPEND _unpacker_args "${PACK_SOURCE}") - elseif(NOT "${PACK_SOURCE}" MATCHES "^.*\\.tar$") - external_error("Unsuported archive type '${PACK_SOURCE}'") - endif() - endif() - - if(${PKG_NAME}_DEBUG) - external_debug("Archive path '${PACK_SOURCE}':") - external_debug(" extract base = '${PACK_DESTINATION}'") - external_debug(" unpacker = '${_unpacker_name}' [${_unpacker}]") - external_debug(" unpacker args = '${_unpacker_args}'") - endif() - - # Expand archive - system_execute( - WORKING_DIR "${PACK_DESTINATION}" - COMMAND "${_unpacker}" ARGS "${_unpacker_args}" - ) -endfunction() - -# ------------------------------------------------------------------------------ -function(externals_select_expand_dir _var _base_dir) - # Set root path - unset(_expand_dir) - if(EXTERNALS_EXPAND_DIR) - list(APPEND _expand_dir "${EXTERNALS_EXPAND_DIR}") - else() - list(APPEND _expand_dir "${CMAKE_BINARY_DIR}" "src.externals") - endif() - - # Add sub (project relative) path - if(EXTERNALS_USE_RELATIVE_DIR AND _base_dir) - externals_relative_path(_tmp "${_base_dir}") - if(_tmp) - list(APPEND _expand_dir "${_tmp}") - endif() - endif() - - # Create directory - list_join(_expand_dir "/" _expand_dir) - file(MAKE_DIRECTORY "${_expand_dir}") - - # Return path - set(${_var} "${_expand_dir}" PARENT_SCOPE) -endfunction() - -# ------------------------------------------------------------------------------ -# external_search_source_path( -# [NAME] # Name of the package the sources have to be found -# [VAR ] # The name of variable that will receive the path -# [CLUES] # List of package's file names that should be found -# # while searching for package's source path -# ) -# -# This function is used to search and return the path to the directory -# containing the source of an external package. -function(external_search_source_path) - # Parse arguments - parse_arguments("PKG" - "" - "NAME;VAR" - "CLUES" - "" - ${ARGN} - ) - - # Check arguments - if(NOT PKG_NAME) - if(NOT PKG_ARGN) - external_error("No name provided for external package to build") - endif() - list(GET PKG_ARGN 0 PKG_NAME) - list(REMOVE_AT PKG_ARGN 0) - endif() - string(TOLOWER "${PKG_NAME}" PKG_NAME_LOWER) - string(TOUPPER "${PKG_NAME}" PKG_NAME_UPPER) - - if(NOT PKG_VAR) - set(PKG_VAR "${PKG_NAME_UPPER}_SOURCE_DIR") - elseif(${PKG_VAR} AND NOT IS_DIRECTORY "${${PKG_VAR}}") - external_error("Invalid source directory '${${PKG_VAR}}'") - endif() - - if(NOT PKG_CLUES) - if(NOT PKG_ARGN) - external_error("No clues given to search ${PKG_NAME}'s source path") - endif() - set(PKG_CLUES "${PKG_ARGN}") - unset(PKG_ARGN) - endif() - - set(_archive_pattern - "((${PKG_NAME}|${PKG_NAME_LOWER})[-_].*)\\.(t[bg]z|tar(\\.(gz|bz2))?|zip)" - ) - unset(_witness_file) - external_find_file(_witness_file - NAMES - "${PKG_CLUES}" "${_archive_pattern}" - PATHS - "${${PKG_VAR}}" - "${${PKG_NAME_UPPER}_ROOT_DIR}" - "${CMAKE_SOURCE_DIR}/third_party" - "${PROJECT_SOURCE_DIR}/third_party" - "${CMAKE_CURRENT_SOURCE_DIR}/third_party" - PATH_SUFFIXES - ${PKG_NAME} - ${PKG_NAME_LOWER} - ) - - set(${PKG_VAR} "NOTFOUND") - if(_witness_file) - get_filename_component(${PKG_VAR} "${_witness_file}" PATH) - - # Check if found file is an archive - if("${_witness_file}" MATCHES "^.*/(${_archive_pattern})$") - set(_archive_base_name "${CMAKE_MATCH_2}") - external_log("Using package archive: ${_witness_file}") - - # Compute path where to expand archive - externals_select_expand_dir(_archive_expand_dir "${${PKG_VAR}}") - - # Extract package's source tree from archive - if(NOT IS_DIRECTORY "${_archive_expand_dir}/${_archive_base_name}") - external_unpack_archive( - SOURCE "${_witness_file}" - DESTINATION "${_archive_expand_dir}" - ) - - if(NOT IS_DIRECTORY "${_archive_expand_dir}/${_archive_base_name}") - external_error("Cannot locate expanded archive") - endif() - - #Check if some patches have to be applied - file(GLOB _patch_files "${${PKG_VAR}}/patches/*") - if(_patch_files) - find_program(PATCH_PROGRAM "patch") - if(NOT PATCH_PROGRAM) - message(FATAL_ERROR - "Command 'patch' is missing.\nCannot apply patches." - ) - endif() - list(SORT _patch_files) - foreach(_patch IN LISTS _patch_files) - external_log("Applying patch: ${_patch}") - system_execute( - WORKING_DIR "${_archive_expand_dir}/${_archive_base_name}" - COMMAND "${PATCH_PROGRAM}" - ARGS -p 1 -i "${_patch}" - ) - endforeach() - endif() - endif() - - # Ensure extracted files matches the searched package - external_find_file(_witness_file - NAMES - "${PKG_CLUES}" - PATHS - "${_archive_expand_dir}/${_archive_base_name}" - ) - - get_filename_component(${PKG_VAR} "${_witness_file}" PATH) - endif() - endif() - - if(${PKG_NAME}_DEBUG) - external_debug("external_search_source_path:") - external_debug(" ${PKG_VAR}=${${PKG_VAR}}") - endif() - - if(NOT (${PKG_VAR} AND IS_DIRECTORY "${${PKG_VAR}}")) - external_error("Cannot determine ${PKG_NAME}'s source directory (${${PKG_VAR}})") - endif() - - # Return the source path - external_log("Found source path: ${${PKG_VAR}}") - set(${PKG_VAR} "${${PKG_VAR}}" PARENT_SCOPE) -endfunction() - -# ------------------------------------------------------------------------------ -function(externals_select_build_dir _var) - # Compute sub-path for package - unset(_build_dir) - if(EXTERNALS_USE_RELATIVE_DIR AND PKG_SOURCE_DIR) - get_filename_component(_tmp "${PKG_SOURCE_DIR}" PATH) - externals_relative_path(_build_dir "${_tmp}") - list(APPEND _build_dir "${PKG_NAME_LOWER}") - else() - string(SHA1 _build_dir "${CMAKE_CURRENT_LIST_DIR}") - string(SUBSTRING "${_build_dir}" 0 5 _build_dir) - endif() - list_join(_build_dir "/" _build_dir) - - # Prepend main path for package - if(EXTERNALS_BINARY_DIR) - set(_build_dir "${EXTERNALS_BINARY_DIR}/${_build_dir}") - else() - set(_build_dir "${CMAKE_BINARY_DIR}/bin.externals/${_build_dir}") - endif() - - file(MAKE_DIRECTORY "${_build_dir}") - - set(${_var} "${_build_dir}" PARENT_SCOPE) -endfunction() - -# ------------------------------------------------------------------------------ -# externals_build( -# [NO_LOG] # Flag to not redirect logs to file -# [NO_COMPONENTS] # Flag to not compile any components -# [NAME] # Name of the package to build -# [[COMPONENTS] ] # List of components to build -# [SOURCE_DIR ] # The path of the package's sources -# [FLAGS ] # The package's build flags -# [EXTRA_FLAGS ] # Extra (i.e. raw) flags to pass-through -# # to package's low level build scripts -# [SOURCE_SEARCH_CLUES ] # List of package's file names that -# # should be found while searching for -# # package's source path -# ) -# -# This function builds the specified external package. -# In case SOURCE_DIR flag IS NOT specified, an attempt will be performed to -# automatically find package's source path from: -# - ${_ROOT_DIR} -# - ${CMAKE_SOURCE_DIR}/third_party -# - ${PROJECT_SOURCE_DIR}/third_party -# - ${CMAKE_CURRENT_SOURCE_DIR}/third_party -# -# In order to help identifying the package's source path, the file names, -# specified in flag named SOURCE_SEARCH_CLUES, will searched in the paths -# mentioned above. -# -# N.B.: If SOURCE_DIR is specified then SOURCE_SEARCH_CLUES flag is ignored. -# -# In addition, the global variable EXTERNALS_BINARY_DIR can be defined to force -# the root path where ALL the externals have to be built. -# N.B.: If defined, this root directory will be used to store ALL -# package's artefact. -# Otherwise, the main project's binary directory will be used as binary -# root path. - -function(externals_build) - # Parse arguments for package's compilation flags - parse_arguments("PKG" - "NO_LOG;NO_COMPONENTS" - "NAME;SOURCE_DIR" - "FLAGS;EXTRA_FLAGS;SOURCE_SEARCH_CLUES;COMPONENTS" - "" - ${ARGN} - ) - - # Check arguments - if(NOT PKG_NAME) - if(NOT PKG_ARGN) - external_error("No name provided for external package to build") - endif() - list(GET PKG_ARGN 0 PKG_NAME) - list(REMOVE_AT PKG_ARGN 0) - endif() - string(TOLOWER "${PKG_NAME}" PKG_NAME_LOWER) - string(TOUPPER "${PKG_NAME}" PKG_NAME_UPPER) - - if(NOT PKG_COMPONENTS AND PKG_ARGN) - set(PKG_COMPONENTS "${PKG_ARGN}") - unset(PKG_ARGN) - endif() - - if(PKG_COMPONENTS AND PKG_NO_COMPONENTS) - external_error("COMPONENTS and NO_COMPONENTS cannot stand together") - endif() - - if(PKG_COMPONENTS) - list(SORT PKG_COMPONENTS) - list_join(PKG_COMPONENTS ", " _tmp) - elseif(NOT PKG_NO_COMPONENTS) - set(_tmp "-ALL-") - else() - set(_tmp "-NONE-") - endif() - external_log("Looking for components: ${_tmp}") - - # Check/Search for source directory - external_search_source_path( - NAME "${PKG_NAME}" - VAR PKG_SOURCE_DIR - CLUES ${PKG_SOURCE_SEARCH_CLUES} - ) - - if(NOT (PKG_SOURCE_DIR AND EXISTS "${PKG_SOURCE_DIR}")) - external_error("No source path provided for package ${PKG_NAME}") - endif() - - if(${PKG_NAME}_DEBUG) - external_debug("PKG_NAME : ${PKG_NAME}") - external_debug("PKG_SOURCE_DIR : ${PKG_SOURCE_DIR}") - external_debug("PKG_COMPONENTS : ${PKG_COMPONENTS}") - external_debug("PKG_NO_COMPONENTS : ${PKG_NO_COMPONENTS}") - external_debug("PKG_FLAGS : ${PKG_FLAGS}") - external_debug("PKG_EXTRA_FLAGS : ${PKG_EXTRA_FLAGS}") - endif() - - # Set-up Package's build directory - externals_select_build_dir(PKG_BUILD_DIR) - if(${PKG_NAME}_DEBUG) - external_debug("PKG_BUILD_DIR: ${PKG_BUILD_DIR}") - endif() - - # Execute External Package's CMake build script - unset(_args) - if(CMAKE_GENERATOR) - list(APPEND _args "-G" "${CMAKE_GENERATOR}") - endif() - - if(CMAKE_GENERATOR_TOOLSET) - list(APPEND _args "-T" "${CMAKE_GENERATOR_TOOLSET}") - endif() - - set(_vars_list - CMAKE_BUILD_TYPE - ${PKG_NAME}_DEBUG - ${PKG_NAME}_FIND_QUIETLY - ) - foreach(_var IN LISTS _vars_list) - if(${_var}) - list(APPEND _args "-D" "${_var}=${${_var}}") - else() - list(APPEND _args "-U" "${_var}") - endif() - endforeach() - - list(APPEND _args "-D" "EXTERNALS_PKG_NAME=${PKG_NAME}") - list(APPEND _args "-D" "EXTERNALS_BOOTSTRAP=ON") - list(APPEND _args "-D" "CMAKE_MODULE_PATH=${_EXTERNALS_DIR}") - - unset(_env_vars) - foreach(_var SOURCE_DIR FLAGS EXTRA_FLAGS COMPONENTS NO_COMPONENTS) - if(PKG_${_var}) - list_join(PKG_${_var} "::" _tmp) - list(APPEND _env_vars "${PKG_NAME_UPPER}_${_var}=${_tmp}") - endif() - endforeach() - - list_join(CMAKE_MODULE_PATH "::" _tmp) - list(APPEND _env_vars "MODULES_SEARCH_PATH=${_tmp}") - - unset(_exec_flags) - unset(_log_file) - if(NOT (${PKG_NAME}_DEBUG OR PKG_NO_LOG)) - file_create_unique(_log_file - PREFIX "${PKG_NAME_UPPER}" - SUFFIX ".log" - ) - list(APPEND _exec_flags LOG "${_log_file}") - elseif(${PKG_NAME}_DEBUG) - list(APPEND _exec_flags NO_LOG DEBUG) - elseif(PKG_NO_LOG) - list(APPEND _exec_flags NO_LOG) - endif() - - system_execute( - ${_exec_flags} - WORKING_DIR "${PKG_BUILD_DIR}" - COMMAND "${CMAKE_COMMAND}" - ARGS "${_args}" "${CMAKE_CURRENT_LIST_DIR}/build" - ENV "${_env_vars}" - ) - - if(_log_file AND EXISTS "${_log_file}") - file(STRINGS "${_log_file}" _logs REGEX "^${_EXTERNALS_LOG_PATTERN}") - foreach(_log IN LISTS _logs) - string(REGEX REPLACE "^${_EXTERNALS_LOG_PATTERN}" "" _log "${_log}") - message(STATUS "${_log}") - endforeach() - file(REMOVE "${_log_file}") - endif() - - set(${PKG_NAME_UPPER}_SOURCE_DIR "${PKG_SOURCE_DIR}" PARENT_SCOPE) - set(${PKG_NAME_UPPER}_BUILD_DIR "${PKG_BUILD_DIR}" PARENT_SCOPE) -endfunction() diff --git a/cmake/external/README.md b/cmake/external/README.md deleted file mode 100644 index 9f725369..00000000 --- a/cmake/external/README.md +++ /dev/null @@ -1,84 +0,0 @@ -## Introduction -These helpers offer a way to embed external packages in a **CMake** based project. -They allow to find these packages that are not **CMake** aware to build during -the generation of the build environment. - -## Available packages -* [Boost](boost/README.md) -* [Botan](botan/README.md) -* [OpenSSL](openssl/README.md) - -## Generic Usage - -### Syntax -``` -find_package( [] REQUIRED - [NO_COMPONENTS|[WITH_COMPONENTS] ] - [FLAGS ] -) -``` -* `NO_COMPONENTS` indicates only package's headers are required. **NO** -components should be compiled when this flag is set -* `WITH_COMPONENTS` must be followed with a list of all additional components -to build - - > **Note** - > - > Both `NO_COMPONENTS` and `WITH_COMPONENTS` **cannot** be used at the same - > time. - -* `FLAGS` can be followed by any of the flags below: - > **Note** - > - > Some additional flags may be available for package. Please refer to specific - > documentations for complete list of flags. - - * `STATIC`: Flag to request static version of libraries - * `RUNTIME_STATIC`: Flag to request libraries linkable to platforms' static -runtime library - * `SOURCE_DIR` ``: The absolute path to the package's source tree - -### Preliminary -In order to allow the use of this script, several points must be done first: - -#### CMakeCommon module -This module **must** be enabled in the project wishing to use the -`Find.cmake` helper. - -Please, refer to this [CMakeCommon](../../README.md#usage)'s help for more -information about how to enable this module. - -#### Source package -The package's source tree must be available from the project's source tree. - -If no [`SOURCE_DIR`](#generic-usage) path is provided from the -`find_package(...)` call, the script will automatically search the source tree -in the following prioritized paths: -1. `${_ROOT_DIR}` -2. `${CMAKE_SOURCE_DIR}/third_party` -3. `${PROJECT_SOURCE_DIR}/third_party` -4. `${CMAKE_CURRENT_SOURCE_DIR}/third_party` - -> **Note** -> -> The name of the root directory that contains the package's source tree should -> be written in lower case. Otherwise, that name **must** match the name provided -> on `find_package(...)` call. - -### Particular settings -For specific reasons it may be needed to force the location where the packages' -binary will be created or where the packages' source tree should be expanded. - -This can be achieved through the following variables: -* `EXTERNALS_BINARY_DIR`: - If set, this variable forces the location where all packages' binaries must - be created. -* `EXTERNALS_EXPAND_DIR`: - If set, this variable forces the location where all packages' source archive - must be expanded. -* `EXTERNALS_USE_RELATIVE_DIR`: - If set, this variable indicates that binaries' and expanded archives' - location must use a sub path matching the caller *CMakeLists.txt*'s one. - -## Adding new package -**TODO** \ No newline at end of file diff --git a/cmake/external/boost/FindBoost.cmake b/cmake/external/boost/FindBoost.cmake deleted file mode 100644 index b38dd6f2..00000000 --- a/cmake/external/boost/FindBoost.cmake +++ /dev/null @@ -1,268 +0,0 @@ -# Module for locating the Boost libraries. - -cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) - -include(FindPackageHandleStandardArgs) -include(EnhancedList) -include(ExternalPackageHelpers) - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== - -# ------------------------------------------------------------------------------ -# Prepare the build context for the Boost package -macro(_boost_setup_build_context) - # Parse arguments for Boost compilation flags - external_parse_arguments(Boost - "STATIC;RUNTIME_STATIC" - "SOURCE_DIR" - "EXTRA_FLAGS" - "" - ) - - if(BOOST_RUNTIME_STATIC AND NOT BOOST_STATIC) - external_error( - "Boost's Option 'RUNTIME_STATIC' cannot be used with shared mode" - ) - endif() - - # Prepare build directory suffix - unset(_build_dir_suffix) - - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND _build_dir_suffix x64) - else() - list(APPEND _build_dir_suffix x86) - endif() - - if(BOOST_STATIC) - list(APPEND _build_dir_suffix ST) - list(APPEND BOOST_FLAGS STATIC) - else() - list(APPEND _build_dir_suffix SH) - endif() - - if(BOOST_RUNTIME_STATIC) - list(APPEND _build_dir_suffix RS) - list(APPEND BOOST_FLAGS RUNTIME_STATIC) - endif() - - string(REPLACE ";" "_" _build_dir_suffix "${_build_dir_suffix}") - string(TOLOWER "${_build_dir_suffix}" _build_dir_suffix) -endmacro() - -# ------------------------------------------------------------------------------ -# Ensure all the components defined in Boost_FIND_COMPONENTS are built -function(boost_build_components) - _boost_setup_build_context() - - # Set-up Boost's source directory - unset(_source_fields) - if(BOOST_SOURCE_DIR) - list(APPEND _source_fields SOURCE_DIR "${BOOST_SOURCE_DIR}") - endif() - list(APPEND _source_fields SOURCE_SEARCH_CLUES "boostcpp.jam") - - # Build/Update Boost package - if(BOOST_NO_COMPONENTS) - set(_components NO_COMPONENTS) - else() - set(_components COMPONENTS ${Boost_FIND_COMPONENTS}) - endif() - - externals_build(Boost - ${_source_fields} - ${_components} - FLAGS "${BOOST_FLAGS}" - EXTRA_FLAGS "${BOOST_EXTRA_FLAGS}" - ) - - # Set-up Boost's variables - set(BOOST_NO_COMPONENTS "${BOOST_NO_COMPONENTS}" PARENT_SCOPE) - - set(BOOST_LIBRARYDIR - "${BOOST_BUILD_DIR}/stage/${_build_dir_suffix}/lib" - PARENT_SCOPE - ) - - set(BOOST_INCLUDEDIR "${BOOST_SOURCE_DIR}" PARENT_SCOPE) - -endfunction() - -# ------------------------------------------------------------------------------ -macro(_boost_find_version) - if(BOOST_INCLUDEDIR AND EXISTS "${BOOST_INCLUDEDIR}/boost/version.hpp") - file(STRINGS - "${BOOST_INCLUDEDIR}/boost/version.hpp" _boost_version_lines - REGEX "#define BOOST(_LIB)?_VERSION " - ) - foreach(_line IN LISTS _boost_version_lines) - if(_line MATCHES ".*#define BOOST_VERSION ([0-9]+).*") - set(BOOST_VERSION "${CMAKE_MATCH_1}") - elseif(_line MATCHES ".*#define BOOST_LIB_VERSION \"([0-9_]+)\".*") - set(Boost_LIB_VERSION "${CMAKE_MATCH_1}") - endif() - endforeach() - - math(EXPR Boost_MAJOR_VERSION "${BOOST_VERSION} / 100000") - math(EXPR Boost_MINOR_VERSION "${BOOST_VERSION} / 100 % 1000") - math(EXPR Boost_SUBMINOR_VERSION "${BOOST_VERSION} % 100") - set(Boost_VERSION - "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}" - ) - else() - external_error("No version header file in Boost's source tree") - endif() -endmacro() - -# ------------------------------------------------------------------------------ -function(_boost_find) - # Extract version of the compiled Boost - _boost_find_version() - foreach(_var "" _LIB _MAJOR _MINOR _SUBMINOR) - set(Boost${_var}_VERSION "${Boost${_var}_VERSION}" PARENT_SCOPE) - if(Boost_DEBUG) - external_debug(" - Boost${_var}_VERSION: '${Boost${_var}_VERSION}'") - endif() - endforeach() - set(Boost_INCLUDE_DIR "${BOOST_INCLUDEDIR}" PARENT_SCOPE) - set(Boost_INCLUDE_DIRS "${BOOST_INCLUDEDIR}" PARENT_SCOPE) - - if(BOOST_NO_COMPONENTS) - # No components to find ... - external_debug("Build WITH NO components requested") - return() - endif() - - # Set-up reg-ex parser for Boost's library name - set(_regex "(lib|)boost") # Library prefix - set(_regex "${_regex}_([^-]+)") # Component name - set(_regex "${_regex}(-.+|)") # Optional compiler's ID - set(_regex "${_regex}(-mt)") # Multi-thread tag - set(_regex "${_regex}(-s?g?d?|)") # ABI tag - set(_regex "${_regex}(-${Boost_LIB_VERSION}|)") # Optional Version - if(CMAKE_HOST_WIN32 AND NOT CYGWIN) - set(_regex "${_regex}\\.(lib)") # Win32 Library extension - elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") - set(_regex "${_regex}\\.(dylib|a)") # Darwin Library extensions - else() - set(_regex "${_regex}\\.(so|a)") # Linux Library extensions - endif() - - # Look-up for all available (i.e. compiled) libraries - file(GLOB _libraries RELATIVE "${BOOST_LIBRARYDIR}" "${BOOST_LIBRARYDIR}/*") - set(Boost_LIBRARY_DIR "${BOOST_LIBRARYDIR}" PARENT_SCOPE) - set(Boost_LIBRARY_DIRS "${BOOST_LIBRARYDIR}" PARENT_SCOPE) - foreach(_lib IN LISTS _libraries) - if("${_lib}" MATCHES "${_regex}") - set(_component "${CMAKE_MATCH_2}") - set(_abi_tag "${CMAKE_MATCH_5}") - if("${Boost_FIND_COMPONENTS}" MATCHES "(.*;|)${_component}(;.*|)") - if("${_abi_tag}" MATCHES "^-s?g?d$") - set(BOOST_${_component}_LIBRARY_DEBUG "${BOOST_LIBRARYDIR}/${_lib}") - else() - set(BOOST_${_component}_LIBRARY_RELEASE "${BOOST_LIBRARYDIR}/${_lib}") - endif() - endif() - endif() - endforeach() - - unset(Boost_LIBRARIES) - foreach(_component IN LISTS Boost_FIND_COMPONENTS) - if(BOOST_${_component}_LIBRARY_RELEASE OR BOOST_${_component}_LIBRARY_DEBUG) - set(Boost_${_component}_FOUND TRUE) - if(NOT BOOST_${_component}_LIBRARY_DEBUG) - list(APPEND Boost_LIBRARIES "${BOOST_${_component}_LIBRARY_RELEASE}") - set(Boost_${_component}_LIBRARY - "${BOOST_${_component}_LIBRARY_RELEASE}" - PARENT_SCOPE - ) - elseif(NOT BOOST_${_component}_LIBRARY_RELEASE) - list(APPEND Boost_LIBRARIES "${BOOST_${_component}_LIBRARY_DEBUG}") - set(Boost_${_component}_LIBRARY - "${BOOST_${_component}_LIBRARY_DEBUG}" - PARENT_SCOPE - ) - else() - list(APPEND Boost_LIBRARIES - optimized "${BOOST_${_component}_LIBRARY_RELEASE}" - debug "${BOOST_${_component}_LIBRARY_DEBUG}" - ) - set(Boost_${_component}_LIBRARY - optimized "${BOOST_${_component}_LIBRARY_RELEASE}" - debug "${BOOST_${_component}_LIBRARY_DEBUG}" - PARENT_SCOPE - ) - endif() - - foreach(_var _RELEASE _DEBUG) - if(Boost_${_component}_LIBRARY${_var}) - set(Boost_${_component}_LIBRARY${_var} - "${BOOST_${_component}_LIBRARY_DEBUG}" - PARENT_SCOPE - ) - endif() - endforeach() - else() - set(Boost_${_component}_FOUND "${_component}-NOTFOUND") - endif() - set(Boost_${_component}_FOUND "${Boost_${_component}_FOUND}" PARENT_SCOPE) - endforeach() - set(Boost_LIBRARIES "${Boost_LIBRARIES}" PARENT_SCOPE) -endfunction() - -# ------------------------------------------------------------------------------ -# Set-up BOOST variables -macro(_boost_setup_vars) - if(Boost_DEBUG) - foreach(_var INCLUDE_DIR INCLUDE_DIRS LIBRARY_DIRS LIBRARIES) - external_debug(" Boost_${_var}:") - foreach(_item IN LISTS Boost_${_var}) - external_debug(" '${_item}'") - endforeach() - endforeach() - endif() - - unset(_lib_vars) - if(NOT BOOST_NO_COMPONENTS) - set(_lib_vars - Boost_LIBRARIES - Boost_LIBRARY_DIR - Boost_LIBRARY_DIRS - ) - endif() - - find_package_handle_standard_args(Boost - FOUND_VAR - Boost_FOUND - REQUIRED_VARS - Boost_VERSION - ${_lib_vars} - Boost_INCLUDE_DIR - Boost_INCLUDE_DIRS - VERSION_VAR - Boost_VERSION - HANDLE_COMPONENTS - ) -endmacro() - -# ============================================================================== -# ==================== C o r e I m p l e m e n t a t i o n =================== -# ============================================================================== - -# Build missing components -set(PKG_NAME Boost) -boost_build_components() - -if(Boost_DEBUG) - foreach(_var BOOST_LIBRARYDIR BOOST_INCLUDEDIR) - external_debug(" ${_var} = '${${_var}}'") - endforeach() -endif() - -# Find package's libraries -_boost_find() - -# Set-up package's variables -_boost_setup_vars() diff --git a/cmake/external/boost/README.md b/cmake/external/boost/README.md deleted file mode 100644 index ef9d1ff0..00000000 --- a/cmake/external/boost/README.md +++ /dev/null @@ -1,53 +0,0 @@ -## Introduction -This script helps CMake's `find_package(...)` feature to find and build the -**Boost** package present in the project's source tree. - -## Provided (ReadOnly) variables: -* `Boost_FOUND`: - Indicates whether the library has been found or not. -* `Boost_VERSION`: - Indicates the version of the library that has been found -* `Boost_INCLUDE_DIRS`: - Points to the **Boost**'s include directories that should be passed to - `include_directories(...)`. -* `Boost_LIBRARIES`: - Points specifically to the **Boost**'s libraries that should be passed to - `target_link_libraries(...)`. -* `Boost_LIBRARY_DIRS`: - Points specifically to the **Boost**'s libraries that should be passed to - `link_directories(...)`. - This may be needed in case of use of auto import pragma feature. - -## Usage - -### Syntax -``` -find_package(Boost [] REQUIRED - [NO_COMPONENTS|[WITH_COMPONENTS] ] - [FLAGS ] -) -``` -* `FLAGS`, in addition to [generics](../README.md#syntax) can be any of the -following: - * `EXTRA_FLAGS` ``: List of extra (i.e. raw) flags to provide to -**Boost**'s build tool (i.e. **bjam**) - -### Example -* Find **Boost** but no components are required: - - ```cmake - find_package(Boost REQUIRED NO_COMPONENTS) - ``` -* Find and compile **Boost** in static link mode with **ALL** components: - - ```cmake - find_package(Boost REQUIRED FLAGS STATIC) - ``` -* Find and compile **Boost** in DLL link mode. Only `program_options` components -is required: - - ```cmake - find_package(Boost REQUIRED - WITH_COMPONENTS program_options - ) - ``` diff --git a/cmake/external/boost/build/CMakeLists.txt b/cmake/external/boost/build/CMakeLists.txt deleted file mode 100644 index 54bab88b..00000000 --- a/cmake/external/boost/build/CMakeLists.txt +++ /dev/null @@ -1,147 +0,0 @@ -# This CMakeLists wraps the Boost build scripts in order to allow compilation -# in a separate build tree. -# -# Expected CMake variables are: -# -# MODULES_SEARCH_PATH : Search path for cmake-common modules -# BOOST_SOURCE_DIR : The absolute path to the Boost's source tree -# BOOST_NO_COMPONENTS : Boolean flag if no components has to be built -# BOOST_COMPONENTS : List of components to build -# BOOST_EXTRA_FLAGS : List of extra raw flags to provide to package -# low level build engine -# BOOST_FLAGS : List of flags to configure Boost's build -# RUNTIME_STATIC : Flag to request static link with runtime -# STATIC : Flag to request compilation of a staic library -# -cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) - -include(ExternalPackageHelpers) -include(HelpersArguments) -include(EnhancedList) -include(boost_build) - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== - -# ------------------------------------------------------------------------------ -# Prepare the build context for the BOOST package -macro(_boost_setup_build_context) - # Parse given flags - if(BOOST_FLAGS) - parse_arguments("BOOST" - "STATIC;RUNTIME_STATIC" - "" - "" - "" - ${BOOST_FLAGS} - ) - endif() - - # Prepare build base flags and build directory suffix - unset(_base_build_flags) - unset(_build_dir_suffix) - - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND _build_dir_suffix x64) - else() - list(APPEND _build_dir_suffix x86) - endif() - - if(BOOST_STATIC) - list(APPEND _base_build_flags STATIC) - list(APPEND _build_dir_suffix ST) - else() - list(APPEND _build_dir_suffix SH) - endif() - - if(BOOST_RUNTIME_STATIC) - list(APPEND _base_build_flags RUNTIME_STATIC) - list(APPEND _build_dir_suffix RS) - endif() - - list_join(_build_dir_suffix "_" _build_dir_suffix) - string(TOLOWER "${_build_dir_suffix}" _build_dir_suffix) - - # Set-up some directories - set(BOOST_BUILD_BASE_DIR - "${CMAKE_CURRENT_BINARY_DIR}/build/${_build_dir_suffix}" - ) - - set(BOOST_INSTALL_DIR - "${CMAKE_CURRENT_BINARY_DIR}/stage/${_build_dir_suffix}" - ) - - external_setup_build_context(Boost "${_build_dir_suffix}") -endmacro() - - -# ============================================================================== -# ==================== C o r e I m p l e m e n t a t i o n =================== -# ============================================================================== - -_boost_setup_build_context() - -if(Boost_DEBUG) - external_debug("Build context status:") - foreach(_var NEED_BUILD NO_COMPONENTS EXTRA_FLAGS COMPONENTS - STATIC RUNTIME_STATIC INSTALL_DIR BUILD_BASE_DIR) - external_debug(" BOOST_${_var} : ${BOOST_${_var}}") - endforeach() -endif() - -if(BOOST_NEED_BUILD) - # Determine what modes the Boost has to be compiled in - if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE) - set(_build_modes "dbg" "rel") - elseif(CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$") - set(_build_modes "dbg") - else() - set(_build_modes "rel") - endif() - - file(MAKE_DIRECTORY "${BOOST_INSTALL_DIR}") - foreach(_mode IN LISTS _build_modes) - # Set build flags - unset(_build_flags) - unset(_lib_mode) - - if(BOOST_NO_COMPONENTS) - list(APPEND _build_flags "NO_LIB") - endif() - - if(_base_build_flags) - list(APPEND _build_flags "${_base_build_flags}") - list(APPEND _lib_mode "${_base_build_flags}") - else() - list(APPEND _lib_mode "SHARED") - endif() - - if(_mode STREQUAL "dbg") - list(APPEND _build_flags "DEBUG") - list(APPEND _lib_mode "DEBUG") - else() - list(APPEND _lib_mode "RELEASE") - endif() - - if(Boost_DEBUG) - external_debug("_build_flags: '${_build_flags}'") - endif() - - # Start the build session - list_join(_lib_mode "/" _lib_mode) - external_log("Building Libraries in mode ${_lib_mode}") - boost_build( - NO_LOG - "${_build_flags}" - SOURCE_DIR "${BOOST_SOURCE_DIR}" - INSTALL_DIR "${BOOST_INSTALL_DIR}" - BUILD_DIR "${BOOST_BUILD_BASE_DIR}/${_mode}" - COMPONENTS ${BOOST_COMPONENTS} - EXTRA_FLAGS "${BOOST_EXTRA_FLAGS}" - ) - endforeach() - - external_teardown_build_context(Boost "${_build_dir_suffix}") - -endif() diff --git a/cmake/external/boost/build/boost_build.cmake b/cmake/external/boost/build/boost_build.cmake deleted file mode 100644 index 90bd60f4..00000000 --- a/cmake/external/boost/build/boost_build.cmake +++ /dev/null @@ -1,244 +0,0 @@ -# This CMAKE script should be included from parent wishing to build BOOST -# libraries. -# -# Valid options are: -# DEBUG # Flag to compile library in debug mode -# STATIC # Flag to compile library in static mode -# RUNTIME_STATIC # Flag to link with platform's static runtime -# SOURCE_DIR # Path to package's source tree -# INSTALL_DIR # Destination where to store built libraries -# BUILD_DIR # Destination where to build libraries -# COMPONENTS # List of names of the components to build -# EXTRA_FLAGS # List of raw flags to provide to boost's scripts -# -# Example: -# include(boost_build) -# -# boost_build( -# SOURCE_DIR "path/to/the/package/source/tree" -# COMPONENTS -# chrono -# filesystem -# iostreams -# EXTRA_FLAGS -# -sNO_BZIP2=1 -# ) -# -if(__H_BOOST_BUILD_INCLUDED) - return() -endif() -set(__H_BOOST_BUILD_INCLUDED TRUE) - -cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) - -include(ExternalPackageHelpers) -include(HelpersArguments) -include(SystemTools) -include(ProcessorCount) - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== - -# ------------------------------------------------------------------------------ -# Initializes the environment that will be needed to build the BOOST package -macro(boost_build_initialization) - # Parse arguments - parse_arguments("BOOST_BUILD" - "NO_LIB;NO_LOG;DEBUG;STATIC;RUNTIME_STATIC" - "SOURCE_DIR;INSTALL_DIR;BUILD_DIR" - "COMPONENTS;EXTRA_FLAGS" - "" - ${ARGN} - ) - - if(NOT BOOST_BUILD_BUILD_DIR) - set(BOOST_BUILD_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/boost") - endif() - - if(NOT BOOST_BUILD_INSTALL_DIR) - set(BOOST_BUILD_INSTALL_DIR "${BOOST_BUILD_BUILD_DIR}/stage") - endif() - - if(Boost_DEBUG) - set(Boost_DEBUG NO_LOG DEBUG) - else() - unset(Boost_DEBUG) - endif() - - set(BOOST_BOOTSTRAP "${BOOST_BUILD_SOURCE_DIR}/bootstrap") - set(BOOST_BUILDER "${BOOST_BUILD_SOURCE_DIR}/bjam") - if(NOT BOOST_BUILD_NO_LOG) - set(BOOST_BUILDER_LOG LOG "${BOOST_BUILD_BUILD_DIR}/boost_builder.log") - else() - set(BOOST_BUILDER_LOG NO_LOG) - endif() - - if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(BOOST_BUILDER "${BOOST_BUILDER}.exe") - set(BOOST_BOOTSTRAP "${BOOST_BOOTSTRAP}.bat") - else() - set(BOOST_BOOTSTRAP "${BOOST_BOOTSTRAP}.sh") - endif() - - # Set-up toolset name and associated ABI flags flags - unset(cxx_flags) - unset(link_flags) - string(TOLOWER "${CMAKE_CXX_COMPILER_ID}" toolset_name) - if ("${toolset_name}" STREQUAL "clang") - set(cxx_flags "cxxflags=-std=c++11 -stdlib=libc++") - set(link_flags "linkflags=-stdlib=libc++") - elseif ("${toolset_name}" STREQUAL "gnu") - set(toolset_name "gcc") - string(REGEX REPLACE - "([0-9]+)(\\.([0-9]+))?.*" - "gcc\\1\\3" - toolset_version - "${CMAKE_CXX_COMPILER_VERSION}" - ) - set(cxx_flags "cxxflags=-std=c++11") - elseif("${toolset_name}" STREQUAL "msvc") - else() - external_error("COMPILER '${CMAKE_CXX_COMPILER_ID}' NOT HANDLED") - endif() - - unset(_arch_cfg) - list(APPEND _arch_cfg "architecture=x86") - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND _arch_cfg "address-model=64") - else() - list(APPEND _arch_cfg "address-model=32") - endif() - - ProcessorCount(_processors_count) - if(_processors_count) - set(_processors_count "-j${_processors_count}") - else() - unset(_processors_count) - endif() - - # Compile BOOST's build tool - if(NOT EXISTS "${BOOST_BUILDER}") - external_log("Compiling Boost's builder") - unset(_tmp) - if(NOT "$ENV{CC}" STREQUAL "") - set(_tmp "ENV" "CC=") - endif() - system_execute( - ${Boost_DEBUG} - "${BOOST_BUILDER_LOG}" - WORKING_DIR "${BOOST_BUILD_SOURCE_DIR}" - COMMAND "${BOOST_BOOTSTRAP}" - ${_tmp} - ) - - # Test we successfully built builder - if(NOT EXISTS "${BOOST_BUILDER}") - if(EXISTS "${BOOST_BUILD_SOURCE_DIR}/bootstrap.log") - file(STRINGS "${BOOST_BUILD_SOURCE_DIR}/bootstrap.log" _tmp) - external_log("===[ ${BOOST_BUILD_SOURCE_DIR}/bootstrap.log ] ===\n") - foreach(_line IN LISTS _tmp) - external_log("${_line}") - endforeach() - external_log("===") - endif() - external_error("Cannot compile Boost's builder.") - endif() - endif() - - # Compute list of possible components - execute_process( - OUTPUT_VARIABLE _boost_libraries - WORKING_DIRECTORY "${BOOST_BUILD_SOURCE_DIR}" - COMMAND "${BOOST_BUILDER}" "--show-libraries" - ) - string(REGEX REPLACE "(^[^:]+:[\t ]*\n|[\t ]+)" "" - _boost_libraries "${_boost_libraries}") - string(REGEX REPLACE "-([^\n]*)\n+" "\\1;" - _boost_libraries "${_boost_libraries}") - string(REGEX REPLACE ";+$" "" - _boost_libraries "${_boost_libraries}") - - # Create BOOST's headers tree in case of Boost Modular repo - if(NOT EXISTS "${BOOST_BUILD_SOURCE_DIR}/boost") - external_log("Create Boost's headers tree") - system_execute( - ${Boost_DEBUG} - "${BOOST_BUILDER_LOG}" - WORKING_DIR "${BOOST_BUILD_SOURCE_DIR}" - COMMAND "${BOOST_BUILDER}" - ARGS headers - ) - endif() -endmacro() - -# ------------------------------------------------------------------------------ -# Build the BOOST package from the given source path -function(boost_build) - - # Prepare BOOST context - boost_build_initialization("${ARGN}") - - # Prepare build options - if(BOOST_BUILD_NO_LIB) - # No libraries to compile... - return() - endif() - - set(_build_options - "${cxx_flags}" - "${link_flags}" - "${_arch_cfg}" - "toolset=${toolset_name}" - "${_processors_count}" - "-q" - "--hash" - --debug-configuration - --layout=versioned - stage - "--stagedir=${BOOST_BUILD_INSTALL_DIR}" - "--build-dir=${BOOST_BUILD_BUILD_DIR}" - ) - - if(NOT BOOST_BUILD_DEBUG) - list(APPEND _build_options variant=release optimization=space) - else() - list(APPEND _build_options variant=debug) - endif() - - unset(_skipped) - list_join(_boost_libraries "|" _filter) - foreach(_component ${BOOST_BUILD_COMPONENTS}) - if("${_component}" MATCHES "^(${_filter})$") - list(APPEND _build_options "--with-${_component}") - else() - list(APPEND _skipped "${_component}") - endif() - endforeach() - if(_skipped) - list_join(_skipped ", " _skipped) - external_log("Skipping implicit libraries: ${_skipped}") - endif() - - if(BOOST_BUILD_EXTRA_FLAGS) - list(APPEND _build_options "${BOOST_BUILD_EXTRA_FLAGS}") - endif() - - if(NOT BOOST_BUILD_STATIC) - list(APPEND _build_options link=shared runtime-link=shared) - elseif(NOT BOOST_BUILD_RUNTIME_STATIC) - list(APPEND _build_options link=static runtime-link=shared) - else() - list(APPEND _build_options link=static runtime-link=static) - endif() - - # Compile BOOST libraries - system_execute( - ${Boost_DEBUG} - "${BOOST_BUILDER_LOG}" - WORKING_DIR "${BOOST_BUILD_SOURCE_DIR}" - COMMAND "${BOOST_BUILDER}" - ARGS "${_build_options}" - ) -endfunction() - diff --git a/cmake/external/gtest/GTest.cmake b/cmake/external/gtest/GTest.cmake deleted file mode 100644 index 60ea61c9..00000000 --- a/cmake/external/gtest/GTest.cmake +++ /dev/null @@ -1,31 +0,0 @@ -# Find and extract the Google Test package - -cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) - -include(FindPackageHandleStandardArgs) -include(EnhancedList) -include(ExternalPackageHelpers) - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== - -# ------------------------------------------------------------------------------ -# Unpack GTest package -function(gtest_unpack_archive) - - # Extract GTest archive - external_search_source_path( - NAME "gtest" - VAR "GTEST_UNPACK_DIR" - CLUES "README" - ) - - # Set GTEST_ROOT_DIR - set( - GTEST_ROOT_DIR - "${GTEST_UNPACK_DIR}" - PARENT_SCOPE - ) - -endfunction() \ No newline at end of file diff --git a/cmake/external/openssl/FindOpenSSL.cmake b/cmake/external/openssl/FindOpenSSL.cmake deleted file mode 100644 index b4767057..00000000 --- a/cmake/external/openssl/FindOpenSSL.cmake +++ /dev/null @@ -1,266 +0,0 @@ -# Find and Build the OpenSSL package - -cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) - -include(FindPackageHandleStandardArgs) -include(EnhancedList) -include(ExternalPackageHelpers) - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== - -# ------------------------------------------------------------------------------ -# Prepare the build context for the OpenSSL package -macro(_openssl_setup_build_context) - # Parse the compilation flags - external_parse_arguments(OpenSSL - "RUNTIME_STATIC;STATIC" - "SOURCE_DIR" - "EXTRA_FLAGS" - "" - ) - - if(OpenSSL_FIND_COMPONENTS) - external_error( - "Components list not supported for this package:\n" - "${OpenSSL_FIND_COMPONENTS}" - ) - endif() - - if(OPENSSL_RUNTIME_STATIC AND NOT CMAKE_HOST_WIN32) - external_error("Invalid platform option 'RUNTIME_STATIC'") - endif() - - # Prepare build directory suffix - unset(_build_dir_suffix) - - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND _build_dir_suffix x64) - else() - list(APPEND _build_dir_suffix x86) - endif() - - if(OPENSSL_STATIC) - list(APPEND _build_dir_suffix st) - list(APPEND OPENSSL_FLAGS STATIC) - set(OPENSSL_STATIC "${OPENSSL_STATIC}" PARENT_SCOPE) - else() - list(APPEND _build_dir_suffix sh) - endif() - - if(OPENSSL_RUNTIME_STATIC) - list(APPEND _build_dir_suffix rs) - list(APPEND OPENSSL_FLAGS RUNTIME_STATIC) - set(OPENSSL_RUNTIME_STATIC "${OPENSSL_RUNTIME_STATIC}" PARENT_SCOPE) - endif() - - string(REPLACE ";" "_" _build_dir_suffix "${_build_dir_suffix}") - string(TOLOWER "${_build_dir_suffix}" _build_dir_suffix) -endmacro() - -# ------------------------------------------------------------------------------ -# Build OpenSSL package -function(openssl_build_package) - # Prepare build context - _openssl_setup_build_context() - - # Set-up OpenSSL's source directory - unset(_source_fields) - if(OPENSSL_SOURCE_DIR) - list(APPEND _source_fields SOURCE_DIR "${OPENSSL_SOURCE_DIR}") - endif() - list(APPEND _source_fields SOURCE_SEARCH_CLUES "openssl.spec") - - # Build/Update OpenSSL package - externals_build(OpenSSL ${_source_fields} - FLAGS "${OPENSSL_FLAGS}" - EXTRA_FLAGS "${OPENSSL_EXTRA_FLAGS}" - ) - - # Set-up OpenSSL's install directory - set(OpenSSL_ROOT_DIR - "${OPENSSL_BUILD_DIR}/stage/${_build_dir_suffix}" - PARENT_SCOPE - ) - -endfunction() - - -# ------------------------------------------------------------------------------ -macro(openssl_setup) - find_package_handle_standard_args(OpenSSL - REQUIRED_VARS - OpenSSL_VERSION - OpenSSL_ROOT_DIR - OpenSSL_INCLUDE_DIRS - OpenSSL_LIBRARIES - VERSION_VAR - OpenSSL_VERSION - HANDLE_COMPONENTS - ) - set(OpenSSL_FOUND "${OPENSSL_FOUND}" PARENT_SCOPE) - if(OPENSSL_FOUND) - foreach(_var ROOT_DIR INCLUDE_DIRS LIBRARIES VERSION FOUND) - set(OpenSSL_${_var} "${OpenSSL_${_var}}" PARENT_SCOPE) - mark_as_advanced(${_var}) - endforeach() - endif() -endmacro() - -# ------------------------------------------------------------------------------ -function(openssl_from_hex HEX DEC) - string(TOUPPER "${HEX}" HEX) - set(_res 0) - string(LENGTH "${HEX}" _strlen) - - while (_strlen GREATER 0) - math(EXPR _res "${_res} * 16") - string(SUBSTRING "${HEX}" 0 1 NIBBLE) - string(SUBSTRING "${HEX}" 1 -1 HEX) - if (NIBBLE STREQUAL "A") - math(EXPR _res "${_res} + 10") - elseif (NIBBLE STREQUAL "B") - math(EXPR _res "${_res} + 11") - elseif (NIBBLE STREQUAL "C") - math(EXPR _res "${_res} + 12") - elseif (NIBBLE STREQUAL "D") - math(EXPR _res "${_res} + 13") - elseif (NIBBLE STREQUAL "E") - math(EXPR _res "${_res} + 14") - elseif (NIBBLE STREQUAL "F") - math(EXPR _res "${_res} + 15") - else() - math(EXPR _res "${_res} + ${NIBBLE}") - endif() - - string(LENGTH "${HEX}" _strlen) - endwhile() - - set(${DEC} ${_res} PARENT_SCOPE) -endfunction() - -# ------------------------------------------------------------------------------ -function(openssl_find_version version_file_header version_var) - # For version string from the given header file - file(STRINGS - "${version_file_header}" - _version_str - REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])+.*" - ) - string(TOUPPER "${_version_str}" _version_str) - - # The version number is encoded as 0xMNNFFPPS: major minor patch tweak status - # The status gives if this is a developer or prerelease and is ignored here. - # Major, minor, and patch directly translate into the version numbers shown in - # the string. The tweak field translates to the single character suffix that - # indicates the bug fix state, which 00 -> nothing, 01 -> a, 02 -> b and so - # on. - set(replace_pattern - "^.*OPENSSL_VERSION_NUMBER[\t ]+0X" # PREFIX - "([0-9A-F])" # MAJOR - "([0-9A-F][0-9A-F])" # MINOR - "([0-9A-F][0-9A-F])" # PATCH - "([0-9A-F][0-9A-F])" # TWEAK - "[0-9A-F]" # STATE - ".*$" # SUFFIX - ) - list_join(replace_pattern "" replace_pattern) - string(REGEX REPLACE - "${replace_pattern}" - "\\1;\\2;\\3;\\4" - _version_str - "${_version_str}" - ) - - - unset(_version) - foreach(_field IN LISTS _version_str) - openssl_from_hex("${_field}" _field) - if(_version) - set(_version "${_version}.") - endif() - set(_version "${_version}${_field}") - endforeach() - -# if(_tweak GREATER 0) -# # 96 is the ASCII code of 'a' minus 1 -# math(EXPR _tweak "${_tweak} + 96") -# # Once anyone knows how OpenSSL would call the patch versions beyond 'z' -# # this should be updated to handle that, too. This has not happened yet -# # so it is simply ignored here for now. -# string(ASCII "${_VERSION_PATCH}" _VERSION_PATCH) -# set(${openssl_revision_var} "${${openssl_revision_var}}${_VERSION_PATCH}") -# endif() - - - # Export variables to parent - set(${version_var} "${_version}" PARENT_SCOPE) -endfunction() - -# ------------------------------------------------------------------------------ -function(openssl_find_package) - - # Look for OpenSSL in DEBUG and RELEASE - unset(OpenSSL_INCLUDE_DIRS CACHE) - unset(OpenSSL_LIBRARY CACHE) - unset(OpenSSL_VERSION CACHE) - - # Check first for OpenSSL's include directory - find_path(OpenSSL_INCLUDE_DIRS - NAMES "openssl/opensslv.h" - PATHS "${OpenSSL_ROOT_DIR}" - PATH_SUFFIXES "include" - NO_DEFAULT_PATH - ) - - if(OpenSSL_INCLUDE_DIRS) - # Extract OpenSSL's version - openssl_find_version( - "${OpenSSL_INCLUDE_DIRS}/openssl/opensslv.h" - OpenSSL_VERSION - ) - - if(NOT DEFINED os_name) - string(TOLOWER "${CMAKE_SYSTEM_NAME}" os_name) - endif() - - if(os_name STREQUAL "windows") - if(OPENSSL_STATIC) - set(_lib_pattern "${OpenSSL_ROOT_DIR}/lib/*.lib") - else() - set(_lib_pattern "${OpenSSL_ROOT_DIR}/dll/*.lib") - endif() - elseif(os_name STREQUAL "darwin") - if(OPENSSL_STATIC) - set(_lib_pattern "${OpenSSL_ROOT_DIR}/lib/lib*.a") - else() - set(_lib_pattern "${OpenSSL_ROOT_DIR}/lib/lib*.dylib") - endif() - else() - if(OPENSSL_STATIC) - set(_lib_pattern "${OpenSSL_ROOT_DIR}/lib/libssl.a") - list(APPEND _lib_pattern "${OpenSSL_ROOT_DIR}/lib/lib*.a") - else() - set(_lib_pattern "${OpenSSL_ROOT_DIR}/lib/lib*.so") - endif() - endif() - - # Find OpenSSL's Libraries - file(GLOB_RECURSE OpenSSL_LIBRARIES ${_lib_pattern}) - endif() - - # Set-up all OpenSSL's global variables - openssl_setup() -endfunction() - -## ============================================================================== -## ==================== C o r e I m p l e m e n t a t i o n =================== -## ============================================================================== - -# Build package -openssl_build_package() - -# Find requested package from installation directory -openssl_find_package() - diff --git a/cmake/external/openssl/README.md b/cmake/external/openssl/README.md deleted file mode 100644 index 32dffad2..00000000 --- a/cmake/external/openssl/README.md +++ /dev/null @@ -1,47 +0,0 @@ -## Introduction -This script helps CMake's `find_package(...)` feature to find and build the -**OpenSSL** package present in the project's source tree. - -## Provided (ReadOnly) variables: -* `OpenSSL_FOUND`: - Indicates whether the library has been found or not. -* `OpenSSL_VERSION`: - Indicates the version of the library that has been found -* `OpenSSL_ROOT_DIR`: - Absolute path where **OpenSSL**'s libraries and headers are located. -* `OpenSSL_INCLUDE_DIRS` - Points to the **OpenSSL**'s include directories that should be passed to - `include_directories(...)`. -* `OpenSSL_LIBRARIES`: - Points specifically to the **OpenSSL**'s libraries that should be passed to - `target_link_libraries(...)`. - -## Usage - -### Syntax -``` -find_package(OpenSSL [] REQUIRED - [FLAGS ] -) -``` -* `FLAGS`, in addition to [generics](../README.md#syntax) can be any of the -following: - * `EXTRA_FLAGS` ``: List of extra (i.e. raw) flags to provide to -**OpenSSL**'s configure script - -> **Note** -> -> It is not possible to specify any additional components through the -> regular `WITH_COMPONENTS` directive. Use `EXTRA_FLAGS` instead. - -### Example -* Find and compile **OpenSSL** in static link mode: - - ```cmake - find_package(OpenSSL REQUIRED FLAGS STATIC) - ``` -* Find and compile **OpenSSL** in DLL link mode: - - ```cmake - find_package(OpenSSL REQUIRED) - ``` diff --git a/cmake/external/openssl/build/CMakeLists.txt b/cmake/external/openssl/build/CMakeLists.txt deleted file mode 100644 index 97868390..00000000 --- a/cmake/external/openssl/build/CMakeLists.txt +++ /dev/null @@ -1,100 +0,0 @@ -# This CMakeLists wraps the OpenSSL build scripts in order to allow compilation -# in a separate build tree. -# -# Expected CMake variables are: -# -# MODULES_SEARCH_PATH : Search path for cmake-common modules -# OPENSSL_SOURCE_DIR : The absolute path to the OpenSSL's source tree -# OPENSSL_EXTRA_FLAGS : Extra flags to provide to OpenSSL's -# configuration script -# OPENSSL_FLAGS : List of flags to configure OpenSSL's build -# RUNTIME_STATIC : Flag to request static link with run-time -# -cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) - -include(ExternalPackageHelpers) -include(HelpersArguments) -include(EnhancedList) -include(openssl_build) - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== - -# ------------------------------------------------------------------------------ -# Prepare the build context for the OpenSSL package -macro(_openssl_setup_build_context) - # Parse given flags - if(OPENSSL_FLAGS) - parse_arguments("OPENSSL" - "RUNTIME_STATIC;STATIC" - "" - "" - "" - ${OPENSSL_FLAGS} - ) - endif() - - # Prepare build base flags and build directory suffix - unset(_base_build_flags) - unset(_build_dir_suffix) - - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND _build_dir_suffix x64) - else() - list(APPEND _build_dir_suffix x86) - endif() - - if(OPENSSL_STATIC) - list(APPEND _base_build_flags STATIC) - list(APPEND _build_dir_suffix st) - else() - list(APPEND _build_dir_suffix sh) - endif() - - if(OPENSSL_RUNTIME_STATIC) - list(APPEND _base_build_flags RUNTIME_STATIC) - list(APPEND _build_dir_suffix rs) - endif() - - list_join(_build_dir_suffix "_" _build_dir_suffix) - string(TOLOWER "${_build_dir_suffix}" _build_dir_suffix) - - # Set-up some directories - set(OPENSSL_BUILD_DIR - "${CMAKE_CURRENT_BINARY_DIR}/build/${_build_dir_suffix}" - ) - - set(OPENSSL_INSTALL_DIR - "${CMAKE_CURRENT_BINARY_DIR}/stage/${_build_dir_suffix}" - ) - - external_setup_build_context(OpenSSL "${_build_dir_suffix}") -endmacro() - -# ------------------------------------------------------------------------------ -# ============================================================================== -# ==================== C o r e I m p l e m e n t a t i o n =================== -# ============================================================================== - -# Prepare build context -_openssl_setup_build_context() - -# Check if package has already been built -if(OPENSSL_NEED_BUILD) - if(OPENSSL_RUNTIME_STATIC) - external_log("[Embedding static runtime]") - endif() - - # Launch build process - openssl_build( - ${_base_build_flags} - SOURCE_DIR "${OPENSSL_SOURCE_DIR}" - INSTALL_DIR "${OPENSSL_INSTALL_DIR}" - BUILD_DIR "${OPENSSL_BUILD_DIR}" - EXTRA_FLAGS "${OPENSSL_EXTRA_FLAGS}" - ) - - external_teardown_build_context(OpenSSL "${_build_dir_suffix}") -endif() - diff --git a/cmake/external/openssl/build/openssl_build.cmake b/cmake/external/openssl/build/openssl_build.cmake deleted file mode 100644 index 9d1151ef..00000000 --- a/cmake/external/openssl/build/openssl_build.cmake +++ /dev/null @@ -1,350 +0,0 @@ -# This CMAKE script should be included from parent wishing to build OpenSSL -# library. -# -# openssl_build( -# RUNTIME_STATIC # Link against static runtime -# STATIC # Build only static libraries -# SOURCE_DIR # Path to package's source tree -# BUILD_DIR # Working directory where package should be built -# INSTALL_DIR # Destination where to store built libraries -# EXTRA_FLAGS # OpenSSL's specific configuration flags -# ) -# -# Example: -# include(openssl_build) -# -# openssl_build( -# SOURCE_DIR "path/to/the/openssl/source/tree" -# DESTINATION_DIR "path/where/to/store/built/libraries" -# ) -# -if(__H_OPENSSL_BUILD_INCLUDED) - return() -endif() -set(__H_OPENSSL_BUILD_INCLUDED TRUE) - -cmake_minimum_required(VERSION 2.8.11 FATAL_ERROR) - -include(ExternalPackageHelpers) -include(FindPerl) -include(HelpersArguments) -include(MultiList) -include(FileEdit) -include(SystemTools) - - -# ============================================================================== -# ======================= D e f i n e f u n c t i o n s ====================== -# ============================================================================== - -# ------------------------------------------------------------------------------ -# Set-up the environment for Unix platforms -# -# Usage: -# openssl_build_setup_unix( -# out_setup_rules # output variable where set-up rules will be stored -# platform # targeted platform -# [extra_args] # Additional arguments for configuration -# ) -# -macro(openssl_build_setup_unix out_setup_rules platform) - # This set-up rule runs the Configure with all configuration options - multi_list(APPEND ${out_setup_rules} - EXEC "${PERL_EXECUTABLE}" ./Configure ${platform} ${ARGN} - LOG "${OPENSSL_CONFIG_LOG}" - ) - - # This next rule is to build the package - multi_list(APPEND ${out_setup_rules} EXEC make all) - - # Finally, this last rule is to install package in case a destination - # has been provided - if(OPENSSL_BUILD_INSTALL_DIR) - multi_list(APPEND ${out_setup_rules} EXEC make install_sw) - endif() -endmacro() - -# ------------------------------------------------------------------------------ -# Set-up the environment for Darwin -# -# Usage: -# openssl_build_setup_darwin( -# out_setup_rules # output variable where set-up rules will be stored -# [extra_args] # Additional arguments for configuration -# ) -# -macro(openssl_build_setup_darwin out_setup_rules) - openssl_build_setup_unix(${out_setup_rules} darwin64-x86_64-cc ${ARGN}) -endmacro() - -# ------------------------------------------------------------------------------ -# Set-up the environment for Linux -# -# Usage: -# openssl_build_setup_linux( -# out_setup_rules # output variable where set-up rules will be stored -# [extra_args] # Additional arguments for configuration -# ) -# -macro(openssl_build_setup_linux out_setup_rules) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - openssl_build_setup_unix(${out_setup_rules} linux-x86_64 ${ARGN}) - else() - openssl_build_setup_unix(${out_setup_rules} linux-elf ${ARGN}) - endif() -endmacro() - -# ------------------------------------------------------------------------------ -# Patches some generated files for the Windows environment -function(openssl_build_patch_windows) - # Find generated Makefile files (i.e. "*.mak") on windows platform - file(GLOB _files "${OPENSSL_BUILD_SOURCE_DIR}/ms/*.mak") - - # Define common path rules - unset(_rules) - list(APPEND _rules - # Remove /Zi and /Fd from *CFLAG and ASM variables - RULE "(^ASM|CFLAG)[\t ]*=" "[\t ]*/(Zi|Fd[^\t ]+)" "" - # Remove option /debug from *LFLAGS variables - RULE "LFLAGS[\t ]*=" "[\t ]*/debug" "" - ) - - if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND _rules - # Add option /safeseh to *LFLAGS and ASM variables - RULE "(^ASM|LFLAGS)[\t ]*=" "(.+)[\t ]*$" "\\1 -safeseh" - ) - endif() - - list(APPEND _rules - # Add option /dynamicbase to *LFLAGS - RULE "LFLAGS[\t ]*=" "(.+)[\t ]*$" "\\1 /dynamicbase" - ) - - # Define runtime specific rules - if(NOT OPENSSL_BUILD_RUNTIME_STATIC) - list(APPEND _rules - # Replace /MT to /MD from CFLAG - RULE "^CFLAG[\t ]*=" "([\t ]*/M)T([d\t ]|$)" "\\1D\\2" - ) - endif() - - # Patch all the generated Makefile files - file_edit( - MERGE_SPLIT - FILE ${_files} - ${_rules} - ) -endfunction() - -# ------------------------------------------------------------------------------ -# Set-up the environment for windows -# -# Usage: -# openssl_build_setup_unix( -# out_setup_rules # output variable where set-up rules will be stored -# [extra_args] # Additional arguments for configuration -# ) -# -macro(openssl_build_setup_windows out_setup_rules) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - multi_list(APPEND ${out_setup_rules} - WRAP "${PERL_EXECUTABLE}" - ARGS "${OPENSSL_SOURCE_DIR}/Configure" VC-WIN64A ${ARGN} - LOG "${OPENSSL_CONFIG_LOG}" - ) - multi_list(APPEND ${out_setup_rules} - WRAP "${OPENSSL_SOURCE_DIR}/ms/do_win64a.bat" - ) - else() - multi_list(APPEND ${out_setup_rules} - WRAP "${PERL_EXECUTABLE}" - ARGS "${OPENSSL_SOURCE_DIR}/Configure" VC-WIN32 ${ARGN} - LOG "${OPENSSL_CONFIG_LOG}" - ) - multi_list(APPEND ${out_setup_rules} - WRAP "${OPENSSL_SOURCE_DIR}/ms/do_nasm.bat" - ) - endif() - - multi_list(APPEND ${out_setup_rules} - EVAL "openssl_build_patch_windows()" - ) - - multi_list(APPEND ${out_setup_rules} - EVAL - "file(MAKE_DIRECTORY \"${OPENSSL_BUILD_BUILD_DIR}/tmp/lib\")" - "file(MAKE_DIRECTORY \"${OPENSSL_BUILD_BUILD_DIR}/tmp/dll\")" - "file(MAKE_DIRECTORY \"${OPENSSL_BUILD_INSTALL_DIR}/lib\")" - "file(MAKE_DIRECTORY \"${OPENSSL_BUILD_INSTALL_DIR}/dll\")" - "file(MAKE_DIRECTORY \"${OPENSSL_BUILD_INSTALL_DIR}/include/openssl\")" - ) - - multi_list(APPEND ${out_setup_rules} - WRAP nmake - ARGS /nologo /f "${OPENSSL_SOURCE_DIR}/ms/nt.mak" - ARGS "OUT_D=${OPENSSL_BUILD_INSTALL_DIR}/lib" - ARGS "TMP_D=${OPENSSL_BUILD_BUILD_DIR}/tmp/lib" - ARGS "INC_D=${OPENSSL_BUILD_INSTALL_DIR}/include" - ARGS "INCO_D=${OPENSSL_BUILD_INSTALL_DIR}/include/openssl" - ARGS headers lib - ) - - multi_list(APPEND ${out_setup_rules} - WRAP nmake - ARGS /nologo /f "${OPENSSL_SOURCE_DIR}/ms/ntdll.mak" - ARGS "OUT_D=${OPENSSL_BUILD_INSTALL_DIR}/dll" - ARGS "TMP_D=${OPENSSL_BUILD_BUILD_DIR}/tmp/dll" - ARGS "INC_D=${OPENSSL_BUILD_INSTALL_DIR}/include" - ARGS "INCO_D=${OPENSSL_BUILD_INSTALL_DIR}/include/openssl" - ARGS headers lib - ) -endmacro() - -# ------------------------------------------------------------------------------ -# Initializes the environment that will be needed to build the OPENSSL package -macro(openssl_build_initialization) - - # Parse arguments - parse_arguments("OPENSSL_BUILD" - "RUNTIME_STATIC;STATIC" - "SOURCE_DIR;INSTALL_DIR;BUILD_DIR" - "EXTRA_FLAGS" - "" - ${ARGN} - ) - - if(OpenSSL_DEBUG) - set(OpenSSL_DEBUG NO_LOG DEBUG) - else() - unset(OpenSSL_DEBUG) - endif() - - # Ensure perl runtime is accessible - if(NOT PERL_FOUND) - external_error( - "No perl interpreter found. " - "Please ensure perl is available from system before running " - "this script" - ) - endif() - - # Set path to log file - if(NOT OPENSSL_BUILD_BUILD_DIR) - set(OPENSSL_BUILD_LOG "${CMAKE_BINARY_DIR}/openssl_build.log") - set(OPENSSL_CONFIG_LOG "${CMAKE_BINARY_DIR}/openssl_configure.log") - else() - set(OPENSSL_BUILD_LOG "${OPENSSL_BUILD_BUILD_DIR}/build.log") - set(OPENSSL_CONFIG_LOG "${OPENSSL_BUILD_BUILD_DIR}/configure.log") - endif() - - # Check source directory - if(NOT IS_DIRECTORY "${OPENSSL_BUILD_SOURCE_DIR}" - OR NOT EXISTS "${OPENSSL_BUILD_SOURCE_DIR}/openssl.spec") - external_error("Source path to OpenSSL package not defined or invalid") - endif() - - # Create working directories - foreach(_tmp "${OPENSSL_BUILD_BUILD_DIR}" "${OPENSSL_BUILD_INSTALL_DIR}") - if(_tmp AND NOT IS_DIRECTORY "${_tmp}") - file(MAKE_DIRECTORY "${_tmp}") - endif() - endforeach() - unset(_tmp) - - # Retrieve OS name - unset(os_name) - if(NOT CMAKE_SYSTEM_NAME MATCHES "^(Darwin|Linux|Windows)$") - external_error("OS '${CMAKE_SYSTEM_NAME}' NOT HANDLED") - endif() - - string(TOLOWER "${CMAKE_SYSTEM_NAME}" os_name) - -endmacro() - -# ------------------------------------------------------------------------------ -# Build the OpenSSL command lines to compile package -macro(openssl_build_compilation_scripts out_setup_rules) - # Compute compilation rules - if(os_name STREQUAL "darwin") - openssl_build_setup_darwin(${out_setup_rules} ${ARGN}) - elseif(os_name STREQUAL "linux") - openssl_build_setup_linux(${out_setup_rules} ${ARGN}) - elseif(os_name STREQUAL "windows") - openssl_build_setup_windows(${out_setup_rules} ${ARGN}) - else() - external_error("COMPILATION FOR OS '${os_name}'NOT HANDLED") - endif() -endmacro() - -# ------------------------------------------------------------------------------ -function(openssl_run) - parse_arguments("RUN" - "" - "" - "EXEC;EVAL;WRAP" - "" - ${ARGN} - ) - - # Ensure that NO unparsed arguments has been found - if(RUN_ARGN) - message(FATAL_ERROR "Unparsed arguments found:\n${RUN_ARGN}") - endif() - - if(RUN_EVAL) - # Proceed to the EVAL (if any) - system_eval(${RUN_EVAL}) - elseif(RUN_EXEC) - # Proceed to the EXEC - system_execute( - COMMAND ${RUN_EXEC} - WORKING_DIR "${OPENSSL_BUILD_SOURCE_DIR}" - ${OpenSSL_DEBUG} - ) - else() - # Proceed to the wrapped execution - external_execute( - COMMAND ${RUN_WRAP} - WORKING_DIR "${OPENSSL_BUILD_SOURCE_DIR}" - ${OpenSSL_DEBUG} - ) - endif() -endfunction() - -# ------------------------------------------------------------------------------ -# Build the OpenSSL package from the given source path -function(openssl_build) - - # Prepare OpenSSL context - openssl_build_initialization("${ARGN}") - - # Set-up list of configuration options - unset(base_configure_options) - if(OPENSSL_BUILD_STATIC) - list(APPEND base_configure_options "no-shared") - else() - list(APPEND base_configure_options "shared") - endif() - - # Complete configure options - if(OPENSSL_BUILD_INSTALL_DIR) - list(APPEND base_configure_options - "--prefix=${OPENSSL_BUILD_INSTALL_DIR}" - ) - endif() - - if(OPENSSL_EXTRA_FLAGS) - list(APPEND base_configure_options "${OPENSSL_EXTRA_FLAGS}") - endif() - - # Generate configure and build scripts - unset(config_scripts) - openssl_build_compilation_scripts(config_scripts ${base_configure_options}) - - # Configure and build OpenSSL - foreach(_command ${config_scripts}) - openssl_run(${_command}) - endforeach() -endfunction() - diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..dd4c06d4 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,202 @@ +cmake_minimum_required(VERSION 2.8) + +# --- Project files declaration +set(VERSIONS_FILES + "${project_BINARY_DIR}/versions.h") + +FILE(GLOB_RECURSE COMMON_BOOST_FIBER_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/common/boost/fiber/*.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/common/boost/fiber/*.cpp") + +FILE(GLOB_RECURSE COMMON_CONFIG_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/common/config/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/common/config/*.cpp") + +FILE(GLOB_RECURSE COMMON_ERROR_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/common/error/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/common/error/*.cpp") + +FILE(GLOB_RECURSE COMMON_LOG_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/common/log/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/common/log/*.cpp") + +FILE(GLOB_RECURSE COMMON_NETWORK_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/common/network/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/common/network/*.cpp") + +set(CORE_COMMAND_LINE_BASE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/core/command_line/base.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/command_line/base.cpp" +) + +FILE(GLOB CORE_COMMAND_LINE_STANDARD_FILES + ${CORE_COMMAND_LINE_BASE_FILES} + "${CMAKE_CURRENT_SOURCE_DIR}/core/command_line/standard/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/command_line/standard/*.cpp") + +FILE(GLOB CORE_COMMAND_LINE_COPY_FILES + ${CORE_COMMAND_LINE_BASE_FILES} + "${CMAKE_CURRENT_SOURCE_DIR}/core/command_line/copy/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/command_line/copy/*.cpp") + +SET(CORE_ASYNC_ENGINE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/core/async_engine.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/async_engine.cpp") + +SET(CORE_NETWORK_PROTOCOL_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/core/network_protocol.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/network_protocol.cpp") + +FILE(GLOB_RECURSE CORE_TRANSPORT_POLICIES_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/core/transport_virtual_layer_policies/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/transport_virtual_layer_policies/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/core/transport_virtual_layer_policies/*.ipp") + +FILE(GLOB_RECURSE CORE_FACTORIES_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/core/factories/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/factories/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/core/factories/*.ipp" + "${CMAKE_CURRENT_SOURCE_DIR}/core/factory_manager/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/factory_manager/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/core/factory_manager/*.ipp" + "${CMAKE_CURRENT_SOURCE_DIR}/core/service_manager/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/service_manager/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/core/service_manager/*.ipp") + +FILE(GLOB BASE_SERVICE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/*.ipp") + +FILE(GLOB_RECURSE ADMIN_SERVICE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/admin/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/admin/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/admin/*.ipp") + +FILE(GLOB_RECURSE DATAGRAMS_TO_FIBERS_SERVICE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/datagrams_to_fibers/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/datagrams_to_fibers/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/datagrams_to_fibers/*.ipp") + +FILE(GLOB_RECURSE FIBERS_TO_DATAGRAMS_SERVICE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/fibers_to_datagrams/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/fibers_to_datagrams/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/fibers_to_datagrams/*.ipp") + +FILE(GLOB_RECURSE SOCKETS_TO_FIBERS_SERVICE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/sockets_to_fibers/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/sockets_to_fibers/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/sockets_to_fibers/*.ipp") + +FILE(GLOB_RECURSE FIBERS_TO_SOCKETS_SERVICE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/fibers_to_sockets/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/fibers_to_sockets/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/fibers_to_sockets/*.ipp") + +FILE(GLOB_RECURSE SOCKS_SERVICE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/socks/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/socks/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/socks/*.ipp") + +FILE(GLOB COPY_FILE_SERVICE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/fiber_to_file/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/fiber_to_file/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/file_to_fiber/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/file_to_fiber/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/file_enquirer/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/file_enquirer/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/packet/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/packet/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/filesystem/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/filesystem/*.cpp") + +FILE(GLOB_RECURSE PROCESS_SERVICE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/process/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/process/*.ipp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/process/*.cpp") + +FILE(GLOB_RECURSE USER_SERVICE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/user_services/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/user_services/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/services/user_services/*.ipp") + +set(CIRCUIT_CONFIG_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/core/circuit/config.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/circuit/config.cpp") + +FILE(GLOB_RECURSE CLIENT_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/core/client/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/client/*.ipp") + +FILE(GLOB_RECURSE SERVER_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/core/server/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/core/server/*.ipp") + +# --- Include windows impl +if (WIN32) + # --- Filesystem globbing + FILE(GLOB_RECURSE COPY_FILE_SERVICE_WINDOWS_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/filesystem/windows/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/services/copy_file/filesystem/windows/*.cpp") + + set(COPY_FILE_SERVICE_FILES + ${COPY_FILE_SERVICE_FILES} + ${COPY_FILE_SERVICE_WINDOWS_FILES}) +endif (WIN32) + +# --- Include linux/unix impl +if (UNIX) + # --- Filesystem globbing + FILE(GLOB_RECURSE COPY_FILE_SERVICE_LINUX_FILES + "services/copy_file/filesystem/linux/*.h" + "services/copy_file/filesystem/linux/*.cpp") + + set(COPY_FILE_SERVICE_FILES + ${COPY_FILE_SERVICE_FILES} + ${COPY_FILE_SERVICE_LINUX_FILES}) +endif (UNIX) + +# --- Set macro SSF service files +set(SERVICES_FILES + ${BASE_SERVICE_FILES} + ${ADMIN_SERVICE_FILES} + ${DATAGRAMS_TO_FIBERS_SERVICE_FILES} + ${FIBERS_TO_DATAGRAMS_SERVICE_FILES} + ${SOCKETS_TO_FIBERS_SERVICE_FILES} + ${FIBERS_TO_SOCKETS_SERVICE_FILES} + ${SOCKS_SERVICE_FILES} + ${USER_SERVICE_FILES} + ${COPY_FILE_SERVICE_FILES} + ${PROCESS_SERVICE_FILES} +) + +# --- Set macro SSF source files +set(SSF_SOURCES + ${VERSIONS_FILES} + ${COMMON_BOOST_FIBER_FILES} + ${COMMON_CONFIG_FILES} + ${COMMON_ERROR_FILES} + ${COMMON_LOG_FILES} + ${CORE_ASYNC_ENGINE_FILES} + ${COMMON_NETWORK_FILES} + ${CORE_NETWORK_PROTOCOL_FILES} + ${CORE_TRANSPORT_POLICIES_FILES} + ${CORE_NETWORK_VIRTUAL_LAYER_POLICIES_FILES} + ${CORE_FACTORIES_FILES} + ${SERVICES_FILES} + ${CIRCUIT_CONFIG_FILES} + ${CLIENT_FILES} + ${SERVER_FILES} +) + +# --- Include source directory +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +add_subdirectory(framework) +add_subdirectory(core) + +if (BUILD_UNIT_TESTS) + add_subdirectory(tests) +endif(BUILD_UNIT_TESTS) diff --git a/src/common/boost/fiber/basic_fiber_demux.hpp b/src/common/boost/fiber/basic_fiber_demux.hpp index d347686c..3a8f5bbf 100644 --- a/src/common/boost/fiber/basic_fiber_demux.hpp +++ b/src/common/boost/fiber/basic_fiber_demux.hpp @@ -9,13 +9,14 @@ #define SSF_COMMON_BOOST_ASIO_FIBER_BASIC_FIBER_DEMUX_HPP_ #if defined(_MSC_VER) && (_MSC_VER >= 1200) -# pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include -#include + +#include #include "common/boost/fiber/basic_fiber_demux_service.hpp" #include "common/boost/fiber/detail/fiber_id.hpp" @@ -30,12 +31,12 @@ namespace fiber { namespace detail { template class basic_fiber_impl; -} // namespace detail +} // namespace detail template > class basic_fiber_demux : private boost::noncopyable { -public: + public: /// Type of the socket over which to demultiplex typedef StreamSocket socket_type; @@ -60,15 +61,16 @@ class basic_fiber_demux : private boost::noncopyable { /// Type of a pointer to the implementation class of the fiber. typedef typename Service::fiber_impl_type fiber_impl_type; - /// Type of the object identifying a fiber (with a remote and a local fiber port) + /// Type of the object identifying a fiber (with a remote and a local fiber + /// port) typedef typename Service::fiber_id fiber_id; -private: - typedef boost::asio::fiber::detail::basic_pending_accept_operation accept_op; + private: + typedef boost::asio::fiber::detail::basic_pending_accept_operation< + StreamSocket> accept_op; typedef std::function close_handler_type; -public: - + public: /// Construct a basic_fiber_demux object /** * This constructor creates a basic_fiber_demux object without any socket to @@ -78,18 +80,15 @@ class basic_fiber_demux : private boost::noncopyable { * dispatch handlers for any asynchronous operations performed on it. */ explicit basic_fiber_demux(boost::asio::io_service& io_service) - : service_(boost::asio::use_service(io_service)), - impl_(nullptr) - { - } + : service_(boost::asio::use_service(io_service)), + impl_(nullptr) {} /// Return the io_service managing the fiber demux. /** * This function is used to recover the io_service managing asynchronous * operations of the fiber demux. */ - boost::asio::io_service& get_io_service() - { + boost::asio::io_service& get_io_service() { return service_.get_io_service(); } @@ -105,11 +104,10 @@ class basic_fiber_demux : private boost::noncopyable { * read from or written to by the user. */ void fiberize(StreamSocket socket, close_handler_type close = []() {}, - size_t mtu = 60 * 1024) - { - if (mtu > 60 * 1024) - { - BOOST_LOG_TRIVIAL(warning) << "fiber demux: MTU too big, replaced with MAX_VALUE"; + size_t mtu = 60 * 1024) { + if (mtu > 60 * 1024) { + SSF_LOG(kLogWarning) + << "fiber demux: MTU too big, replaced with MAX_VALUE"; mtu = 60 * 1024; } @@ -122,7 +120,8 @@ class basic_fiber_demux : private boost::noncopyable { * This function binds a fiber acceptor to the specified local fiber port * on the io_fiber demultiplexer. * - * @param local_port A local fiber port to which the fiber acceptor will be bound. + * @param local_port A local fiber port to which the fiber acceptor will be + *bound. * * @param fib_impl The fiber acceptor implementation object. * @@ -142,9 +141,7 @@ class basic_fiber_demux : private boost::noncopyable { * @param local_port The local fiber port. * @param ec Set to indicate what error occurred, if any. */ - void listen(local_port_type local_port, - boost::system::error_code& ec) - { + void listen(local_port_type local_port, boost::system::error_code& ec) { service_.listen(impl_, local_port, ec); } @@ -157,8 +154,7 @@ class basic_fiber_demux : private boost::noncopyable { * * @param fib_impl The implementation object of the fiber being connected */ - void async_send_ack(fiber_impl_type fib_impl, accept_op* op) - { + void async_send_ack(fiber_impl_type fib_impl, accept_op* op) { service_.async_send_ack(impl_, fib_impl, op); } @@ -167,16 +163,16 @@ class basic_fiber_demux : private boost::noncopyable { * This function is used to asynchronously connect a * fiber. The function call always returns immediately. * - * @param remote_port The remote fiber port to which the fiber should be connected. + * @param remote_port The remote fiber port to which the fiber should be + *connected. * * @param fib_impl The implementation object of the fiber. * - * @note When the connection process is finished, the handler connect_user_handler + * @note When the connection process is finished, the handler + *connect_user_handler * of the implementation object will be called. */ - void async_connect(remote_port_type remote_port, - fiber_impl_type fib_impl) - { + void async_connect(remote_port_type remote_port, fiber_impl_type fib_impl) { service_.async_connect(impl_, remote_port, fib_impl); } @@ -203,40 +199,30 @@ class basic_fiber_demux : private boost::noncopyable { * boost::asio::io_service::post(). */ template - void async_receive(const MutableBufferSequence& buffers, - Handler& handler) - { - + void async_receive(const MutableBufferSequence& buffers, Handler& handler) { return service_.async_receive(impl_, buffers, handler); } template - void async_send(const ConstBufferSequence& buffers, - fiber_id id, - Handler& handler) - { - + void async_send(const ConstBufferSequence& buffers, fiber_id id, + Handler& handler) { return service_.async_send_data(impl_, id, buffers, handler); } template void async_send_dgr(const ConstBufferSequence& buffers, remote_port_type remote_port, fiber_impl_type fib_impl, - Handler& handler) - { - return service_.async_send_datagram(impl_, - remote_port, - fib_impl, - buffers, + Handler& handler) { + return service_.async_send_datagram(impl_, remote_port, fib_impl, buffers, handler); } /// Close fiber. /** - * This closes a fiber immediatly. It cancels all pending operations from this fiber. + * This closes a fiber immediatly. It cancels all pending operations from this + * fiber. */ - void close_fiber(fiber_impl_type fib_impl) - { + void close_fiber(fiber_impl_type fib_impl) { service_.close_fiber(impl_, fib_impl); } @@ -244,22 +230,19 @@ class basic_fiber_demux : private boost::noncopyable { /** * This synchronously close the fiber demux. */ - void close() - { - if (impl_.get()) - { + void close() { + if (impl_.get()) { service_.close(impl_); } } -private: + private: service_type& service_; implementation_type impl_; - }; -} // namespace fiber -} // namespace asio -} // namespace boost +} // namespace fiber +} // namespace asio +} // namespace boost #include diff --git a/src/common/boost/fiber/basic_fiber_demux_service.ipp b/src/common/boost/fiber/basic_fiber_demux_service.ipp index 5da1a9d3..2cc196a5 100644 --- a/src/common/boost/fiber/basic_fiber_demux_service.ipp +++ b/src/common/boost/fiber/basic_fiber_demux_service.ipp @@ -14,12 +14,13 @@ #include #include -#include #include #include #include #include +#include + #include "common/error/error.h" #include "common/boost/fiber/detail/basic_fiber_demux_impl.hpp" #include "common/boost/fiber/detail/fiber_buffer.hpp" @@ -33,11 +34,11 @@ namespace fiber { template void basic_fiber_demux_service::fiberize(implementation_type impl) { if (!impl) { - BOOST_LOG_TRIVIAL(debug) << "demux: fiberizing NOK " << ssf::error::broken_pipe; + SSF_LOG(kLogDebug) << "demux: fiberizing NOK " << ::error::broken_pipe; return; } - BOOST_LOG_TRIVIAL(trace) << "demux: fiberizing"; + SSF_LOG(kLogTrace) << "demux: fiberizing"; async_poll_packets(impl); } @@ -48,8 +49,8 @@ void basic_fiber_demux_service::bind(implementation_type impl, fiber_impl_type fib_impl, boost::system::error_code& ec) { if (!impl) { - BOOST_LOG_TRIVIAL(debug) << "demux: bind NOK " << ssf::error::broken_pipe; - ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + SSF_LOG(kLogDebug) << "demux: bind NOK " << ::error::broken_pipe; + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); return; } @@ -65,11 +66,11 @@ void basic_fiber_demux_service::bind(implementation_type impl, { boost::recursive_mutex::scoped_lock lock1(impl->bound_mutex); boost::recursive_mutex::scoped_lock lock2(impl->used_ports_mutex); - BOOST_LOG_TRIVIAL(debug) << "demux: try to bind " << fib_impl << " to " + SSF_LOG(kLogDebug) << "demux: try to bind " << fib_impl << " to " << id.local_port() << "," << id.remote_port() << " debug " << &id << "," << &fib_impl->id; if (receiving_id.remote_port() && !impl->bound.count(receiving_id)) { - BOOST_LOG_TRIVIAL(debug) << "demux: bind OK"; + SSF_LOG(kLogDebug) << "demux: bind OK"; impl->bound[receiving_id] = fib_impl; impl->used_ports.insert(id.local_port()); @@ -78,12 +79,12 @@ void basic_fiber_demux_service::bind(implementation_type impl, fib_impl->closed = false; } - ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + ec.assign(::error::success, ::error::get_ssf_category()); } else { - BOOST_LOG_TRIVIAL(debug) << "demux: bind NOK " - << ssf::error::device_or_resource_busy; - ec.assign(ssf::error::device_or_resource_busy, - ssf::error::get_ssf_category()); + SSF_LOG(kLogDebug) << "demux: bind NOK " + << ::error::device_or_resource_busy; + ec.assign(::error::device_or_resource_busy, + ::error::get_ssf_category()); } } } @@ -92,7 +93,7 @@ template bool basic_fiber_demux_service::is_bound(implementation_type impl, const fiber_id& id) { if (!impl) { - BOOST_LOG_TRIVIAL(debug) << "demux: is_bound NOK " << ssf::error::broken_pipe; + SSF_LOG(kLogDebug) << "demux: is_bound NOK " << ::error::broken_pipe; return false; } @@ -104,13 +105,13 @@ template void basic_fiber_demux_service::unbind(implementation_type impl, const fiber_id& id) { if (!impl) { - BOOST_LOG_TRIVIAL(debug) << "demux: unbind NOK " << ssf::error::broken_pipe; + SSF_LOG(kLogDebug) << "demux: unbind NOK " << ::error::broken_pipe; return; } boost::recursive_mutex::scoped_lock lock1(impl->bound_mutex); boost::recursive_mutex::scoped_lock lock2(impl->used_ports_mutex); - BOOST_LOG_TRIVIAL(trace) << "demux: unbound " << id.local_port() << "," + SSF_LOG(kLogTrace) << "demux: unbound " << id.local_port() << "," << id.remote_port(); impl->bound.erase(id.returning_id()); @@ -122,8 +123,8 @@ void basic_fiber_demux_service::listen(implementation_type impl, local_port_type local_port, boost::system::error_code& ec) { if (!impl) { - BOOST_LOG_TRIVIAL(debug) << "demux: listen NOK " << ssf::error::broken_pipe; - ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + SSF_LOG(kLogDebug) << "demux: listen NOK " << ::error::broken_pipe; + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); return; } @@ -131,16 +132,16 @@ void basic_fiber_demux_service::listen(implementation_type impl, if (!impl->listening.count(local_port) && is_bound(impl, fiber_id(0, local_port))) { - BOOST_LOG_TRIVIAL(debug) << "demux: listening on " << local_port; + SSF_LOG(kLogDebug) << "demux: listening on " << local_port; impl->listening.insert(local_port); - ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + ec.assign(::error::success, ::error::get_ssf_category()); } else if (impl->listening.count(local_port)) { - ec.assign(ssf::error::device_or_resource_busy, - ssf::error::get_ssf_category()); + ec.assign(::error::device_or_resource_busy, + ::error::get_ssf_category()); } else { - ec.assign(ssf::error::protocol_error, ssf::error::get_ssf_category()); + ec.assign(::error::protocol_error, ::error::get_ssf_category()); } } @@ -148,7 +149,7 @@ template bool basic_fiber_demux_service::is_listening(implementation_type impl, local_port_type local_port) { if (!impl) { - BOOST_LOG_TRIVIAL(debug) << "demux: is_listening NOK " << ssf::error::broken_pipe; + SSF_LOG(kLogDebug) << "demux: is_listening NOK " << ::error::broken_pipe; return false; } @@ -160,14 +161,14 @@ template void basic_fiber_demux_service::stop_listening(implementation_type impl, local_port_type local_port) { if (!impl) { - BOOST_LOG_TRIVIAL(debug) << "demux: stop_listening NOK " - << ssf::error::broken_pipe; + SSF_LOG(kLogDebug) << "demux: stop_listening NOK " + << ::error::broken_pipe; return; } boost::recursive_mutex::scoped_lock lock1(impl->listening_mutex); boost::recursive_mutex::scoped_lock lock2(impl->used_ports_mutex); - BOOST_LOG_TRIVIAL(debug) << "demux: stopped listening on " << local_port; + SSF_LOG(kLogDebug) << "demux: stopped listening on " << local_port; impl->listening.erase(local_port); impl->used_ports.erase(local_port); @@ -191,7 +192,7 @@ void basic_fiber_demux_service::async_poll_packets( this->dispatch_buffer(impl, p_fiber_buff); this->async_poll_packets(impl); } else { - BOOST_LOG_TRIVIAL(debug) << "demux: error in dispatch handler " << ec.value() + SSF_LOG(kLogDebug) << "demux: error in dispatch handler " << ec.value() << ":" << ec.message() << " | " << " " << bytes_transferred << " bytes transferred"; @@ -229,8 +230,8 @@ void basic_fiber_demux_service::async_push_packets( boost::asio::async_write(impl->socket, toSendPriority.buffer, handler); } else { impl->socket.get_io_service().post(boost::bind( - handler, boost::system::error_code(ssf::error::connection_aborted, - ssf::error::get_ssf_category()), + handler, boost::system::error_code(::error::connection_aborted, + ::error::get_ssf_category()), 0)); } } @@ -242,7 +243,7 @@ void basic_fiber_demux_service::dispatch_buffer( const auto flags = header.flags(); - BOOST_LOG_TRIVIAL(debug) << "demux: dispatch " << uint32_t(header.version()) << " " + SSF_LOG(kLogDebug) << "demux: dispatch " << uint32_t(header.version()) << " " << header.id().remote_port() << " " << header.id().local_port() << " " << uint32_t(flags) << " " << header.data_size(); @@ -271,7 +272,7 @@ void basic_fiber_demux_service::dispatch_buffer( template void basic_fiber_demux_service::handle_dgr(implementation_type impl, p_fiber_buffer p_fiber_buff) { - BOOST_LOG_TRIVIAL(debug) << "demux: handle dgr"; + SSF_LOG(kLogDebug) << "demux: handle dgr"; const auto& full_id = p_fiber_buff->header().id(); const auto& half_id = fiber_id(p_fiber_buff->header().id().remote_port(), 0); boost::recursive_mutex::scoped_lock lock(impl->bound_mutex); @@ -296,7 +297,7 @@ void basic_fiber_demux_service::handle_dgr(implementation_type impl, template void basic_fiber_demux_service::handle_push(implementation_type impl, p_fiber_buffer p_fiber_buff) { - BOOST_LOG_TRIVIAL(debug) << "demux: handle push"; + SSF_LOG(kLogDebug) << "demux: handle push"; const auto& header = p_fiber_buff->header(); boost::recursive_mutex::scoped_lock lock(impl->bound_mutex); @@ -314,7 +315,7 @@ void basic_fiber_demux_service::handle_ack(implementation_type impl, p_fiber_buffer p_fiber_buff) { const auto& header = p_fiber_buff->header(); boost::recursive_mutex::scoped_lock lock_bound(impl->bound_mutex); - BOOST_LOG_TRIVIAL(debug) << "demux: handle ack"; + SSF_LOG(kLogDebug) << "demux: handle ack"; if (impl->bound.count(header.id())) { auto p_fib_impl = impl->bound[header.id()]; @@ -325,8 +326,8 @@ void basic_fiber_demux_service::handle_ack(implementation_type impl, if (p_fib_impl->connecting) { p_fib_impl->set_connected(); auto on_ack = p_fib_impl->access_connect_handler(); - on_ack(boost::system::error_code(ssf::error::success, - ssf::error::get_ssf_category())); + on_ack(boost::system::error_code(::error::success, + ::error::get_ssf_category())); } } else { async_send_rst(impl, header.id().returning_id(), @@ -337,7 +338,7 @@ void basic_fiber_demux_service::handle_ack(implementation_type impl, template void basic_fiber_demux_service::handle_syn(implementation_type impl, p_fiber_buffer p_fiber_buff) { - BOOST_LOG_TRIVIAL(debug) << "demux: handle syn"; + SSF_LOG(kLogDebug) << "demux: handle syn"; const auto& header = p_fiber_buff->header(); boost::recursive_mutex::scoped_lock lock1(impl->bound_mutex); boost::recursive_mutex::scoped_lock lock2(impl->listening_mutex); @@ -355,7 +356,7 @@ void basic_fiber_demux_service::handle_syn(implementation_type impl, template void basic_fiber_demux_service::handle_rst(implementation_type impl, p_fiber_buffer p_fiber_buff) { - BOOST_LOG_TRIVIAL(debug) << "demux: handle rst"; + SSF_LOG(kLogDebug) << "demux: handle rst"; const auto& header = p_fiber_buff->header(); auto returning_id = header.id().returning_id(); boost::recursive_mutex::scoped_lock lock_bound(impl->bound_mutex); @@ -369,8 +370,8 @@ void basic_fiber_demux_service::handle_rst(implementation_type impl, if (p_fib_impl->connecting || p_fib_impl->connected) { if (p_fib_impl->connecting) { auto on_connection = p_fib_impl->access_connect_handler(); - on_connection(boost::system::error_code(ssf::error::connection_refused, - ssf::error::get_ssf_category())); + on_connection(boost::system::error_code(::error::connection_refused, + ::error::get_ssf_category())); } else { p_fib_impl->set_disconnected(); auto rst_sent = [this, impl, returning_id, p_fib_impl]() { @@ -410,8 +411,8 @@ void basic_fiber_demux_service::async_send_push(implementation_type impl, p_timer->async_wait(lambda); } } else { - handler(boost::system::error_code(ssf::error::protocol_error, - ssf::error::get_ssf_category()), + handler(boost::system::error_code(::error::protocol_error, + ::error::get_ssf_category()), 0); } } @@ -430,7 +431,7 @@ void basic_fiber_demux_service::async_send_dgr(implementation_type impl, boost::system::error_code ec; bind(impl, fib_impl->id.local_port(), fib_impl, ec); if (ec) { - BOOST_LOG_TRIVIAL(debug) << "demux: error dgr " << ec.message() << ec.value(); + SSF_LOG(kLogDebug) << "demux: error dgr " << ec.message() << ec.value(); io_service_.post(boost::bind(handler, ec, 0)); return; } @@ -452,8 +453,8 @@ void basic_fiber_demux_service::async_send_dgr(implementation_type impl, } } else { io_service_.post(boost::bind( - handler, boost::system::error_code(ssf::error::protocol_error, - ssf::error::get_ssf_category()), + handler, boost::system::error_code(::error::protocol_error, + ::error::get_ssf_category()), 0)); } } @@ -469,14 +470,14 @@ void basic_fiber_demux_service::async_send_ack(implementation_type impl, bind(impl, fib_impl->id.local_port(), fib_impl, ec); fib_impl->set_connected(); if (ec) { - BOOST_LOG_TRIVIAL(debug) << "demux: error send ack " << ec.message() << ec.value(); + SSF_LOG(kLogDebug) << "demux: error send ack " << ec.message() << ec.value(); op->complete(ec, 0); } else { auto handler = [=](const boost::system::error_code& ec, std::size_t) { if (!!ec) { - BOOST_LOG_TRIVIAL(debug) << "demux: error send ack handler " << ec.message(); + SSF_LOG(kLogDebug) << "demux: error send ack handler " << ec.message(); } else { - BOOST_LOG_TRIVIAL(trace) << "demux: ack sent"; + SSF_LOG(kLogTrace) << "demux: ack sent"; } op->complete(ec, 0); @@ -502,7 +503,7 @@ void basic_fiber_demux_service::async_send_syn(implementation_type impl, // Bind reverse the id in bound map so reverse it... if ((impl->bound).count(id.returning_id())) { auto p_fib_impl = impl->bound[id.returning_id()]; - BOOST_LOG_TRIVIAL(debug) << "demux: async send syn"; + SSF_LOG(kLogDebug) << "demux: async send syn"; boost::recursive_mutex::scoped_lock lock_state(p_fib_impl->state_mutex); @@ -510,9 +511,9 @@ void basic_fiber_demux_service::async_send_syn(implementation_type impl, p_fib_impl->set_connecting(); auto handler = [](const boost::system::error_code& ec, std::size_t) { if (!!ec) { - BOOST_LOG_TRIVIAL(debug) << "demux: error " << ec.message(); + SSF_LOG(kLogDebug) << "demux: error " << ec.message(); } else { - BOOST_LOG_TRIVIAL(trace) << "demux: syn sent"; + SSF_LOG(kLogTrace) << "demux: syn sent"; } }; @@ -528,14 +529,14 @@ template void basic_fiber_demux_service::async_send_rst( implementation_type impl, fiber_id id, const Handler& close_handler) { - BOOST_LOG_TRIVIAL(debug) << "demux: async send rst"; + SSF_LOG(kLogDebug) << "demux: async send rst"; auto handler = [this, id, close_handler](const boost::system::error_code& ec, std::size_t) { if (!!ec) { - BOOST_LOG_TRIVIAL(debug) << "demux: async send rst error " << ec.value() << " : " + SSF_LOG(kLogDebug) << "demux: async send rst error " << ec.value() << " : " << ec.message(); } else { - BOOST_LOG_TRIVIAL(trace) << "demux: rst sent " << id.local_port() << " " + SSF_LOG(kLogTrace) << "demux: rst sent " << id.local_port() << " " << id.remote_port(); } @@ -571,7 +572,7 @@ void basic_fiber_demux_service::async_connect( implementation_type impl, boost::asio::fiber::detail::fiber_id::remote_port_type remote_port, fiber_impl_type fib_impl) { - BOOST_LOG_TRIVIAL(debug) << "demux: async connect to remote port : " + SSF_LOG(kLogDebug) << "demux: async connect to remote port : " << remote_port; fib_impl->id.set_remote_port(remote_port); @@ -599,8 +600,8 @@ void basic_fiber_demux_service::async_send( if (buffers_size > impl->mtu) { if (flags & kFlagDatagram) { io_service_.post(boost::bind( - handler, boost::system::error_code(ssf::error::message_too_long, - ssf::error::get_ssf_category()), 0)); + handler, boost::system::error_code(::error::message_too_long, + ::error::get_ssf_category()), 0)); return; } buffers_size = impl->mtu; @@ -639,10 +640,10 @@ void basic_fiber_demux_service::async_send( auto& header_b = p_fiber_buffer->header(); auto flags_b = header_b.flags(); - BOOST_LOG_TRIVIAL(debug) << "demux: sending " << uint32_t(header_b.version()) << " " - << header_b.id().remote_port() << " " - << header_b.id().local_port() << " " - << uint32_t(flags_b) << " " << header_b.data_size(); + SSF_LOG(kLogDebug) << "demux: sending " << uint32_t(header_b.version()) << " " + << header_b.id().remote_port() << " " + << header_b.id().local_port() << " " << uint32_t(flags_b) + << " " << header_b.data_size(); impl->socket.get_io_service().post(do_push_packets); } diff --git a/src/common/boost/fiber/datagram_fiber_service.hpp b/src/common/boost/fiber/datagram_fiber_service.hpp index 5ddd2319..f68b08cb 100644 --- a/src/common/boost/fiber/datagram_fiber_service.hpp +++ b/src/common/boost/fiber/datagram_fiber_service.hpp @@ -223,8 +223,8 @@ class datagram_fiber_service { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::not_connected, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::not_connected, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -235,8 +235,8 @@ class datagram_fiber_service if (boost::asio::buffer_size(buffers) == 0) { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::success, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::success, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -284,8 +284,8 @@ class datagram_fiber_service { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::not_connected, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::not_connected, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -296,8 +296,8 @@ class datagram_fiber_service if (boost::asio::buffer_size(buffers) == 0) { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::success, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::success, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -336,8 +336,8 @@ class datagram_fiber_service { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::not_connected, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::not_connected, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -348,8 +348,8 @@ class datagram_fiber_service if (boost::asio::buffer_size(buffers) == 0) { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::success, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::success, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -401,8 +401,8 @@ class datagram_fiber_service { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::not_connected, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::not_connected, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -413,8 +413,8 @@ class datagram_fiber_service if (boost::asio::buffer_size(buffers) == 0) { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::success, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::success, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); diff --git a/src/common/boost/fiber/detail/basic_fiber_impl.hpp b/src/common/boost/fiber/detail/basic_fiber_impl.hpp index 82a0fc39..da8964c8 100644 --- a/src/common/boost/fiber/detail/basic_fiber_impl.hpp +++ b/src/common/boost/fiber/detail/basic_fiber_impl.hpp @@ -10,12 +10,14 @@ #if defined(_MSC_VER) && (_MSC_VER >= 1200) #pragma once -#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include #include +#include + #include "common/error/error.h" #include "common/boost/fiber/detail/fiber_id.hpp" #include "common/boost/fiber/detail/fiber_header.hpp" @@ -30,51 +32,48 @@ namespace fiber { namespace detail { template -struct make_asio_queue -{ +struct make_asio_queue { typedef boost::asio::detail::op_queue type; typedef T* value_type; }; template -struct make_queue -{ +struct make_queue { typedef std::queue type; typedef T value_type; }; template class basic_fiber_impl - : public std::enable_shared_from_this> -{ -private: + : public std::enable_shared_from_this> { + private: /// Type for a local fiber port typedef boost::asio::fiber::detail::fiber_id::local_port_type local_port_type; /// Type for a remote fiber port - typedef boost::asio::fiber::detail::fiber_id::remote_port_type remote_port_type; + typedef boost::asio::fiber::detail::fiber_id::remote_port_type + remote_port_type; /// Type for a pointer to a fiber impl typedef std::shared_ptr> p_impl; /// Type for an object storing the user read request - typedef boost::asio::fiber::detail::basic_pending_read_operation - read_op; - + typedef boost::asio::fiber::detail::basic_pending_read_operation read_op; + /// Type for an object storing the user read request typedef boost::asio::fiber::detail::basic_pending_dgr_read_operation - dgr_read_op; + dgr_read_op; /// Type for an object storing the user accept request - typedef boost::asio::fiber::detail::basic_pending_accept_operation - accept_op; + typedef boost::asio::fiber::detail::basic_pending_accept_operation< + StreamSocket> accept_op; /// Type for the fiber demultiplexer typedef boost::asio::fiber::basic_fiber_demux fiber_demux_type; /// Type for the queue storing the pending read requests typedef make_asio_queue::type read_op_queue_type; - + /// Type for the queue storing the pending read requests for datagrams typedef make_asio_queue::type read_dgr_op_queue_type; @@ -97,28 +96,28 @@ class basic_fiber_impl /// Type of the handler provided by the user when connecting a new fiber typedef std::function - connect_user_handler_type; + connect_user_handler_type; /// Type of the handler used when receiving a new packet typedef std::function&&, std::size_t)> - receive_handler_type; + receive_handler_type; /// Type of the handler used when receiving a new datagram - typedef std::function< - void(std::vector&&, remote_port_type remote_port, std::size_t)> - receive_dgr_handler_type; + typedef std::function&&, + remote_port_type remote_port, std::size_t)> + receive_dgr_handler_type; /// Type of the handler used when closing a fiber typedef std::function close_handler_type; /// Type of the handler provided by the user when closing a fiber typedef std::function - close_user_handler_type; + close_user_handler_type; /// Type of the handler used when an unknown error occurs typedef std::function error_handler_type; -private: + private: /// Constructor for a fiber implementation object /** * @param f_demux The demultiplexer used for this fiber @@ -126,65 +125,60 @@ class basic_fiber_impl * @param prio (Unused) The priority for this fiber on the demultiplexer * @param dgr The fiber accepts datagrams */ - basic_fiber_impl(fiber_demux_type* p_f_demux, - remote_port_type remote_port, - uint8_t prio, bool dgr) : - id(remote_port), - p_fib_demux(p_f_demux), - ready_in(true), - ready_out(true), - priority(prio), - state_mutex(), - closed(true), - connecting(false), - connected(false), - disconnecting(false), - disconnected(true), - read_op_queue_mutex(), - read_op_queue(), - data_queue_mutex(), - data_queue(), - dgr_data_queue_(), - accept_op_queue_mutex(), - accept_op_queue(), - port_queue_mutex(), - port_queue(), - accepts_dgr(dgr) - { - } - - basic_fiber_impl() : id(0), - p_fib_demux(nullptr), - ready_in(true), - ready_out(true), - priority(0), - state_mutex(), - closed(true), - connecting(false), - connected(false), - disconnecting(false), - disconnected(true), - read_op_queue_mutex(), - read_op_queue(), - data_queue_mutex(), - data_queue(), - dgr_data_queue_(), - accept_op_queue_mutex(), - accept_op_queue(), - port_queue_mutex(), - port_queue(), - accepts_dgr() - { - } - -public: + basic_fiber_impl(fiber_demux_type* p_f_demux, remote_port_type remote_port, + uint8_t prio, bool dgr) + : id(remote_port), + p_fib_demux(p_f_demux), + ready_in(true), + ready_out(true), + priority(prio), + state_mutex(), + closed(true), + connecting(false), + connected(false), + disconnecting(false), + disconnected(true), + read_op_queue_mutex(), + read_op_queue(), + data_queue_mutex(), + data_queue(), + dgr_data_queue_(), + accept_op_queue_mutex(), + accept_op_queue(), + port_queue_mutex(), + port_queue(), + accepts_dgr(dgr) {} + + basic_fiber_impl() + : id(0), + p_fib_demux(nullptr), + ready_in(true), + ready_out(true), + priority(0), + state_mutex(), + closed(true), + connecting(false), + connected(false), + disconnecting(false), + disconnected(true), + read_op_queue_mutex(), + read_op_queue(), + data_queue_mutex(), + data_queue(), + dgr_data_queue_(), + accept_op_queue_mutex(), + accept_op_queue(), + port_queue_mutex(), + port_queue(), + accepts_dgr() {} + + public: /// Destructor ~basic_fiber_impl() {} -public: + public: /// Initialize the fiber impl by setting all its handler - void init() - { + void init() { accept_handler = [this](remote_port_type remote_port) { { boost::recursive_mutex::scoped_lock lock(this->port_queue_mutex); @@ -201,40 +195,40 @@ class basic_fiber_impl this->connect_user_handler(ec); }; - receive_handler = [this](std::vector&& data, - std::size_t bytes_transfered) { - { - boost::recursive_mutex::scoped_lock lock(this->data_queue_mutex); - boost::asio::streambuf::mutable_buffers_type buffers = - this->data_queue.prepare(bytes_transfered); - - boost::asio::buffer_copy(buffers, boost::asio::buffer(data)); - this->data_queue.commit(bytes_transfered); - } - this->r_queues_handler(); - }; + receive_handler = + [this](std::vector&& data, std::size_t bytes_transfered) { + { + boost::recursive_mutex::scoped_lock lock(this->data_queue_mutex); + boost::asio::streambuf::mutable_buffers_type buffers = + this->data_queue.prepare(bytes_transfered); + + boost::asio::buffer_copy(buffers, boost::asio::buffer(data)); + this->data_queue.commit(bytes_transfered); + } + this->r_queues_handler(); + }; - receive_dgr_handler = [this](std::vector&& data, - remote_port_type remote_port, - std::size_t bytes_transfered) { - { - boost::recursive_mutex::scoped_lock lock1(this->data_queue_mutex); - boost::recursive_mutex::scoped_lock lock2(this->port_queue_mutex); - this->port_queue.push(remote_port); - data.resize(bytes_transfered); - this->dgr_data_queue_.push(data); - } - this->r_dgr_queues_handler(); - }; + receive_dgr_handler = + [this](std::vector&& data, remote_port_type remote_port, + std::size_t bytes_transfered) { + { + boost::recursive_mutex::scoped_lock lock1(this->data_queue_mutex); + boost::recursive_mutex::scoped_lock lock2(this->port_queue_mutex); + this->port_queue.push(remote_port); + data.resize(bytes_transfered); + this->dgr_data_queue_.push(data); + } + this->r_dgr_queues_handler(); + }; close_handler = [this]() { - boost::system::error_code ec(ssf::error::connection_reset, - ssf::error::get_ssf_category()); + boost::system::error_code ec(::error::connection_reset, + ::error::get_ssf_category()); cancel_operations(ec); - BOOST_LOG_TRIVIAL(trace) << "fiber impl : close handler " - << this->id.remote_port() - << ":" << this->id.local_port(); + SSF_LOG(kLogTrace) << "fiber impl: close handler " + << this->id.remote_port() << ":" + << this->id.local_port(); this->set_closed(); }; @@ -243,8 +237,7 @@ class basic_fiber_impl } /// Accessor for the accept handler - accept_handler_type access_accept_handler() - { + accept_handler_type access_accept_handler() { auto self = this->shared_from_this(); auto lambda = [self, this](remote_port_type remote_port) { this->accept_handler(remote_port); @@ -252,11 +245,8 @@ class basic_fiber_impl return lambda; } - - /// Accessor for the connect handler - connect_handler_type access_connect_handler() - { + connect_handler_type access_connect_handler() { auto self = this->shared_from_this(); auto lambda = [self, this](boost::system::error_code ec) { this->connect_handler(ec); @@ -265,8 +255,7 @@ class basic_fiber_impl } /// Accessor for the receive handler - receive_handler_type access_receive_handler() - { + receive_handler_type access_receive_handler() { auto self = this->shared_from_this(); auto lambda = [self, this](std::vector&& data, std::size_t bytes_transfered) { @@ -276,28 +265,25 @@ class basic_fiber_impl } /// Accessor for the receive handler for datagrams - receive_dgr_handler_type access_receive_dgr_handler() - { + receive_dgr_handler_type access_receive_dgr_handler() { auto self = this->shared_from_this(); auto lambda = [self, this](std::vector&& data, - remote_port_type remote_port, - std::size_t bytes_transfered) { + remote_port_type remote_port, + std::size_t bytes_transfered) { this->receive_dgr_handler(std::move(data), remote_port, bytes_transfered); }; return lambda; } /// Accessor for the close handler - close_handler_type access_close_handler() - { + close_handler_type access_close_handler() { auto self = this->shared_from_this(); auto lambda = [self, this]() { this->close_handler(); }; return lambda; } /// Accessor for the error handler - error_handler_type access_error_handler() - { + error_handler_type access_error_handler() { auto self = this->shared_from_this(); auto lambda = [self, this]() { this->error_handler(); }; return lambda; @@ -310,19 +296,18 @@ class basic_fiber_impl * @param prio (Unused) The priority for this fiber on the demultiplexer * @param dgr The fiber accepts datagrams */ - static p_impl create(fiber_demux_type* p_f_demux, remote_port_type remote_port, - uint8_t prio = 0, bool dgr = false) - { + static p_impl create(fiber_demux_type* p_f_demux, + remote_port_type remote_port, uint8_t prio = 0, + bool dgr = false) { p_impl res = p_impl( - new basic_fiber_impl(p_f_demux, remote_port, prio, dgr)); + new basic_fiber_impl(p_f_demux, remote_port, prio, dgr)); res->init(); return res; } /// Create a new shared pointer to a fiber impl - static p_impl create() - { + static p_impl create() { p_impl res = p_impl(new basic_fiber_impl()); res->init(); @@ -334,15 +319,12 @@ class basic_fiber_impl * @param ec The error code corresponding to the previous call to this function */ void a_queues_handler( - boost::system::error_code ec = boost::system::error_code()) - { + boost::system::error_code ec = boost::system::error_code()) { boost::recursive_mutex::scoped_lock lock1(accept_op_queue_mutex); boost::recursive_mutex::scoped_lock lock2(port_queue_mutex); - if (!ec) - { - if (!accept_op_queue.empty() && !port_queue.empty()) - { + if (!ec) { + if (!accept_op_queue.empty() && !port_queue.empty()) { auto remote_port = port_queue.front(); port_queue.pop(); auto op = accept_op_queue.front(); @@ -353,17 +335,14 @@ class basic_fiber_impl p_fib_demux->async_send_ack(op->get_p_fib(), op); - BOOST_LOG_TRIVIAL(debug) - << "fiber impl : new connection from remote port: " << remote_port; + SSF_LOG(kLogDebug) << "fiber impl: new connection from remote port: " + << remote_port; p_fib_demux->get_io_service().dispatch(boost::bind( - &basic_fiber_impl::a_queues_handler, this->shared_from_this(), ec)); + &basic_fiber_impl::a_queues_handler, this->shared_from_this(), ec)); } - } - else - { - if (!accept_op_queue.empty()) - { + } else { + if (!accept_op_queue.empty()) { auto op = accept_op_queue.front(); accept_op_queue.pop(); @@ -378,48 +357,37 @@ class basic_fiber_impl * @param ec The error code corresponding to the previous call to this function */ void r_queues_handler( - boost::system::error_code ec = boost::system::error_code()) - { + boost::system::error_code ec = boost::system::error_code()) { boost::recursive_mutex::scoped_lock lock1(read_op_queue_mutex); boost::recursive_mutex::scoped_lock lock2(data_queue_mutex); { boost::recursive_mutex::scoped_lock lock(in_mutex); if (((data_queue.size() > 60 * 1024 * 1024) && ready_in) || - ((data_queue.size() < 40 * 1024 * 1024) && !ready_in)) - { + ((data_queue.size() < 40 * 1024 * 1024) && !ready_in)) { p_fib_demux->async_send_ack(this->shared_from_this(), nullptr); } } - BOOST_LOG_TRIVIAL(trace) << "fiber impl: queue empty : " - << read_op_queue.empty() - << " | queue size " - << data_queue.size() - << " | ec " - << ec.value(); - if (!ec) - { - if (!read_op_queue.empty() && data_queue.size()) - { + SSF_LOG(kLogTrace) << "fiber impl: queue empty : " << read_op_queue.empty() + << " | queue size " << data_queue.size() << " | ec " + << ec.value(); + if (!ec) { + if (!read_op_queue.empty() && data_queue.size()) { auto op = read_op_queue.front(); read_op_queue.pop(); size_t copied = op->fill_buffers(data_queue); - auto do_complete = [=]() { - op->complete(boost::system::error_code(), copied); - }; + auto do_complete = + [=]() { op->complete(boost::system::error_code(), copied); }; p_fib_demux->get_io_service().post(do_complete); p_fib_demux->get_io_service().dispatch(boost::bind( - &basic_fiber_impl::r_queues_handler, this->shared_from_this(), ec)); + &basic_fiber_impl::r_queues_handler, this->shared_from_this(), ec)); } - } - else - { - if (!read_op_queue.empty()) - { + } else { + if (!read_op_queue.empty()) { auto op = read_op_queue.front(); read_op_queue.pop(); op->complete(ec, 0); @@ -433,20 +401,18 @@ class basic_fiber_impl * @param ec The error code corresponding to the previous call to this function */ void r_dgr_queues_handler( - boost::system::error_code ec = boost::system::error_code()) - { + boost::system::error_code ec = boost::system::error_code()) { boost::recursive_mutex::scoped_lock lock1(read_op_queue_mutex); boost::recursive_mutex::scoped_lock lock2(data_queue_mutex); boost::recursive_mutex::scoped_lock lock3(port_queue_mutex); - BOOST_LOG_TRIVIAL(trace) << "fiber impl: queue empty: " << read_op_queue.empty() - << " | port queue empty : " << port_queue.empty() - << " | queue size " << data_queue.size() - << " | dgr queue size " << dgr_data_queue_.size() - << " | ec " << ec.value(); - if (!ec) - { - if (!read_dgr_op_queue.empty() && !port_queue.empty() && !dgr_data_queue_.empty()) - { + SSF_LOG(kLogTrace) << "fiber impl: queue empty: " << read_op_queue.empty() + << " | port queue empty : " << port_queue.empty() + << " | queue size " << data_queue.size() + << " | dgr queue size " << dgr_data_queue_.size() + << " | ec " << ec.value(); + if (!ec) { + if (!read_dgr_op_queue.empty() && !port_queue.empty() && + !dgr_data_queue_.empty()) { auto op = read_dgr_op_queue.front(); read_dgr_op_queue.pop(); @@ -455,32 +421,30 @@ class basic_fiber_impl auto data = dgr_data_queue_.front(); dgr_data_queue_.pop(); - + size_t copied = op->fill_buffers(data); op->set_remote_port(remote_port); - auto do_complete = [=]() { - op->complete(boost::system::error_code(), copied); - }; + auto do_complete = + [=]() { op->complete(boost::system::error_code(), copied); }; p_fib_demux->get_io_service().post(do_complete); - p_fib_demux->get_io_service().dispatch(boost::bind( - &basic_fiber_impl::r_dgr_queues_handler, this->shared_from_this(), ec)); + p_fib_demux->get_io_service().dispatch( + boost::bind(&basic_fiber_impl::r_dgr_queues_handler, + this->shared_from_this(), ec)); } - } - else - { - if (!read_op_queue.empty()) - { + } else { + if (!read_op_queue.empty()) { auto op = read_op_queue.front(); read_op_queue.pop(); auto do_complete = [=]() { op->complete(ec, 0); }; p_fib_demux->get_io_service().post(do_complete); - p_fib_demux->get_io_service().dispatch(boost::bind( - &basic_fiber_impl::r_dgr_queues_handler, this->shared_from_this(), ec)); + p_fib_demux->get_io_service().dispatch( + boost::bind(&basic_fiber_impl::r_dgr_queues_handler, + this->shared_from_this(), ec)); } } } @@ -490,17 +454,14 @@ class basic_fiber_impl * @param ec The error code that will be given to the pending operations */ void cancel_operations( - boost::system::error_code ec = - boost::system::error_code(ssf::error::interrupted, - ssf::error::get_ssf_category())) - { + boost::system::error_code ec = boost::system::error_code( + ::error::interrupted, ::error::get_ssf_category())) { r_queues_handler(ec); a_queues_handler(ec); } /// Make the fiber able to send and unable to receive - void init_accept_in_out() - { + void init_accept_in_out() { boost::recursive_mutex::scoped_lock lock1(in_mutex); boost::recursive_mutex::scoped_lock lock2(out_mutex); @@ -509,8 +470,7 @@ class basic_fiber_impl } /// Make the fiber able to send and receive - void init_connect_in_out() - { + void init_connect_in_out() { boost::recursive_mutex::scoped_lock lock1(in_mutex); boost::recursive_mutex::scoped_lock lock2(out_mutex); @@ -519,15 +479,13 @@ class basic_fiber_impl } /// Toggle the fiber ability to receive - void toggle_in() - { + void toggle_in() { boost::recursive_mutex::scoped_lock lock(in_mutex); ready_in = !ready_in; } /// Toggle the fiber ability to send - void toggle_out() - { + void toggle_out() { boost::recursive_mutex::scoped_lock lock(out_mutex); ready_out = !ready_out; } @@ -543,8 +501,7 @@ class basic_fiber_impl } /// Set implementation in connecting state - void set_connecting() - { + void set_connecting() { boost::recursive_mutex::scoped_lock lock_state(state_mutex); connecting = true; connected = false; @@ -553,8 +510,7 @@ class basic_fiber_impl } /// Set implementation in connected state - void set_connected() - { + void set_connected() { boost::recursive_mutex::scoped_lock lock_state(state_mutex); connecting = false; connected = true; @@ -564,8 +520,7 @@ class basic_fiber_impl } /// Set implementation in disconnecting state - void set_disconnecting() - { + void set_disconnecting() { boost::recursive_mutex::scoped_lock lock_state(state_mutex); connecting = false; connected = false; @@ -574,8 +529,7 @@ class basic_fiber_impl } /// Set implementation in disconnected state - void set_disconnected() - { + void set_disconnected() { boost::recursive_mutex::scoped_lock lock_state(state_mutex); connecting = false; connected = false; @@ -637,7 +591,7 @@ class basic_fiber_impl /// Fiber accepts datagram bool accepts_dgr; -private: + private: accept_handler_type accept_handler; connect_handler_type connect_handler; receive_handler_type receive_handler; @@ -646,9 +600,9 @@ class basic_fiber_impl error_handler_type error_handler; }; -} // namespace detail -} // namespace fiber -} // namespace asio -} // namespace boost +} // namespace detail +} // namespace fiber +} // namespace asio +} // namespace boost #endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_BASIC_FIBER_IMPL_HPP_ diff --git a/src/common/boost/fiber/fiber_acceptor_service.hpp b/src/common/boost/fiber/fiber_acceptor_service.hpp index 56dff2d2..3a66687a 100644 --- a/src/common/boost/fiber/fiber_acceptor_service.hpp +++ b/src/common/boost/fiber/fiber_acceptor_service.hpp @@ -18,6 +18,8 @@ #include #include +#include + #include "common/boost/fiber/basic_fiber_demux.hpp" #include "common/boost/fiber/detail/fiber_id.hpp" #include "common/boost/fiber/basic_endpoint.hpp" @@ -163,15 +165,14 @@ class fiber_acceptor_service is_convertible::value>::type* = 0) { boost::asio::detail::async_result_init - init(BOOST_ASIO_MOVE_CAST(AcceptHandler)(handler)); - + init(BOOST_ASIO_MOVE_CAST(AcceptHandler)(handler)); + { boost::recursive_mutex::scoped_lock lock_state(impl->state_mutex); if (impl->closed) { auto handler_to_post = [init]() mutable { - init.handler( - boost::system::error_code(ssf::error::not_connected, - ssf::error::get_ssf_category())); + init.handler(boost::system::error_code(::error::not_connected, + ::error::get_ssf_category())); }; this->get_io_service().post(handler_to_post); @@ -185,10 +186,11 @@ class fiber_acceptor_service fiber_impl->p_fib_demux = impl->p_fib_demux; fiber_impl->id.set_local_port(impl->id.local_port()); - BOOST_LOG_TRIVIAL(debug) << "fiber acceptor: local port set " << impl->id.local_port(); + SSF_LOG(kLogDebug) << "fiber acceptor: local port set " + << impl->id.local_port(); - typedef detail::pending_accept_operation< - AcceptHandler, typename Protocol::socket_type> op; + typedef detail::pending_accept_operation op; typename op::ptr p = { boost::asio::detail::addressof(init.handler), boost_asio_handler_alloc_helpers::allocate(sizeof(op), init.handler), diff --git a/src/common/boost/fiber/stream_fiber_service.hpp b/src/common/boost/fiber/stream_fiber_service.hpp index 78e0174a..d1506710 100644 --- a/src/common/boost/fiber/stream_fiber_service.hpp +++ b/src/common/boost/fiber/stream_fiber_service.hpp @@ -21,7 +21,8 @@ #include #include #include -#include + +#include #include "common/boost/fiber/detail/io_fiber_read_op.hpp" @@ -216,8 +217,8 @@ class stream_fiber_service { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::bad_file_descriptor, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::bad_file_descriptor, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -230,8 +231,8 @@ class stream_fiber_service if (!impl->connected) { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::not_connected, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::not_connected, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -244,8 +245,8 @@ class stream_fiber_service { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::success, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::success, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -278,8 +279,8 @@ class stream_fiber_service if (!impl->connected) { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::not_connected, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::not_connected, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); @@ -291,8 +292,8 @@ class stream_fiber_service if (boost::asio::buffer_size(buffers) == 0) { auto handler_to_post = [init]() mutable { init.handler( - boost::system::error_code(ssf::error::success, - ssf::error::get_ssf_category()), + boost::system::error_code(::error::success, + ::error::get_ssf_category()), 0); }; this->get_io_service().post(handler_to_post); diff --git a/src/common/config/config.cpp b/src/common/config/config.cpp index 8e1c7ff0..12b61542 100644 --- a/src/common/config/config.cpp +++ b/src/common/config/config.cpp @@ -1,67 +1,236 @@ -#include "common/config/config.h" - #include -#include -#include +#include #include #include +#include + +#include "common/config/config.h" #include "common/error/error.h" -ssf::Config ssf::LoadConfig(const std::string& filepath, - boost::system::error_code& ec) { - using boost::property_tree::ptree; - ptree pt; - ssf::Config config; +namespace ssf { +namespace config { - try { - if (filepath != "") { - boost::property_tree::read_json(filepath, pt); - - auto tls_optional = pt.get_child_optional("ssf.tls"); - if (tls_optional) { - auto& tls = tls_optional.get(); - - auto ca_cert_path_optional = tls.get_child_optional("ca_cert_path"); - if (ca_cert_path_optional) { - config.tls.ca_cert_path = ca_cert_path_optional.get().data(); - } - - auto cert_path_optional = tls.get_child_optional("cert_path"); - if (cert_path_optional) { - config.tls.cert_path = cert_path_optional.get().data(); - } - - auto key_path_optional = tls.get_child_optional("key_path"); - if (key_path_optional) { - config.tls.key_path = key_path_optional.get().data(); - } - - auto key_password_optional = tls.get_child_optional("key_password"); - if (key_password_optional) { - config.tls.key_password = key_password_optional.get().data(); - } - - auto dh_path_optional = tls.get_child_optional("dh_path"); - if (dh_path_optional) { - config.tls.dh_path = dh_path_optional.get().data(); - } - - auto cipher_alg_optional = tls.get_child_optional("cipher_alg"); - if (cipher_alg_optional) { - config.tls.cipher_alg = cipher_alg_optional.get().data(); - } - } +Tls::Tls() + : ca_cert_path_("./certs/trusted/ca.crt"), + cert_path_("./certs/certificate.crt"), + key_path_("./certs/private.key"), + key_password_(""), + dh_path_("./certs/dh4096.pem"), + cipher_alg_("DHE-RSA-AES256-GCM-SHA384") {} + +void Tls::Log() const { +#ifdef TLS_OVER_TCP_LINK + SSF_LOG(kLogInfo) << "config[tls]: CA cert path: <" << ca_cert_path_ << ">"; + SSF_LOG(kLogInfo) << "config[tls]: cert path: <" << cert_path_ << ">"; + SSF_LOG(kLogInfo) << "config[tls]: key path: <" << key_path_ << ">"; + SSF_LOG(kLogInfo) << "config[tls]: key password: <" << key_password_ << ">"; + SSF_LOG(kLogInfo) << "config[tls]: dh path: <" << dh_path_ << ">"; + SSF_LOG(kLogInfo) << "config[tls]: cipher suite: <" << cipher_alg_ << ">"; +#endif +} + +Proxy::Proxy() + : host_(""), + port_(""), + username_(""), + domain_(""), + password_(""), + reuse_ntlm_(true), + reuse_kerb_(true) {} + +void Proxy::Log() const { + if (IsSet()) { + SSF_LOG(kLogInfo) << "config[HTTP proxy]: <" << host_ << ":" << port_ + << ">"; + if (!username_.empty()) { + SSF_LOG(kLogInfo) << "config[HTTP proxy]: username: <" << username_ + << ">"; + } + SSF_LOG(kLogInfo) << "config[HTTP proxy]: reuse NTLM credentials <" + << (reuse_ntlm_ ? "true" : "false") << ">"; + SSF_LOG(kLogInfo) << "config[HTTP proxy]: reuse Kerberos credentials <" + << (reuse_kerb_ ? "true" : "false") << ">"; + } else { + SSF_LOG(kLogInfo) << "config[HTTP proxy]: "; + } +} + +ProcessService::ProcessService() + : path_(SSF_PROCESS_SERVICE_BINARY_PATH), args_("") {} + +ProcessService::ProcessService(const ProcessService& process_service) + : path_(process_service.path_), args_(process_service.args_) {} + +Services::Services() : process_() {} + +Services::Services(const Services& services) : process_(services.process_) {} + +void Services::UpdateProcessService(const boost::property_tree::ptree& pt) { + auto shell_optional = pt.get_child_optional("shell"); + if (!shell_optional) { + SSF_LOG(kLogDebug) + << "config[update]: shell service configuration not found"; + return; + } + + auto& shell_prop = shell_optional.get(); + auto path = shell_prop.get_child_optional("path"); + if (path) { + process_.set_path(path.get().data()); + } + auto args = shell_prop.get_child_optional("args"); + if (args) { + process_.set_args(args.get().data()); + } +} + +void Services::Log() const { + SSF_LOG(kLogInfo) << "config[services][shell]: path: <" + << process_service().path() << ">"; + std::string args(process_service().args()); + if (!args.empty()) { + SSF_LOG(kLogInfo) << "config[services][shell]: args: <" << args << ">"; + } +} + +Config::Config() : tls_(), http_proxy_(), services_() {} + +void Config::Update(const std::string& filepath, + boost::system::error_code& ec) { + std::string conf_file("config.json"); + ec.assign(::error::success, ::error::get_ssf_category()); + if (filepath.empty()) { + std::ifstream ifile(conf_file); + if (!ifile.good()) { + return; } - ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + } else { + conf_file = filepath; + } + + SSF_LOG(kLogInfo) << "config[ssf]: loading file <" << conf_file << ">"; - return config; + try { + boost::property_tree::ptree pt; + boost::property_tree::read_json(conf_file, pt); + + UpdateTls(pt); + UpdateHttpProxy(pt); + UpdateServices(pt); } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) - << "config: error reading SSF config file : " << e.what(); - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + SSF_LOG(kLogError) << "config[ssf]: error reading SSF config file: " + << e.what(); + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + } +} + +void Config::Log() const { + tls_.Log(); + http_proxy_.Log(); + services_.Log(); +} - return ssf::Config(); +void Config::UpdateTls(const boost::property_tree::ptree& pt) { + auto tls_optional = pt.get_child_optional("ssf.tls"); + if (!tls_optional) { + SSF_LOG(kLogDebug) << "config[update]: TLS configuration not found"; + return; + } + + auto& tls_prop = tls_optional.get(); + + auto ca_cert_path_optional = tls_prop.get_child_optional("ca_cert_path"); + if (ca_cert_path_optional) { + tls_.set_ca_cert_path(ca_cert_path_optional.get().data()); + } + + auto cert_path_optional = tls_prop.get_child_optional("cert_path"); + if (cert_path_optional) { + tls_.set_cert_path(cert_path_optional.get().data()); + } + + auto key_path_optional = tls_prop.get_child_optional("key_path"); + if (key_path_optional) { + tls_.set_key_path(key_path_optional.get().data()); + } + + auto key_password_optional = tls_prop.get_child_optional("key_password"); + if (key_password_optional) { + tls_.set_key_password(key_password_optional.get().data()); + } + + auto dh_path_optional = tls_prop.get_child_optional("dh_path"); + if (dh_path_optional) { + tls_.set_dh_path(dh_path_optional.get().data()); + } + + auto cipher_alg_optional = tls_prop.get_child_optional("cipher_alg"); + if (cipher_alg_optional) { + tls_.set_cipher_alg(cipher_alg_optional.get().data()); } } + +void Config::UpdateHttpProxy(const boost::property_tree::ptree& pt) { + auto proxy_optional = pt.get_child_optional("ssf.http_proxy"); + if (!proxy_optional) { + SSF_LOG(kLogDebug) << "config[update]: proxy configuration not found"; + return; + } + + auto& proxy_prop = proxy_optional.get(); + + auto host_optional = proxy_prop.get_child_optional("host"); + if (host_optional) { + http_proxy_.set_host(host_optional.get().data()); + } + + auto port_optional = proxy_prop.get_child_optional("port"); + if (port_optional) { + http_proxy_.set_port(port_optional.get().data()); + } + + auto cred_username_optional = + proxy_prop.get_child_optional("credentials.username"); + if (cred_username_optional) { + http_proxy_.set_username(cred_username_optional.get().data()); + } + + auto cred_domain_optional = + proxy_prop.get_child_optional("credentials.domain"); + if (cred_domain_optional) { + http_proxy_.set_domain(cred_domain_optional.get().data()); + } + + auto cred_password_optional = + proxy_prop.get_child_optional("credentials.password"); + if (cred_password_optional) { + http_proxy_.set_password(cred_password_optional.get().data()); + } + + auto cred_reuse_ntlm_optional = + proxy_prop.get_child_optional("credentials.reuse_ntlm"); + if (cred_reuse_ntlm_optional) { + http_proxy_.set_reuse_ntlm(cred_reuse_ntlm_optional.get().data() == "true"); + } + + auto cred_reuse_kerb_optional = + proxy_prop.get_child_optional("credentials.reuse_kerb"); + if (cred_reuse_kerb_optional) { + http_proxy_.set_reuse_kerb(cred_reuse_kerb_optional.get().data() == "true"); + } +} + +void Config::UpdateServices(const boost::property_tree::ptree& pt) { + auto services_optional = pt.get_child_optional("ssf.services"); + if (!services_optional) { + SSF_LOG(kLogDebug) << "config[update]: services configuration not found"; + return; + } + + services_.UpdateProcessService(services_optional.get()); +} + +} // config +} // ssf diff --git a/src/common/config/config.h b/src/common/config/config.h index 691acdc8..956da9d3 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -3,33 +3,247 @@ #include +#include + +#include #include +#if defined(BOOST_ASIO_WINDOWS) +#define SSF_PROCESS_SERVICE_BINARY_PATH "C:\\windows\\system32\\cmd.exe" +#else +#define SSF_PROCESS_SERVICE_BINARY_PATH "/bin/bash" +#endif + namespace ssf { -struct Config { - Config() : tls() {} - - struct Tls { - Tls() - : ca_cert_path("./certs/trusted/ca.crt"), - cert_path("./certs/certificate.crt"), - key_path("./certs/private.key"), - key_password(""), - dh_path("./certs/dh4096.pem"), - cipher_alg("DHE-RSA-AES256-GCM-SHA384") {} - - std::string ca_cert_path; - std::string cert_path; - std::string key_path; - std::string key_password; - std::string dh_path; - std::string cipher_alg; - }; - Tls tls; +namespace config { + +class Tls { + public: + Tls(); + + public: + void Log() const; + + inline std::string ca_cert_path() const { return ca_cert_path_; } + + inline void set_ca_cert_path(const std::string& ca_cert_path) { + ca_cert_path_ = ca_cert_path; + } + + inline std::string cert_path() const { return cert_path_; } + + inline void set_cert_path(const std::string& cert_path) { + cert_path_ = cert_path; + } + + inline std::string key_path() const { return key_path_; } + + inline void set_key_path(const std::string& key_path) { + key_path_ = key_path; + } + + inline std::string key_password() const { return key_password_; } + + inline void set_key_password(const std::string& key_password) { + key_password_ = key_password; + } + + inline std::string dh_path() const { return dh_path_; } + + inline void set_dh_path(const std::string& dh_path) { dh_path_ = dh_path; } + + inline std::string cipher_alg() const { return cipher_alg_; } + + inline void set_cipher_alg(const std::string& cipher_alg) { + cipher_alg_ = cipher_alg; + } + + private: + // CA certificate filepath + std::string ca_cert_path_; + // Client certificate filepath + std::string cert_path_; + // Client key filepath + std::string key_path_; + // Client key password + std::string key_password_; + // Diffie-Hellman ephemeral parameters filepath + std::string dh_path_; + // Allowed cipher suite algorithms + std::string cipher_alg_; +}; + +struct Proxy { + public: + Proxy(); + + public: + void Log() const; + + inline bool IsSet() const { + return !host_.empty() && !port_.empty(); + } + + inline std::string host() const { return host_; } + + inline void set_host(const std::string& host) { + host_ = host; + } + + inline std::string port() const { return port_; } + + inline void set_port(const std::string& port) { + port_ = port; + } + + inline std::string username() const { return username_; } + + inline void set_username(const std::string& username) { + username_ = username; + } + + inline std::string domain() const { return domain_; } + + inline void set_domain(const std::string& domain) { + domain_ = domain; + } + + inline std::string password() const { return password_; } + + inline void set_password(const std::string& password) { + password_ = password; + } + + inline bool reuse_ntlm() const { return reuse_ntlm_; } + + inline void set_reuse_ntlm(bool reuse_ntlm) { + reuse_ntlm_ = reuse_ntlm; + } + + inline bool reuse_kerb() const { return reuse_kerb_; } + + inline void set_reuse_kerb(bool reuse_kerb) { + reuse_kerb_ = reuse_kerb; + } + + private: + // Proxy host + std::string host_; + // Proxy port + std::string port_; + // Credentials username + std::string username_; + // Credentials user's domain + std::string domain_; + // Credentials password + std::string password_; + // Reuse default NTLM credentials + bool reuse_ntlm_; + // Reuse default Kerberos/Negotiate credentials + bool reuse_kerb_; +}; + +class ProcessService { + public: + ProcessService(); + ProcessService(const ProcessService& process_service); + + inline std::string path() const { return path_; } + inline void set_path(const std::string& path) { path_ = path; } + + inline std::string args() const { return args_; } + inline void set_args(const std::string& args) { args_ = args; } + + private: + std::string path_; + std::string args_; }; -Config LoadConfig(const std::string& filepath, boost::system::error_code& ec); +class Services { + public: + Services(); + Services(const Services& services); + + inline const ProcessService& process_service() const { return process_; } + inline ProcessService& process() { return process_; } + + void UpdateProcessService(const boost::property_tree::ptree& pt); + void Log() const; + + private: + ProcessService process_; +}; + +class Config { + public: + Config(); + + public: + /** + * Update configuration with JSON file + * If no file provided, try to load config from "config.json" file + * @param filepath config filepath (relative or absolute) + * @param ec error code set if update failed + * + * Format example (default values): + * { + * "ssf": { + * "tls" : { + * "ca_cert_path": "./certs/trusted/ca.crt", + * "cert_path": "./certs/certificate.crt", + * "key_path": "./certs/private.key", + * "key_password": "", + * "dh_path": "./certs/dh4096.pem", + * "cipher_alg": "DHE-RSA-AES256-GCM-SHA384" + * }, + * "proxy" : { + * "host": "", + * "port": "", + * "credentials": { + * "username": "", + * "password": "", + * "domain": "", + * "reuse_ntlm": "true", + * "reuse_nego": "true" + * } + * }, + * "services": { + * "process": { + * "path": "/bin/bash", + * "args": "" + * } + * } + * } + * } + */ + void Update(const std::string& filepath, boost::system::error_code& ec); + + /** + * Log configuration + */ + void Log() const; + + inline const Tls& tls() const { return tls_; } + inline Tls& tls() { return tls_; } + + inline const Proxy& http_proxy() const { return http_proxy_; } + inline Proxy& http_proxy() { return http_proxy_; } + + inline const Services& services() const { return services_; } + inline Services& services() { return services_; } + + private: + void UpdateTls(const boost::property_tree::ptree& pt); + void UpdateHttpProxy(const boost::property_tree::ptree& pt); + void UpdateServices(const boost::property_tree::ptree& pt); + + private: + Tls tls_; + Proxy http_proxy_; + Services services_; +}; +} // config } // ssf #endif // SSF_COMMON_CONFIG_CONFIG_H_ diff --git a/src/common/error/error.cpp b/src/common/error/error.cpp index 014c3411..4e97e895 100644 --- a/src/common/error/error.cpp +++ b/src/common/error/error.cpp @@ -1,11 +1,10 @@ -#include "error.h" +#include "common/error/error.h" -const char* ssf::error::detail::ssf_category::name() const - BOOST_SYSTEM_NOEXCEPT { +const char* error::detail::ssf_category::name() const BOOST_SYSTEM_NOEXCEPT { return "ssf"; } -std::string ssf::error::detail::ssf_category::message(int value) const { +std::string error::detail::ssf_category::message(int value) const { switch (value) { case error::success: return "success"; @@ -64,6 +63,12 @@ std::string ssf::error::detail::ssf_category::message(int value) const { case error::out_of_range: return "out of range"; break; + case error::process_not_created: + return "process not created"; + break; + case error::file_not_found: + return "file not found"; + break; default: return "ssf error"; } diff --git a/src/common/error/error.h b/src/common/error/error.h index dfc59122..6cb6c5f4 100644 --- a/src/common/error/error.h +++ b/src/common/error/error.h @@ -6,7 +6,6 @@ #include #include -namespace ssf { namespace error { enum errors { @@ -28,7 +27,9 @@ enum errors { operation_canceled = boost::system::errc::operation_canceled, service_not_found = 10000, service_not_started = 10001, - out_of_range = 10002 + out_of_range = 10002, + process_not_created = 10003, + file_not_found = 10004 }; namespace detail { @@ -46,5 +47,5 @@ inline const boost::system::error_category& get_ssf_category() { } } // error -} // ssf + #endif // SSF_COMMON_ERROR_ERROR_H_ diff --git a/src/common/log/log.cpp b/src/common/log/log.cpp new file mode 100644 index 00000000..67cb1792 --- /dev/null +++ b/src/common/log/log.cpp @@ -0,0 +1,11 @@ +#include + +#include "common/log/log.h" + +namespace ssf { +namespace log { + +void Configure(LogLevel level) { ssf::log::Log::SetSeverityLevel(level); } + +} // log +} // ssf \ No newline at end of file diff --git a/src/common/log/log.h b/src/common/log/log.h new file mode 100644 index 00000000..ffc6a717 --- /dev/null +++ b/src/common/log/log.h @@ -0,0 +1,12 @@ +#ifndef SSF_COMMON_LOG_LOG_H_ +#define SSF_COMMON_LOG_LOG_H_ + +namespace ssf { +namespace log { + +void Configure(LogLevel level = ssf::log::kLogInfo); + +} // log +} // ssf + +#endif // SSF_COMMON_LOG_LOG_H_ diff --git a/src/common/network/datagram_link.h b/src/common/network/datagram_link.h index 58a83725..1a6e1155 100644 --- a/src/common/network/datagram_link.h +++ b/src/common/network/datagram_link.h @@ -27,8 +27,7 @@ struct DatagramLink : public std::enable_shared_from_this< RemoteEndpointLeft, LeftEndSocket>> { private: template - struct make_queue - { + struct make_queue { typedef std::queue type; typedef T value_type; }; @@ -40,19 +39,20 @@ struct DatagramLink : public std::enable_shared_from_this< RemoteEndpointLeft, LeftEndSocket> DatagramLinkOperatorSpec; typedef std::shared_ptr DatagramLinkOperatorSpecPtr; - + typedef std::vector Datagram; typedef typename make_queue::type DatagramQueue; public: - static DatagramLinkPtr Create(RightEndSocket& right, LeftEndSocket left, - RemoteEndpointRight remote_endpoint_right, - RemoteEndpointLeft remote_endpoint_left, - boost::asio::io_service& io_service, - DatagramLinkOperatorSpecPtr oper) { - return DatagramLinkPtr(new DatagramLink(right, - std::move(left), remote_endpoint_right, remote_endpoint_left, io_service, oper)); - } + static DatagramLinkPtr Create(RightEndSocket& right, LeftEndSocket left, + RemoteEndpointRight remote_endpoint_right, + RemoteEndpointLeft remote_endpoint_left, + boost::asio::io_service& io_service, + DatagramLinkOperatorSpecPtr oper) { + return DatagramLinkPtr( + new DatagramLink(right, std::move(left), remote_endpoint_right, + remote_endpoint_left, io_service, oper)); + } /// Feed data to be sent to the "left endpoint" void Feed(boost::asio::const_buffer buffer, size_t length) { @@ -81,7 +81,6 @@ struct DatagramLink : public std::enable_shared_from_this< reenter(coro_external_) { for (;;) { - // If no error occured but no data is available, we wait if (!ec && dgr_queue_.empty()) { timer_.expires_from_now(boost::posix_time::seconds(120)); @@ -95,7 +94,7 @@ struct DatagramLink : public std::enable_shared_from_this< !dgr_queue_.empty()) { if (l_.is_open()) { yield l_.async_send_to( - boost::asio::buffer(dgr_queue_.front()), remote_endpoint_left_, + boost::asio::buffer(dgr_queue_.front()), remote_endpoint_left_, boost::bind(&DatagramLink::ExternalFeed, this->shared_from_this(), _1, _2)); dgr_queue_.pop(); @@ -159,23 +158,22 @@ struct DatagramLink : public std::enable_shared_from_this< private: DatagramLink(RightEndSocket& right, LeftEndSocket left, - RemoteEndpointRight remote_endpoint_right, - RemoteEndpointLeft remote_endpoint_left, - boost::asio::io_service& io_service, - DatagramLinkOperatorSpecPtr oper) - : coro_auto_(), - coro_external_(), - r_(right), - l_(std::move(left)), - bytes_to_transfer_(0), - transferred_bytes_(0), - timer_(io_service), - remote_endpoint_right_(remote_endpoint_right), - remote_endpoint_left_(remote_endpoint_left), - stopping_(false), - p_operator_(oper), - auto_feeding_(false) { - } + RemoteEndpointRight remote_endpoint_right, + RemoteEndpointLeft remote_endpoint_left, + boost::asio::io_service& io_service, + DatagramLinkOperatorSpecPtr oper) + : coro_auto_(), + coro_external_(), + r_(right), + l_(std::move(left)), + bytes_to_transfer_(0), + transferred_bytes_(0), + timer_(io_service), + remote_endpoint_right_(remote_endpoint_right), + remote_endpoint_left_(remote_endpoint_left), + stopping_(false), + p_operator_(oper), + auto_feeding_(false) {} void Shutdown() { boost::recursive_mutex::scoped_lock lock(p_operator_mutex_); @@ -199,7 +197,7 @@ struct DatagramLink : public std::enable_shared_from_this< RemoteEndpointLeft remote_endpoint_left_; boost::recursive_mutex datagram_queue_mutex_; boost::recursive_mutex p_operator_mutex_; - bool stopping_; + bool stopping_; DatagramLinkOperatorSpecPtr p_operator_; bool auto_feeding_; }; diff --git a/src/common/network/datagram_link_operator.h b/src/common/network/datagram_link_operator.h index 9e2809a2..30e2f037 100644 --- a/src/common/network/datagram_link_operator.h +++ b/src/common/network/datagram_link_operator.h @@ -21,81 +21,80 @@ class DatagramLinkOperator : public std::enable_shared_from_this< DatagramLinkOperator> { -private: + private: // Types for DatagramLinks - typedef DatagramLink DatagramLinkSpec; + typedef DatagramLink DatagramLinkSpec; typedef std::shared_ptr DatagramLinkPtr; -public: - static std::shared_ptr Create(RightEndSocket& right_end_socket) { - return std::shared_ptr(new DatagramLinkOperator(right_end_socket)); + public: + static std::shared_ptr Create( + RightEndSocket& right_end_socket) { + return std::shared_ptr( + new DatagramLinkOperator(right_end_socket)); } - ~DatagramLinkOperator() {} - - /// Function called when data is received from the right end socket - bool Feed(RemoteEndpointRight received_from, boost::asio::const_buffer buffer, - size_t length) { - - boost::recursive_mutex::scoped_lock lock(links_mutex_); - - // If the endpoint is registered, transfer the data to its DataLink - if (links_.count(received_from)) { - links_[received_from]->Feed(buffer, length); - return true; - } else { - return false; - } - } - - /// Function to add a new DataLink - void AddLink(LeftEndSocket left, RemoteEndpointRight right_endpoint, - RemoteEndpointLeft left_endpoint, - boost::asio::io_service& io_service) { - - boost::recursive_mutex::scoped_lock lock(links_mutex_); - boost::recursive_mutex::scoped_lock lock2(endpoints_mutex_); - - auto link = DatagramLinkSpec::Create( - right_end_socket_, std::move(left), right_endpoint, left_endpoint, - io_service, this->shared_from_this()); - links_[right_endpoint] = link; - endpoints_[link] = right_endpoint; - link->ExternalFeed(); - } - - /// Function to stop a DataLink - void Stop(DatagramLinkPtr link) { - link->Stop(); - - boost::recursive_mutex::scoped_lock lock(links_mutex_); - boost::recursive_mutex::scoped_lock lock2(endpoints_mutex_); - if (endpoints_.count(link)) { - links_.erase(endpoints_[link]); - endpoints_.erase(link); - } - } - - /// Function to stop all DataLinks - void StopAll() { - { - boost::recursive_mutex::scoped_lock lock(links_mutex_); - for (auto& link : links_) { - link.second->Stop(); - } - links_.clear(); - } - - boost::recursive_mutex::scoped_lock lock2(endpoints_mutex_); - endpoints_.clear(); - } - -private: - DatagramLinkOperator(RightEndSocket& right_end_socket) - : right_end_socket_(right_end_socket) { + ~DatagramLinkOperator() {} + + /// Function called when data is received from the right end socket + bool Feed(RemoteEndpointRight received_from, boost::asio::const_buffer buffer, + size_t length) { + boost::recursive_mutex::scoped_lock lock(links_mutex_); + + // If the endpoint is registered, transfer the data to its DataLink + if (links_.count(received_from)) { + links_[received_from]->Feed(buffer, length); + return true; + } else { + return false; + } + } + + /// Function to add a new DataLink + void AddLink(LeftEndSocket left, RemoteEndpointRight right_endpoint, + RemoteEndpointLeft left_endpoint, + boost::asio::io_service& io_service) { + boost::recursive_mutex::scoped_lock lock(links_mutex_); + boost::recursive_mutex::scoped_lock lock2(endpoints_mutex_); + + auto link = DatagramLinkSpec::Create(right_end_socket_, std::move(left), + right_endpoint, left_endpoint, + io_service, this->shared_from_this()); + links_[right_endpoint] = link; + endpoints_[link] = right_endpoint; + link->ExternalFeed(); + } + + /// Function to stop a DataLink + void Stop(DatagramLinkPtr link) { + link->Stop(); + + boost::recursive_mutex::scoped_lock lock(links_mutex_); + boost::recursive_mutex::scoped_lock lock2(endpoints_mutex_); + if (endpoints_.count(link)) { + links_.erase(endpoints_[link]); + endpoints_.erase(link); + } + } + + /// Function to stop all DataLinks + void StopAll() { + { + boost::recursive_mutex::scoped_lock lock(links_mutex_); + for (auto& link : links_) { + link.second->Stop(); + } + links_.clear(); + } + + boost::recursive_mutex::scoped_lock lock2(endpoints_mutex_); + endpoints_.clear(); } + private: + DatagramLinkOperator(RightEndSocket& right_end_socket) + : right_end_socket_(right_end_socket) {} + boost::recursive_mutex endpoints_mutex_; std::map endpoints_; boost::recursive_mutex links_mutex_; diff --git a/src/common/network/network_policy_traits.h b/src/common/network/network_policy_traits.h deleted file mode 100644 index c5c815b6..00000000 --- a/src/common/network/network_policy_traits.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef SSF_COMMON_NETWORK_NETWORK_POLICY_TRAITS_H_ -#define SSF_COMMON_NETWORK_NETWORK_POLICY_TRAITS_H_ - -namespace ssf { -// Traits defining the return types of layer policies - -template -struct SocketOf { - typedef typename Policy::socket_type type; -}; - -template -struct SocketPtrOf { - typedef typename Policy::p_socket_type type; -}; - -template -struct AcceptorOf { - typedef typename Policy::acceptor_type type; -}; - -template -struct AcceptorPtrOf { - typedef typename Policy::p_acceptor_type type; -}; -} - -#endif // SSF_COMMON_NETWORK_NETWORK_POLICY_TRAITS_H_ \ No newline at end of file diff --git a/src/common/network/session_forwarder.h b/src/common/network/session_forwarder.h deleted file mode 100644 index 81fd1fcd..00000000 --- a/src/common/network/session_forwarder.h +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef SSF_COMMON_NETWORK_SESSION_FORWARDER_H -#define SSF_COMMON_NETWORK_SESSION_FORWARDER_H - -#include -#include - -#include -#include - -#include "common/network/base_session.h" // NOLINT -#include "common/network/manager.h" -#include "common/network/socket_link.h" - -namespace ssf { - -/// Create a Full Duplex Forwarding Link -template -class SessionForwarder : public ssf::BaseSession { -private: - /// Buffer type for the transiting data - typedef std::array buffer_type; - - /// Type for the class managing the different forwarding links - typedef ItemManager SessionManager; - -public: - typedef std::shared_ptr p_SessionForwarder; - - -public: - /// Return a shared pointer to a new SessionForwarder object - template - static p_SessionForwarder create(Args&& ... args) { - return std::shared_ptr( - new SessionForwarder(std::forward(args)...)); - } - - /// Start forwarding - virtual void start(boost::system::error_code&) { - DoForward(); - } - - /// Stop forwarding - virtual void stop(boost::system::error_code&) { - boost::system::error_code ec; - inbound_.lowest_layer().shutdown( - boost::asio::ip::tcp::socket::shutdown_both, ec); - inbound_.lowest_layer().close(); - outbound_.lowest_layer().shutdown( - boost::asio::ip::tcp::socket::shutdown_both, ec); - outbound_.lowest_layer().close(); - } - -private: - /// Function taking a member function and returning a handler - /** - * Function taking a memeber function and returning a handler including - * reference counting functionality. - * - * @param handler A member function with one argument. - */ - - //template - //auto Then(Handler handler, This me) - // -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { - // return boost::bind(handler, me->SelfFromThis(), _1); - //} - - template - auto Then(Handler handler, This me) -> decltype(boost::bind(handler, - me->SelfFromThis(), - _1)) { - return boost::bind(handler, me->SelfFromThis(), _1); - } - - /// It is needed to cast the shared pointer from shared_from_this because it - /// is the base class which inherit from enable_shared_from_this - std::shared_ptr SelfFromThis() { - return std::static_pointer_cast(this->shared_from_this()); - } - -private: - /// The constructor is made private to ensure users only use create() - SessionForwarder(SessionManager* p_manager, InwardStream inbound, - ForwardStream outbound) - : inbound_(std::move(inbound)), - outbound_(std::move(outbound)), - p_manager_(p_manager) {} - - /// Start forwarding - void DoForward() { - // Make two Half Duplex links to have a Full Duplex Link - AsyncEstablishHDLink(ReadFrom(inbound_), WriteTo(outbound_), - boost::asio::buffer(inwardBuffer_), - Then(&SessionForwarder::StopHandler, this->SelfFromThis())); - - AsyncEstablishHDLink(ReadFrom(outbound_), WriteTo(inbound_), - boost::asio::buffer(forwardBuffer_), - Then(&SessionForwarder::StopHandler, this->SelfFromThis())); - } - - /// Stop forwarding - void StopHandler(const boost::system::error_code& ec) { - boost::system::error_code e; - p_manager_->stop(SelfFromThis(), e); - } - -private: - // The streams to forward to each other - InwardStream inbound_; - ForwardStream outbound_; - - /// The manager handling multiple SessionForwarder - SessionManager* p_manager_; - - // One buffer for each Half Duplex Link - buffer_type inwardBuffer_; - buffer_type forwardBuffer_; - -}; -} // ssf - -#endif // SSF_COMMON_NETWORK_SESSION_FORWARDER_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 02bfe4ed..1e5e3208 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -3,9 +3,6 @@ cmake_minimum_required(VERSION 2.8) if(NOT DEP_BOOST_VERSION) set(DEP_BOOST_VERSION 1.55.0) endif() - + add_subdirectory(./client) add_subdirectory(./server) - - - diff --git a/src/core/async_engine.cpp b/src/core/async_engine.cpp new file mode 100644 index 00000000..e396930e --- /dev/null +++ b/src/core/async_engine.cpp @@ -0,0 +1,44 @@ +#include + +#include "core/async_engine.h" + +#include "ssf/log/log.h" + +namespace ssf { + +AsyncEngine::AsyncEngine() : io_service_(), p_worker_(nullptr), threads_() {} + +AsyncEngine::~AsyncEngine() { Stop(); } + +boost::asio::io_service& AsyncEngine::get_io_service() { return io_service_; } + +void AsyncEngine::Start() { + if (IsStarted()) { + return; + } + + SSF_LOG(kLogDebug) << "async engine: starting"; + p_worker_.reset(new boost::asio::io_service::work(io_service_)); + for (uint8_t i = 0; i < boost::thread::hardware_concurrency(); ++i) { + threads_.create_thread([this]() { + boost::system::error_code ec; + io_service_.run(ec); + if (ec) { + SSF_LOG(kLogError) << "async engine: when running io_service: " + << ec.message(); + } + }); + } +} + +void AsyncEngine::Stop() { + SSF_LOG(kLogDebug) << "async engine: stopping"; + p_worker_.reset(nullptr); + threads_.join_all(); + io_service_.stop(); + io_service_.reset(); +} + +bool AsyncEngine::IsStarted() const { return p_worker_.get() != nullptr; } + +} // ssf diff --git a/src/core/async_engine.h b/src/core/async_engine.h new file mode 100644 index 00000000..abeae96a --- /dev/null +++ b/src/core/async_engine.h @@ -0,0 +1,37 @@ +#ifndef SSF_CORE_ASYNC_ENGINE_H_ +#define SSF_CORE_ASYNC_ENGINE_H_ + +#include + +#include +#include + +namespace ssf { + +class AsyncEngine { + private: + using WorkerPtr = std::unique_ptr; + + public: + AsyncEngine(); + ~AsyncEngine(); + + AsyncEngine(const AsyncEngine&) = delete; + AsyncEngine& operator=(const AsyncEngine&) = delete; + + boost::asio::io_service& get_io_service(); + + void Start(); + void Stop(); + + bool IsStarted() const; + + private: + boost::asio::io_service io_service_; + WorkerPtr p_worker_; + boost::thread_group threads_; +}; + +} // ssf + +#endif // SSF_CORE_ASYNC_RUNNER_H_ diff --git a/src/core/circuit/config.cpp b/src/core/circuit/config.cpp new file mode 100644 index 00000000..ba71a71c --- /dev/null +++ b/src/core/circuit/config.cpp @@ -0,0 +1,71 @@ +#include "core/circuit/config.h" + +#include + +#include + +#include "common/error/error.h" + +namespace ssf { +namespace circuit { + +CircuitNode::CircuitNode(const std::string& addr, const std::string& port) + : addr_(addr), port_(port) {} + +Config::Config() : nodes_() {} + +void Config::Update(const std::string& filepath, + boost::system::error_code& ec) { + std::string conf_file("circuit.txt"); + if (filepath.empty()) { + std::ifstream ifile(conf_file); + if (!ifile.good()) { + return; + } + ifile.close(); + } else { + conf_file = filepath; + } + + SSF_LOG(kLogInfo) << "config[circuit]: loading file <" << conf_file << ">"; + + std::ifstream file(conf_file); + + if (!file.is_open()) { + SSF_LOG(kLogError) << "config[circuit]: could not open file <" << conf_file + << ">"; + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + return; + } + + std::string line; + + while (std::getline(file, line)) { + size_t position = line.find(":"); + if (position == std::string::npos) { + SSF_LOG(kLogError) << "config[circuit]: invalid line " << line; + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + break; + } + nodes_.emplace_back(line.substr(0, position), line.substr(position + 1)); + } + + file.close(); +} + +void Config::Log() const { + if (nodes_.size() == 0) { + SSF_LOG(kLogInfo) << "config[circuit]: "; + return; + } + + unsigned int i = 0; + for (auto& node : nodes_) { + ++i; + SSF_LOG(kLogInfo) << "config[circuit]: " << std::to_string(i) << ". <" + << node.addr() << ":" << node.port() << ">"; + } +} + +} // parser +} // ssf \ No newline at end of file diff --git a/src/core/circuit/config.h b/src/core/circuit/config.h new file mode 100644 index 00000000..033cafd4 --- /dev/null +++ b/src/core/circuit/config.h @@ -0,0 +1,57 @@ +#ifndef SSF_CORE_CIRCUIT_CONFIG_H_ +#define SSF_CORE_CIRCUIT_CONFIG_H_ + +#include +#include + +#include + +namespace ssf { +namespace circuit { + +class CircuitNode { + public: + CircuitNode(const std::string& addr, const std::string& port); + + public: + inline std::string addr() const { return addr_; } + inline void set_addr(const std::string& addr) { addr_ = addr; } + + inline std::string port() const { return port_; } + inline void set_port(const std::string& port) { port_ = port; } + + private: + std::string addr_; + std::string port_; +}; + +using NodeList = std::list; + +class Config { + public: + public: + Config(); + + public: + /** + * Parse circuit file + * If no file provided, try to load circuit data "circuit.txt" file + * @param filepath + */ + void Update(const std::string& filepath, boost::system::error_code& ec); + + /** + * Log configuration + */ + void Log() const; + + inline NodeList nodes() const { return nodes_; }; + + private: + NodeList nodes_; +}; + +} // circuit +} // ssf + +#endif // SSF_CORE_CIRCUIT_CONFIG_H_ diff --git a/src/core/client/CMakeLists.txt b/src/core/client/CMakeLists.txt index fdc0a33e..6991dde5 100644 --- a/src/core/client/CMakeLists.txt +++ b/src/core/client/CMakeLists.txt @@ -1,53 +1,66 @@ cmake_minimum_required(VERSION 2.8) -set(project_NAME "ssfc") -project(${project_NAME}) - -set(CLIENT_FILES - main.cpp) - -set(CLIENT_CP_PROJECT_NAME "ssfcp") -set(CLIENT_CP_FILES - main_cp.cpp) - include_directories( ${OpenSSL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) -# --- SSF_Client -add_target(${project_NAME} +# --- Copy test certs for client +set(project_BINARY_SERVER_DIR "${project_BINARY_DIR}/src/core/client") +set(project_BINARY_SERVER_CERT_DIR ${project_BINARY_SERVER_DIR}/certs) + +file(MAKE_DIRECTORY ${project_BINARY_SERVER_CERT_DIR}) +file(MAKE_DIRECTORY ${project_BINARY_SERVER_CERT_DIR}/trusted) + +file(COPY ${SSF_CERT_TEST_ROOT_FILES} + DESTINATION ${project_BINARY_SERVER_CERT_DIR}) +file(COPY ${SSF_CERT_TEST_TRUSTED_FILES} + DESTINATION ${project_BINARY_SERVER_CERT_DIR}/trusted) + +# --- ssfc +set(CLIENT_NAME "ssfc") + +set(CLIENT_MAIN + main.cpp) + +add_target(${CLIENT_NAME} TYPE - executable ${EXEC_FLAG} + executable ${EXEC_FLAG} INSTALL FILES - ${CLIENT_FILES} ${SSF_SOURCES} ${CORE_COMMAND_LINE_STANDARD_FILES} ${ICON_RC} + ${CLIENT_MAIN} + LINKS + ${Boost_LIBRARIES} + ${OpenSSL_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network PREFIX_SKIP ${project_SRC_DIR}) -if (APPLE) - set_source_files_properties(${ICON_RC} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") - set_target_properties(${project_NAME} PROPERTIES MACOSX_BUNDLE_ICON_FILE ${ICON_NAME}) -endif (APPLE) +# --- ssfcp +set(CLIENT_CP_NAME "ssfcp") +set(CLIENT_CP_MAIN + main_cp.cpp) -target_link_libraries(${project_NAME} ${Boost_LIBRARIES} ${OpenSSL_LIBRARIES} ${PLATFORM_SPECIFIC_LIB_DEP}) - -# --- SSF_Client_cp -add_target(${CLIENT_CP_PROJECT_NAME} +add_target(${CLIENT_CP_NAME} TYPE - executable ${EXEC_FLAG} + executable ${EXEC_FLAG} INSTALL FILES - ${CLIENT_CP_FILES} ${SSF_SOURCES} ${CORE_COMMAND_LINE_COPY_FILES} ${ICON_RC} + ${CLIENT_CP_MAIN} + LINKS + ${Boost_LIBRARIES} + ${OpenSSL_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network PREFIX_SKIP ${project_SRC_DIR}) -if (APPLE) - set_source_files_properties(${ICON_RC} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") - set_target_properties(${CLIENT_CP_PROJECT_NAME} PROPERTIES MACOSX_BUNDLE_ICON_FILE ${ICON_NAME}) -endif (APPLE) - -target_link_libraries(${CLIENT_CP_PROJECT_NAME} ${Boost_LIBRARIES} ${OpenSSL_LIBRARIES} ${PLATFORM_SPECIFIC_LIB_DEP}) +install( + TARGETS ssfc ssfcp + RUNTIME + DESTINATION ssf/${project_EXEC_DIR_NAME} +) diff --git a/src/core/client/client.h b/src/core/client/client.h index de89f3f9..f0d08330 100644 --- a/src/core/client/client.h +++ b/src/core/client/client.h @@ -1,100 +1,83 @@ #ifndef SSF_CORE_CLIENT_CLIENT_H_ #define SSF_CORE_CLIENT_CLIENT_H_ -#include -#include +#include +#include #include #include -#include -#include +#include +#include #include "common/boost/fiber/stream_fiber.hpp" #include "common/boost/fiber/basic_fiber_demux.hpp" #include "common/config/config.h" -#include "services/initialisation.h" -#include "services/admin/admin.h" -#include "services/sockets_to_fibers/sockets_to_fibers.h" -#include "services/fibers_to_sockets/fibers_to_sockets.h" -#include "services/fibers_to_datagrams/fibers_to_datagrams.h" -#include "services/datagrams_to_fibers/datagrams_to_fibers.h" -#include "services/socks/socks_server.h" - -#include "services/admin/requests/create_service_request.h" - +#include "core/async_engine.h" #include "core/service_manager/service_manager.h" -#include "services/base_service.h" +#include "services/initialisation.h" +#include "services/user_services/base_user_service.h" namespace ssf { -template class LinkAuthenticationPolicy, - template class> - class NetworkVirtualLayerPolicy, +template class TransportVirtualLayerPolicy> -class SSFClient : public NetworkVirtualLayerPolicy, - public TransportVirtualLayerPolicy< - typename PhysicalVirtualLayer::socket_type> { - +class SSFClient + : public TransportVirtualLayerPolicy { private: - typedef typename PhysicalVirtualLayer::socket_type socket_type; - typedef typename PhysicalVirtualLayer::p_socket_type p_socket_type; - typedef std::map Parameters; - - typedef std::vector vector_error_code_type; + using NetworkSocket = typename NetworkProtocol::socket; + using NetworkSocketPtr = std::shared_ptr; + using NetworkEndpoint = typename NetworkProtocol::endpoint; + using NetworkResolver = typename NetworkProtocol::resolver; + using NetworkQuery = typename NetworkProtocol::resolver::query; public: - typedef boost::asio::fiber::basic_fiber_demux demux; - typedef typename ssf::services::BaseUserService::BaseUserServicePtr - BaseUserServicePtr; - typedef std::function< - void(ssf::services::initialisation::type, - BaseUserServicePtr, - const boost::system::error_code&)> - client_callback_type; + using Demux = boost::asio::fiber::basic_fiber_demux; + using BaseUserServicePtr = + typename ssf::services::BaseUserService::BaseUserServicePtr; + using ClientCallback = + std::function; public: - SSFClient(boost::asio::io_service& io_service, const std::string& remote_addr, - const std::string& port, const ssf::Config& ssf_config, - std::vector user_services, - client_callback_type callback); + SSFClient(std::vector user_services, + const ssf::config::Services& services_config, + ClientCallback callback); + + ~SSFClient(); - void run(Parameters& parameters); + void Run(const NetworkQuery& query, boost::system::error_code& ec); - void stop(); + void Stop(); + + boost::asio::io_service& get_io_service(); private: - void DoSSFStart(p_socket_type p_socket, const boost::system::error_code& ec); + void NetworkToTransport(const boost::system::error_code& ec, + NetworkSocketPtr p_socket); - void DoFiberize(p_socket_type p_socket, boost::system::error_code& ec); + void DoSSFStart(NetworkSocketPtr p_socket, + const boost::system::error_code& ec); - void OnDemuxClose(); + void DoFiberize(NetworkSocketPtr p_socket, boost::system::error_code& ec); - void NetworkToTransport(p_socket_type p_socket, vector_error_code_type v_ec); - bool PrintErrorVector(vector_error_code_type v_ec); + void OnDemuxClose(); void Notify(ssf::services::initialisation::type type, - BaseUserServicePtr p_user_service, - boost::system::error_code ec) { - if (callback_) { - io_service_.post( - boost::bind(callback_, std::move(type), p_user_service, std::move(ec))); - } - } + BaseUserServicePtr p_user_service, boost::system::error_code ec); private: - boost::asio::io_service& io_service_; - demux fiber_demux_; + AsyncEngine async_engine_; + + Demux fiber_demux_; std::vector user_services_; - std::string remote_addr_; - std::string remote_port_; + ssf::config::Services services_config_; - client_callback_type callback_; + ClientCallback callback_; }; + } // ssf #include "core/client/client.ipp" diff --git a/src/core/client/client.ipp b/src/core/client/client.ipp index c522716b..e208d320 100644 --- a/src/core/client/client.ipp +++ b/src/core/client/client.ipp @@ -1,167 +1,175 @@ #ifndef SSF_CORE_CLIENT_CLIENT_IPP_ #define SSF_CORE_CLIENT_CLIENT_IPP_ -#include +#include "common/error/error.h" +#include "core/factories/service_factory.h" + +#include "services/admin/admin.h" #include "services/admin/requests/create_service_request.h" #include "services/admin/requests/stop_service_request.h" #include "services/admin/requests/service_status.h" -#include "core/factories/service_factory.h" +#include "services/copy_file/fiber_to_file/fiber_to_file.h" +#include "services/copy_file/file_enquirer/file_enquirer.h" +#include "services/copy_file/file_to_fiber/file_to_fiber.h" +#include "services/datagrams_to_fibers/datagrams_to_fibers.h" +#include "services/fibers_to_datagrams/fibers_to_datagrams.h" +#include "services/fibers_to_datagrams/fibers_to_datagrams.h" +#include "services/fibers_to_sockets/fibers_to_sockets.h" +#include "services/sockets_to_fibers/sockets_to_fibers.h" +#include "services/socks/socks_server.h" +#include "services/process/server.h" -#include "services/user_services/base_user_service.h" -#include "services/user_services/socks.h" -#include "services/user_services/remote_socks.h" -#include "services/user_services/port_forwarding.h" -#include "services/user_services/remote_port_forwarding.h" -#include "services/user_services/copy_file_service.h" +#include "ssf/log/log.h" namespace ssf { -template class L, - template class> class N, - template class T> -SSFClient::SSFClient(boost::asio::io_service& io_service, - const std::string& remote_addr, - const std::string& port, - const ssf::Config& ssf_config, - std::vector user_services, - client_callback_type callback) - : N(io_service, ssf_config), - T( - boost::bind(&SSFClient::DoSSFStart, this, _1, _2)), - io_service_(io_service), - fiber_demux_(io_service), + +template class T> +SSFClient::SSFClient(std::vector user_services, + const ssf::config::Services& services_config, + ClientCallback callback) + : T( + boost::bind(&SSFClient::DoSSFStart, this, _1, _2)), + async_engine_(), + fiber_demux_(async_engine_.get_io_service()), user_services_(user_services), - remote_addr_(remote_addr), - remote_port_(port), + services_config_(services_config), callback_(std::move(callback)) {} -template class L, - template class> class N, - template class T> -void SSFClient::run(Parameters& parameters) { - this->AddRoute( - parameters, - boost::bind(&SSFClient::NetworkToTransport, this, _1, _2)); +template class T> +SSFClient::~SSFClient() { + Stop(); +} + +template class T> +void SSFClient::Run(const NetworkQuery& query, + boost::system::error_code& ec) { + if (async_engine_.IsStarted()) { + ec.assign(::error::device_or_resource_busy, ::error::get_ssf_category()); + SSF_LOG(kLogError) << "client: already running"; + return; + } + + // Create network socket + NetworkSocketPtr p_socket = + std::make_shared(async_engine_.get_io_service()); + + // resolve remote endpoint with query + NetworkResolver resolver(async_engine_.get_io_service()); + auto endpoint_it = resolver.resolve(query, ec); + if (ec) { + Notify(ssf::services::initialisation::NETWORK, nullptr, ec); + SSF_LOG(kLogError) << "client: could not resolve network endpoint"; + return; + } + + async_engine_.Start(); + + // async connect client to given endpoint + p_socket->async_connect( + *endpoint_it, + boost::bind(&SSFClient::NetworkToTransport, this, _1, p_socket)); } -template class L, - template class> class N, - template class T> -void SSFClient::stop() { +template class T> +void SSFClient::Stop() { fiber_demux_.close(); + + async_engine_.Stop(); } -//------------------------------------------------------------------------------- -template class L, - template class> class N, - template class T> -void SSFClient::NetworkToTransport(p_socket_type p_socket, - vector_error_code_type v_ec) { - if (PrintErrorVector(v_ec)) { +template class T> +boost::asio::io_service& SSFClient::get_io_service() { + return async_engine_.get_io_service(); +} + +template class T> +void SSFClient::NetworkToTransport(const boost::system::error_code& ec, + NetworkSocketPtr p_socket) { + if (!ec) { this->DoSSFInitiate(p_socket); - } else { - if (p_socket) { - boost::system::error_code ec; - p_socket->close(ec); - } - for (size_t i = 0; i < v_ec.size(); ++i) { - if (v_ec[i]) { - Notify(ssf::services::initialisation::NETWORK, nullptr, v_ec[i]); - return; - } - } + return; } -} -//------------------------------------------------------------------------------ - -template class L, - template class> class N, - template class T> -bool SSFClient::PrintErrorVector(vector_error_code_type v_ec) { - for (size_t i = 0; i < v_ec.size(); ++i) { - if (v_ec[i]) { - BOOST_LOG_TRIVIAL(error) << "client: node " << i << " status: " << v_ec[i] - << " message: " << v_ec[i].message(); - return false; - } + + SSF_LOG(kLogError) << "client: error when connecting to server " + << ec.message(); + + if (p_socket) { + boost::system::error_code close_ec; + p_socket->close(close_ec); } - return true; + + Notify(ssf::services::initialisation::NETWORK, nullptr, ec); } -//------------------------------------------------------------------------------ -template class L, - template class> class N, - template class T> -void SSFClient::DoSSFStart(p_socket_type p_socket, - const boost::system::error_code& ec) { +template class T> +void SSFClient::DoSSFStart(NetworkSocketPtr p_socket, + const boost::system::error_code& ec) { Notify(ssf::services::initialisation::NETWORK, nullptr, ec); if (!ec) { - BOOST_LOG_TRIVIAL(trace) << "client: SSF reply ok"; + SSF_LOG(kLogTrace) << "client: SSF reply ok"; boost::system::error_code ec2; - this->DoFiberize(p_socket, ec2); + DoFiberize(p_socket, ec2); Notify(ssf::services::initialisation::TRANSPORT, nullptr, ec); } else { - BOOST_LOG_TRIVIAL(error) << "client: SSF protocol error " << ec.message(); + SSF_LOG(kLogError) << "client: SSF protocol error " << ec.message(); } } -template class L, - template class> class N, - template class T> -void SSFClient::DoFiberize(p_socket_type p_socket, - boost::system::error_code& ec) { +template class T> +void SSFClient::DoFiberize(NetworkSocketPtr p_socket, + boost::system::error_code& ec) { // Register supported admin commands - services::admin::CreateServiceRequest::RegisterToCommandFactory(); - services::admin::StopServiceRequest::RegisterToCommandFactory(); - services::admin::ServiceStatus::RegisterToCommandFactory(); + services::admin::CreateServiceRequest::RegisterToCommandFactory(); + services::admin::StopServiceRequest::RegisterToCommandFactory(); + services::admin::ServiceStatus::RegisterToCommandFactory(); - auto close_demux_handler = [this]() { this->OnDemuxClose(); }; + auto close_demux_handler = [this]() { OnDemuxClose(); }; fiber_demux_.fiberize(std::move(*p_socket), close_demux_handler); // Make a new service manager - auto p_service_manager = std::make_shared>(); + auto p_service_manager = std::make_shared>(); // Make a new service factory - auto p_service_factory = ServiceFactory::Create( - io_service_, fiber_demux_, p_service_manager); + auto p_service_factory = ServiceFactory::Create( + async_engine_.get_io_service(), fiber_demux_, p_service_manager); // Register supported micro services - services::socks::SocksServer::RegisterToServiceFactory( + services::socks::SocksServer::RegisterToServiceFactory( p_service_factory); - services::fibers_to_sockets::FibersToSockets::RegisterToServiceFactory( + services::fibers_to_sockets::FibersToSockets::RegisterToServiceFactory( p_service_factory); - services::sockets_to_fibers::SocketsToFibers::RegisterToServiceFactory( + services::sockets_to_fibers::SocketsToFibers::RegisterToServiceFactory( p_service_factory); services::fibers_to_datagrams::FibersToDatagrams< - demux>::RegisterToServiceFactory(p_service_factory); + Demux>::RegisterToServiceFactory(p_service_factory); services::datagrams_to_fibers::DatagramsToFibers< - demux>::RegisterToServiceFactory(p_service_factory); + Demux>::RegisterToServiceFactory(p_service_factory); services::copy_file::file_to_fiber::FileToFiber< - demux>::RegisterToServiceFactory(p_service_factory); + Demux>::RegisterToServiceFactory(p_service_factory); services::copy_file::fiber_to_file::FiberToFile< - demux>::RegisterToServiceFactory(p_service_factory); + Demux>::RegisterToServiceFactory(p_service_factory); services::copy_file::file_enquirer::FileEnquirer< - demux>::RegisterToServiceFactory(p_service_factory); + Demux>::RegisterToServiceFactory(p_service_factory); + services::process::Server::RegisterToServiceFactory( + p_service_factory, services_config_.process()); // Start the admin micro service std::map empty_map; - auto p_admin_service = services::admin::Admin::Create( - io_service_, fiber_demux_, empty_map); + auto p_admin_service = services::admin::Admin::Create( + async_engine_.get_io_service(), fiber_demux_, empty_map); p_admin_service->set_client(user_services_, callback_); p_service_manager->start(p_admin_service, ec); } -//------------------------------------------------------------------------------ -template class L, - template class> class N, - template class T> -void SSFClient::OnDemuxClose() { +template class T> +void SSFClient::OnDemuxClose() { auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&fiber_demux_); + ServiceFactoryManager::GetServiceFactory(&fiber_demux_); if (p_service_factory) { p_service_factory->Destroy(); } @@ -169,6 +177,16 @@ void SSFClient::OnDemuxClose() { boost::system::error_code()); } +template class T> +void SSFClient::Notify(ssf::services::initialisation::type type, + BaseUserServicePtr p_user_service, + boost::system::error_code ec) { + if (callback_) { + async_engine_.get_io_service().post( + boost::bind(callback_, std::move(type), p_user_service, std::move(ec))); + } +} + } // ssf #endif // SSF_CORE_CLIENT_CLIENT_IPP_ diff --git a/src/core/client/main.cpp b/src/core/client/main.cpp index e104e318..43d570f0 100644 --- a/src/core/client/main.cpp +++ b/src/core/client/main.cpp @@ -1,22 +1,20 @@ -#include +#include +#include #include -#include -#include -#include +#include #include +#include + #include "common/config/config.h" +#include "common/log/log.h" +#include "core/circuit/config.h" #include "core/client/client.h" - #include "core/command_line/standard/command_line.h" -#include "core/parser/bounce_parser.h" #include "core/factories/service_option_factory.h" -#include "core/network_virtual_layer_policies/bounce_protocol_policy.h" -#include "core/network_virtual_layer_policies/link_policies/ssl_policy.h" -#include "core/network_virtual_layer_policies/link_policies/tcp_policy.h" -#include "core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h" +#include "core/network_protocol.h" #include "core/transport_virtual_layer_policies/transport_protocol_policy.h" #include "services/initialisation.h" @@ -25,150 +23,242 @@ #include "services/user_services/remote_socks.h" #include "services/user_services/port_forwarding.h" #include "services/user_services/remote_port_forwarding.h" +#include "services/user_services/process.h" +#include "services/user_services/remote_process.h" #include "services/user_services/udp_port_forwarding.h" #include "services/user_services/udp_remote_port_forwarding.h" -void Init() { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); -} +using NetworkProtocol = ssf::network::NetworkProtocol; + +using Client = + ssf::SSFClient; + +using Demux = Client::Demux; +using BaseUserServicePtr = Client::BaseUserServicePtr; +using ClientServices = std::vector; +using CircuitNodeList = ssf::circuit::NodeList; +using CircuitConfig = ssf::circuit::Config; +using ParsedParameters = + ssf::command_line::standard::CommandLine::ParsedParameters; + +// Register the supported client services (port forwarding, SOCKS) +void RegisterSupportedClientServices(); + +// Initialize request client services from parameters +void InitializeClientServices(ClientServices* p_client_services, + const ParsedParameters& parameters, + boost::system::error_code& ec); + +// Generate network query +NetworkProtocol::Query GenerateNetworkQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& config, const CircuitConfig& circuit_config); int main(int argc, char** argv) { -#ifdef TLS_OVER_TCP_LINK - using Client = - ssf::SSFClient; -#elif TCP_ONLY_LINK - using Client = - ssf::SSFClient; -#endif - - using demux = Client::demux; - using BaseUserServicePtr = - ssf::services::BaseUserService::BaseUserServicePtr; - using BounceParser = ssf::parser::BounceParser; - - Init(); - - // Register user services supported - ssf::services::Socks::RegisterToServiceOptionFactory(); - ssf::services::RemoteSocks::RegisterToServiceOptionFactory(); - ssf::services::PortForwading::RegisterToServiceOptionFactory(); - ssf::services::RemotePortForwading::RegisterToServiceOptionFactory(); - ssf::services::UdpPortForwading::RegisterToServiceOptionFactory(); - ssf::services::UdpRemotePortForwading< - demux>::RegisterToServiceOptionFactory(); + RegisterSupportedClientServices(); + // Generate options description from supported services boost::program_options::options_description options = - ssf::ServiceOptionFactory::GetOptionDescriptions(); + ssf::ServiceOptionFactory::GetOptionDescriptions(); - // Parse the command line ssf::command_line::standard::CommandLine cmd; - std::vector user_services; boost::system::error_code ec; - auto parameters = cmd.parse(argc, argv, options, ec); + ParsedParameters parameters = cmd.Parse(argc, argv, options, ec); + + if (ec.value() == ::error::operation_canceled) { + return 0; + } if (ec) { - BOOST_LOG_TRIVIAL(error) << "client: wrong command line arguments"; + SSF_LOG(kLogError) << "client: wrong command line arguments"; return 1; } - // Initialize requested user services (socks, port forwarding) - for (const auto& parameter : parameters) { - for (const auto& single_parameter : parameter.second) { - boost::system::error_code ec; - - auto p_service_options = - ssf::ServiceOptionFactory::ParseServiceLine( - parameter.first, single_parameter, ec); + ssf::log::Configure(cmd.log_level()); - if (!ec) { - user_services.push_back(p_service_options); - } else { - BOOST_LOG_TRIVIAL(error) << "client: wrong parameter " - << parameter.first << " : " << single_parameter - << " : " << ec.message(); - } - } + if (!cmd.host_set()) { + SSF_LOG(kLogError) << "client: no hostname provided -- Exiting"; + return 1; } - if (!cmd.IsAddrSet()) { - BOOST_LOG_TRIVIAL(error) << "client: no hostname provided -- Exiting"; + if (!cmd.port_set()) { + SSF_LOG(kLogError) << "client: no host port provided -- Exiting"; return 1; } - if (!cmd.IsPortSet()) { - BOOST_LOG_TRIVIAL(error) << "client: no host port provided -- Exiting"; + ClientServices user_services; + InitializeClientServices(&user_services, parameters, ec); + + if (ec) { + SSF_LOG(kLogError) + << "client: initialization of client services failed -- Exiting"; return 1; } // Load SSF config if any - boost::system::error_code ec_config; - ssf::Config ssf_config = ssf::LoadConfig(cmd.config_file(), ec_config); + ssf::config::Config ssf_config; - if (ec_config) { - BOOST_LOG_TRIVIAL(error) << "client: invalid config file format" - << std::endl; - return 0; + ssf_config.Update(cmd.config_file(), ec); + + if (ec) { + SSF_LOG(kLogError) << "client: invalid config file format -- Exiting"; + return 1; } - // Initialize the asynchronous engine - boost::asio::io_service io_service; - boost::asio::io_service::work worker(io_service); - boost::thread_group threads; - - for (uint8_t i = 0; i < boost::thread::hardware_concurrency(); ++i) { - auto lambda = [&]() { - boost::system::error_code ec; - try { - io_service.run(ec); - } catch (std::exception e) { - BOOST_LOG_TRIVIAL(error) << "client: exception in io_service run " - << e.what(); - } - }; + ssf_config.Log(); - threads.create_thread(lambda); - } + CircuitConfig circuit_config; + circuit_config.Update(cmd.circuit_file(), ec); - auto callback = [](ssf::services::initialisation::type, BaseUserServicePtr, - const boost::system::error_code&) {}; + if (ec) { + SSF_LOG(kLogError) << "client: invalid circuit config file -- Exiting"; + return 1; + } - // Initiate and start client - Client client(io_service, cmd.addr(), std::to_string(cmd.port()), ssf_config, - user_services, callback); + circuit_config.Log(); + + auto endpoint_query = GenerateNetworkQuery( + cmd.host(), std::to_string(cmd.port()), ssf_config, circuit_config); + + std::condition_variable wait_stop_cv; + std::mutex mutex; + bool stopped = false; + + auto callback = [&wait_stop_cv, &mutex, &stopped]( + ssf::services::initialisation::type type, BaseUserServicePtr p_service, + const boost::system::error_code& ec) { + switch (type) { + case ssf::services::initialisation::NETWORK: { + if (ec) { + SSF_LOG(kLogError) << "client: connected to remote server NOK"; + { + boost::lock_guard lock(mutex); + stopped = true; + } + wait_stop_cv.notify_all(); + } else { + SSF_LOG(kLogInfo) << "client: connected to remote server OK"; + } + break; + } + case ssf::services::initialisation::SERVICE: { + if (p_service.get() != nullptr) { + if (ec) { + SSF_LOG(kLogError) << "client: service <" << p_service->GetName() + << "> NOK"; + } else { + SSF_LOG(kLogInfo) << "client: service <" << p_service->GetName() + << "> OK"; + } + } + break; + } + case ssf::services::initialisation::CLOSE: { + SSF_LOG(kLogInfo) << "client: connection closed"; + { + boost::lock_guard lock(mutex); + stopped = true; + } + wait_stop_cv.notify_all(); + } + default: + break; + } + }; - std::map params; + // Initiate and run client + Client client(user_services, ssf_config.services(), callback); - std::list bouncers = - BounceParser::ParseBounceFile(cmd.bounce_file()); + client.Run(endpoint_query, ec); - if (bouncers.size()) { - auto first = bouncers.front(); - bouncers.pop_front(); - params["remote_addr"] = BounceParser::GetRemoteAddress(first); - params["remote_port"] = BounceParser::GetRemotePort(first); - bouncers.push_back(cmd.addr() + ":" + std::to_string(cmd.port())); - } else { - params["remote_addr"] = cmd.addr(); - params["remote_port"] = std::to_string(cmd.port()); + if (ec) { + SSF_LOG(kLogError) << "client: error happened when running client : " + << ec.message(); + return 1; } - std::ostringstream ostrs; - boost::archive::text_oarchive ar(ostrs); - ar << BOOST_SERIALIZATION_NVP(bouncers); + SSF_LOG(kLogInfo) << "client: connecting to <" << cmd.host() << ":" + << cmd.port() << ">"; - params["bouncing_nodes"] = ostrs.str(); + boost::asio::signal_set signal(client.get_io_service(), SIGINT, SIGTERM); - client.run(params); + signal.async_wait([&wait_stop_cv, &mutex, &stopped]( + const boost::system::error_code& ec, int signum) { + if (ec) { + return; + } + { + boost::lock_guard lock(mutex); + stopped = true; + } + wait_stop_cv.notify_all(); + }); - getchar(); + SSF_LOG(kLogInfo) << "client: running (Ctrl + C to stop)"; - client.stop(); - threads.join_all(); - io_service.stop(); + std::unique_lock lock(mutex); + wait_stop_cv.wait(lock, [&stopped] { return stopped; }); + lock.unlock(); + + SSF_LOG(kLogInfo) << "client: stop"; + signal.cancel(ec); + client.Stop(); return 0; } + +void RegisterSupportedClientServices() { + ssf::services::Socks::RegisterToServiceOptionFactory(); + ssf::services::RemoteSocks::RegisterToServiceOptionFactory(); + ssf::services::PortForwarding::RegisterToServiceOptionFactory(); + ssf::services::RemotePortForwarding::RegisterToServiceOptionFactory(); + ssf::services::UdpPortForwarding::RegisterToServiceOptionFactory(); + ssf::services::UdpRemotePortForwarding< + Demux>::RegisterToServiceOptionFactory(); + ssf::services::Process::RegisterToServiceOptionFactory(); + ssf::services::RemoteProcess::RegisterToServiceOptionFactory(); +} + +void InitializeClientServices(ClientServices* p_client_services, + const ParsedParameters& parameters, + boost::system::error_code& ec) { + // Initialize requested user services (socks, port forwarding) + for (const auto& parameter : parameters) { + for (const auto& single_parameter : parameter.second) { + auto p_service_options = + ssf::ServiceOptionFactory::ParseServiceLine( + parameter.first, single_parameter, ec); + + if (!ec) { + p_client_services->push_back(p_service_options); + } else { + SSF_LOG(kLogError) << "client: wrong parameter " << parameter.first + << ": " << single_parameter << ": " << ec.message(); + return; + } + } + } +} + +NetworkProtocol::Query GenerateNetworkQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& ssf_config, + const CircuitConfig& circuit_config) { + std::string first_node_addr; + std::string first_node_port; + CircuitNodeList nodes = circuit_config.nodes(); + if (nodes.size()) { + auto first_node = nodes.front(); + nodes.pop_front(); + first_node_addr = first_node.addr(); + first_node_port = first_node.port(); + nodes.emplace_back(remote_addr, remote_port); + } else { + first_node_addr = remote_addr; + first_node_port = remote_port; + } + + return NetworkProtocol::GenerateClientQuery(first_node_addr, first_node_port, + ssf_config, nodes); +} diff --git a/src/core/client/main_cp.cpp b/src/core/client/main_cp.cpp index b7b531b8..f1bca8c5 100644 --- a/src/core/client/main_cp.cpp +++ b/src/core/client/main_cp.cpp @@ -1,64 +1,57 @@ -#include -#include +#include +#include #include -#include -#include -#include +#include #include +#include + #include "common/config/config.h" +#include "common/log/log.h" +#include "core/circuit/config.h" #include "core/client/client.h" - #include "core/command_line/copy/command_line.h" -#include "core/parser/bounce_parser.h" #include "core/factories/service_option_factory.h" -#include "core/network_virtual_layer_policies/bounce_protocol_policy.h" -#include "core/network_virtual_layer_policies/link_policies/ssl_policy.h" -#include "core/network_virtual_layer_policies/link_policies/tcp_policy.h" -#include "core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h" +#include "core/network_protocol.h" #include "core/transport_virtual_layer_policies/transport_protocol_policy.h" #include "services/initialisation.h" #include "services/user_services/base_user_service.h" #include "services/user_services/copy_file_service.h" -void Init() { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); -} +using NetworkProtocol = ssf::network::NetworkProtocol; +using Client = + ssf::SSFClient; -int main(int argc, char** argv) { -#ifdef TLS_OVER_TCP_LINK - using Client = - ssf::SSFClient; -#elif TCP_ONLY_LINK - using Client = - ssf::SSFClient; -#endif - - using Demux = Client::demux; - using BaseUserServicePtr = - ssf::services::BaseUserService::BaseUserServicePtr; - using BounceParser = ssf::parser::BounceParser; - - Init(); +using Demux = Client::Demux; +using BaseUserServicePtr = Client::BaseUserServicePtr; +using CircuitNodeList = ssf::circuit::NodeList; +using CircuitConfig = ssf::circuit::Config; + +// Generate network query +NetworkProtocol::Query GenerateNetworkQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& config, const CircuitConfig& circuit_config); +int main(int argc, char** argv) { // Parse the command line ssf::command_line::copy::CommandLine cmd; - std::vector user_services; boost::system::error_code ec; + cmd.Parse(argc, argv, ec); - cmd.parse(argc, argv, ec); + if (ec.value() == ::error::operation_canceled) { + return 0; + } if (ec) { - BOOST_LOG_TRIVIAL(error) << "client: wrong command line arguments"; + SSF_LOG(kLogError) << "client: wrong command line arguments"; return 1; } + + ssf::log::Configure(cmd.log_level()); // Create and initialize copy user service auto p_copy_service = @@ -67,99 +60,139 @@ int main(int argc, char** argv) { cmd.output_pattern(), ec); if (ec) { - BOOST_LOG_TRIVIAL(error) << "client: copy service could not be created"; + SSF_LOG(kLogError) << "client: copy service could not be created"; return 1; } + std::vector user_services; user_services.push_back(p_copy_service); - if (!cmd.IsAddrSet()) { - BOOST_LOG_TRIVIAL(error) << "client: no remote host provided -- Exiting"; + if (!cmd.host_set()) { + SSF_LOG(kLogError) << "client: no remote host provided -- Exiting"; return 1; } - if (!cmd.IsPortSet()) { - BOOST_LOG_TRIVIAL(error) << "client: no host port provided -- Exiting"; + if (!cmd.port_set()) { + SSF_LOG(kLogError) << "client: no host port provided -- Exiting"; return 1; } - // Load SSF config if any boost::system::error_code ec_config; - ssf::Config ssf_config = ssf::LoadConfig(cmd.config_file(), ec_config); + ssf::config::Config ssf_config; + ssf_config.Update(cmd.config_file(), ec_config); if (ec_config) { - BOOST_LOG_TRIVIAL(error) << "client: invalid config file format" - << std::endl; - return 0; + SSF_LOG(kLogError) << "client: invalid config file format"; + return 1; } - // Initialize the asynchronous engine - boost::asio::io_service io_service; - std::unique_ptr p_worker( - new boost::asio::io_service::work(io_service)); - boost::thread_group threads; + ssf_config.Log(); + std::promise closed; - for (uint8_t i = 0; i < boost::thread::hardware_concurrency(); ++i) { - auto lambda = [&]() { - boost::system::error_code ec; - try { - io_service.run(ec); - } catch (std::exception e) { - BOOST_LOG_TRIVIAL(error) << "client: exception in io_service run " - << e.what(); - } - }; + CircuitConfig circuit_config; + circuit_config.Update(cmd.circuit_file(), ec); - threads.create_thread(lambda); + if (ec) { + SSF_LOG(kLogError) << "client: invalid circuit config file -- Exiting"; + return 1; } - auto callback = - [&closed](ssf::services::initialisation::type type, BaseUserServicePtr, - const boost::system::error_code&) { - if (type == ssf::services::initialisation::CLOSE) { - closed.set_value(true); - return; - } - }; + circuit_config.Log(); - // Initiating and starting the client - Client client(io_service, cmd.addr(), std::to_string(cmd.port()), ssf_config, - user_services, callback); + auto endpoint_query = GenerateNetworkQuery( + cmd.host(), std::to_string(cmd.port()), ssf_config, circuit_config); - std::map params; + std::condition_variable wait_stop_cv; + std::mutex mutex; + bool stopped = false; - std::list bouncers = - BounceParser::ParseBounceFile(cmd.bounce_file()); + auto callback = [&wait_stop_cv, &mutex, &stopped]( + ssf::services::initialisation::type type, BaseUserServicePtr, + const boost::system::error_code& ec) { + switch (type) { + case ssf::services::initialisation::NETWORK: { + SSF_LOG(kLogInfo) << "client: connected to remote server " + << (!ec ? "OK" : "NOK"); + break; + } + case ssf::services::initialisation::CLOSE: { + { + boost::lock_guard lock(mutex); + stopped = true; + } + wait_stop_cv.notify_all(); + return; + } + default: + break; + } + if (ec) { + { + boost::lock_guard lock(mutex); + stopped = true; + } + wait_stop_cv.notify_all(); + } + }; - if (bouncers.size()) { - auto first = bouncers.front(); - bouncers.pop_front(); - params["remote_addr"] = BounceParser::GetRemoteAddress(first); - params["remote_port"] = BounceParser::GetRemotePort(first); - bouncers.push_back(cmd.addr() + ":" + std::to_string(cmd.port())); - } else { - params["remote_addr"] = cmd.addr(); - params["remote_port"] = std::to_string(cmd.port()); - } + // Initiating and starting the client + Client client(user_services, ssf_config.services(), callback); - std::ostringstream ostrs; - boost::archive::text_oarchive ar(ostrs); - ar << BOOST_SERIALIZATION_NVP(bouncers); + client.Run(endpoint_query, ec); - params["bouncing_nodes"] = ostrs.str(); + if (ec) { + SSF_LOG(kLogError) << "client: error happened when running client : " + << ec.message(); + return 1; + } - client.run(params); + SSF_LOG(kLogInfo) << "client: connecting to <" << cmd.host() << ":" + << cmd.port() << ">"; + + boost::asio::signal_set signal(client.get_io_service(), SIGINT, SIGTERM); + signal.async_wait([&wait_stop_cv, &mutex, &stopped]( + const boost::system::error_code& ec, int signum) { + if (ec) { + return; + } + { + boost::lock_guard lock(mutex); + stopped = true; + } + wait_stop_cv.notify_all(); + }); // wait end transfer - closed.get_future().get(); + SSF_LOG(kLogInfo) << "client: wait end of file transfer"; + std::unique_lock lock(mutex); + wait_stop_cv.wait(lock, [&stopped] { return stopped; }); + lock.unlock(); + signal.cancel(ec); - p_worker.reset(); + client.Stop(); - threads.join_all(); + return 0; +} - client.stop(); - io_service.stop(); +NetworkProtocol::Query GenerateNetworkQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& ssf_config, + const CircuitConfig& circuit_config) { + std::string first_node_addr; + std::string first_node_port; + CircuitNodeList nodes = circuit_config.nodes(); + if (nodes.size()) { + auto first_node = nodes.front(); + nodes.pop_front(); + first_node_addr = first_node.addr(); + first_node_port = first_node.port(); + nodes.emplace_back(remote_addr, remote_port); + } else { + first_node_addr = remote_addr; + first_node_port = remote_port; + } - return 0; + return NetworkProtocol::GenerateClientQuery(first_node_addr, first_node_port, + ssf_config, nodes); } diff --git a/src/core/command_line/base.cpp b/src/core/command_line/base.cpp new file mode 100644 index 00000000..be989927 --- /dev/null +++ b/src/core/command_line/base.cpp @@ -0,0 +1,248 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common/error/error.h" + +#include "core/command_line/base.h" + +#include + +#include "versions.h" + +namespace ssf { +namespace command_line { + +BaseCommandLine::BaseCommandLine() + : host_(""), + port_(), + config_file_(""), + circuit_file_(""), + log_level_(ssf::log::kLogInfo), + host_set_(false), + port_set_(false) {} + +BaseCommandLine::ParsedParameters BaseCommandLine::Parse( + int argc, char* argv[], boost::system::error_code& ec) { + OptionDescription services; + return Parse(argc, argv, services, ec); +} + +BaseCommandLine::ParsedParameters BaseCommandLine::Parse( + int ac, char* av[], const OptionDescription& services, + boost::system::error_code& ec) { + // Basic options + OptionDescription basic_opts("Basic options"); + InitBasicOptions(basic_opts); + PopulateBasicOptions(basic_opts); + + // Local options + OptionDescription local_opts("Local options"); + InitLocalOptions(local_opts); + PopulateLocalOptions(local_opts); + + try { + OptionDescription cmd_line; + + cmd_line.add(basic_opts).add(local_opts); + + // Other options + PopulateCommandLine(cmd_line); + + // Positional options + PosOptionDescription pos_opts; + PopulatePositionalOptions(pos_opts); + + cmd_line.add(services); + + VariableMap vm; + boost::program_options::store( + boost::program_options::command_line_parser(ac, av) + .options(cmd_line) + .positional(pos_opts) + .run(), + vm); + + if (DisplayHelp(vm, cmd_line)) { + ec.assign(::error::operation_canceled, ::error::get_ssf_category()); + return {}; + } + + boost::program_options::notify(vm); + + return DoParse(services, vm, ec); + } catch (const std::exception& e) { + SSF_LOG(kLogCritical) << "command line: parsing failed: " << e.what(); + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + return std::map>(); + } +} + +void BaseCommandLine::PopulateBasicOptions(OptionDescription& basic_opts) {} + +void BaseCommandLine::PopulateLocalOptions(OptionDescription& local_opts) {} + +void BaseCommandLine::PopulatePositionalOptions( + PosOptionDescription& pos_opts) {} + +void BaseCommandLine::PopulateCommandLine(OptionDescription& command_line) {} + +void BaseCommandLine::ParseOptions(const VariableMap& value, + ParsedParameters& parsed_params, + boost::system::error_code& ec) {} + +bool BaseCommandLine::IsServerCli() { return false; } + +bool BaseCommandLine::DisplayHelp(const VariableMap& vm, + const OptionDescription& cli) { + if (!vm.count("help")) { + return false; + } + + std::cout << "SSF " << ssf::versions::major << "." << ssf::versions::minor + << "." << ssf::versions::fix << std::endl << std::endl; + + std::cout << "Usage: " << GetUsageDesc() << std::endl; + + std::cout << cli << std::endl; + + std::cout << "Using Boost " << ssf::versions::boost_version << " and OpenSSL " + << ssf::versions::openssl_version << std::endl + << std::endl; + + return true; +} + +void BaseCommandLine::ParseBasicOptions(const VariableMap& vm, + boost::system::error_code& ec) { + for (const auto& option : vm) { + if (option.first == "quiet") { + log_level_ = ssf::log::kLogNone; + } else if (option.first == "verbosity") { + set_log_level(option.second.as()); + } else if (option.first == "port") { + int port = option.second.as(); + if (port > 0 && port < 65536) { + port_ = static_cast(port); + port_set_ = true; + } else { + SSF_LOG(kLogError) + << "command line: parsing failed: port option is not " + "between 1 - 65536"; + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + } + } else if (option.first == "circuit") { + circuit_file_ = option.second.as(); + } else if (option.first == "config") { + config_file_ = option.second.as(); + } + } +} + +BaseCommandLine::ParsedParameters BaseCommandLine::DoParse( + const OptionDescription& services, const VariableMap& vm, + boost::system::error_code& ec) { + ParsedParameters result; + + ParseBasicOptions(vm, ec); + if (ec) { + return {}; + } + + ParseOptions(vm, result, ec); + if (ec) { + return {}; + } + + for (const auto& option : vm) { + if (services.find_nothrow(option.first, false) != nullptr) { + // Register service options + result[option.first] = option.second.as>(); + } + } + + ec.assign(::error::success, ::error::get_ssf_category()); + + return result; +} + +void BaseCommandLine::InitBasicOptions(OptionDescription& basic_opts) { + // clang-format off + basic_opts.add_options() + ("help,h", "Produce help message"); + + basic_opts.add_options() + ("verbosity,v", + boost::program_options::value() + ->value_name("level") + ->default_value("info"), + "Verbosity:\n critical|error|warning|info|debug|trace"); + + basic_opts.add_options() + ("quiet,q", "Do not display log"); + // clang-format on +} + +void BaseCommandLine::InitLocalOptions(OptionDescription& local_opts) { + // clang-format off + local_opts.add_options() + ("config,c", + boost::program_options::value() + ->value_name("config_file_path"), + "Set config file"); + + if (!IsServerCli()) { + local_opts.add_options() + ("circuit,b", + boost::program_options::value() + ->value_name("circuit_file_path"), + "Set circuit file"); + + local_opts.add_options() + ("port,p", + boost::program_options::value()->default_value(8011) + ->value_name("port"), + "Set remote SSF server port"); + } else { + local_opts.add_options() + ("port,p", + boost::program_options::value()->default_value(8011) + ->value_name("port"), + "Set local SSF server port"); + } + // clang-format on +} + +void BaseCommandLine::set_log_level(const std::string& level) { + if (log_level_ == ssf::log::kLogNone) { + // Quiet set + return; + } + + if (level == "critical") { + log_level_ = ssf::log::kLogCritical; + } else if (level == "error") { + log_level_ = ssf::log::kLogError; + } else if (level == "warning") { + log_level_ = ssf::log::kLogWarning; + } else if (level == "info") { + log_level_ = ssf::log::kLogInfo; + } else if (level == "debug") { + log_level_ = ssf::log::kLogDebug; + } else if (level == "trace") { + log_level_ = ssf::log::kLogTrace; + } else { + log_level_ = ssf::log::kLogInfo; + } +} + +} // command_line +} // ssf \ No newline at end of file diff --git a/src/core/command_line/base.h b/src/core/command_line/base.h new file mode 100644 index 00000000..67087e8a --- /dev/null +++ b/src/core/command_line/base.h @@ -0,0 +1,98 @@ +#ifndef SSF_CORE_COMMAND_LINE_BASE_H +#define SSF_CORE_COMMAND_LINE_BASE_H + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace ssf { +namespace command_line { + +class BaseCommandLine { + public: + using OptionDescription = boost::program_options::options_description; + using PosOptionDescription = + boost::program_options::positional_options_description; + using VariableMap = boost::program_options::variables_map; + using ParsedParameters = std::map>; + + public: + virtual ~BaseCommandLine() {} + + ParsedParameters Parse(int argc, char* argv[], boost::system::error_code& ec); + + ParsedParameters Parse(int ac, char* av[], const OptionDescription& services, + boost::system::error_code& ec); + + inline std::string host() const { return host_; } + + inline uint16_t port() const { return port_; }; + + inline std::string circuit_file() const { return circuit_file_; } + + inline std::string config_file() const { return config_file_; } + + inline ssf::log::LogLevel log_level() const { return log_level_; } + + inline bool host_set() const { return host_set_; } + + inline bool port_set() const { return port_set_; } + + protected: + BaseCommandLine(); + + // Populate CLI with basic options + virtual void PopulateBasicOptions(OptionDescription& basic_opts); + // Populate CLI with local options + virtual void PopulateLocalOptions(OptionDescription& local_opts); + // Populate CLI with positional options + virtual void PopulatePositionalOptions(PosOptionDescription& pos_opts); + // Populate CLI with custom categories and options + virtual void PopulateCommandLine(OptionDescription& command_line); + // Parse custom options + virtual void ParseOptions(const VariableMap& value, + ParsedParameters& parsed_params, + boost::system::error_code& ec); + // Return usage description for help message + virtual std::string GetUsageDesc() = 0; + + // Return true for server CLI + virtual bool IsServerCli(); + + private: + void InitBasicOptions(OptionDescription& basic_opts); + void InitLocalOptions(OptionDescription& local_opts); + + bool DisplayHelp(const VariableMap& vm, const OptionDescription& cli); + + void ParseBasicOptions(const VariableMap& vm, boost::system::error_code& ec); + + ParsedParameters DoParse(const OptionDescription& services, + const VariableMap& vm, + boost::system::error_code& ec); + + void set_log_level(const std::string& level); + + protected: + std::string host_; + uint16_t port_; + std::string config_file_; + std::string circuit_file_; + ssf::log::LogLevel log_level_; + bool host_set_; + bool port_set_; +}; + +} // command_line +} // ssf + +#endif // SSF_CORE_COMMAND_LINE_BASE_H diff --git a/src/core/command_line/copy/command_line.cpp b/src/core/command_line/copy/command_line.cpp index e3cc015d..64b2ca04 100644 --- a/src/core/command_line/copy/command_line.cpp +++ b/src/core/command_line/copy/command_line.cpp @@ -14,79 +14,46 @@ #include "common/error/error.h" +#include "versions.h" + namespace ssf { namespace command_line { namespace copy { -CommandLine::CommandLine() - : addr_(""), bounce_file_(""), addr_set_(false), port_set_(false) {} - -void CommandLine::parse(int argc, char* argv[], boost::system::error_code& ec) { - try { - // clang-format off - int opt; - boost::program_options::options_description desc("Basic options"); - desc.add_options() - ("help,h", "Produce help message"); - - boost::program_options::options_description options("Client options"); - options.add_options() - ("port,p", - boost::program_options::value(&opt)->default_value(8011), - "Set remote SSF server port") - ("bounces,b", - boost::program_options::value(), - "Set bounce file") - ("config,c", - boost::program_options::value(), - "Set config file"); - - boost::program_options::options_description copy_options("Copy options"); - copy_options.add_options() - ("stdin,t", boost::program_options::bool_switch()->default_value(false), "Input will be stdin") - ("arg1", - boost::program_options::value(), - "[host:]/absolute/path/file if host is present, the file will be copied from the remote host to local") - ("arg2", - boost::program_options::value(), - "[host:]/absolute/path/file if host is present, the file will be copied from local to host"); - // clang-format on - - boost::program_options::positional_options_description position_options; - position_options.add("arg1", 1); - position_options.add("arg2", 1); - - boost::program_options::options_description cmd_line; - cmd_line.add(desc).add(options).add(copy_options); - boost::program_options::variables_map vm; - boost::program_options::store( - boost::program_options::command_line_parser(argc, argv) - .options(cmd_line) - .positional(position_options) - .run(), - vm); - boost::program_options::notify(vm); - - if (vm.count("help")) { - std::cout << "usage : ssfcp [-p port] [-b bounces_file] [-c " - "config] [-h] [-t] arg1 [arg2]" << std::endl; - std::cout << cmd_line << std::endl; - } +CommandLine::CommandLine() : BaseCommandLine(), from_stdin_(false) {} - ec.assign(ssf::error::success, ssf::error::get_ssf_category()); - InternalParsing(vm, ec); - } catch (const std::exception&) { - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); - } -} +void CommandLine::PopulateBasicOptions(OptionDescription& basic_opts) {} -uint16_t CommandLine::port() const { return port_; } +void CommandLine::PopulateLocalOptions(OptionDescription& local_opts) {} -std::string CommandLine::addr() const { return addr_; } +void CommandLine::PopulatePositionalOptions(PosOptionDescription& pos_opts) { + pos_opts.add("arg1", 1); + pos_opts.add("arg2", 1); +} -std::string CommandLine::bounce_file() const { return bounce_file_; } +void CommandLine::PopulateCommandLine(OptionDescription& command_line) { + // clang-format off + boost::program_options::options_description copy_options("Copy options"); + copy_options.add_options() + ("stdin,t", + boost::program_options::bool_switch()->default_value(false), + "Input will be stdin") + ("arg1", + boost::program_options::value()->required() + ->value_name("[host@]/absolute/path/file"), + "[host@]/absolute/path/file if host is present, " \ + "the file will be copied from the remote host to local") + ("arg2", + boost::program_options::value() + ->value_name("[host@]/absolute/path/file"), + "[host@]/absolute/path/file if host is present, " \ + "the file will be copied from local to host"); + // clang-format on + + command_line.add(copy_options); +} -std::string CommandLine::config_file() const { return config_file_; } +bool CommandLine::IsServerCli() { return false; } bool CommandLine::from_stdin() const { return from_stdin_; } @@ -96,77 +63,47 @@ std::string CommandLine::input_pattern() const { return input_pattern_; } std::string CommandLine::output_pattern() const { return output_pattern_; } -bool CommandLine::IsPortSet() const { return port_set_; } - -bool CommandLine::IsAddrSet() const { return addr_set_; } +void CommandLine::ParseOptions(const VariableMap& vm, + ParsedParameters& parsed_params, + boost::system::error_code& ec) { + const auto& stdin_it = vm.find("stdin"); + const auto& vm_end_it = vm.end(); -void CommandLine::InternalParsing( - const boost::program_options::variables_map& vm, - boost::system::error_code& ec) { - auto port_it = vm.find("port"); - if (port_it != vm.end()) { - ParsePort(port_it->second.as(), ec); - } - - auto stdin_it = vm.find("stdin"); - if (stdin_it != vm.end()) { + if (stdin_it != vm_end_it) { from_stdin_ = stdin_it->second.as(); } - auto bounces_file_it = vm.find("bounces"); - if (bounces_file_it != vm.end()) { - bounce_file_ = bounces_file_it->second.as(); - } - - auto config_it = vm.find("config"); - if (config_it != vm.end()) { - config_file_ = config_it->second.as(); - } - - auto first_arg_it = vm.find("arg1"); - if (first_arg_it != vm.end()) { - ParseFirstArgument(first_arg_it->second.as(), ec); - } else { - // there should always exist a first arg (input file, or host:input_file or - // host:output_file) - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); - return; - } + ParseFirstArgument(vm["arg1"].as(), ec); - auto second_arg_it = vm.find("arg2"); - if (second_arg_it != vm.end()) { - ParseSecondArgument(second_arg_it->second.as(), ec); + const auto& arg2_it = vm.find("arg2"); + if (arg2_it != vm_end_it) { + ParseSecondArgument(arg2_it->second.as(), ec); } } -void CommandLine::ParsePort(int port, boost::system::error_code& parse_ec) { - if (port > 0 && port < 65536) { - port_ = static_cast(port); - port_set_ = true; - } else { - parse_ec.assign(ssf::error::invalid_argument, - ssf::error::get_ssf_category()); - } +std::string CommandLine::GetUsageDesc() { + return "ssfcp [options] [host@]/absolute/path/file " + "[[host@]/absolute/path/file]"; } void CommandLine::ParseFirstArgument(const std::string& first_arg, boost::system::error_code& parse_ec) { if (from_stdin_) { - // expecting host:filepath syntax - ExtractHostPattern(first_arg, &addr_, &output_pattern_, parse_ec); + // Expecting host:filepath syntax + ExtractHostPattern(first_arg, &host_, &output_pattern_, parse_ec); if (!parse_ec) { - addr_set_ = true; + host_set_ = true; from_local_to_remote_ = true; } } else { - // expecting host:dirpath or filepath syntax + // Expecting host:dirpath or filepath syntax boost::system::error_code extract_ec; - ExtractHostPattern(first_arg, &addr_, &input_pattern_, extract_ec); + ExtractHostPattern(first_arg, &host_, &input_pattern_, extract_ec); if (!extract_ec) { - addr_set_ = true; + host_set_ = true; from_local_to_remote_ = false; } else { - // not host:dirpath syntax so it is filepath syntax + // Not host:dirpath syntax so it is filepath syntax input_pattern_ = first_arg; from_local_to_remote_ = true; } @@ -176,30 +113,33 @@ void CommandLine::ParseFirstArgument(const std::string& first_arg, void CommandLine::ParseSecondArgument(const std::string& second_arg, boost::system::error_code& parse_ec) { if (from_stdin_) { - // no second arg should be provided - parse_ec.assign(ssf::error::invalid_argument, - ssf::error::get_ssf_category()); - } else { - // expecting host:filepath or filepath syntax - if (from_local_to_remote_) { - // expecting host:dirpath - ExtractHostPattern(second_arg, &addr_, &output_pattern_, parse_ec); - if (parse_ec) { - addr_set_ = false; - return; - } - - addr_set_ = true; - } else { - // expecting dirpath - output_pattern_ = second_arg; - } + // No second arg should be provided with stdin option + SSF_LOG(kLogError) << "command line: parsing failed: two args provided " + "with stdin option, expecting one"; + parse_ec.assign(::error::invalid_argument, ::error::get_ssf_category()); - // Insert trailing slash if not present - auto last_slash_pos = output_pattern_.find_last_of('/'); - if (last_slash_pos != output_pattern_.size() - 1) { - output_pattern_ += '/'; + return; + } + + // Expecting host:filepath or filepath syntax + if (from_local_to_remote_) { + // Expecting host:dirpath + ExtractHostPattern(second_arg, &host_, &output_pattern_, parse_ec); + if (parse_ec) { + host_set_ = false; + return; } + + host_set_ = true; + } else { + // Expecting dirpath + output_pattern_ = second_arg; + } + + // Insert trailing slash if not present + auto last_slash_pos = output_pattern_.find_last_of('/'); + if (last_slash_pos != output_pattern_.size() - 1) { + output_pattern_.append("/"); } } @@ -209,13 +149,13 @@ void CommandLine::ExtractHostPattern(const std::string& string, boost::system::error_code& ec) const { std::size_t found = string.find_first_of(GetHostDirectorySeparator()); if (found == std::string::npos || string.empty()) { - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return; } *p_host = string.substr(0, found); *p_pattern = string.substr(found + 1); - ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + ec.assign(::error::success, ::error::get_ssf_category()); } char CommandLine::GetHostDirectorySeparator() const { return '@'; } diff --git a/src/core/command_line/copy/command_line.h b/src/core/command_line/copy/command_line.h index 858d8493..f368fb25 100644 --- a/src/core/command_line/copy/command_line.h +++ b/src/core/command_line/copy/command_line.h @@ -12,23 +12,19 @@ #include #include +#include + +#include "core/command_line/base.h" + namespace ssf { namespace command_line { namespace copy { -class CommandLine { +class CommandLine : public BaseCommandLine { public: CommandLine(); - void parse(int argc, char* argv[], boost::system::error_code& ec); - - uint16_t port() const; - - std::string addr() const; - - std::string bounce_file() const; - - std::string config_file() const; + virtual ~CommandLine() {} bool from_stdin() const; @@ -38,16 +34,17 @@ class CommandLine { std::string output_pattern() const; - bool IsPortSet() const; - - bool IsAddrSet() const; + protected: + void PopulateBasicOptions(OptionDescription& desc) override; + void PopulateLocalOptions(OptionDescription& desc) override; + void PopulatePositionalOptions(PosOptionDescription& desc) override; + void PopulateCommandLine(OptionDescription& command_line) override; + bool IsServerCli() override; + void ParseOptions(const VariableMap& value, ParsedParameters& parsed_params, + boost::system::error_code& ec) override; + std::string GetUsageDesc() override; private: - void InternalParsing(const boost::program_options::variables_map& vm, - boost::system::error_code& ec); - - void ParsePort(int port, boost::system::error_code& parse_ec); - void ParseFirstArgument(const std::string& first_arg, boost::system::error_code& parse_ec); @@ -61,16 +58,10 @@ class CommandLine { char GetHostDirectorySeparator() const; private: - uint16_t port_; - std::string addr_; - std::string bounce_file_; - std::string config_file_; std::string input_pattern_; std::string output_pattern_; bool from_stdin_; bool from_local_to_remote_; - bool addr_set_; - bool port_set_; }; } // copy diff --git a/src/core/command_line/standard/command_line.cpp b/src/core/command_line/standard/command_line.cpp index b3fc025d..e07fb75a 100644 --- a/src/core/command_line/standard/command_line.cpp +++ b/src/core/command_line/standard/command_line.cpp @@ -1,17 +1,5 @@ #include "core/command_line/standard/command_line.h" -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - #include "common/error/error.h" namespace ssf { @@ -19,128 +7,46 @@ namespace command_line { namespace standard { CommandLine::CommandLine(bool is_server) - : bounce_file_(""), - addr_set_(false), - port_set_(false), - is_server_(is_server) {} - -std::map> CommandLine::parse( - int argc, char* argv[], boost::system::error_code& ec) { - boost::program_options::options_description services; - return parse(argc, argv, services, ec); -} - -std::map> CommandLine::parse( - int ac, char* av[], - const boost::program_options::options_description& services, - boost::system::error_code& ec) { - try { - // clang-format off - int opt; - boost::program_options::options_description desc("Basic options"); - desc.add_options() - ("help,h", "Produce help message"); - - boost::program_options::options_description options("Local options"); - - boost::program_options::positional_options_description p; - if (!is_server_) { - options.add_options() - ("port,p", - boost::program_options::value(&opt)->default_value(8011), - "Set remote SSF server port"); - - options.add_options() - ("host,H", - boost::program_options::value(), - "Set host"); - - p.add("host", 1); - - options.add_options() - ("bounces,b", - boost::program_options::value(), - "Set bounce file"); - } else { - options.add_options() - ("port,p", - boost::program_options::value(&opt)->default_value(8011), - "Set local SSF server port"); - } + : BaseCommandLine(), is_server_(is_server) {} - options.add_options() - ("config,c", - boost::program_options::value(), - "Set config file"); - // clang-format on +void CommandLine::PopulateBasicOptions(OptionDescription& basic_opts) {} - boost::program_options::options_description cmd_line; - cmd_line.add(desc).add(options).add(services); - - boost::program_options::variables_map vm; - boost::program_options::store( - boost::program_options::command_line_parser(ac, av) - .options(cmd_line) - .positional(p) - .run(), - vm); - boost::program_options::notify(vm); - - if (vm.count("help")) { - std::cout << cmd_line << std::endl; - } - - ec.assign(ssf::error::success, ssf::error::get_ssf_category()); - return InternalParsing(vm, ec); - } catch (const std::exception&) { - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); - return std::map>(); +void CommandLine::PopulateLocalOptions(OptionDescription& local_opts) { + auto* p_host_option = + boost::program_options::value(&host_)->value_name("host"); + if (!IsServerCli()) { + p_host_option->required(); } + // clang-format off + local_opts.add_options() + ("host,H", + p_host_option, + "Set host"); + // clang-format on } -uint16_t CommandLine::port() { return port_; } - -std::string CommandLine::addr() { return addr_; } - -std::string CommandLine::bounce_file() { return bounce_file_; } - -std::string CommandLine::config_file() { return config_file_; } - -bool CommandLine::IsPortSet() { return port_set_; } +void CommandLine::PopulatePositionalOptions(PosOptionDescription& pos_opts) { + pos_opts.add("host", 1); +} -bool CommandLine::IsAddrSet() { return addr_set_; } +void CommandLine::PopulateCommandLine(OptionDescription& command_line) {} -std::map> CommandLine::InternalParsing( - const boost::program_options::variables_map& vm, - boost::system::error_code& ec) { - std::map> result; +bool CommandLine::IsServerCli() { return is_server_; } - for (auto& variable : vm) { - if (variable.first == "help") { - } else if (variable.first == "port") { - int port = vm[variable.first].as(); - if (port > 0 && port < 65536) { - port_ = static_cast(port); - port_set_ = true; - } else { - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); - } - } else if (variable.first == "host") { - addr_ = vm[variable.first].as(); - addr_set_ = true; - } else if (variable.first == "bounces") { - bounce_file_ = vm[variable.first].as(); - } else if (variable.first == "config") { - config_file_ = vm[variable.first].as(); - } else { - result[variable.first] = - vm[variable.first].as>(); - } +void CommandLine::ParseOptions(const VariableMap& vm, + ParsedParameters& parsed_params, + boost::system::error_code& ec) { + if (vm.count("host")) { + host_ = vm["host"].as(); + host_set_ = true; } +} - return result; +std::string CommandLine::GetUsageDesc() { + return "ssf[c|s] [options] [host]"; } + } // standard } // command_line } // ssf diff --git a/src/core/command_line/standard/command_line.h b/src/core/command_line/standard/command_line.h index a92950b1..5cd49fe2 100644 --- a/src/core/command_line/standard/command_line.h +++ b/src/core/command_line/standard/command_line.h @@ -12,45 +12,34 @@ #include #include +#include + +#include "core/command_line/base.h" + namespace ssf { namespace command_line { namespace standard { -class CommandLine { +class CommandLine : public BaseCommandLine { public: - CommandLine(bool is_server = false); - - std::map> parse( - int argc, char* argv[], boost::system::error_code& ec); - - std::map> parse( - int ac, char* av[], - const boost::program_options::options_description& services, - boost::system::error_code& ec); + using ParsedParameters = std::map>; - uint16_t port(); - - std::string addr(); - - std::string bounce_file(); - - std::string config_file(); + public: + CommandLine(bool is_server = false); - bool IsPortSet(); + virtual ~CommandLine() {} - bool IsAddrSet(); + protected: + void PopulateBasicOptions(OptionDescription& desc) override; + void PopulateLocalOptions(OptionDescription& desc) override; + void PopulatePositionalOptions(PosOptionDescription& desc) override; + void PopulateCommandLine(OptionDescription& command_line) override; + bool IsServerCli() override; + void ParseOptions(const VariableMap& vm, ParsedParameters& parsed_params, + boost::system::error_code& ec) override; + std::string GetUsageDesc() override; private: - std::map> InternalParsing( - const boost::program_options::variables_map& vm, - boost::system::error_code& ec); - - uint16_t port_; - std::string addr_; - std::string bounce_file_; - std::string config_file_; - bool addr_set_; - bool port_set_; bool is_server_; }; diff --git a/src/core/factories/command_factory.h b/src/core/factories/command_factory.h index cf3456c4..cddd96a9 100644 --- a/src/core/factories/command_factory.h +++ b/src/core/factories/command_factory.h @@ -10,37 +10,35 @@ #include #include - namespace boost { -namespace archive { - class text_iarchive; +namespace archive { +class text_iarchive; } // archive } // boost - namespace ssf { + template class CommandFactory { public: - typedef std::function CommandExecuterType; - - typedef std::function CommandReplierType; + using CommandExecuterType = std::function; + + using CommandReplierType = + std::function; + private: - typedef std::map CommandExecuterMap; - typedef std::map CommandReplierMap; - typedef std::map CommandReplyIndexMap; + using CommandExecuterMap = std::map; + using CommandReplierMap = std::map; + using CommandReplyIndexMap = std::map; public: - static bool RegisterOnReceiveCommand(uint32_t index, + static bool RegisterOnReceiveCommand(uint32_t index, CommandExecuterType executer) { boost::recursive_mutex::scoped_lock lock(executer_mutex_); auto inserted = - command_executers_.insert(std::make_pair(index, std::move(executer))); + command_executers_.insert(std::make_pair(index, std::move(executer))); return inserted.second; } @@ -48,12 +46,11 @@ class CommandFactory { CommandReplierType replier) { boost::recursive_mutex::scoped_lock lock(replier_mutex_); auto inserted = - command_repliers_.insert(std::make_pair(index, std::move(replier))); + command_repliers_.insert(std::make_pair(index, std::move(replier))); return inserted.second; } - static bool RegisterReplyCommandIndex(uint32_t index, - uint32_t reply_index) { + static bool RegisterReplyCommandIndex(uint32_t index, uint32_t reply_index) { boost::recursive_mutex::scoped_lock lock(command_reply_mutex_); auto inserted = command_reply_indexes.insert( std::make_pair(index, std::move(reply_index))); @@ -105,7 +102,6 @@ class CommandFactory { static CommandReplyIndexMap command_reply_indexes; }; - template boost::recursive_mutex CommandFactory::executer_mutex_; template @@ -114,11 +110,15 @@ template boost::recursive_mutex CommandFactory::command_reply_mutex_; template -typename CommandFactory::CommandExecuterMap CommandFactory::command_executers_; +typename CommandFactory::CommandExecuterMap + CommandFactory::command_executers_; template -typename CommandFactory::CommandReplierMap CommandFactory::command_repliers_; +typename CommandFactory::CommandReplierMap + CommandFactory::command_repliers_; template -typename CommandFactory::CommandReplyIndexMap CommandFactory::command_reply_indexes; +typename CommandFactory::CommandReplyIndexMap + CommandFactory::command_reply_indexes; } // ssf + #endif // SSF_CORE_FACTORIES_COMMAND_FACTORY_H_ diff --git a/src/core/factories/service_factory.h b/src/core/factories/service_factory.h index b39c872a..ec9c0c63 100644 --- a/src/core/factories/service_factory.h +++ b/src/core/factories/service_factory.h @@ -11,43 +11,44 @@ #include #include +#include "common/error/error.h" + #include "core/service_manager/service_manager.h" #include "services/base_service.h" #include "core/factory_manager/service_factory_manager.h" namespace ssf { -template -class ServiceFactory : public std::enable_shared_from_this> { -private: - typedef std::map Parameters; - typedef std::shared_ptr> BaseServicePtr; - - typedef std::function ServiceCreator; - typedef std::map ServiceCreatorMap; - - typedef std::shared_ptr> ServiceManagerPtr; -public: - static std::shared_ptr Create( - boost::asio::io_service& io_service, Demux& demux, - ServiceManagerPtr p_service_manager) { - auto p_service_factory = std::shared_ptr( - new ServiceFactory(io_service, demux, p_service_manager)); - - if (p_service_factory) { - ServiceFactoryManager::RegisterServiceFactory(&demux, - p_service_factory); - } +template +class ServiceFactory + : public std::enable_shared_from_this> { + private: + using Parameters = std::map; + using BaseServicePtr = std::shared_ptr>; + using ServiceCreator = std::function; + using ServiceCreatorMap = std::map; + using ServiceManagerPtr = std::shared_ptr>; + + public: + static std::shared_ptr Create( + boost::asio::io_service& io_service, Demux& demux, + ServiceManagerPtr p_service_manager) { + auto p_service_factory = std::shared_ptr( + new ServiceFactory(io_service, demux, p_service_manager)); + + if (p_service_factory) { + ServiceFactoryManager::RegisterServiceFactory(&demux, + p_service_factory); + } - return std::move(p_service_factory); - } + return std::move(p_service_factory); + } - ~ServiceFactory() {} + ~ServiceFactory() {} - bool RegisterServiceCreator(uint32_t index, ServiceCreator creator) { + bool RegisterServiceCreator(uint32_t index, ServiceCreator creator) { boost::recursive_mutex::scoped_lock lock(service_creators_mutex_); if (service_creators_.count(index)) { return false; @@ -57,29 +58,26 @@ class ServiceFactory : public std::enable_shared_from_this } } - uint32_t CreateRunNewService(uint32_t index, - Parameters parameters, - boost::system::error_code& ec) { + uint32_t CreateRunNewService(uint32_t index, Parameters parameters, + boost::system::error_code& ec) { boost::recursive_mutex::scoped_lock lock(service_creators_mutex_); auto it = service_creators_.find(index); if (it != std::end(service_creators_)) { auto& service_creator = it->second; - auto p_service = service_creator(io_service_, - demux_, - parameters); + auto p_service = service_creator(io_service_, demux_, parameters); if (p_service) { auto service_id = p_service_manager_->start(p_service, ec); p_service->set_local_id(service_id); return service_id; } else { - ec.assign(ssf::error::service_not_started, ssf::error::get_ssf_category()); + ec.assign(::error::service_not_started, error::get_ssf_category()); return 0; } } else { - ec.assign(ssf::error::service_not_found, ssf::error::get_ssf_category()); + ec.assign(::error::service_not_found, error::get_ssf_category()); return 0; } } @@ -89,65 +87,51 @@ class ServiceFactory : public std::enable_shared_from_this p_service_manager_->stop_with_id(id, ec); } - void StopAllLocalServices() { - p_service_manager_->stop_all(); - } + void StopAllLocalServices() { p_service_manager_->stop_all(); } void Destroy() { ServiceFactoryManager::UnregisterServiceFactory(&demux_); } - bool UpdateRemoteServiceStatus(uint32_t id, - uint32_t index, + bool UpdateRemoteServiceStatus(uint32_t id, uint32_t index, uint32_t error_code_value, Parameters parameters, boost::system::error_code& ec) { - return p_service_manager_->update_remote(id, - index, - error_code_value, - parameters, - ec); + return p_service_manager_->update_remote(id, index, error_code_value, + parameters, ec); } - bool UpdateRemoteServiceStatus(uint32_t id, - uint32_t error_code_value, + bool UpdateRemoteServiceStatus(uint32_t id, uint32_t error_code_value, boost::system::error_code& ec) { - return p_service_manager_->update_remote(id, - error_code_value, - ec); + return p_service_manager_->update_remote(id, error_code_value, ec); } uint32_t GetIdFromParameters(uint32_t index, Parameters parameters) { return p_service_manager_->get_id(index, parameters); } - uint32_t GetStatus(uint32_t id) { - return p_service_manager_->get_status(id); - } + uint32_t GetStatus(uint32_t id) { return p_service_manager_->get_status(id); } uint32_t GetStatus(uint32_t index, Parameters parameters, uint32_t id) { return p_service_manager_->get_status(index, parameters, id); } -private: - ServiceFactory(boost::asio::io_service& io_service, Demux& demux, - ServiceManagerPtr p_service_manager) - : io_service_(io_service), - demux_(demux), - p_service_manager_(p_service_manager) {} + private: + ServiceFactory(boost::asio::io_service& io_service, Demux& demux, + ServiceManagerPtr p_service_manager) + : io_service_(io_service), + demux_(demux), + p_service_manager_(p_service_manager) {} -private: + private: boost::asio::io_service& io_service_; Demux& demux_; ServiceManagerPtr p_service_manager_; boost::recursive_mutex service_creators_mutex_; ServiceCreatorMap service_creators_; - - }; } // ssf - -#endif // SSF_CORE_FACTORIES_SERVICE_FACTORY_H_ \ No newline at end of file +#endif // SSF_CORE_FACTORIES_SERVICE_FACTORY_H_ diff --git a/src/core/factories/service_option_factory.h b/src/core/factories/service_option_factory.h index 96f9c803..4dea36b9 100644 --- a/src/core/factories/service_option_factory.h +++ b/src/core/factories/service_option_factory.h @@ -15,75 +15,82 @@ #include "services/user_services/base_user_service.h" namespace ssf { + template class ServiceOptionFactory { -private: - typedef std::function>(const std::string&, - boost::system::error_code&)> - ServiceParserType; + private: + using ServiceParserType = + std::function>( + const std::string&, boost::system::error_code&)>; struct ParserDescriptor { ServiceParserType parser; std::string fullname; + std::string value_name; std::string description; }; - typedef std::map ServiceParserMap; + using ServiceParserMap = std::map; -public: - static bool RegisterUserServiceParser(std::string index, std::string full_name, - std::string description, - ServiceParserType parser) { + public: + static bool RegisterUserServiceParser(std::string index, + std::string full_name, + std::string value_name, + std::string description, + ServiceParserType parser) { boost::recursive_mutex::scoped_lock lock(service_options_mutex_); if (service_options_.count(index)) { return false; } else { service_options_[index] = {std::move(parser), std::move(full_name), - std::move(description)}; + std::move(value_name), std::move(description)}; return true; } } - static bool UnregisterUserServiceParser(std::string index) { - boost::recursive_mutex::scoped_lock lock(service_options_mutex_); - auto it = service_options_.find(index); + static bool UnregisterUserServiceParser(std::string index) { + boost::recursive_mutex::scoped_lock lock(service_options_mutex_); + auto it = service_options_.find(index); - if (it != std::end(service_options_)) { - service_options_.erase(it); - return true; - } else { - return false; - } - } + if (it != std::end(service_options_)) { + service_options_.erase(it); + return true; + } else { + return false; + } + } static boost::program_options::options_description GetOptionDescriptions() { - boost::program_options::options_description desc("Supported service commands"); + boost::program_options::options_description desc( + "Supported service commands"); boost::recursive_mutex::scoped_lock lock(service_options_mutex_); for (auto& option : service_options_) { - desc.add_options()(option.second.fullname.c_str(), - boost::program_options::value>(), - option.second.description.c_str()); + desc.add_options()( + option.second.fullname.c_str(), + boost::program_options::value>()->value_name( + option.second.value_name.c_str()), + option.second.description.c_str()); } return desc; } static std::shared_ptr> - ParseServiceLine(std::string option, std::string parameters, - boost::system::error_code& ec) { + ParseServiceLine(std::string option, std::string parameters, + boost::system::error_code& ec) { boost::recursive_mutex::scoped_lock lock(service_options_mutex_); auto it = service_options_.find(option); if (it != std::end(service_options_)) { return it->second.parser(parameters, ec); } else { - ec.assign(ssf::error::service_not_found, ssf::error::get_ssf_category()); + ec.assign(::error::service_not_found, ::error::get_ssf_category()); return std::shared_ptr>(nullptr); } } -private: + private: static boost::recursive_mutex service_options_mutex_; static ServiceParserMap service_options_; }; @@ -92,9 +99,9 @@ template boost::recursive_mutex ServiceOptionFactory::service_options_mutex_; template -typename ServiceOptionFactory::ServiceParserMap ServiceOptionFactory::service_options_; +typename ServiceOptionFactory::ServiceParserMap + ServiceOptionFactory::service_options_; } // ssf - -#endif // SSF_CORE_FACTORIES_SERVICE_OPTION_FACTORY_H_ \ No newline at end of file +#endif // SSF_CORE_FACTORIES_SERVICE_OPTION_FACTORY_H_ diff --git a/src/core/factory_manager/service_factory_manager.h b/src/core/factory_manager/service_factory_manager.h index 466d8154..ef148ae6 100644 --- a/src/core/factory_manager/service_factory_manager.h +++ b/src/core/factory_manager/service_factory_manager.h @@ -7,22 +7,24 @@ #include namespace ssf { + template class ServiceFactory; template class ServiceFactoryManager { -private: - typedef std::shared_ptr> ServiceFactoryPtr; - typedef std::map ServiceFactoryMap; - -public: - static bool RegisterServiceFactory(Demux* index, ServiceFactoryPtr p_factory) { - boost::recursive_mutex::scoped_lock lock(mutex_); - auto inserted = - service_factories_.insert(std::make_pair(index, std::move(p_factory))); - return inserted.second; - } + private: + using ServiceFactoryPtr = std::shared_ptr>; + using ServiceFactoryMap = std::map; + + public: + static bool RegisterServiceFactory(Demux* index, + ServiceFactoryPtr p_factory) { + boost::recursive_mutex::scoped_lock lock(mutex_); + auto inserted = + service_factories_.insert(std::make_pair(index, std::move(p_factory))); + return inserted.second; + } static bool UnregisterServiceFactory(Demux* index) { boost::recursive_mutex::scoped_lock lock(mutex_); @@ -51,7 +53,7 @@ class ServiceFactoryManager { } } -private: + private: static boost::recursive_mutex mutex_; static ServiceFactoryMap service_factories_; }; @@ -60,7 +62,9 @@ template boost::recursive_mutex ServiceFactoryManager::mutex_; template -typename ServiceFactoryManager::ServiceFactoryMap ServiceFactoryManager::service_factories_; +typename ServiceFactoryManager::ServiceFactoryMap + ServiceFactoryManager::service_factories_; } // ssf + #endif // SSF_CORE_FACTORY_MANAGER_SERVICE_FACTORY_MANAGER_H_ diff --git a/src/core/network_protocol.cpp b/src/core/network_protocol.cpp new file mode 100644 index 00000000..7c273197 --- /dev/null +++ b/src/core/network_protocol.cpp @@ -0,0 +1,170 @@ +#include "core/network_protocol.h" + +namespace ssf { +namespace network { + +NetworkProtocol::Query NetworkProtocol::GenerateClientQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& ssf_config, + const ssf::circuit::NodeList& circuit_nodes) { +#ifdef TLS_OVER_TCP_LINK + return GenerateClientTLSQuery(remote_addr, remote_port, ssf_config, + circuit_nodes); +#elif TCP_ONLY_LINK + return GenerateClientTCPQuery(remote_addr, remote_port, circuit_nodes); +#endif +} + +NetworkProtocol::Query NetworkProtocol::GenerateServerQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& ssf_config) { +#ifdef TLS_OVER_TCP_LINK + return GenerateServerTLSQuery(remote_addr, remote_port, ssf_config); +#elif TCP_ONLY_LINK + return NetworkProtocol::GenerateServerTCPQuery(remote_addr, remote_port); +#endif +} + +NetworkProtocol::Query NetworkProtocol::GenerateClientTCPQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& ssf_config, + const ssf::circuit::NodeList& circuit_nodes) { + ssf::layer::LayerParameters proxy_param_layer = + ProxyConfigToLayerParameters(ssf_config); + + ssf::layer::LayerParameters default_param_layer = {{"default", "true"}}; + + ssf::layer::data_link::NodeParameterList nodes; + + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode({{"addr", remote_addr}, {"port", remote_port}}); + nodes.AddTopLayerToBackNode(proxy_param_layer); + + for (auto& circuit_node : circuit_nodes) { + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode( + {{"addr", circuit_node.addr()}, {"port", circuit_node.port()}}); + nodes.AddTopLayerToBackNode(default_param_layer); + } + + return ssf::layer::data_link::make_client_full_circuit_parameter_stack( + "client", nodes); +} + +NetworkProtocol::Query NetworkProtocol::GenerateClientTLSQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& ssf_config, + const ssf::circuit::NodeList& circuit_nodes) { + ssf::layer::LayerParameters tls_param_layer = + TlsConfigToLayerParameters(ssf_config); + + ssf::layer::LayerParameters proxy_param_layer = + ProxyConfigToLayerParameters(ssf_config); + + ssf::layer::LayerParameters default_param_layer = {{"default", "true"}}; + + ssf::layer::data_link::NodeParameterList nodes; + + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode({{"addr", remote_addr}, {"port", remote_port}}); + nodes.AddTopLayerToBackNode(proxy_param_layer); + nodes.AddTopLayerToBackNode(tls_param_layer); + + for (auto& circuit_node : circuit_nodes) { + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode( + {{"addr", circuit_node.addr()}, {"port", circuit_node.port()}}); + nodes.AddTopLayerToBackNode(default_param_layer); + nodes.AddTopLayerToBackNode(default_param_layer); + } + + Query query = ssf::layer::data_link::make_client_full_circuit_parameter_stack( + "client", nodes); + + query.push_front(tls_param_layer); + + return query; +} + +NetworkProtocol::Query NetworkProtocol::GenerateServerTCPQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& ssf_config) { + NetworkProtocol::Query query; + ssf::layer::LayerParameters physical_parameters; + physical_parameters["port"] = remote_port; + if (!remote_addr.empty()) { + physical_parameters["addr"] = remote_addr; + } + + ssf::layer::LayerParameters proxy_param_layer = + ProxyConfigToLayerParameters(ssf_config); + + ssf::layer::ParameterStack layer_parameters; + layer_parameters.push_front(physical_parameters); + + ssf::layer::ParameterStack default_parameters = {proxy_param_layer, {}}; + + return ssf::layer::data_link::make_forwarding_acceptor_parameter_stack( + "server", default_parameters, layer_parameters); +} + +NetworkProtocol::Query NetworkProtocol::GenerateServerTLSQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& ssf_config) { + ssf::layer::LayerParameters tls_param_layer = + TlsConfigToLayerParameters(ssf_config); + + ssf::layer::LayerParameters physical_parameters; + physical_parameters["port"] = remote_port; + if (!remote_addr.empty()) { + physical_parameters["addr"] = remote_addr; + } + + ssf::layer::LayerParameters proxy_param_layer = + ProxyConfigToLayerParameters(ssf_config); + + ssf::layer::ParameterStack layer_parameters; + layer_parameters.push_front(physical_parameters); + layer_parameters.push_front(proxy_param_layer); + layer_parameters.push_front(tls_param_layer); + + ssf::layer::ParameterStack default_parameters = { + {}, tls_param_layer, proxy_param_layer, {}}; + + Query query = ssf::layer::data_link::make_forwarding_acceptor_parameter_stack( + "server", default_parameters, layer_parameters); + + query.push_front(tls_param_layer); + + return query; +} + +ssf::layer::LayerParameters NetworkProtocol::TlsConfigToLayerParameters( + const ssf::config::Config& ssf_config) { + return {{"ca_src", "file"}, + {"crt_src", "file"}, + {"key_src", "file"}, + {"dhparam_src", "file"}, + {"ca_file", ssf_config.tls().ca_cert_path()}, + {"crt_file", ssf_config.tls().cert_path()}, + {"key_file", ssf_config.tls().key_path()}, + {"password", ssf_config.tls().key_password()}, + {"dhparam_file", ssf_config.tls().dh_path()}, + {"set_cipher_suit", ssf_config.tls().cipher_alg()}}; +} + +ssf::layer::LayerParameters NetworkProtocol::ProxyConfigToLayerParameters( + const ssf::config::Config& ssf_config) { + return {{"http_host", ssf_config.http_proxy().host()}, + {"http_port", ssf_config.http_proxy().port()}, + {"http_username", ssf_config.http_proxy().username()}, + {"http_domain", ssf_config.http_proxy().domain()}, + {"http_password", ssf_config.http_proxy().password()}, + {"http_reuse_ntlm", + ssf_config.http_proxy().reuse_ntlm() ? "true" : "false"}, + {"http_reuse_kerb", + ssf_config.http_proxy().reuse_kerb() ? "true" : "false"}}; +} + +} // network +} // ssf \ No newline at end of file diff --git a/src/core/network_protocol.h b/src/core/network_protocol.h new file mode 100644 index 00000000..2231aa12 --- /dev/null +++ b/src/core/network_protocol.h @@ -0,0 +1,89 @@ +#ifndef SSF_CORE_NETWORK_PROTOCOL_H_ +#define SSF_CORE_NETWORK_PROTOCOL_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "common/config/config.h" +#include "core/circuit/config.h" + +namespace ssf { +namespace network { + +class NetworkProtocol { + public: + using Query = ssf::layer::ParameterStack; + + using ProxyTCPProtocol = + ssf::layer::proxy::basic_ProxyProtocol; + + template + using TLSboLayer = ssf::layer::cryptography::basic_CryptoStreamProtocol< + Layer, ssf::layer::cryptography::buffered_tls>; + + using TLSPhysicalProtocol = TLSboLayer; + + using CircuitTLSProtocol = ssf::layer::data_link::basic_CircuitProtocol< + TLSPhysicalProtocol, ssf::layer::data_link::CircuitPolicy>; + using TLSoCircuitTLSProtocol = TLSboLayer; + + using CircuitProtocol = ssf::layer::data_link::basic_CircuitProtocol< + ProxyTCPProtocol, ssf::layer::data_link::CircuitPolicy>; + + using PlainProtocol = CircuitProtocol; + using TLSProtocol = CircuitTLSProtocol; + using FullTLSProtocol = TLSoCircuitTLSProtocol; + +#ifdef TLS_OVER_TCP_LINK + using Protocol = FullTLSProtocol; +#elif TCP_ONLY_LINK + using Protocol = PlainProtocol; +#endif + + public: + static Query GenerateClientQuery(const std::string& remote_addr, + const std::string& remote_port, + const ssf::config::Config& ssf_config, + const ssf::circuit::NodeList& circuit_nodes); + + static Query GenerateServerQuery(const std::string& remote_addr, + const std::string& remote_port, + const ssf::config::Config& ssf_config); + + static Query GenerateClientTCPQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& ssf_config, + const ssf::circuit::NodeList& circuit_nodes); + + static Query GenerateClientTLSQuery( + const std::string& remote_addr, const std::string& remote_port, + const ssf::config::Config& ssf_config, + const ssf::circuit::NodeList& circuit_nodes); + + static Query GenerateServerTCPQuery(const std::string& remote_addr, + const std::string& remote_port, + const ssf::config::Config& ssf_config); + + static Query GenerateServerTLSQuery(const std::string& remote_addr, + const std::string& remote_port, + const ssf::config::Config& ssf_config); + + static ssf::layer::LayerParameters TlsConfigToLayerParameters( + const ssf::config::Config& ssf_config); + + static ssf::layer::LayerParameters ProxyConfigToLayerParameters( + const ssf::config::Config& ssf_config); +}; + +} // network +} // ssf + +#endif // SSF_CORE_NETWORK_PROTOCOL_H_ diff --git a/src/core/network_virtual_layer_policies/bounce_protocol_policy.h b/src/core/network_virtual_layer_policies/bounce_protocol_policy.h deleted file mode 100644 index 705eb48a..00000000 --- a/src/core/network_virtual_layer_policies/bounce_protocol_policy.h +++ /dev/null @@ -1,731 +0,0 @@ -#ifndef SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_BOUNCE_PROTOCOL_POLICY_H_ -#define SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_BOUNCE_PROTOCOL_POLICY_H_ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include "common/config/config.h" - -#include "common/network/network_policy_traits.h" - -#include "common/network/manager.h" -#include "common/network/base_session.h" -#include "common/network/session_forwarder.h" - -#include "core/parser/bounce_parser.h" - -#include "versions.h" - -namespace ssf { - -template class LinkAuthenticationPolicy> -class BounceProtocolPolicy - : public LinkPolicy, - public LinkAuthenticationPolicy::type> { -public: - typedef typename SocketOf::type socket_type; - typedef typename SocketPtrOf::type p_socket_type; - typedef typename AcceptorOf::type acceptor_type; - typedef typename AcceptorPtrOf::type p_acceptor_type; - typedef std::map Parameters; - -private: - using BounceParser = ssf::parser::BounceParser; - typedef std::shared_ptr p_uint32_t; - typedef std::shared_ptr> p_vector_uint32_t; - typedef std::vector vector_error_code_type; - typedef std::shared_ptr p_streambuf; - - typedef std::function - callback_type; - - typedef std::map acceptor_map_type; - - typedef ItemManager SessionManager; - typedef SessionForwarder - SocketSessionForwarder; - -public: - virtual ~BounceProtocolPolicy() {} - - BounceProtocolPolicy(boost::asio::io_service& io_service, - const ssf::Config& ssf_config) - : LinkPolicy(io_service, ssf_config), - io_service_(io_service) {} - - void AddRoute(Parameters& parameters, callback_type callback) { - std::list bouncing_nodes = this->GetBouncingNodes(parameters); - parameters["local"] = "true"; - - size_t total_size = bouncing_nodes.size() + 1; - auto p_ec_values = std::make_shared>(total_size, 0); - - auto handler = [this, parameters, p_ec_values, total_size]( - p_uint32_t p_ec_value, p_socket_type p_socket, callback_type callback) { - this->GetAllEcs(parameters, p_ec_values, 0, (uint32_t)total_size, p_ec_value, p_socket, - callback); - }; - - auto handler_to_do_add_route = [this, handler, callback]( - Parameters parameters, p_socket_type p_socket, - const boost::system::error_code& ec) { - if (!ec) { - BOOST_LOG_TRIVIAL(trace) << "network: do add route"; - this->DoAddRoute(handler, parameters, callback); - } else { - BOOST_LOG_TRIVIAL(trace) << "network: handler to add route end"; - this->ProtocolEnd(nullptr, ec, callback); - } - }; - - this->GetCredentials(parameters, handler_to_do_add_route, nullptr); - } - - void DeleteRoute(socket_type& socket) { - BOOST_LOG_TRIVIAL(trace) << "network: delete route"; - this->CloseLink(socket); - } - - void AcceptNewRoutes(uint16_t port, callback_type callback) { - auto p_acceptor = std::make_shared(io_service_); - - boost::system::error_code ec; - InitAcceptor(*p_acceptor, port, ec); - - if (!ec) { - { - boost::recursive_mutex::scoped_lock lock(acceptors_mutex_); - acceptors_[port] = p_acceptor; - } - - this->AcceptLinks( - p_acceptor, - boost::bind(&BounceProtocolPolicy::NewLinkConnectedHandler, this, - callback, _1)); - } else { - auto p_socket = p_socket_type(nullptr); - this->ProtocolEnd(p_socket, ec, callback); - } - } - - void StopAcceptingRoutes() { - boost::recursive_mutex::scoped_lock lock(acceptors_mutex_); - for (auto& p_acceptor : acceptors_) { - boost::system::error_code ec; - p_acceptor.second->close(ec); - } - acceptors_.clear(); - } - - - //------------------------------------------------------- - -private: - static void InitAcceptor(acceptor_type& acceptor, uint16_t port, - boost::system::error_code& ec) { - boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port); - boost::asio::socket_base::reuse_address option(true); - - acceptor.open(endpoint.protocol(), ec); - if (!ec) { - acceptor.set_option(option, ec); - if (!ec) { - acceptor.bind(endpoint, ec); - if (!ec) { - acceptor.listen(100, ec); /// - if (ec) { - BOOST_LOG_TRIVIAL(error) << "network: error in listen" << ec.message() << " " << ec.value() - << std::endl; - } - } else { - BOOST_LOG_TRIVIAL(error) << "network: error in bind" << ec.message() << " " << ec.value() - << std::endl; - } - } else { - BOOST_LOG_TRIVIAL(error) << "network: error in set_option" << ec.message() << " " << ec.value() - << std::endl; - } - } else { - BOOST_LOG_TRIVIAL(error) << "network: error in open" << ec.message() << " " << ec.value() - << std::endl; - } - } - - static void CloseAcceptor(acceptor_type& acceptor) { - boost::system::error_code ec; - acceptor.cancel(ec); - acceptor.close(ec); - } - - //------------------------------------------------------- - - private: - - //------------------------------------------------------- - template - void DoAddRoute(Handler handler, const Parameters& parameters, - callback_type callback) { - BOOST_LOG_TRIVIAL(trace) << "network: establish link"; - this->EstablishLink( - parameters, - boost::bind(&BounceProtocolPolicy::LinkEstablishedHandler, - this, handler, parameters, callback, _1, _2)); - } - - template - void LinkEstablishedHandler(Handler handler, const Parameters& parameters, - callback_type callback, p_socket_type p_socket, - const boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(trace) << "network: link established handler"; - if (!ec && p_socket) { - auto p_version = std::make_shared(0); - - boost::asio::async_read( - *p_socket, boost::asio::buffer(p_version.get(), sizeof(*p_version)), - boost::bind( - &BounceProtocolPolicy::BounceVersionReceivedHandler, - this, handler, parameters, callback, p_version, p_socket, _1, - _2)); - } else { - if (p_socket) { - this->CloseLink(*p_socket); - } - this->ProtocolEnd(nullptr, ec, callback); - return; - } - } - - template - void BounceVersionReceivedHandler( - Handler handler, const Parameters& parameters, callback_type callback, - std::shared_ptr p_version, p_socket_type p_socket, - const boost::system::error_code& ec, size_t length) { - if (!ec) { - BOOST_LOG_TRIVIAL(trace) << "network: bounce version received: " << *p_version; - - if (IsSupportedVersion(*p_version)) { - auto p_answer = std::make_shared(1); - boost::asio::async_write( - *p_socket, boost::asio::buffer(p_answer.get(), sizeof(*p_answer)), - boost::bind( - &BounceProtocolPolicy::BounceAnswerSentHandler, - this, handler, parameters, callback, p_answer, p_socket, _1, - _2)); - } else { - BOOST_LOG_TRIVIAL(error) << "network: bounce version NOT supported " - << *p_version; - boost::system::error_code result_ec(ssf::error::wrong_protocol_type, - ssf::error::get_ssf_category()); - this->CloseLink(*p_socket); - this->ProtocolEnd(nullptr, result_ec, callback); - return; - } - } else { - BOOST_LOG_TRIVIAL(error) << "network: bounce version NOT received " - << ec.message(); - this->CloseLink(*p_socket); - this->ProtocolEnd(nullptr, ec, callback); - return; - } - } - - template - void BounceAnswerSentHandler(Handler handler, const Parameters& parameters, - callback_type callback, - std::shared_ptr p_answer, - p_socket_type p_socket, - const boost::system::error_code& ec, - size_t length) { - if (!ec) { - BOOST_LOG_TRIVIAL(info) << "network: bounce answer sent"; - - if (*p_answer) { - BOOST_LOG_TRIVIAL(info) << "network: bounce answer OK"; - std::list bouncing_nodes = this->GetBouncingNodes(parameters); - - std::ostringstream ostrs; - boost::archive::text_oarchive ar(ostrs); - - ar << BOOST_SERIALIZATION_NVP(bouncing_nodes); - - std::string result(ostrs.str()); - - auto p_bounce_size = std::make_shared((uint32_t)result.size()); - - boost::asio::async_write( - *p_socket, - boost::asio::buffer(p_bounce_size.get(), sizeof(*p_bounce_size)), - boost::bind( - &BounceProtocolPolicy::SentBounceSizeHandler, this, - handler, result, bouncing_nodes.size(), p_bounce_size, p_socket, - callback, _1, _2)); - } else { - boost::system::error_code result_ec(ssf::error::wrong_protocol_type, - ssf::error::get_ssf_category()); - BOOST_LOG_TRIVIAL(error) << "network: bounce answer NOT ok " - << result_ec.message(); - this->CloseLink(*p_socket); - this->ProtocolEnd(nullptr, result_ec, callback); - return; - } - } else { - BOOST_LOG_TRIVIAL(error) << "network: could NOT send Bounce answer " - << ec.message(); - this->CloseLink(*p_socket); - this->ProtocolEnd(nullptr, ec, callback); - return; - } - } - - template - void SentBounceSizeHandler(Handler handler, std::string result, - size_t bounce_number, p_uint32_t p_bounce_size, - p_socket_type p_socket, callback_type callback, - const boost::system::error_code& ec, - size_t length) { - if (!ec) { - auto p_buf = std::make_shared(); - std::ostream os(p_buf.get()); - os << result; - - boost::asio::async_write( - *p_socket, p_buf->data(), - boost::bind( - &BounceProtocolPolicy::SentBouncingNodesHandler, this, - handler, bounce_number, p_buf, p_socket, callback, _1, _2)); - } else { - this->CloseLink(*p_socket); - ProtocolEnd(p_socket, ec, callback); - } - } - - template - void SentBouncingNodesHandler(Handler handler, size_t bounce_number, - p_streambuf p_buf, p_socket_type p_socket, - callback_type callback, - const boost::system::error_code& ec, - size_t length) { - if (!ec) { - this->ReceiveOneEc(handler, p_socket, callback); - } else { - this->CloseLink(*p_socket); - this->ProtocolEnd(p_socket, ec, callback); - } - } - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - template - void ReceiveOneEc(Handler handler, p_socket_type p_socket, - callback_type callback) { - auto p_ec_value = std::make_shared(0); - BOOST_LOG_TRIVIAL(trace) << "network: bounce protocol receive one ec async read some"; - boost::asio::async_read( - *p_socket, boost::asio::buffer(p_ec_value.get(), sizeof(*p_ec_value)), - boost::bind(&BounceProtocolPolicy::ReceivedOneEcHandler, this, - handler, p_ec_value, p_socket, callback, _1, _2)); - } - - template - void ReceivedOneEcHandler(Handler handler, p_uint32_t p_ec_value, - p_socket_type p_socket, callback_type callback, - const boost::system::error_code& ec, - size_t length) { - if (!ec) { - handler(p_ec_value, p_socket, callback); - } else { - this->CloseLink(*p_socket); - *p_ec_value = ec.value(); - handler(p_ec_value, p_socket_type(nullptr), callback); - } - } - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - void GetAllEcs(const Parameters& parameters, p_vector_uint32_t p_ec_values, - uint32_t already_done, uint32_t total_size, - p_uint32_t p_ec_value, p_socket_type p_socket, - callback_type callback) { - BOOST_LOG_TRIVIAL(trace) << "network: get all ecs done : " << already_done; - if (already_done < total_size) { - (*p_ec_values)[already_done] = *p_ec_value; - } - - if (already_done < total_size - 1) { - if (!(*p_ec_value)) { - - auto handler = [this, p_ec_values, already_done, total_size, callback]( - const Parameters& parameters, p_socket_type p_socket, - const boost::system::error_code& ec) { - if (!ec) { - this->ReceiveOneEc< - std::function>( - boost::bind(&BounceProtocolPolicy::GetAllEcs, this, - parameters, p_ec_values, already_done + 1, - total_size, _1, _2, _3), - p_socket, callback); - } else { - (*p_ec_values)[already_done] = ec.value(); - this->ProtocolEnd(p_socket, p_ec_values, callback); - } - }; - - Parameters params; - params["bouncing_nodes"] = parameters.find("bouncing_nodes")->second; - params["node_id"] = std::to_string(already_done); - - this->SetCredentials(params, handler, p_socket); - } else { - this->ProtocolEnd(p_socket, p_ec_values, callback); - } - } else { - this->ProtocolEnd(p_socket, p_ec_values, callback); - } - } - - //******************************** - - - //******************************** - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - template - void SendOneEc(Handler handler, p_uint32_t p_ec_value, - p_socket_type p_socket, callback_type callback) { - boost::asio::async_write( - *p_socket, boost::asio::buffer(p_ec_value.get(), sizeof(*p_ec_value)), - boost::bind(&BounceProtocolPolicy::SentOneEcHandler, this, - p_ec_value, p_socket, handler, callback, _1, _2)); - } - - template - void SentOneEcHandler(p_uint32_t p_ec_value, p_socket_type p_socket, - Handler handler, callback_type callback, - const boost::system::error_code& ec, size_t length) { - if (!ec) { - handler(p_socket, callback); - } else { - this->CloseLink(*p_socket); - this->ProtocolEnd(p_socket, ec, callback); - } - } - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - //------------------------------------------------------- - void NewLinkConnectedHandler(callback_type callback, p_socket_type p_socket) { - if (!p_socket) { - boost::system::error_code ec(ssf::error::not_a_socket, - ssf::error::get_ssf_category()); - this->ProtocolEnd(nullptr, vector_error_code_type(1, ec), callback); - return; - } - - BOOST_LOG_TRIVIAL(info) << "network: starting bounce protocol"; - - auto p_version = std::make_shared(GetVersion()); - - boost::asio::async_write( - *p_socket, boost::asio::buffer(p_version.get(), sizeof(*p_version)), - boost::bind(&BounceProtocolPolicy::BounceVersionSentHandler, this, - callback, p_version, p_socket, _1, _2)); - } - - void BounceVersionSentHandler(callback_type callback, - std::shared_ptr p_version, - p_socket_type p_socket, - const boost::system::error_code& ec, - size_t length) { - if (!ec) { - BOOST_LOG_TRIVIAL(info) << "network: bounce request sent"; - - auto p_answer = std::make_shared(0); - - boost::asio::async_read( - *p_socket, boost::asio::buffer(p_answer.get(), sizeof(*p_answer)), - boost::bind(&BounceProtocolPolicy::BounceAnwserReceivedHandler, - this, callback, p_answer, p_socket, _1, _2)); - } else { - BOOST_LOG_TRIVIAL(error) << "network: could NOT send the Bounce request " - << ec.message(); - this->CloseLink(*p_socket); - this->ProtocolEnd(nullptr, vector_error_code_type(1, ec), callback); - return; - } - } - - void BounceAnwserReceivedHandler(callback_type callback, - std::shared_ptr p_answer, - p_socket_type p_socket, - const boost::system::error_code& ec, - size_t length) { - if (!ec) { - BOOST_LOG_TRIVIAL(info) << "network: bounce answer received"; - - if (*p_answer) { - BOOST_LOG_TRIVIAL(info) << "network: bounce answer OK"; - auto p_bounce_size = std::make_shared(0); - - boost::asio::async_read( - *p_socket, - boost::asio::buffer(p_bounce_size.get(), sizeof(*p_bounce_size)), - boost::bind(&BounceProtocolPolicy::ReceivedBounceSizeHandler, - this, p_bounce_size, p_socket, callback, _1, _2)); - } else { - boost::system::error_code result_ec(ssf::error::wrong_protocol_type, - ssf::error::get_ssf_category()); - BOOST_LOG_TRIVIAL(error) << "network: bounce anwser NOT ok " << ec.message(); - this->CloseLink(*p_socket); - this->ProtocolEnd(nullptr, vector_error_code_type(1, result_ec), - callback); - return; - } - } else { - BOOST_LOG_TRIVIAL(error) << "network: could NOT receive the Bounce anwser " - << ec.message(); - this->CloseLink(*p_socket); - this->ProtocolEnd(nullptr, vector_error_code_type(1, ec), callback); - return; - } - } - - void ReceivedBounceSizeHandler(p_uint32_t p_bounce_size, - p_socket_type p_socket, - callback_type callback, - const boost::system::error_code& ec, - size_t length) { - if (!ec) { - auto p_buffer = std::make_shared(); - boost::asio::streambuf::mutable_buffers_type bufs = - p_buffer->prepare(*p_bounce_size); - - BOOST_LOG_TRIVIAL(trace) << "network: bounce protocol receive bounce size"; - boost::asio::async_read( - *p_socket, bufs, - boost::bind(&BounceProtocolPolicy::ReceivedBouncingNodesHandler, - this, p_buffer, p_socket, callback, _1, _2)); - } else { - this->CloseLink(*p_socket); - this->ProtocolEnd(p_socket, vector_error_code_type(1, ec), callback); - } - } - - void ReceivedBouncingNodesHandler(p_streambuf p_buf, - p_socket_type p_socket, - callback_type callback, - const boost::system::error_code& ec, - size_t length) { - if (!ec) { - p_buf->commit(length); - std::istream is(p_buf.get()); - boost::archive::text_iarchive ar(is); - std::list bouncing_nodes; - - try { - ar >> BOOST_SERIALIZATION_NVP(bouncing_nodes); - - auto p_ec_value = std::make_shared(ec.value()); - - SendOneEc>( - boost::bind(&BounceProtocolPolicy::EstablishRoute, this, - bouncing_nodes, _1, _2), - p_ec_value, p_socket, callback); - return; - } catch (const std::exception&) { - this->CloseLink(*p_socket); - this->ProtocolEnd( - p_socket, - vector_error_code_type( - 1, boost::system::error_code(ssf::error::protocol_error, - ssf::error::get_ssf_category())), - callback); - return; - } - } else { - this->CloseLink(*p_socket); - this->ProtocolEnd(p_socket, vector_error_code_type(1, ec), callback); - return; - } - } - - void EstablishRoute(const std::list& bouncing_nodes, - p_socket_type p_socket, callback_type callback) { - if (!bouncing_nodes.size()) { - this->ProtocolEnd(p_socket, 0, callback); - } else { - this->ForwardLink(bouncing_nodes, p_socket); - } - } - - //------------------------------------------------------- - - void ForwardLink(std::list bouncing_nodes, - p_socket_type p_socket_in) { - std::string remote_endpoint_string = this->PopEndpointString(bouncing_nodes); - std::string remote_addr = BounceParser::GetRemoteAddress(remote_endpoint_string); - std::string remote_port = BounceParser::GetRemotePort(remote_endpoint_string); - - if (remote_addr == "" || remote_port == "") { - this->CloseLink(*p_socket_in); - return; - } - BOOST_LOG_TRIVIAL(trace) << "network: forward link " << remote_addr << ":" << remote_port; - Parameters parameters; - parameters["remote_addr"] = remote_addr; - parameters["remote_port"] = remote_port; - - std::ostringstream ostrs; - boost::archive::text_oarchive ar(ostrs); - ar << BOOST_SERIALIZATION_NVP(bouncing_nodes); - std::string bouncers(ostrs.str()); - - parameters["bouncing_nodes"] = bouncers; - - auto callback = [this, p_socket_in](p_socket_type p_socket_out, - vector_error_code_type v_ec) { - if (v_ec[0]) { - this->CloseLink(*p_socket_in); - } - }; - - std::function - handler_to_establish_session = [this](p_socket_type p_socket_out, - p_socket_type p_socket_in, - callback_type) { - if (p_socket_in && p_socket_out) { - this->EstablishSession(p_socket_in, p_socket_out); - } else { - this->CloseLink(*p_socket_in); - } - }; - - auto handler_to_send_one_ec = [this, p_socket_in, - handler_to_establish_session]( - p_uint32_t p_ec_value, p_socket_type p_socket_out, - callback_type callback) { - this->SendOneEc>( - boost::bind(handler_to_establish_session, p_socket_out, _1, _2), - p_ec_value, p_socket_in, callback); - }; - - auto handler_to_do_add_route = [this, handler_to_send_one_ec, - callback]( - Parameters parameters, p_socket_type p_socket_in, - const boost::system::error_code& ec) { - if (!ec) { - this->DoAddRoute(handler_to_send_one_ec, parameters, callback); - } else { - auto p_ec_value = std::make_shared(ec.value()); - handler_to_send_one_ec(p_ec_value, p_socket_type(nullptr), callback); - //this->ProtocolEnd(p_socket_in, ec, callback); - } - }; - - this->GetCredentials(parameters, handler_to_do_add_route, p_socket_in); - } - - void EstablishSession(p_socket_type p_socket_in, p_socket_type p_socket_out) { - boost::system::error_code ec; - - // ! Can't std::move ssl stream ! // - auto p_session = SocketSessionForwarder::create( - &manager_, std::move(*p_socket_in), std::move(*p_socket_out)); - manager_.start(p_session, ec); - } - - //------------------------------------------------------- - void ProtocolEnd(p_socket_type p_socket, const boost::system::error_code& ec, - callback_type callback) { - this->ProtocolEnd(p_socket, vector_error_code_type(1, ec), callback); - } - - void ProtocolEnd(p_socket_type p_socket, uint32_t ec_value, - callback_type callback) { - boost::system::error_code ec(ec_value, boost::system::system_category()); - - this->ProtocolEnd(p_socket, ec, callback); - } - - void ProtocolEnd(p_socket_type p_socket, p_vector_uint32_t p_status, - callback_type callback) { - vector_error_code_type v_ec; - for (size_t i = 0; i < p_status->size(); ++i) { - v_ec.push_back(boost::system::error_code( - (*p_status)[i], boost::system::system_category())); - } - - this->ProtocolEnd(p_socket, v_ec, callback); - } - - void ProtocolEnd(p_socket_type p_socket, vector_error_code_type v_ec, - callback_type callback) { - io_service_.post(boost::bind(callback, p_socket, v_ec)); - } - - //------------------------------------------------------- - - uint32_t GetVersion() { - uint32_t version = versions::major; - version = version << 8; - - version |= versions::minor; - version = version << 8; - - version |= versions::bounce; - version = version << 8; - - version |= uint8_t(boost::archive::BOOST_ARCHIVE_VERSION()); - - return version; - } - - bool IsSupportedVersion(uint32_t input_version) { - boost::archive::library_version_type serialization(input_version & - 0x000000FF); - input_version = input_version >> 8; - - uint8_t bounce = (input_version & 0x000000FF); - input_version = input_version >> 8; - - uint8_t minor = (input_version & 0x000000FF); - input_version = input_version >> 8; - - uint8_t major = (input_version & 0x000000FF); - - return (major == versions::major) && (minor == versions::minor) && - (bounce == versions::bounce) && - (serialization == boost::archive::BOOST_ARCHIVE_VERSION()); - } - -private: - boost::asio::io_service& io_service_; - - boost::recursive_mutex acceptors_mutex_; - acceptor_map_type acceptors_; - - SessionManager manager_; -}; - -} // ssf - -#endif // SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_BOUNCE_PROTOCOL_POLICY_H_ \ No newline at end of file diff --git a/src/core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h b/src/core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h deleted file mode 100644 index f5b6d8b4..00000000 --- a/src/core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_LINK_AUTHENTICATION_POLICIES_NULL_LINK_AUTHENTICATION_POLICY_H_ -#define SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_LINK_AUTHENTICATION_POLICIES_NULL_LINK_AUTHENTICATION_POLICY_H_ - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace ssf { - -template -class NullLinkAuthenticationPolicy { - private: - typedef SocketType socket_type; - typedef std::shared_ptr p_socket_type; - typedef std::shared_ptr p_uint32_t; - typedef std::map Parameters; - - typedef std::function - callback_type; - -public: - void GetCredentials(Parameters& parameters, callback_type callback, - p_socket_type p_socket) { - auto p_value = std::make_shared(0); - if (parameters["local"] == "true") { - callback(parameters, p_socket, boost::system::error_code()); - } else { - boost::asio::async_write( - *p_socket, boost::asio::buffer(p_value.get(), sizeof(*p_value)), - boost::bind( - &NullLinkAuthenticationPolicy::RemoteConnectionEstablishedHandler, - this, parameters, callback, p_value, p_socket, _1, _2)); - } - } - - void SetCredentials(const Parameters& parameters, callback_type callback, - p_socket_type p_socket) { - auto p_value = std::make_shared(0); - - boost::asio::async_read( - *p_socket, boost::asio::buffer(p_value.get(), sizeof(*p_value)), - boost::bind( - &NullLinkAuthenticationPolicy::NextConnectionEstablishedHandler, - this, parameters, callback, p_value, p_socket, _1, _2)); - } - - std::list GetBouncingNodes(const Parameters& parameters) { - if (parameters.count("bouncing_nodes")) { - auto serialized_list = parameters.find("bouncing_nodes")->second; - - std::istringstream istrs(serialized_list); - boost::archive::text_iarchive ar(istrs); - std::list bouncing_nodes; - - try { - ar >> BOOST_SERIALIZATION_NVP(bouncing_nodes); - return bouncing_nodes; - } catch (const std::exception&) { - return std::list(); - } - } else { - return std::list(); - } - } - - std::string PopEndpointString(std::list& bouncing_nodes) { - if (bouncing_nodes.size()) { - auto first = bouncing_nodes.front(); - bouncing_nodes.pop_front(); - return first; - } else { - return ""; - } - } - -private: - void RemoteConnectionEstablishedHandler(const Parameters& parameters, - callback_type callback, - p_uint32_t p_value, - p_socket_type p_socket, - const boost::system::error_code& ec, - size_t length) { - callback(parameters, p_socket, ec); - } - - void NextConnectionEstablishedHandler(const Parameters& parameters, - callback_type callback, - p_uint32_t p_value, - p_socket_type p_socket, - const boost::system::error_code& ec, - size_t length) { - - callback(parameters, p_socket, ec); - } -}; -} // ssf - -#endif // SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_LINK_AUTHENTICATION_POLICIES_NULL_LINK_AUTHENTICATION_POLICY_H_ diff --git a/src/core/network_virtual_layer_policies/link_policies/ssl_policy.h b/src/core/network_virtual_layer_policies/link_policies/ssl_policy.h deleted file mode 100644 index 70da68a0..00000000 --- a/src/core/network_virtual_layer_policies/link_policies/ssl_policy.h +++ /dev/null @@ -1,848 +0,0 @@ -#ifndef SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_SSL_POLICY_H_ -#define SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_SSL_POLICY_H_ - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "versions.h" -#include "common/config/config.h" -#include "common/error/error.h" -#include "common/boost/fiber/detail/io_ssl_read_op.hpp" -#include "common/utils/cleaner.h" - -namespace ssf { - -//---------------------------------------------------------------------------- - -/// The class in charge of receiving data from an ssl stream into a buffer -template -class SSLWrapperPuller - : public std::enable_shared_from_this> { -private: - enum { - lower_queue_size_bound = 1 * 1024 * 1024, - higher_queue_size_bound = 16 * 1024 * 1024, - receive_buffer_size = 50 * 1024 - }; - -private: - typedef boost::asio::ssl::stream ssl_type; - -public: - typedef SSLWrapperPuller puller_type; - typedef std::shared_ptr p_puller_type; - typedef boost::asio::detail::op_queue< - boost::asio::fiber::detail::basic_pending_ssl_operation> op_queue_type; - -public: - SSLWrapperPuller(const SSLWrapperPuller &) = delete; - SSLWrapperPuller &operator=(const SSLWrapperPuller &) = delete; - ~SSLWrapperPuller() {} - - static p_puller_type create(ssl_type &socket, - boost::asio::io_service::strand &strand) { - return p_puller_type(new puller_type(socket, strand)); - } - - /// Start receiving data - void StartPulling() { - { - boost::recursive_mutex::scoped_lock lock(pulling_mutex_); - if (!pulling_) { - pulling_ = true; - BOOST_LOG_TRIVIAL(debug) << "link: SSLWrapperPuller pulling"; - io_service_.post( - boost::bind(&SSLWrapperPuller::AsyncPullPackets, - this->shared_from_this())); - } - } - } - - /// User interface for receiving some data - template - void async_read_some(MutableBufferSequence &buffers, Handler &handler) { - BOOST_LOG_TRIVIAL(trace) << "link: SSLWrapperPuller async_read_some"; - auto buffer_size = boost::asio::buffer_size(buffers); - - if (buffer_size) { - typedef boost::asio::fiber::detail::pending_ssl_read_operation< - MutableBufferSequence, Handler> op; - typename op::ptr p = { - boost::asio::detail::addressof(handler), - boost_asio_handler_alloc_helpers::allocate(sizeof(op), handler), 0}; - - p.p = new (p.v) op(buffers, handler); - - { - boost::recursive_mutex::scoped_lock lock(op_queue_mutex_); - op_queue_.push(p.p); - } - - p.v = p.p = 0; - - io_service_.dispatch( - boost::bind(&SSLWrapperPuller::HandleDataNOps, - this->shared_from_this())); - } else { - auto lambda = - [handler]() mutable { handler(boost::system::error_code(), 0); }; - io_service_.post(lambda); - } - } - -private: - SSLWrapperPuller(ssl_type &socket, boost::asio::io_service::strand &strand) - : socket_(socket), strand_(strand), io_service_(strand.get_io_service()), - pulling_(false) {} - - /// Check if data is available for user requests - void HandleDataNOps() { - if (!status_) { - { - boost::recursive_mutex::scoped_lock lock(pulling_mutex_); - if ((data_queue_.size() < lower_queue_size_bound) && !pulling_) { - this->StartPulling(); - } - } - - boost::recursive_mutex::scoped_lock lock1(data_queue_mutex_); - boost::recursive_mutex::scoped_lock lock2(op_queue_mutex_); - if (!op_queue_.empty() && data_queue_.size()) { - auto op = op_queue_.front(); - op_queue_.pop(); - - size_t copied = op->fill_buffers(data_queue_); - - auto do_complete = - [=]() { op->complete(boost::system::error_code(), copied); }; - io_service_.post(do_complete); - - io_service_.dispatch( - boost::bind(&SSLWrapperPuller::HandleDataNOps, - this->shared_from_this())); - } - } else { - boost::recursive_mutex::scoped_lock lock1(data_queue_mutex_); - boost::recursive_mutex::scoped_lock lock2(op_queue_mutex_); - if (!op_queue_.empty()) { - auto op = op_queue_.front(); - op_queue_.pop(); - auto self = this->shared_from_this(); - auto do_complete = - [op, this, self]() { op->complete(this->status_, 0); }; - io_service_.post(do_complete); - - io_service_.dispatch( - boost::bind(&SSLWrapperPuller::HandleDataNOps, - this->shared_from_this())); - } - } - } - - /// Receive some data - void AsyncPullPackets() { - BOOST_LOG_TRIVIAL(trace) << "link: SSLWrapperPuller AsyncPullPackets"; - auto self = this->shared_from_this(); - auto handler = [this, self](const boost::system::error_code &ec, - size_t length) { - BOOST_LOG_TRIVIAL(trace) << "link: SSLWrapperPuller AsyncPullPackets handler"; - if (!ec) { - { - boost::recursive_mutex::scoped_lock lock1(this->data_queue_mutex_); - this->data_queue_.commit(length); - } - - this->io_service_.dispatch( - boost::bind(&SSLWrapperPuller::AsyncPullPackets, - this->shared_from_this())); - } else { - this->status_ = ec; - this->data_queue_.commit(length); - BOOST_LOG_TRIVIAL(info) << "link: SSLWrapperPuller SSL connection terminated"; - } - - this->io_service_.dispatch( - boost::bind(&SSLWrapperPuller::HandleDataNOps, - this->shared_from_this())); - }; - - { - boost::recursive_mutex::scoped_lock lock(data_queue_mutex_); - boost::asio::streambuf::mutable_buffers_type bufs = - data_queue_.prepare(receive_buffer_size); - - if (data_queue_.size() < higher_queue_size_bound) { - auto lambda = [this, bufs, handler, self]() { - BOOST_LOG_TRIVIAL(trace) - << "link: SSLWrapperPuller Lambda async read some buf"; - this->socket_.async_read_some(bufs, this->strand_.wrap(handler)); - }; - strand_.dispatch(lambda); - } else { - pulling_ = false; - BOOST_LOG_TRIVIAL(debug) << "link: SSLWrapperPuller not pulling"; - } - } - } - - /// The ssl strea mto receive from - ssl_type &socket_; - - /// The strand to insure that read and writes are not concurrent - boost::asio::io_service::strand &strand_; - - /// The io_service handling asynchronous operations - boost::asio::io_service &io_service_; - - /// Errors during async_read are saved here - boost::system::error_code status_; - - /// Handle the data received - boost::recursive_mutex data_queue_mutex_; - boost::asio::streambuf data_queue_; - - /// Handle pending user operations - boost::recursive_mutex op_queue_mutex_; - op_queue_type op_queue_; - - boost::recursive_mutex pulling_mutex_; - bool pulling_; -}; -//---------------------------------------------------------------------------- - -//---------------------------------------------------------------------------- - -/// The class wrapping an ssl stream to allow buffer optimization, non -/// concurrent io and move operations -template class SSLWrapper { -private: - typedef boost::asio::ssl::stream ssl_type; - typedef std::shared_ptr p_ssl_type; - typedef std::shared_ptr p_context_type; - typedef std::shared_ptr p_streambuf; - typedef SSLWrapperPuller puller_type; - typedef std::shared_ptr> p_puller_type; - typedef boost::asio::io_service::strand strand_type; - typedef std::shared_ptr p_strand_type; - -public: - typedef typename ssl_type::lowest_layer_type lowest_layer_type; - typedef typename ssl_type::handshake_type handshake_type; - -public: - SSLWrapper(boost::asio::io_service &io_service, p_context_type p_ctx) - : p_ctx_(p_ctx), p_socket_(new ssl_type(io_service, *p_ctx)), - socket_(*p_socket_), - p_strand_(std::make_shared(io_service)), - p_puller_(puller_type::create(*p_socket_, *p_strand_)) {} - - SSLWrapper(SSLWrapper &&other) - : p_ctx_(std::move(other.p_ctx_)), p_socket_(std::move(other.p_socket_)), - socket_(*p_socket_), p_strand_(std::move(other.p_strand_)), - p_puller_(std::move(other.p_puller_)) { - other.socket_ = *(other.p_socket_); - } - - SSLWrapper(const SSLWrapper &) = delete; - SSLWrapper &operator=(const SSLWrapper &) = delete; - - boost::asio::io_service &get_io_service() { - return socket_.get().lowest_layer().get_io_service(); - } - - lowest_layer_type &lowest_layer() { return socket_.get().lowest_layer(); } - - /// Forward the call to the ssl stream and start pulling packets on - /// completion - template - void async_handshake(handshake_type type, Handler handler) { - auto do_user_handler = - [this, handler](const boost::system::error_code &ec) { - if (!ec) { - this->p_puller_->StartPulling(); - } - handler(ec); - }; - - auto lambda = [this, type, do_user_handler]() { - this->socket_.get().async_handshake(type, - p_strand_->wrap(do_user_handler)); - }; - p_strand_->dispatch(lambda); - } - - /// Forward the call to the SSLWrapperPuller object - template - void async_read_some(const MutableBufferSequence &buffers, - BOOST_ASIO_MOVE_ARG(Handler) handler) { - BOOST_LOG_TRIVIAL(trace) << "link: SSLWrapper async read some"; - p_puller_->async_read_some(buffers, handler); - } - - /// Forward the call directly to the ssl stream (wrapped in an strand) - template - void async_write_some(const ConstBufferSequence &buffers, - BOOST_ASIO_MOVE_ARG(Handler) handler) { - auto lambda = [this, buffers, handler]() { - BOOST_LOG_TRIVIAL(trace) << "link: SSLWrapper lambda async write some"; - this->socket_.get().async_write_some(buffers, - this->p_strand_->wrap(handler)); - }; - p_strand_->dispatch(lambda); - } - - /// Forward the call to the lowest layer of the ssl stream - bool is_open() { return socket_.get().lowest_layer().is_open(); } - - void close(boost::system::error_code &ec) { - socket_.get().lowest_layer().close(ec); - } - - void shutdown(boost::asio::socket_base::shutdown_type type, - boost::system::error_code &ec) { - socket_.get().lowest_layer().shutdown(type, ec); - } - - boost::asio::ssl::context &context() { return *p_ctx_; } - boost::asio::ssl::stream &socket() { return socket_; } - boost::asio::io_service::strand &strand() { return *p_strand_; } - -private: - /// The ssl ctx in a shared_ptr to be able to move it - p_context_type p_ctx_; - - /// The ssl stream in a shared_ptr to be able to move it - p_ssl_type p_socket_; - - /// A reference to the ssl stream to avoid dereferencing a pointer - std::reference_wrapper socket_; - - /// The strand in a shared_ptr to be able to move it - p_strand_type p_strand_; - - /// The SSLWrapperPuller in a shared_ptr to be able to move it - p_puller_type p_puller_; -}; -//---------------------------------------------------------------------------- - -//---------------------------------------------------------------------------- - -/// The class which handles SSL links -template -class BaseSSLPolicy { -public: - /// The socket object type used - typedef SSLWrapper socket_type; - - /// The pointer type returned to the higher layer - typedef std::shared_ptr p_socket_type; - - /// The acceptor object type - typedef boost::asio::ip::tcp::acceptor acceptor_type; - typedef std::shared_ptr p_acceptor_type; - - /// Type of the higher layer callback for establishing a new link - typedef std::function - connect_callback_type; - - /// Type of the higher layer callback for accepting new links - typedef std::function accept_callback_type; - - /// Type of the generalized parameters - typedef std::map Parameters; - -public: - virtual ~BaseSSLPolicy() {} - - BaseSSLPolicy(boost::asio::io_service &io_service, - const ssf::Config &ssf_config) - : io_service_(io_service), config_(ssf_config) {} - -private: - bool InitTLSContext(boost::asio::ssl::context &ctx, bool is_server) { - // Set the callback to decipher the private key - if (is_server || (config_.tls.key_password != "")) { - ctx.set_password_callback( - boost::bind(&BaseSSLPolicy::GetPassword, this, _1, _2)); - } - - // Set the mutual authentication - ctx.set_verify_mode(boost::asio::ssl::verify_peer | - boost::asio::ssl::verify_fail_if_no_peer_cert); - - // Set the callback to verify the cetificate chains of the peer - ctx.set_verify_callback(boost::bind( - &BaseSSLPolicy::verify_certificate, this, _1, _2)); - - // Set various security options - ctx.set_options(boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::no_sslv3 | - boost::asio::ssl::context::no_tlsv1 | - boost::asio::ssl::context::single_dh_use); - - SSL_CTX_set_options(ctx.native_handle(), - SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TICKET); - - // [not used] Set compression methods - SSL_COMP_add_compression_method(0, COMP_rle()); - SSL_COMP_add_compression_method(1, COMP_zlib()); - - // Load the file containing the trusted certificate authorities - auto loaded = SSL_CTX_load_verify_locations( - ctx.native_handle(), config_.tls.ca_cert_path.c_str(), NULL); - - if (!loaded) { - return false; - } - - boost::system::error_code ec; - - // The certificate used by the local peer - ctx.use_certificate_chain_file(config_.tls.cert_path, ec); - - if (ec) { - return false; - } - - // The private key used by the local peer - ctx.use_private_key_file(config_.tls.key_path.c_str(), - boost::asio::ssl::context::pem, ec); - - if (ec) { - return false; - } - - // The Diffie-Hellman parameter file - ctx.use_tmp_dh_file(config_.tls.dh_path, ec); - - return !ec; - } - -public: - /// Connect to a remote server - void EstablishLink(const Parameters ¶meters, - const connect_callback_type &connect_callback) { - auto addr = GetRemoteAddr(parameters); - auto port = GetRemotePort(parameters); - auto auth_cert = GetAuthCertInVector(parameters); - auto cert = GetCertInVector(parameters); - auto key = GetKeyInVector(parameters); - - BOOST_LOG_TRIVIAL(info) << "link: connecting to " << addr << ":" << port; - - auto p_ctx = std::make_shared( - boost::asio::ssl::context::tlsv12); - - // Initialize the ssl context with enhanced security parameters - auto ctx_set = this->InitTLSContext(*p_ctx, false); - - if (!ctx_set) { - BOOST_LOG_TRIVIAL(error) << "TLS context not initialized"; - BOOST_LOG_TRIVIAL(error) << "Check your configuration"; - boost::system::error_code ec(ssf::error::invalid_argument, - ssf::error::get_ssf_category()); - this->ToNextLayerHandler(p_socket_type(nullptr), connect_callback, ec); - return; - } - - // If an other certificate and key are provided, use them - if (cert.size() && key.size()) { - boost::system::error_code ec; - - // If a new CA is provided, use it - if (auth_cert.size()) { - X509 *x509_ca = NULL; - ScopeCleaner cleaner([&x509_ca]() { - X509_free(x509_ca); - x509_ca = NULL; - }); - - auto p_auth_cert = auth_cert.data(); - d2i_X509(&x509_ca, (const unsigned char **)&p_auth_cert, - (uint32_t)auth_cert.size()); - X509_STORE *store = X509_STORE_new(); - SSL_CTX_set_cert_store(p_ctx->native_handle(), store); - X509_STORE_add_cert(store, x509_ca); - } - - { - X509 *x509_cert = NULL; - ScopeCleaner cleaner([&x509_cert]() { - X509_free(x509_cert); - x509_cert = NULL; - }); - - auto p_cert = cert.data(); - d2i_X509(&x509_cert, (const unsigned char **)&p_cert, (uint32_t)cert.size()); - SSL_CTX_use_certificate(p_ctx->native_handle(), x509_cert); - } - - { - EVP_PKEY *RSA_key = NULL; - ScopeCleaner cleaner([&RSA_key]() { - EVP_PKEY_free(RSA_key); - RSA_key = NULL; - }); - - auto p_key = key.data(); - d2i_PrivateKey(EVP_PKEY_RSA, &RSA_key, (const unsigned char **)&p_key, - (uint32_t)key.size()); - SSL_CTX_use_PrivateKey(p_ctx->native_handle(), RSA_key); - } - } - - // If the address and port are valid - if (addr != "" && port != "") { - // Resolve the address - boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), - addr, port); - - boost::system::error_code resolve_ec; - auto iterator = resolver.resolve(query, resolve_ec); - - if (resolve_ec) { - BOOST_LOG_TRIVIAL(error) << "link: could not resolve " << addr << ":" - << port; - boost::system::error_code ec(ssf::error::invalid_argument, - ssf::error::get_ssf_category()); - this->ToNextLayerHandler(p_socket_type(nullptr), connect_callback, - ec); - return; - } - - auto p_socket = std::make_shared(io_service_, p_ctx); - - boost::asio::async_connect(p_socket->lowest_layer(), iterator, - boost::bind(&BaseSSLPolicy::ConnectedHandler, - this, p_socket, connect_callback, - _1)); - } else { - boost::system::error_code ec(ssf::error::invalid_argument, - ssf::error::get_ssf_category()); - this->ToNextLayerHandler(p_socket_type(nullptr), connect_callback, ec); - return; - } - } - - /// Accept a new connection - void AcceptLinks(p_acceptor_type p_acceptor, - const accept_callback_type &accept_callback) { - - if (!p_acceptor->is_open()) { - return; - } - - auto p_ctx = std::make_shared( - boost::asio::ssl::context::tlsv12); - - // Initialize the ssl context with enhanced security parameters - auto ctx_set = this->InitTLSContext(*p_ctx, true); - - if (!ctx_set) { - BOOST_LOG_TRIVIAL(error) << "TLS context not initialized"; - BOOST_LOG_TRIVIAL(error) << "Check your configuration"; - this->ToNextLayerHandler(nullptr, accept_callback); - return; - } - - // Force a strong cipher suite by default - SSL_CTX_set_cipher_list(p_ctx->native_handle(), - config_.tls.cipher_alg.c_str()); - - auto p_socket = std::make_shared(io_service_, p_ctx); - - BOOST_LOG_TRIVIAL(trace) << "link: accepting"; - p_acceptor->async_accept(p_socket->lowest_layer(), - boost::bind(&BaseSSLPolicy::AcceptedHandler, this, - p_acceptor, p_socket, accept_callback, - _1)); - } - - void CloseLink(socket_type &socket) { - boost::system::error_code ec; - socket.socket().shutdown(ec); - socket.lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - socket.lowest_layer().close(ec); - BOOST_LOG_TRIVIAL(info) << "link: SSL connection closed"; - } - -private: - void ConnectedHandler(p_socket_type p_socket, - const connect_callback_type &connect_callback, - const boost::system::error_code &ec) { - // Send version - if (!ec) { - std::shared_ptr p_version = std::make_shared(GetVersion()); - /*boost::asio::async_write(p_socket->socket().next_layer(), - boost::asio::buffer(p_version.get(), sizeof(*p_version)), - boost::bind(&BaseSSLPolicy::HandshakeHandler, this, - p_socket, p_version, connect_callback, _1, _2));*/ - HandshakeHandler(p_socket, p_version, connect_callback, ec, 0); - } else { - BOOST_LOG_TRIVIAL(error) << "link: connection failed " - << ec.message(); - this->CloseLink(*p_socket); - this->ToNextLayerHandler(nullptr, connect_callback, ec); - } - } - - void HandshakeHandler(p_socket_type p_socket, - std::shared_ptr p_version, - const connect_callback_type &connect_callback, - const boost::system::error_code &ec, size_t length) { - if (!ec) { - BOOST_LOG_TRIVIAL(trace) << "link: connected"; - p_socket->async_handshake( - boost::asio::ssl::stream_base::client, - boost::bind(&BaseSSLPolicy::HandshakedConnectHandler, this, - p_socket, connect_callback, _1)); - } else { - BOOST_LOG_TRIVIAL(error) << "link: exchange version failed " - << ec.message(); - this->CloseLink(*p_socket); - this->ToNextLayerHandler(nullptr, connect_callback, ec); - } - } - - void HandshakedConnectHandler(p_socket_type p_socket, - const connect_callback_type &connect_callback, - const boost::system::error_code &ec) { - if (!ec) { - BOOST_LOG_TRIVIAL(trace) << "link: authenticated"; - this->ToNextLayerHandler(p_socket, connect_callback, ec); - } else { - BOOST_LOG_TRIVIAL(error) << "link: no handshake " << ec.message(); - this->CloseLink(*p_socket); - this->ToNextLayerHandler(nullptr, connect_callback, ec); - } - } - - void ToNextLayerHandler(p_socket_type p_socket, - const connect_callback_type &connect_callback, - const boost::system::error_code &ec) { - io_service_.post(boost::bind(connect_callback, p_socket, ec)); - } - - /// Read ssl version - void AcceptedHandler(p_acceptor_type p_acceptor, p_socket_type p_socket, - const accept_callback_type &accept_callback, - const boost::system::error_code &ec) { - if (!ec) { - BOOST_LOG_TRIVIAL(trace) << "link: accepted"; - /*std::shared_ptr p_version = std::make_shared(); - boost::asio::async_read(p_socket->socket().next_layer(), - boost::asio::buffer(p_version.get(), sizeof(*p_version)), - boost::bind(&BaseSSLPolicy::AcceptedHandshakeHandler, this, - p_socket, p_version, accept_callback, _1, _2));*/ - std::shared_ptr p_version = - std::make_shared(GetVersion()); - AcceptedHandshakeHandler(p_socket, p_version, accept_callback, ec, 0); - this->AcceptLinks(p_acceptor, accept_callback); - } else { - this->CloseLink(*p_socket); - this->ToNextLayerHandler(nullptr, accept_callback); - } - } - - /// Operate the ssl handshake if version supported and no error - void AcceptedHandshakeHandler(p_socket_type p_socket, - std::shared_ptr p_version, - const accept_callback_type &accept_callback, - const boost::system::error_code &ec, - size_t bytes_transferred) { - auto version_supported = IsVersionSupported(*p_version); - if (!ec && version_supported) { - BOOST_LOG_TRIVIAL(trace) << "link: version supported"; - p_socket->async_handshake( - boost::asio::ssl::stream_base::server, - boost::bind(&BaseSSLPolicy::HandshakedAcceptHandler, this, p_socket, - accept_callback, _1)); - } else { - if (!version_supported) { - BOOST_LOG_TRIVIAL(error) << "link: version NOT supported " - << *p_version; - } - if (ec) { - BOOST_LOG_TRIVIAL(error) << "link: error on read version " - << "ec : " << ec.message(); - } - this->CloseLink(*p_socket); - this->ToNextLayerHandler(nullptr, accept_callback); - } - } - - void HandshakedAcceptHandler(p_socket_type p_socket, - const accept_callback_type &accept_callback, - const boost::system::error_code &ec) { - if (!ec) { - BOOST_LOG_TRIVIAL(trace) << "link: authenticated"; - if (p_socket) { - BOOST_LOG_TRIVIAL(trace) - << "link: cipher suite " - << SSL_get_cipher(p_socket->socket().native_handle()); - } - this->ToNextLayerHandler(p_socket, accept_callback); - } else { - BOOST_LOG_TRIVIAL(error) << "link: NOT Authenticated " << ec.message(); - this->CloseLink(*p_socket); - this->ToNextLayerHandler(nullptr, accept_callback); - } - } - - void ToNextLayerHandler(p_socket_type p_socket, - const accept_callback_type &accept_callback) { - io_service_.post(boost::bind(accept_callback, p_socket)); - } - - uint32_t GetVersion() { - uint32_t version = ssf::versions::Versions::major; - version = version << 8; - - version |= ssf::versions::Versions::minor; - version = version << 8; - - version |= ssf::versions::Versions::security; - version = version << 8; - - version |= uint8_t(boost::archive::BOOST_ARCHIVE_VERSION()); - - return version; - } - - bool IsVersionSupported(uint32_t input_version) { - boost::archive::library_version_type serialization(input_version & - 0x000000FF); - input_version = input_version >> 8; - - uint8_t security = (input_version & 0x000000FF); - input_version = input_version >> 8; - - uint8_t minor = (input_version & 0x000000FF); - input_version = input_version >> 8; - - uint8_t major = (input_version & 0x000000FF); - - return (major == ssf::versions::Versions::major) && - (minor == ssf::versions::Versions::minor) && - (security == ssf::versions::Versions::security) && - (serialization == boost::archive::BOOST_ARCHIVE_VERSION()); - } - - std::string GetPassword(std::size_t, - boost::asio::ssl::context::password_purpose) const { - return config_.tls.key_password; - } - - bool verify_certificate(bool preverified, - boost::asio::ssl::verify_context &ctx) { - - X509_STORE_CTX *cts = ctx.native_handle(); - X509 *cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); - - char subject_name[256]; - X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); - BOOST_LOG_TRIVIAL(info) << "link: verifying " << subject_name << "\n"; - - X509 *issuer = cts->current_issuer; - if (issuer) { - X509_NAME_oneline(X509_get_subject_name(issuer), subject_name, 256); - BOOST_LOG_TRIVIAL(info) << "link: issuer " << subject_name << "\n"; - } - - // More checking ? - return preverified; - } - - std::string GetRemoteAddr(const Parameters ¶meters) { - if (parameters.count("remote_addr")) { - return parameters.find("remote_addr")->second; - } else { - return ""; - } - } - - std::string GetRemotePort(const Parameters ¶meters) { - if (parameters.count("remote_port")) { - return parameters.find("remote_port")->second; - } else { - return ""; - } - } - - std::vector GetDeserializedVector(const std::string &serialized) { - std::istringstream istrs(serialized); - boost::archive::text_iarchive ar(istrs); - std::vector deserialized; - - try { - ar >> BOOST_SERIALIZATION_NVP(deserialized); - return deserialized; - } catch (const std::exception&) { - return std::vector(0); - } - } - - std::vector GetAuthCertInVector(const Parameters ¶meters) { - if (parameters.count("auth_cert_in_vector")) { - auto serialized = parameters.find("auth_cert_in_vector")->second; - auto deserialized = GetDeserializedVector(serialized); - return deserialized; - } else { - return std::vector(); - } - } - - std::vector GetCertInVector(const Parameters ¶meters) { - if (parameters.count("cert_in_vector")) { - auto serialized = parameters.find("cert_in_vector")->second; - auto deserialized = GetDeserializedVector(serialized); - return deserialized; - } else { - return std::vector(); - } - } - - std::vector GetKeyInVector(const Parameters ¶meters) { - if (parameters.count("key_in_vector")) { - auto serialized = parameters.find("key_in_vector")->second; - auto deserialized = GetDeserializedVector(serialized); - return deserialized; - } else { - return std::vector(); - } - } - -private: - boost::asio::io_service &io_service_; - ssf::Config config_; -}; - -typedef BaseSSLPolicy<> SSLPolicy; -//---------------------------------------------------------------------------- - -} // ssf - -#endif // SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_SSL_POLICY_H_ diff --git a/src/core/network_virtual_layer_policies/link_policies/tcp_policy.h b/src/core/network_virtual_layer_policies/link_policies/tcp_policy.h deleted file mode 100644 index a95e20a2..00000000 --- a/src/core/network_virtual_layer_policies/link_policies/tcp_policy.h +++ /dev/null @@ -1,252 +0,0 @@ -#ifndef SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_TCP_POLICY_H_ -#define SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_TCP_POLICY_H_ - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "versions.h" -#include "common/config/config.h" -#include "common/error/error.h" - -namespace ssf { - -/// A connect policy for a client to use plain TCP as transport -class TCPPolicy { - public: - /// Policy required transport socket type - typedef boost::asio::ip::tcp::socket socket_type; - - /// Policy required pointer type - typedef std::shared_ptr p_socket_type; - - typedef boost::asio::ip::tcp::acceptor acceptor_type; - typedef std::shared_ptr p_acceptor_type; - - private: - typedef std::function - connect_callback_type; - typedef std::function accept_callback_type; - typedef std::map Parameters; - - public: - virtual ~TCPPolicy() {} - - TCPPolicy(boost::asio::io_service& io_service, - const ssf::Config& ssf_config) - : io_service_(io_service) { } - - void EstablishLink(const Parameters& parameters, - connect_callback_type connect_callback) { - auto addr = GetRemoteAddr(parameters); - auto port = GetRemotePort(parameters); - - BOOST_LOG_TRIVIAL(info) << "link: connecting " << addr << " " << port; - if (addr != "" && port != "") { - boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), - addr, port); - boost::system::error_code resolve_ec; - auto iterator = resolver.resolve(query, resolve_ec); - - if (resolve_ec) { - BOOST_LOG_TRIVIAL(error) << "link: could not resolve " << addr << ":" - << port; - boost::system::error_code ec(ssf::error::invalid_argument, - ssf::error::get_ssf_category()); - ToNextLayerHandler(p_socket_type(nullptr), connect_callback, ec); - return; - } - - auto p_socket = std::make_shared(io_service_); - - boost::asio::async_connect( - *p_socket, iterator, - boost::bind(&TCPPolicy::ConnectedHandler, this, p_socket, - connect_callback, _1)); - } else { - boost::system::error_code ec(ssf::error::invalid_argument, - ssf::error::get_ssf_category()); - ToNextLayerHandler(p_socket_type(nullptr), connect_callback, ec); - } - } - - /// Accept a new connection - void AcceptLinks(p_acceptor_type p_acceptor, - accept_callback_type accept_callback) { - - if (!p_acceptor->is_open()) { - return; - } - - auto p_socket = std::make_shared(io_service_); - - BOOST_LOG_TRIVIAL(trace) << "link: accepting"; - p_acceptor->async_accept( - *p_socket, boost::bind(&TCPPolicy::AcceptedHandler, this, p_acceptor, - p_socket, accept_callback, _1)); - } - - void CloseLink(socket_type& socket) { - boost::system::error_code ec; - socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - socket.close(ec); - } - - private: - /// Write link version - void ConnectedHandler(p_socket_type p_socket, - connect_callback_type connect_callback, - const boost::system::error_code& ec) { - if (!ec) { - auto p_version = std::make_shared(GetVersion()); - /*boost::asio::async_write(*p_socket, - boost::asio::buffer(p_version.get(), sizeof(*p_version)), - boost::bind(&TCPPolicy::WriteVersionHandler, this, - p_socket, p_version, connect_callback, _1, _2));*/ - WriteVersionHandler(p_socket, p_version, connect_callback, ec, 0); - } else { - BOOST_LOG_TRIVIAL(error) << "link: connection failed " << ec.message(); - CloseLink(*p_socket); - this->ToNextLayerHandler(nullptr, connect_callback, ec); - } - } - - void WriteVersionHandler(p_socket_type p_socket, - std::shared_ptr p_version, - connect_callback_type connect_callback, - const boost::system::error_code& ec, - size_t bytes_transferred) { - if (!ec) { - BOOST_LOG_TRIVIAL(info) << "link: connected"; - this->ToNextLayerHandler(p_socket, connect_callback, ec); - } else { - BOOST_LOG_TRIVIAL(error) << "link: connection failed " << ec.message(); - CloseLink(*p_socket); - this->ToNextLayerHandler(nullptr, connect_callback, ec); - } - } - - void ToNextLayerHandler(p_socket_type p_socket, - connect_callback_type connect_callback, - const boost::system::error_code& ec) { - io_service_.post(boost::bind(connect_callback, p_socket, ec)); - } - - /// Read link version - void AcceptedHandler(p_acceptor_type p_acceptor, p_socket_type p_socket, - accept_callback_type accept_callback, - const boost::system::error_code& ec) { - if (!ec) { - /*std::shared_ptr p_version = std::make_shared(); - boost::asio::async_read(*p_socket, - boost::asio::buffer(p_version.get(), sizeof(*p_version)), - boost::bind(&TCPPolicy::ReadVersionHandler, this, - p_socket, p_version, accept_callback, _1, _2));*/ - std::shared_ptr p_version = - std::make_shared(GetVersion()); - ReadVersionHandler(p_socket, p_version, accept_callback, ec, 0); - this->AcceptLinks(p_acceptor, accept_callback); - } else { - BOOST_LOG_TRIVIAL(error) << "link: NOT Authenticated " << ec.message(); - this->CloseLink(*p_socket); - this->ToNextLayerHandler(nullptr, accept_callback); - } - } - - void ReadVersionHandler(p_socket_type p_socket, - std::shared_ptr p_version, - accept_callback_type accept_callback, - const boost::system::error_code& ec, - size_t bytes_transferred) { - auto version_supported = IsVersionSupported(*p_version); - if (!ec && version_supported) { - this->ToNextLayerHandler(p_socket, accept_callback); - BOOST_LOG_TRIVIAL(trace) << "link: authenticated"; - } else { - if (!version_supported) { - BOOST_LOG_TRIVIAL(error) << "link: version NOT supported " - << *p_version; - } - if (ec) { - BOOST_LOG_TRIVIAL(error) << "link: error on read version " - << "ec : " << ec.message(); - } - BOOST_LOG_TRIVIAL(error) << "link: NOT Authenticated "; - CloseLink(*p_socket); - this->ToNextLayerHandler(nullptr, accept_callback); - } - } - - void ToNextLayerHandler(p_socket_type p_socket, - accept_callback_type accept_callback) { - io_service_.post(boost::bind(accept_callback, p_socket)); - } - - std::string GetRemoteAddr(const Parameters& parameters) { - if (parameters.count("remote_addr")) { - return parameters.find("remote_addr")->second; - } else { - return ""; - } - } - - std::string GetRemotePort(const Parameters& parameters) { - if (parameters.count("remote_port")) { - return parameters.find("remote_port")->second; - } else { - return ""; - } - } - - uint32_t GetVersion() { - uint32_t version = ssf::versions::Versions::major; - version = version << 8; - - version |= ssf::versions::Versions::minor; - version = version << 8; - - version |= ssf::versions::Versions::security; - version = version << 8; - - version |= uint8_t(boost::archive::BOOST_ARCHIVE_VERSION()); - - return version; - } - - bool IsVersionSupported(uint32_t input_version) { - boost::archive::library_version_type serialization(input_version & - 0x000000FF); - input_version = input_version >> 8; - - uint8_t security = (input_version & 0x000000FF); - input_version = input_version >> 8; - - uint8_t minor = (input_version & 0x000000FF); - input_version = input_version >> 8; - - uint8_t major = (input_version & 0x000000FF); - - return (major == ssf::versions::Versions::major) && - (minor == ssf::versions::Versions::minor) && - (security == ssf::versions::Versions::security) && - (serialization == boost::archive::BOOST_ARCHIVE_VERSION()); - } - - private: - boost::asio::io_service& io_service_; -}; - -} // ssf - -#endif // SSF_CORE_NETWORK_VIRTUAL_LAYER_POLICIES_TCP_POLICY_H_ diff --git a/src/core/parser/bounce_parser.cpp b/src/core/parser/bounce_parser.cpp deleted file mode 100644 index 8b4b022a..00000000 --- a/src/core/parser/bounce_parser.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "core/parser/bounce_parser.h" - -#include - -namespace ssf { -namespace parser { - -BounceParser::BounceList BounceParser::ParseBounceFile( - const std::string& filepath) { - BounceList list; - - if (filepath != "") { - std::ifstream file(filepath); - - if (file.is_open()) { - std::string line; - - while (std::getline(file, line)) { - list.push_back(line); - } - - file.close(); - } - } - - return list; -} - -std::string BounceParser::GetRemoteAddress(const std::string& bounce_line) { - size_t position = bounce_line.find(":"); - - if (position != std::string::npos) { - return bounce_line.substr(0, position); - } else { - return ""; - } -} - -std::string BounceParser::GetRemotePort(const std::string& bounce_line) { - size_t position = bounce_line.find(":"); - - if (position != std::string::npos) { - return bounce_line.substr(position + 1); - } else { - return ""; - } -} - -} // parser -} // ssf \ No newline at end of file diff --git a/src/core/parser/bounce_parser.h b/src/core/parser/bounce_parser.h deleted file mode 100644 index ef283a1e..00000000 --- a/src/core/parser/bounce_parser.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef SSF_CORE_PARSER_BOUNCE_PARSER_H_ -#define SSF_CORE_PARSER_BOUNCE_PARSER_H_ - -#include -#include - -namespace ssf { -namespace parser { - -class BounceParser { - public: - using BounceList = std::list; - - public: - static BounceList ParseBounceFile(const std::string& filepath); - - static std::string GetRemoteAddress(const std::string& bounce_line); - - static std::string GetRemotePort(const std::string& bounce_line); -}; - -} // parser -} // ssf - -#endif // SSF_CORE_PARSER_BOUNCE_PARSER_H_ diff --git a/src/core/server/CMakeLists.txt b/src/core/server/CMakeLists.txt index 0fc2d339..f1000b84 100644 --- a/src/core/server/CMakeLists.txt +++ b/src/core/server/CMakeLists.txt @@ -1,29 +1,43 @@ cmake_minimum_required(VERSION 2.8) -set(project_NAME "ssfs") -project(${project_NAME}) +# --- Copy test certs for server +set(project_BINARY_SERVER_DIR "${project_BINARY_DIR}/src/core/server") +set(project_BINARY_SERVER_CERT_DIR ${project_BINARY_SERVER_DIR}/certs) -set(SERVER_FILES +file(MAKE_DIRECTORY ${project_BINARY_SERVER_CERT_DIR}) +file(MAKE_DIRECTORY ${project_BINARY_SERVER_CERT_DIR}/trusted) + +file(COPY ${SSF_CERT_TEST_ROOT_FILES} + DESTINATION ${project_BINARY_SERVER_CERT_DIR}) +file(COPY ${SSF_CERT_TEST_TRUSTED_FILES} + DESTINATION ${project_BINARY_SERVER_CERT_DIR}/trusted) + +set(SERVER_NAME "ssfs") +set(SERVER_MAIN "${project_SRC_DIR}/core/server/main.cpp") include_directories( ${OpenSSL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) -add_target(${project_NAME} +add_target(${SERVER_NAME} TYPE - executable ${EXEC_FLAG} + executable ${EXEC_FLAG} INSTALL FILES - ${SERVER_FILES} ${CORE_COMMAND_LINE_STANDARD_FILES} ${SSF_SOURCES} ${ICON_RC} + ${SERVER_MAIN} + LINKS + ${Boost_LIBRARIES} + ${OpenSSL_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network PREFIX_SKIP ${project_SRC_DIR}) -if (APPLE) - set_source_files_properties(${ICON_RC} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") - set_target_properties(${project_NAME} PROPERTIES MACOSX_BUNDLE_ICON_FILE ${ICON_NAME}) -endif (APPLE) - -target_link_libraries(${project_NAME} ${Boost_LIBRARIES} ${OpenSSL_LIBRARIES} ${PLATFORM_SPECIFIC_LIB_DEP}) +install( + TARGETS ssfs + RUNTIME + DESTINATION ssf/${project_EXEC_DIR_NAME} +) diff --git a/src/core/server/main.cpp b/src/core/server/main.cpp index 102d7d0c..4fd489b8 100644 --- a/src/core/server/main.cpp +++ b/src/core/server/main.cpp @@ -1,92 +1,100 @@ -#include +#include +#include #include -#include -#include -#include +#include #include +#include + #include "common/config/config.h" +#include "common/log/log.h" #include "core/command_line/standard/command_line.h" - -#include "core/network_virtual_layer_policies/link_policies/ssl_policy.h" -#include "core/network_virtual_layer_policies/link_policies/tcp_policy.h" -#include "core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h" -#include "core/network_virtual_layer_policies/bounce_protocol_policy.h" +#include "core/network_protocol.h" #include "core/server/server.h" #include "core/transport_virtual_layer_policies/transport_protocol_policy.h" -void Init() { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); -} +using NetworkProtocol = ssf::network::NetworkProtocol; -int main(int argc, char **argv) { -#ifdef TLS_OVER_TCP_LINK - using Server = - ssf::SSFServer; -#elif TCP_ONLY_LINK - using Server = - ssf::SSFServer; -#endif - - Init(); +using Server = + ssf::SSFServer; +int main(int argc, char** argv) { // The command line parser ssf::command_line::standard::CommandLine cmd(true); // Parse the command line boost::system::error_code ec; - cmd.parse(argc, argv, ec); + cmd.Parse(argc, argv, ec); + + if (ec.value() == ::error::operation_canceled) { + return 0; + } if (ec) { - BOOST_LOG_TRIVIAL(error) << "server: wrong arguments" << std::endl; + SSF_LOG(kLogError) << "server: wrong arguments -- Exiting"; return 1; } + + ssf::log::Configure(cmd.log_level()); // Load SSF config if any - boost::system::error_code ec_config; - ssf::Config ssf_config = ssf::LoadConfig(cmd.config_file(), ec_config); + ssf::config::Config ssf_config; + ssf_config.Update(cmd.config_file(), ec); - if (ec_config) { - BOOST_LOG_TRIVIAL(error) << "server: invalid config file format" - << std::endl; + if (ec) { + SSF_LOG(kLogError) << "server: invalid config file format -- Exiting"; return 1; } - // Start the asynchronous engine - boost::asio::io_service io_service; - boost::asio::io_service::work worker(io_service); - boost::thread_group threads; - - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - auto lambda = [&]() { - boost::system::error_code ec; - try { - io_service.run(ec); - } catch (std::exception e) { - BOOST_LOG_TRIVIAL(error) << "server: exception in io_service run " - << e.what(); - } - }; - - threads.create_thread(lambda); - } - - BOOST_LOG_TRIVIAL(info) << "Start SSF server on port " << cmd.port(); + ssf_config.Log(); // Initiate and start the server - Server server(io_service, ssf_config, cmd.port()); + Server server(ssf_config.services()); - server.run(); + // construct endpoint parameter stack + auto endpoint_query = NetworkProtocol::GenerateServerQuery( + cmd.host(), std::to_string(cmd.port()), ssf_config); + + SSF_LOG(kLogInfo) << "server: listening on <" + << (!cmd.host().empty() ? cmd.host() : "*") << ":" + << cmd.port() << ">"; + + server.Run(endpoint_query, ec); + + if (ec) { + SSF_LOG(kLogError) << "server: error happened when running server: " + << ec.message(); + return 1; + } - getchar(); - server.stop(); - io_service.stop(); - threads.join_all(); + std::condition_variable wait_stop_cv; + std::mutex mutex; + bool stopped = false; + boost::asio::signal_set signal(server.get_io_service(), SIGINT, SIGTERM); + + signal.async_wait([&wait_stop_cv, &mutex, &stopped]( + const boost::system::error_code& ec, int signum) { + if (ec) { + return; + } + { + boost::lock_guard lock(mutex); + stopped = true; + } + wait_stop_cv.notify_all(); + }); + + SSF_LOG(kLogInfo) << "server: running (Ctrl + C to stop)"; + + std::unique_lock lock(mutex); + wait_stop_cv.wait(lock, [&stopped] { return stopped; }); + lock.unlock(); + + SSF_LOG(kLogInfo) << "server: stop"; + signal.cancel(ec); + server.Stop(); return 0; } \ No newline at end of file diff --git a/src/core/server/server.h b/src/core/server/server.h index d7fffe46..2a3ac09d 100644 --- a/src/core/server/server.h +++ b/src/core/server/server.h @@ -5,78 +5,66 @@ #include #include -#include #include -#include "common/boost/fiber/stream_fiber.hpp" #include "common/boost/fiber/basic_fiber_demux.hpp" -#include "common/config/config.h" - -#include "services/admin/admin.h" -#include "services/socks/socks_server.h" -#include "services/fibers_to_sockets/fibers_to_sockets.h" -#include "services/sockets_to_fibers/sockets_to_fibers.h" -#include "services/fibers_to_datagrams/fibers_to_datagrams.h" -#include "services/datagrams_to_fibers/datagrams_to_fibers.h" -#include "services/copy_file/file_to_fiber/file_to_fiber.h" -#include "services/copy_file/fiber_to_file/fiber_to_file.h" - -#include "core/service_manager/service_manager.h" -#include "services/base_service.h" - +#include "core/async_engine.h" #include "core/factories/service_factory.h" +#include "core/service_manager/service_manager.h" namespace ssf { -template class LinkAuthenticationPolicy, - template class> - class NetworkVirtualLayerPolicy, +template class TransportVirtualLayerPolicy> -class SSFServer : public NetworkVirtualLayerPolicy, - public TransportVirtualLayerPolicy< - typename PhysicalVirtualLayer::socket_type> { +class SSFServer + : public TransportVirtualLayerPolicy { private: - typedef typename PhysicalVirtualLayer::socket_type socket_type; - typedef typename PhysicalVirtualLayer::p_socket_type p_socket_type; - - typedef std::vector vector_error_code_type; + using NetworkSocket = typename NetworkProtocol::socket; + using NetworkSocketPtr = std::shared_ptr; + using NetworkEndpoint = typename NetworkProtocol::endpoint; + using NetworkEndpointPtr = std::shared_ptr; + using NetworkResolver = typename NetworkProtocol::resolver; + using NetworkQuery = typename NetworkProtocol::resolver::query; + using NetworkAcceptor = typename NetworkProtocol::acceptor; public: - typedef boost::asio::fiber::basic_fiber_demux demux; + using demux = boost::asio::fiber::basic_fiber_demux; private: - typedef std::shared_ptr p_demux; + using DemuxPtr = std::shared_ptr; + using DemuxPtrSet = std::set; + using ServiceManagerPtr = std::shared_ptr>; + using ServiceManagerPtrMap = std::map; - typedef std::shared_ptr> p_ServiceManager; + public: + SSFServer(const ssf::config::Services& services_config); - typedef std::set demux_set; - typedef std::map socket_map; - typedef std::map service_manager_map; + ~SSFServer(); - public: - SSFServer(boost::asio::io_service& io_service, const ssf::Config& ssf_config, - uint16_t local_port); + void Run(const NetworkQuery& query, boost::system::error_code& ec); - void run(); - void stop(); + void Stop(); + + boost::asio::io_service& get_io_service(); private: - void AddDemux(p_demux p_fiber_demux, p_ServiceManager p_service_manager); - void RemoveDemux(p_demux p_fiber_demux); + void AsyncAcceptConnection(); + void NetworkToTransport(const boost::system::error_code& ec, + NetworkSocketPtr p_socket); + void AddDemux(DemuxPtr p_fiber_demux, ServiceManagerPtr p_service_manager); + void DoSSFStart(NetworkSocketPtr p_socket, + const boost::system::error_code& ec); + void DoFiberize(NetworkSocketPtr p_socket, boost::system::error_code& ec); + void RemoveDemux(DemuxPtr p_fiber_demux); void RemoveAllDemuxes(); - void DoSSFStart(p_socket_type p_socket, const boost::system::error_code& ec); - void DoFiberize(p_socket_type p_socket, boost::system::error_code& ec); - void NetworkToTransport(p_socket_type p_socket, vector_error_code_type v_ec); private: - boost::asio::io_service& io_service_; - - uint16_t local_port_; + AsyncEngine async_engine_; + NetworkAcceptor network_acceptor_; + ssf::config::Services services_config_; - demux_set p_fiber_demuxes_; - service_manager_map p_service_managers_; + DemuxPtrSet p_fiber_demuxes_; + ServiceManagerPtrMap p_service_managers_; boost::recursive_mutex storage_mutex_; }; diff --git a/src/core/server/server.ipp b/src/core/server/server.ipp index 55ab15d5..4a1ed9fd 100644 --- a/src/core/server/server.ipp +++ b/src/core/server/server.ipp @@ -1,98 +1,166 @@ #ifndef SSF_CORE_SERVER_SERVER_IPP_ #define SSF_CORE_SERVER_SERVER_IPP_ +#include + +#include "common/error/error.h" + +#include "core/factories/service_factory.h" + +#include "services/admin/admin.h" #include "services/admin/requests/create_service_request.h" #include "services/admin/requests/stop_service_request.h" #include "services/admin/requests/service_status.h" -#include "core/factories/service_factory.h" +#include "services/base_service.h" +#include "services/copy_file/file_to_fiber/file_to_fiber.h" +#include "services/copy_file/fiber_to_file/fiber_to_file.h" +#include "services/datagrams_to_fibers/datagrams_to_fibers.h" +#include "services/fibers_to_sockets/fibers_to_sockets.h" +#include "services/fibers_to_datagrams/fibers_to_datagrams.h" +#include "services/process/server.h" +#include "services/sockets_to_fibers/sockets_to_fibers.h" +#include "services/socks/socks_server.h" namespace ssf { -template class L, - template class> class N, - template class T> -SSFServer::SSFServer(boost::asio::io_service& io_service, - const ssf::Config& ssf_config, - uint16_t local_port) - : N(io_service, ssf_config), - T( - boost::bind(&SSFServer::DoSSFStart, this, _1, _2)), - io_service_(io_service), - local_port_(local_port) {} + +template class T> +SSFServer::SSFServer(const ssf::config::Services& services_config) + : T( + boost::bind(&SSFServer::DoSSFStart, this, _1, _2)), + async_engine_(), + network_acceptor_(async_engine_.get_io_service()), + services_config_(services_config) {} + +template class T> +SSFServer::~SSFServer() { + Stop(); +} /// Start accepting connections -template class L, - template class> class N, - template class T> -void SSFServer::run() { - this->AcceptNewRoutes( - local_port_, - boost::bind(&SSFServer::NetworkToTransport, this, _1, _2)); +template class T> +void SSFServer::Run(const NetworkQuery& query, + boost::system::error_code& ec) { + if (async_engine_.IsStarted()) { + ec.assign(::error::device_or_resource_busy, ::error::get_ssf_category()); + SSF_LOG(kLogError) << "server: already running"; + return; + } + + // resolve remote endpoint with query + NetworkResolver resolver(async_engine_.get_io_service()); + auto endpoint_it = resolver.resolve(query, ec); + + if (ec) { + SSF_LOG(kLogError) << "server: could not resolve network endpoint"; + return; + } + + // set acceptor + network_acceptor_.open(); + network_acceptor_.set_option(boost::asio::socket_base::reuse_address(true), + ec); + network_acceptor_.bind(*endpoint_it, ec); + if (ec) { + SSF_LOG(kLogError) << "server: could not bind acceptor to network endpoint"; + return; + } + + network_acceptor_.listen(100, ec); + if (ec) { + SSF_LOG(kLogError) << "server: could not listen for new connections"; + return; + } + + async_engine_.Start(); + + // start accepting connection + AsyncAcceptConnection(); } /// Stop accepting connections and end all on going connections -template class L, - template class> class N, - template class T> -void SSFServer::stop() { - this->StopAcceptingRoutes(); - this->RemoveAllDemuxes(); +template class T> +void SSFServer::Stop() { + RemoveAllDemuxes(); + + // close acceptor + boost::system::error_code close_ec; + network_acceptor_.close(close_ec); + + async_engine_.Stop(); } -//------------------------------------------------------------------------------- -template class L, - template class> class N, - template class T> -void SSFServer::NetworkToTransport(p_socket_type p_socket, - vector_error_code_type v_ec) { - if (!v_ec[0]) { +template class T> +boost::asio::io_service& SSFServer::get_io_service() { + return async_engine_.get_io_service(); +} + +template class T> +void SSFServer::AsyncAcceptConnection() { + if (network_acceptor_.is_open()) { + NetworkSocketPtr p_socket = + std::make_shared(async_engine_.get_io_service()); + + network_acceptor_.async_accept( + *p_socket, + boost::bind(&SSFServer::NetworkToTransport, this, _1, p_socket)); + } +} + +template class T> +void SSFServer::NetworkToTransport(const boost::system::error_code& ec, + NetworkSocketPtr p_socket) { + if (!ec) { this->DoSSFInitiateReceive(p_socket); + AsyncAcceptConnection(); } else { - BOOST_LOG_TRIVIAL(error) << "server: network error: " << v_ec[0].message(); + boost::system::error_code close_ec; + p_socket->shutdown(boost::asio::socket_base::shutdown_both, close_ec); + p_socket->close(close_ec); + SSF_LOG(kLogError) << "server: network error: " << ec.message(); } } -//------------------------------------------------------------------------------ - -//------------------------------------------------------------------------------ -template class L, - template class> class N, - template class T> -void SSFServer::DoSSFStart(p_socket_type p_socket, - const boost::system::error_code& ec) { + +template class T> +void SSFServer::DoSSFStart(NetworkSocketPtr p_socket, + const boost::system::error_code& ec) { if (!ec) { - BOOST_LOG_TRIVIAL(trace) << "server: SSF reply ok"; + SSF_LOG(kLogTrace) << "server: SSF reply ok"; boost::system::error_code ec2; - this->DoFiberize(p_socket, ec2); + DoFiberize(p_socket, ec2); } else { - BOOST_LOG_TRIVIAL(error) << "server: SSF protocol error " << ec.message(); + boost::system::error_code close_ec; + p_socket->shutdown(boost::asio::socket_base::shutdown_both, close_ec); + p_socket->close(close_ec); + SSF_LOG(kLogError) << "server: SSF protocol error " << ec.message(); } } -template class L, - template class> class N, - template class T> -void SSFServer::DoFiberize(p_socket_type p_socket, - boost::system::error_code& ec) { +template class T> +void SSFServer::DoFiberize(NetworkSocketPtr p_socket, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(storage_mutex_); + // Register supported admin commands services::admin::CreateServiceRequest::RegisterToCommandFactory(); services::admin::StopServiceRequest::RegisterToCommandFactory(); services::admin::ServiceStatus::RegisterToCommandFactory(); // Make a new fiber demux and fiberize - auto p_fiber_demux = std::make_shared(io_service_); + auto p_fiber_demux = std::make_shared(async_engine_.get_io_service()); auto close_demux_handler = - [this, p_fiber_demux]() { this->RemoveDemux(p_fiber_demux); }; + [this, p_fiber_demux]() { RemoveDemux(p_fiber_demux); }; p_fiber_demux->fiberize(std::move(*p_socket), close_demux_handler); // Make a new service manager auto p_service_manager = std::make_shared>(); // Save the demux, the socket and the service manager - this->AddDemux(p_fiber_demux, p_service_manager); + AddDemux(p_fiber_demux, p_service_manager); // Make a new service factory auto p_service_factory = ServiceFactory::Create( - io_service_, *p_fiber_demux, p_service_manager); + async_engine_.get_io_service(), *p_fiber_demux, p_service_manager); // Register supported micro services services::socks::SocksServer::RegisterToServiceFactory( @@ -109,35 +177,32 @@ void SSFServer::DoFiberize(p_socket_type p_socket, demux>::RegisterToServiceFactory(p_service_factory); services::copy_file::fiber_to_file::FiberToFile< demux>::RegisterToServiceFactory(p_service_factory); + services::process::Server::RegisterToServiceFactory( + p_service_factory, services_config_.process()); // Start the admin micro service std::map empty_map; auto p_admin_service = services::admin::Admin::Create( - io_service_, *p_fiber_demux, empty_map); + async_engine_.get_io_service(), *p_fiber_demux, empty_map); p_admin_service->set_server(); p_service_manager->start(p_admin_service, ec); } -//------------------------------------------------------------------------------ -template class L, - template class> class N, - template class T> -void SSFServer::AddDemux(p_demux p_fiber_demux, - p_ServiceManager p_service_manager) { +template class T> +void SSFServer::AddDemux(DemuxPtr p_fiber_demux, + ServiceManagerPtr p_service_manager) { boost::recursive_mutex::scoped_lock lock(storage_mutex_); - BOOST_LOG_TRIVIAL(trace) << "server: adding a new demux"; + SSF_LOG(kLogTrace) << "server: adding a new demux"; p_fiber_demuxes_.insert(p_fiber_demux); p_service_managers_[p_fiber_demux] = p_service_manager; } -template class L, - template class> class N, - template class T> -void SSFServer::RemoveDemux(p_demux p_fiber_demux) { +template class T> +void SSFServer::RemoveDemux(DemuxPtr p_fiber_demux) { boost::recursive_mutex::scoped_lock lock(storage_mutex_); - BOOST_LOG_TRIVIAL(trace) << "server: removing a demux"; + SSF_LOG(kLogTrace) << "server: removing a demux"; p_fiber_demux->close(); p_fiber_demuxes_.erase(p_fiber_demux); @@ -155,20 +220,17 @@ void SSFServer::RemoveDemux(p_demux p_fiber_demux) { } } -template class L, - template class> class N, - template class T> -void SSFServer::RemoveAllDemuxes() { +template class T> +void SSFServer::RemoveAllDemuxes() { boost::recursive_mutex::scoped_lock lock(storage_mutex_); - BOOST_LOG_TRIVIAL(trace) << "server: removing all demuxes"; + SSF_LOG(kLogTrace) << "server: removing all demuxes"; for (auto& p_fiber_demux : p_fiber_demuxes_) { + p_fiber_demux->close(); + if (p_service_managers_.count(p_fiber_demux)) { auto p_service_manager = p_service_managers_[p_fiber_demux]; - if (p_service_manager) { - p_service_manager->stop_all(); - } - p_fiber_demux->close(); + p_service_manager->stop_all(); p_service_managers_.erase(p_fiber_demux); } diff --git a/src/core/service_manager/service_manager.h b/src/core/service_manager/service_manager.h index 099f4c70..9996a618 100644 --- a/src/core/service_manager/service_manager.h +++ b/src/core/service_manager/service_manager.h @@ -7,26 +7,28 @@ #include #include +#include + #include #include -#include "common/network/manager.h" #include "services/base_service.h" namespace ssf { + template class ServiceManager : public ItemManager::BaseServicePtr> { -private: - typedef std::map Parameters; - typedef std::pair parameters_id_pair; - typedef std::list service_instance_id_list; - typedef std::map - service_type_id_to_instances_list_map; - typedef std::map id_to_status_map; - typedef std::map id_to_service_id_map; - -public: + private: + using Parameters = std::map; + using ParametersIdPair = std::pair; + using ServiceInstanceIdList = std::list; + using ServiceTypeIdToInstancesListMap = + std::map; + using IdToStatusMap = std::map; + using IdToServiceIdMap = std::map; + + public: uint32_t get_id(uint32_t service_type_id, Parameters parameters) { boost::recursive_mutex::scoped_lock lock(status_n_instance_list_mutex_); @@ -42,19 +44,19 @@ class ServiceManager } uint32_t find_error(uint32_t service_type_id, Parameters parameters) { - boost::recursive_mutex::scoped_lock lock(status_n_instance_list_mutex_); + boost::recursive_mutex::scoped_lock lock(status_n_instance_list_mutex_); - auto& error_list = error_lists_[service_type_id]; + auto& error_list = error_lists_[service_type_id]; - for (const auto& error : error_list) { - if (error.first == parameters) { - return error.second; - } + for (const auto& error : error_list) { + if (error.first == parameters) { + return error.second; } - - return 0; } + return 0; + } + uint32_t get_status(uint32_t id) { if (status_.count(id)) { return status_[id]; @@ -77,10 +79,8 @@ class ServiceManager } } - bool update_remote(uint32_t id, - uint32_t service_type_id, - uint32_t error_code_value, - Parameters parameters, + bool update_remote(uint32_t id, uint32_t service_type_id, + uint32_t error_code_value, Parameters parameters, boost::system::error_code& ec) { boost::recursive_mutex::scoped_lock lock(status_n_instance_list_mutex_); @@ -92,15 +92,14 @@ class ServiceManager } } - bool update_remote(uint32_t id, - uint32_t error_code_value, + bool update_remote(uint32_t id, uint32_t error_code_value, boost::system::error_code& ec) { boost::recursive_mutex::scoped_lock lock(status_n_instance_list_mutex_); if (!status_.count(id)) { return false; - } else { // check for value 4... stopping - if (error_code_value == 4) {// Service stopped + } else { // check for value 4... stopping + if (error_code_value == 4) { // Service stopped status_.erase(id); remove_id_from_instances(service_ids_[id], id); service_ids_.erase(id); @@ -111,22 +110,19 @@ class ServiceManager } } -private: - void add_remote(uint32_t id, - uint32_t service_type_id, - uint32_t error_code_value, - Parameters parameters) { + private: + void add_remote(uint32_t id, uint32_t service_type_id, + uint32_t error_code_value, Parameters parameters) { boost::recursive_mutex::scoped_lock lock(status_n_instance_list_mutex_); if (id) { auto& instance_list = intance_lists_[service_type_id]; - instance_list.push_front(parameters_id_pair(parameters, id)); + instance_list.push_front(ParametersIdPair(parameters, id)); service_ids_[id] = service_type_id; status_[id] = error_code_value; } else { auto& instance_list = error_lists_[service_type_id]; - instance_list.push_front( - parameters_id_pair(parameters, error_code_value)); + instance_list.push_front(ParametersIdPair(parameters, error_code_value)); if (instance_list.size() > 100) { instance_list.pop_back(); } @@ -146,13 +142,13 @@ class ServiceManager } } -private: + private: boost::recursive_mutex status_n_instance_list_mutex_; - id_to_status_map status_; - id_to_service_id_map service_ids_; - service_type_id_to_instances_list_map intance_lists_; - service_type_id_to_instances_list_map error_lists_; + IdToStatusMap status_; + IdToServiceIdMap service_ids_; + ServiceTypeIdToInstancesListMap intance_lists_; + ServiceTypeIdToInstancesListMap error_lists_; }; } // ssf diff --git a/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.h b/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.h index 0c70479f..9b8cbe7d 100644 --- a/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.h +++ b/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.h @@ -11,26 +11,23 @@ namespace ssf { class SSFReply { public: - enum { - field_number = 1, - total_size = sizeof(bool) - }; + enum { field_number = 1, total_size = sizeof(bool) }; public: - SSFReply(); - SSFReply(bool result); + SSFReply(); + SSFReply(bool result); - bool result() const; + bool result() const; - std::array const_buffer() const; + std::array const_buffer() const; - std::array buffer(); + std::array buffer(); private: bool result_; }; -typedef std::shared_ptr SSFReplyPtr; +using SSFReplyPtr = std::shared_ptr; } // ssf diff --git a/src/core/transport_virtual_layer_policies/init_packets/ssf_request.cpp b/src/core/transport_virtual_layer_policies/init_packets/ssf_request.cpp index 50af6faa..650c27ec 100644 --- a/src/core/transport_virtual_layer_policies/init_packets/ssf_request.cpp +++ b/src/core/transport_virtual_layer_policies/init_packets/ssf_request.cpp @@ -1,32 +1,25 @@ #include "ssf_request.h" - namespace ssf { SSFRequest::SSFRequest() : version_(0) {} -SSFRequest::SSFRequest(version_field_type version) - : version_(version) {} +SSFRequest::SSFRequest(VersionField version) : version_(version) {} -SSFRequest::version_field_type SSFRequest::version() const { return version_; } +SSFRequest::VersionField SSFRequest::version() const { return version_; } std::array SSFRequest::const_buffer() const { std::array buf = { - { - boost::asio::const_buffer(&version_, sizeof(version_)) - } - }; + {boost::asio::const_buffer(&version_, sizeof(version_))}}; return buf; } -std::array SSFRequest::buffer() { +std::array +SSFRequest::buffer() { std::array buf = { - { - boost::asio::mutable_buffer(&version_, sizeof(version_)) - } - }; + {boost::asio::mutable_buffer(&version_, sizeof(version_))}}; return buf; } diff --git a/src/core/transport_virtual_layer_policies/init_packets/ssf_request.h b/src/core/transport_virtual_layer_policies/init_packets/ssf_request.h index b7cf6df2..4f1edddf 100644 --- a/src/core/transport_virtual_layer_policies/init_packets/ssf_request.h +++ b/src/core/transport_virtual_layer_policies/init_packets/ssf_request.h @@ -7,31 +7,28 @@ #include -namespace ssf { +namespace ssf { class SSFRequest { public: - typedef uint32_t version_field_type; + using VersionField = uint32_t; - enum { - field_number = 1, - total_size = sizeof(version_field_type) - }; + enum { field_number = 1, total_size = sizeof(VersionField) }; -public: + public: SSFRequest(); - SSFRequest(version_field_type version); + SSFRequest(VersionField version); - version_field_type version() const; + VersionField version() const; std::array const_buffer() const; std::array buffer(); -private: - version_field_type version_; + private: + VersionField version_; }; -typedef std::shared_ptr SSFRequestPtr; +using SSFRequestPtr = std::shared_ptr; } // ssf diff --git a/src/core/transport_virtual_layer_policies/transport_protocol_policy.h b/src/core/transport_virtual_layer_policies/transport_protocol_policy.h index 3242284c..51256653 100644 --- a/src/core/transport_virtual_layer_policies/transport_protocol_policy.h +++ b/src/core/transport_virtual_layer_policies/transport_protocol_policy.h @@ -6,8 +6,14 @@ #include #include +#include + +#include #include -#include + +#include + +#include "common/error/error.h" #include "core/transport_virtual_layer_policies/init_packets/ssf_reply.h" #include "core/transport_virtual_layer_policies/init_packets/ssf_request.h" @@ -19,15 +25,15 @@ namespace ssf { template class TransportProtocolPolicy { private: - typedef std::shared_ptr p_socket_type; - typedef std::function - callback_type; + using SocketPtr = std::shared_ptr; + using Callback = + std::function; public: - TransportProtocolPolicy(callback_type callback) : callback_(callback) {} + TransportProtocolPolicy(Callback callback) : callback_(callback) {} - void DoSSFInitiate(p_socket_type p_socket) { - BOOST_LOG_TRIVIAL(info) << "transport: starting SSF protocol"; + void DoSSFInitiate(SocketPtr p_socket) { + SSF_LOG(kLogInfo) << "transport: starting SSF protocol"; uint32_t version = GetVersion(); auto p_ssf_request = std::make_shared(version); @@ -38,7 +44,7 @@ class TransportProtocolPolicy { p_ssf_request, p_socket, _1, _2)); } - void DoSSFInitiateReceive(p_socket_type p_socket) { + void DoSSFInitiateReceive(SocketPtr p_socket) { auto p_ssf_request = std::make_shared(); boost::asio::async_read( *p_socket, p_ssf_request->buffer(), @@ -46,36 +52,36 @@ class TransportProtocolPolicy { p_ssf_request, p_socket, _1, _2)); } - void DoSSFValid(SSFRequestPtr p_ssf_request, p_socket_type p_socket, - const boost::system::error_code& ec, size_t length) { + void DoSSFValid(SSFRequestPtr p_ssf_request, SocketPtr p_socket, + const boost::system::error_code& ec, size_t length) { if (!ec) { uint32_t version = p_ssf_request->version(); - BOOST_LOG_TRIVIAL(trace) << "transport: SSF version read: " << version; + SSF_LOG(kLogTrace) << "transport: SSF version read: " << version; if (IsSupportedVersion(version)) { auto p_ssf_reply = std::make_shared(true); boost::asio::async_write( *p_socket, p_ssf_reply->const_buffer(), - boost::bind( - &TransportProtocolPolicy::DoSSFProtocolFinished, this, - p_ssf_reply, p_socket, _1, _2)); + boost::bind(&TransportProtocolPolicy::DoSSFProtocolFinished, + this, p_ssf_reply, p_socket, _1, _2)); } else { - BOOST_LOG_TRIVIAL(error) << "transport: SSF version NOT supported " << version; - boost::system::error_code result_ec(ssf::error::wrong_protocol_type, - ssf::error::get_ssf_category()); + SSF_LOG(kLogError) << "transport: SSF version NOT supported " + << version; + boost::system::error_code result_ec(::error::wrong_protocol_type, + ::error::get_ssf_category()); callback_(p_socket, result_ec); } } else { - BOOST_LOG_TRIVIAL(error) << "transport: SSF version NOT read " << ec.message(); + SSF_LOG(kLogError) << "transport: SSF version NOT read " << ec.message(); callback_(p_socket, ec); } } - void DoSSFValidReceive(SSFRequestPtr p_ssf_request, p_socket_type p_socket, - const boost::system::error_code& ec, size_t length) { + void DoSSFValidReceive(SSFRequestPtr p_ssf_request, SocketPtr p_socket, + const boost::system::error_code& ec, size_t length) { if (!ec) { - BOOST_LOG_TRIVIAL(info) << "transport: SSF request sent"; + SSF_LOG(kLogInfo) << "transport: SSF request sent"; auto p_ssf_reply = std::make_shared(); @@ -84,27 +90,28 @@ class TransportProtocolPolicy { boost::bind(&TransportProtocolPolicy::DoSSFProtocolFinished, this, p_ssf_reply, p_socket, _1, _2)); } else { - BOOST_LOG_TRIVIAL(error) << "transport: could NOT send the SSF request " - << ec.message(); + SSF_LOG(kLogError) << "transport: could NOT send the SSF request " + << ec.message(); callback_(p_socket, ec); } } - void DoSSFProtocolFinished(SSFReplyPtr p_ssf_reply, p_socket_type p_socket, - const boost::system::error_code& ec, - size_t length) { + void DoSSFProtocolFinished(SSFReplyPtr p_ssf_reply, SocketPtr p_socket, + const boost::system::error_code& ec, + size_t length) { if (!ec) { if (p_ssf_reply->result()) { - BOOST_LOG_TRIVIAL(info) << "transport: SSF reply OK"; + SSF_LOG(kLogInfo) << "transport: SSF reply OK"; callback_(p_socket, ec); } else { - boost::system::error_code result_ec(ssf::error::wrong_protocol_type, - ssf::error::get_ssf_category()); - BOOST_LOG_TRIVIAL(error) << "transport: SSF reply NOT ok " << ec.message(); + boost::system::error_code result_ec(::error::wrong_protocol_type, + ::error::get_ssf_category()); + SSF_LOG(kLogError) << "transport: SSF reply NOT ok " << ec.message(); callback_(p_socket, result_ec); } } else { - BOOST_LOG_TRIVIAL(error) << "transport: could NOT read SSF reply " << ec.message(); + SSF_LOG(kLogError) << "transport: could NOT read SSF reply " + << ec.message(); callback_(p_socket, ec); } } @@ -126,7 +133,7 @@ class TransportProtocolPolicy { bool IsSupportedVersion(uint32_t input_version) { boost::archive::library_version_type serialization(input_version & - 0x000000FF); + 0x000000FF); input_version = input_version >> 8; uint8_t transport = (input_version & 0x000000FF); @@ -138,13 +145,14 @@ class TransportProtocolPolicy { uint8_t major = (input_version & 0x000000FF); return (major == versions::major) && (minor == versions::minor) && - (transport == versions::transport) && - (serialization == boost::archive::BOOST_ARCHIVE_VERSION()); + (transport == versions::transport) && + (serialization == boost::archive::BOOST_ARCHIVE_VERSION()); } private: - callback_type callback_; + Callback callback_; }; -} //ssf + +} // ssf #endif // SSF_CORE_TRANSPORT_VIRTUAL_LAYER_POLICIES_TRANSPORT_PROTOCOL_POLICY_H diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt new file mode 100644 index 00000000..c7f6cfa1 --- /dev/null +++ b/src/framework/CMakeLists.txt @@ -0,0 +1,145 @@ +cmake_minimum_required(VERSION 2.8) + +# specific platform lib +set(PLATFORM_LIB "") + +file(GLOB_RECURSE SSF_FRAMEWORK_COMMON_IO_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/io/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/io/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/io/*.ipp") + +set(SSF_FRAMEWORK_COMMON_ERROR_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/error/error.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/error/error.ipp") + +set(SSF_FRAMEWORK_COMMON_LOG_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/log/log.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/log/log.cpp") + +file(GLOB_RECURSE SSF_FRAMEWORK_COMMON_NETWORK_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/network/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/network/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/network/*.ipp") + +file(GLOB_RECURSE SSF_FRAMEWORK_COMMON_UTILS_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/utils/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/network/*.ipp" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/network/*.cpp" +) + +# layer files +file(GLOB SSF_FRAMEWORK_LAYER_BASE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/*.cpp") + +file(GLOB SSF_FRAMEWORK_LAYER_CONGESTION_FILES +"${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/congestion/*.h") + +file(GLOB_RECURSE SSF_FRAMEWORK_LAYER_CRYPTOGRAPHY_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/cryptography/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/cryptography/*.cpp") + +file(GLOB SSF_FRAMEWORK_LAYER_DATA_LINK_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/data_link/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/data_link/*.cpp") + +file(GLOB SSF_FRAMEWORK_LAYER_DATAGRAM_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/datagram/*.h") + +file(GLOB SSF_FRAMEWORK_LAYER_INTERFACE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/interface_layer/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/interface_layer/*.cc") + +file(GLOB SSF_FRAMEWORK_LAYER_MULTIPLEXING_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/multiplexing/*.h") + +file(GLOB SSF_FRAMEWORK_LAYER_NETWORK_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/network/*.h") + +file(GLOB SSF_FRAMEWORK_LAYER_PHYSICAL_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/physical/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/physical/*.cpp") + +file(GLOB SSF_FRAMEWORK_LAYER_PROXY_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/proxy/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/proxy/*.cpp") + +if (WIN32) + list(APPEND PLATFORM_LIB "Secur32.lib") + list(APPEND SSF_FRAMEWORK_LAYER_PROXY_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/proxy/windows/sspi_auth_impl.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/proxy/windows/sspi_auth_impl.cpp") +endif (WIN32) +if (UNIX) + list(APPEND SSF_FRAMEWORK_LAYER_PROXY_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/proxy/unix/gssapi_auth_impl.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/proxy/unix/gssapi_auth_impl.cpp") +endif (UNIX) + +file(GLOB SSF_FRAMEWORK_LAYER_QUEUE_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/queue/*.h") + +file(GLOB SSF_FRAMEWORK_LAYER_ROUTING_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/layer/routing/*.h") + +set(SSF_FRAMEWORK_LAYER_FILES + "${SSF_FRAMEWORK_LAYER_BASE_FILES}" + "${SSF_FRAMEWORK_LAYER_CONGESTION_FILES}" + "${SSF_FRAMEWORK_LAYER_CRYPTOGRAPHY_FILES}" + "${SSF_FRAMEWORK_LAYER_DATA_LINK_FILES}" + "${SSF_FRAMEWORK_LAYER_DATAGRAM_FILES}" + "${SSF_FRAMEWORK_LAYER_INTERFACE_FILES}" + "${SSF_FRAMEWORK_LAYER_MULTIPLEXING_FILES}" + "${SSF_FRAMEWORK_LAYER_NETWORK_FILES}" + "${SSF_FRAMEWORK_LAYER_PHYSICAL_FILES}" + "${SSF_FRAMEWORK_LAYER_PROXY_FILES}" + "${SSF_FRAMEWORK_LAYER_QUEUE_FILES}" + "${SSF_FRAMEWORK_LAYER_ROUTING_FILES}" +) + +file(GLOB_RECURSE SSF_FRAMEWORK_INTERFACES_SYSTEM_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/system/*interface*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/system/*interface*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/system/*interface*.ipp") + +file(GLOB_RECURSE SSF_FRAMEWORK_ROUTERS_SYSTEM_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/system/*router*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/system/*router*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/ssf/system/*router*.ipp") + +add_target("lib_ssf_network" + TYPE + library ${EXEC_FLAG} STATIC + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_http_parser + ${PLATFORM_LIB} + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${SSF_FRAMEWORK_COMMON_LOG_FILES} + ${SSF_FRAMEWORK_COMMON_ERROR_FILES} + ${SSF_FRAMEWORK_COMMON_IO_FILES} + ${SSF_FRAMEWORK_COMMON_NETWORK_FILES} + ${SSF_FRAMEWORK_COMMON_UTILS_FILES} + ${SSF_FRAMEWORK_LAYER_FILES} + #${SSF_FRAMEWORK_INTERFACES_SYSTEM_FILES} + #${SSF_FRAMEWORK_ROUTERS_SYSTEM_FILES} + INCLUDE_DIRECTORIES + ${OpenSSL_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_include_directories(lib_ssf_network + INTERFACE + ${OpenSSL_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR} +) + +if (BUILD_FRAMEWORK_UNIT_TESTS) + add_subdirectory(tests) +endif(BUILD_FRAMEWORK_UNIT_TESTS) diff --git a/src/framework/ssf/error/error.h b/src/framework/ssf/error/error.h new file mode 100644 index 00000000..ba9129e8 --- /dev/null +++ b/src/framework/ssf/error/error.h @@ -0,0 +1,61 @@ +#ifndef SSF_ERROR_ERROR_H_ +#define SSF_ERROR_ERROR_H_ + +#include + +#include +#include + +namespace ssf { +namespace error { + +enum errors { + success = boost::system::errc::success, + interrupted = boost::system::errc::interrupted, + io_error = boost::system::errc::io_error, + bad_file_descriptor = boost::system::errc::bad_file_descriptor, + device_or_resource_busy = boost::system::errc::device_or_resource_busy, + invalid_argument = boost::system::errc::invalid_argument, + not_a_socket = boost::system::errc::not_a_socket, + broken_pipe = boost::system::errc::broken_pipe, + filename_too_long = boost::system::errc::filename_too_long, + function_not_supported = boost::system::errc::function_not_supported, + connection_aborted = boost::system::errc::connection_aborted, + connection_refused = boost::system::errc::connection_refused, + connection_reset = boost::system::errc::connection_reset, + not_connected = boost::system::errc::not_connected, + protocol_error = boost::system::errc::protocol_error, + wrong_protocol_type = boost::system::errc::wrong_protocol_type, + operation_canceled = boost::system::errc::operation_canceled, + identifier_removed = boost::system::errc::identifier_removed, + address_in_use = boost::system::errc::address_in_use, + address_not_available = boost::system::errc::address_not_available, + destination_address_required = + boost::system::errc::destination_address_required, + bad_address = boost::system::errc::bad_address, + message_size = boost::system::errc::message_size, + network_down = boost::system::errc::network_down, + no_buffer_space = boost::system::errc::no_buffer_space, + no_link = boost::system::errc::no_link, + service_not_found = 10000, + out_of_range = 10001, + import_crt_error = 10002, + set_crt_error = 10003, + no_crt_error = 10004, + import_key_error = 10005, + set_key_error = 10006, + no_key_error = 10007, + no_dh_param_error = 10008, + buffer_is_full_error = 11000, + missing_config_parameters = 12000, + cannot_resolve_endpoint = 12001 +}; + +extern BOOST_ASIO_DECL const boost::system::error_category& get_ssf_category(); + +} // error +} // ssf + +#include "ssf/error/error.ipp" + +#endif // SSF_ERROR_ERROR_H_ diff --git a/src/framework/ssf/error/error.ipp b/src/framework/ssf/error/error.ipp new file mode 100644 index 00000000..0cd17b43 --- /dev/null +++ b/src/framework/ssf/error/error.ipp @@ -0,0 +1,111 @@ +#ifndef SSF_ERROR_ERROR_IPP_ +#define SSF_ERROR_ERROR_IPP_ + +#include + +#include + +#include "ssf/error/error.h" + +namespace ssf { +namespace error { + +namespace detail { +class ssf_category : public boost::system::error_category { + public: + const char* name() const BOOST_ASIO_ERROR_CATEGORY_NOEXCEPT { + return "ssf"; + } + + std::string message(int value) const { + switch (value) { + case error::success: + return "success"; + case error::io_error: + return "io_error"; + case error::interrupted: + return "connection interrupted"; + case error::bad_file_descriptor: + return "bad file descriptor"; + case error::device_or_resource_busy: + return "device or resource busy"; + case error::invalid_argument: + return "invalid argument"; + case error::not_a_socket: + return "no socket could be created"; + case error::broken_pipe: + return "broken pipe"; + case error::filename_too_long: + return "filename too long"; + case error::function_not_supported: + return "function not supported"; + case error::connection_aborted: + return "connection aborted"; + case error::connection_refused: + return "connection refused"; + case error::connection_reset: + return "connection reset"; + case error::not_connected: + return "not connected"; + case error::protocol_error: + return "protocol error"; + case error::wrong_protocol_type: + return "wrong protocol type"; + case error::operation_canceled: + return "operation canceled"; + case error::identifier_removed: + return "identifier removed"; + case error::address_in_use: + return "address in use"; + case error::address_not_available: + return "address not available"; + case error::bad_address: + return "bad address"; + case error::message_size: + return "message size"; + case error::network_down: + return "network down"; + case error::no_buffer_space: + return "no buffer space"; + case error::no_link: + return "no link"; + case error::service_not_found: + return "service not found"; + case error::out_of_range: + return "out of range"; + case error::import_crt_error: + return "could not import certificate"; + case error::set_crt_error: + return "could not use certificate"; + case error::no_crt_error: + return "no certificate found"; + case error::import_key_error: + return "could not import key"; + case error::set_key_error: + return "could not use key"; + case error::no_key_error: + return "no key found"; + case error::no_dh_param_error: + return "no dh parameter found"; + case error::buffer_is_full_error: + return "buffer is full"; + case error::missing_config_parameters: + return "missing config parameters"; + case error::cannot_resolve_endpoint: + return "cannot resolve endpoint"; + default: + return "ssf error"; + } + } +}; +} // detail + +inline const boost::system::error_category& get_ssf_category() { + static detail::ssf_category instance; + return instance; +} + +} // error +} // ssf + +#endif // SSF_COMMON_ERROR_ERROR_IPP_ diff --git a/src/framework/ssf/io/accept_op.h b/src/framework/ssf/io/accept_op.h new file mode 100644 index 00000000..f8702210 --- /dev/null +++ b/src/framework/ssf/io/accept_op.h @@ -0,0 +1,142 @@ +#ifndef SSF_IO_ACCEPT_OP_H_ +#define SSF_IO_ACCEPT_OP_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ssf/io/op.h" + +#include + +namespace ssf { +namespace io { + +/// Base class for pending fiber accept operations +template +class basic_pending_accept_operation : public basic_pending_io_operation { + protected: + typedef boost::asio::basic_socket + socket_type; + typedef typename Protocol::endpoint endpoint_type; + + protected: + /// Constructor + /** + * @param func The completion handler + * @param impl The implementation of the accepted socket + * @param p_endpoint The remote endpoint of the accepted socket + */ + basic_pending_accept_operation(basic_pending_io_operation::func_type func, + socket_type& peer, endpoint_type* p_endpoint) + : basic_pending_io_operation(func), + peer_(peer), + p_endpoint_(p_endpoint) {} + + public: + /// Set the remote endpoint of the accepted socket + /** + * @param e The remote endpoint to set + */ + void set_p_endpoint(const endpoint_type& e) { + if (p_endpoint_) { + *p_endpoint_ = e; + } + } + + /// Get the accepted socket implementation + socket_type& peer() { return peer_; } + + private: + socket_type& peer_; + endpoint_type* p_endpoint_; +}; + +/// Class to store accept operations on a fiber +/** +* @tparam Handler The type of the handler to be called upon completion +* @tparam StreamSocket +*/ +template +class pending_accept_operation + : public basic_pending_accept_operation { +private: + typedef typename basic_pending_accept_operation::socket_type + socket_type; + typedef typename basic_pending_accept_operation::endpoint_type + endpoint_type; + + public: + BOOST_ASIO_DEFINE_HANDLER_PTR(pending_accept_operation); + + /// Constructor + /** + * @param impl The socket implementation of the accepted socket + * @param p_endpoint The remote endpoint of the accepted socket + * @param handler The handler to call upon completion + */ + pending_accept_operation(socket_type& peer, endpoint_type* p_endpoint, + Handler handler) + : basic_pending_accept_operation( + &pending_accept_operation::do_complete, peer, p_endpoint), + handler_(std::move(handler)) {} + + /// Implementation of the completion callback + /** + * @param base A pointer to the base class + * @param destroy A boolean to decide if the op should be destroyed + * @param result_ec The error_code of the operation + */ + static void do_complete(basic_pending_io_operation* base, bool destroy, + const boost::system::error_code& result_ec) { + boost::system::error_code ec(result_ec); + + // take ownership of the operation object + pending_accept_operation* o(static_cast(base)); + + ptr p = {boost::asio::detail::addressof(o->handler_), o, o}; + + BOOST_ASIO_HANDLER_COMPLETION((o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + boost::asio::detail::binder1 handler( + o->handler_, ec); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (!destroy) { + boost::asio::detail::fenced_block b( + boost::asio::detail::fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + boost_asio_handler_invoke_helpers::invoke(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + + private: + Handler handler_; +}; + +} // io +} // ssf + +#include + +#endif // SSF_IO_ACCEPT_OP_H_ diff --git a/src/framework/ssf/io/buffers.h b/src/framework/ssf/io/buffers.h new file mode 100644 index 00000000..1608cd2b --- /dev/null +++ b/src/framework/ssf/io/buffers.h @@ -0,0 +1,53 @@ +#ifndef SSF_IO_BUFFERS_H_ +#define SSF_IO_BUFFERS_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +namespace ssf { +namespace io { + +template +class fixed_buffer_sequence { + public: + typedef std::vector buffer_type; + typedef typename buffer_type::value_type value_type; + typedef typename buffer_type::iterator iterator; + typedef typename buffer_type::const_iterator const_iterator; + + fixed_buffer_sequence() : buffers_() {} + + template + fixed_buffer_sequence(const BufferSequence& buffers) + : buffers_() { + for (const auto& buffer : buffers) { + buffers_.push_back(buffer); + } + } + + iterator begin() { return buffers_.begin(); } + iterator end() { return buffers_.end(); } + + const_iterator begin() const { return buffers_.begin(); } + const_iterator end() const { return buffers_.end(); } + + void push_back(const value_type& val) { buffers_.push_back(val); } + void push_back(value_type&& val) { buffers_.push_back(std::move(val)); } + + private: + buffer_type buffers_; +}; + +typedef fixed_buffer_sequence + fixed_mutable_buffer_sequence; + +typedef fixed_buffer_sequence + fixed_const_buffer_sequence; + +} // io +} // ssf + +#endif // SSF_IO_BUFFERS_H_ diff --git a/src/framework/ssf/io/composed_op.h b/src/framework/ssf/io/composed_op.h new file mode 100644 index 00000000..c86956dc --- /dev/null +++ b/src/framework/ssf/io/composed_op.h @@ -0,0 +1,82 @@ +#ifndef SSF_IO_COMPOSED_OP_H_ +#define SSF_IO_COMPOSED_OP_H_ + +#include +#include + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace ssf { +namespace io { + +template +struct ComposedOp { + private: + typedef typename std::remove_reference::type CompHandler; + typedef typename std::remove_reference::type ContextHandler; + + public: + ComposedOp(CompHandler h, ContextHandler c) + : handler(std::move(h)), context(std::move(c)) {} + + inline void operator()(const boost::system::error_code& ec, + std::size_t length) { + handler(ec, length); + } + + inline void operator()(const boost::system::error_code& ec, + std::size_t length) const { + handler(ec, length); + } + + inline void operator()(const boost::system::error_code& ec) { handler(ec); } + + inline void operator()(const boost::system::error_code& ec) const { + handler(ec); + } + + CompHandler handler; + ContextHandler context; +}; + +template +inline void* asio_handler_allocate( + std::size_t size, ComposedOp* this_handler) { + return boost_asio_handler_alloc_helpers::allocate(size, + this_handler->context); +} + +template +inline void asio_handler_deallocate( + void* pointer, std::size_t size, + ComposedOp* this_handler) { + boost_asio_handler_alloc_helpers::deallocate(pointer, size, + this_handler->context); +} + +template +inline bool asio_handler_is_continuation( + ComposedOp* this_handler) { + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->context); +} + +template +inline void asio_handler_invoke( + const Function& function, + ComposedOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->context); +} + +template +inline void asio_handler_invoke( + Function& function, ComposedOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->context); +} + +} // io +} // ssf + +#endif // SSF_IO_COMPOSED_OP_H_ diff --git a/src/framework/ssf/io/connect_op.h b/src/framework/ssf/io/connect_op.h new file mode 100644 index 00000000..3fb719a8 --- /dev/null +++ b/src/framework/ssf/io/connect_op.h @@ -0,0 +1,104 @@ +#ifndef SSF_IO_CONNECT_OP_H_ +#define SSF_IO_CONNECT_OP_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ssf/io/op.h" + +#include + +namespace ssf { +namespace io { + +/// Base class for pending connect operations +template +class basic_pending_connect_operation : public basic_pending_io_operation { + protected: + /// Constructor + /** + * @param func The completion handler + */ + basic_pending_connect_operation(basic_pending_io_operation::func_type func) + : basic_pending_io_operation(func) {} +}; + +/// Class to store connect operations +/** +* @tparam Handler The type of the handler to be called upon completion +* @tparam StreamSocket +*/ +template +class pending_connect_operation + : public basic_pending_connect_operation { + public: + BOOST_ASIO_DEFINE_HANDLER_PTR(pending_connect_operation); + + /// Constructor + /** + * @param handler The handler to call upon completion + */ + pending_connect_operation(Handler handler) + : basic_pending_connect_operation( + &pending_connect_operation::do_complete), + handler_(std::move(handler)) {} + + /// Implementation of the completion callback + /** + * @param base A pointer to the base class + * @param destroy A boolean to decide if the op should be destroyed + * @param result_ec The error_code of the operation + */ + static void do_complete(basic_pending_io_operation* base, bool destroy, + const boost::system::error_code& result_ec) { + boost::system::error_code ec(result_ec); + + // take ownership of the operation object + pending_connect_operation* o(static_cast(base)); + + ptr p = {boost::asio::detail::addressof(o->handler_), o, o}; + + BOOST_ASIO_HANDLER_COMPLETION((o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + boost::asio::detail::binder1 handler( + o->handler_, ec); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (!destroy) { + boost::asio::detail::fenced_block b( + boost::asio::detail::fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + boost_asio_handler_invoke_helpers::invoke(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + + private: + Handler handler_; +}; + +} // io +} // ssf + +#include + +#endif // SSF_COMMON_IO_CONNECT_OP_H_ diff --git a/src/framework/ssf/io/get_op.h b/src/framework/ssf/io/get_op.h new file mode 100644 index 00000000..eaccc121 --- /dev/null +++ b/src/framework/ssf/io/get_op.h @@ -0,0 +1,89 @@ +#ifndef SSF_IO_GET_OP_H_ +#define SSF_IO_GET_OP_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include +#include +#include + +#include + +namespace ssf { +namespace io { + +template +class basic_pending_get_operation BOOST_ASIO_INHERIT_TRACKED_HANDLER { + public: + void complete(const boost::system::error_code& ec, T element) { + auto destroy = false; + func_(this, destroy, ec, std::move(element)); + } + + void destroy() { + auto destroy = true; + func_(this, destroy, boost::system::error_code(), T()); + } + + protected: + typedef void (*func_type)(basic_pending_get_operation*, bool, + const boost::system::error_code&, T); + + basic_pending_get_operation(func_type func) : next_(nullptr), func_(func) {} + + ~basic_pending_get_operation() {} + + friend class boost::asio::detail::op_queue_access; + basic_pending_get_operation* next_; + func_type func_; +}; + +template +class pending_get_operation : public basic_pending_get_operation { + public: + BOOST_ASIO_DEFINE_HANDLER_PTR(pending_get_operation); + + pending_get_operation(Handler handler) + : basic_pending_get_operation(&pending_get_operation::do_complete), + handler_(std::move(handler)) {} + + static void do_complete(basic_pending_get_operation* base, bool destroy, + const boost::system::error_code& result_ec, + T element) { + boost::system::error_code ec(result_ec); + + pending_get_operation* o(static_cast(base)); + + ptr p = {boost::asio::detail::addressof(o->handler_), o, o}; + + BOOST_ASIO_HANDLER_COMPLETION((o)); + + boost::asio::detail::binder2 handler( + o->handler_, ec, std::move(element)); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + if (!destroy) { + boost::asio::detail::fenced_block b( + boost::asio::detail::fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + boost_asio_handler_invoke_helpers::invoke(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + + private: + Handler handler_; +}; + +} // io +} // ssf + +#include + +#endif // SSF_COMMON_IO_GET_OP_H_ diff --git a/src/framework/ssf/io/handler_helpers.h b/src/framework/ssf/io/handler_helpers.h new file mode 100644 index 00000000..837829e3 --- /dev/null +++ b/src/framework/ssf/io/handler_helpers.h @@ -0,0 +1,57 @@ +#ifndef SSF_IO_HANDLER_HELPER_H_ +#define SSF_IO_HANDLER_HELPER_H_ + +#include + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +namespace ssf { +namespace io { + +template +void PostHandler(Poster& poster, Handler&& handler, Arg1&& arg1) { + poster.post(boost::asio::detail::bind_handler(std::forward(handler), + std::forward(arg1))); +} + +template +void PostHandler(Poster& poster, Handler&& handler, Arg1&& arg1, Arg2&& arg2) { + poster.post(boost::asio::detail::bind_handler(std::forward(handler), + std::forward(arg1), + std::forward(arg2))); +} + +template +void PostHandler(Poster& poster, Handler&& handler, Arg1&& arg1, Arg2&& arg2, + Arg3&& arg3) { + poster.post(boost::asio::detail::bind_handler( + std::forward(handler), std::forward(arg1), + std::forward(arg2), std::forward(arg3))); +} + +template +void PostHandler(Poster& poster, Handler&& handler, Arg1&& arg1, Arg2&& arg2, + Arg3&& arg3, Arg4&& arg4) { + poster.post(boost::asio::detail::bind_handler( + std::forward(handler), std::forward(arg1), + std::forward(arg2), std::forward(arg3), + std::forward(arg4))); +} + +template +void PostHandler(Poster& poster, Handler&& handler, Arg1&& arg1, Arg2&& arg2, + Arg3&& arg3, Arg4&& arg4, Arg5&& arg5) { + poster.post(boost::asio::detail::bind_handler( + std::forward(handler), std::forward(arg1), + std::forward(arg2), std::forward(arg3), + std::forward(arg4), std::forward(arg5))); +} + +} // io +} // ssf + +#endif // SSF_IO_HANDLER_HELPER_H_ diff --git a/src/framework/ssf/io/op.h b/src/framework/ssf/io/op.h new file mode 100644 index 00000000..4c8ccd1e --- /dev/null +++ b/src/framework/ssf/io/op.h @@ -0,0 +1,77 @@ +#ifndef SSF_IO_OP_H_ +#define SSF_IO_OP_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include + +namespace ssf { +namespace io { + +/// Base class for pending io operations without size information +class basic_pending_io_operation BOOST_ASIO_INHERIT_TRACKED_HANDLER { + public: + /// Function called on completion + /** + * @param ec The error code resulting of the completed operation + */ + void complete(const boost::system::error_code& ec) { + auto destroy = false; + func_(this, destroy, ec); + } + + void destroy() { + auto destroy = true; + func_(this, destroy, boost::system::error_code()); + } + + protected: + typedef void (*func_type)(basic_pending_io_operation*, bool, + const boost::system::error_code& ec); + + basic_pending_io_operation(func_type func) : next_(0), func_(func) {} + + ~basic_pending_io_operation() {} + + friend class boost::asio::detail::op_queue_access; + basic_pending_io_operation* next_; + func_type func_; +}; + +/// Base class for pending io operations with size information +class basic_pending_sized_io_operation BOOST_ASIO_INHERIT_TRACKED_HANDLER { +public: + /// Function called on completion + /** + * @param ec The error code resulting of the completed operation + */ + void complete(const boost::system::error_code& ec, std::size_t length) { + auto destroy = false; + func_(this, destroy, ec, length); + } + + void destroy() { + auto destroy = true; + func_(this, destroy, boost::system::error_code(), 0); + } + +protected: + typedef void(*func_type)(basic_pending_sized_io_operation*, bool, + const boost::system::error_code& ec, std::size_t length); + + basic_pending_sized_io_operation(func_type func) : next_(0), func_(func) {} + + ~basic_pending_sized_io_operation() {} + + friend class boost::asio::detail::op_queue_access; + basic_pending_sized_io_operation* next_; + func_type func_; +}; + +} // io +} // ssf + +#endif // SSF_IO_OP_H_ diff --git a/src/framework/ssf/io/push_op.h b/src/framework/ssf/io/push_op.h new file mode 100644 index 00000000..4a8e7987 --- /dev/null +++ b/src/framework/ssf/io/push_op.h @@ -0,0 +1,93 @@ +#ifndef SSF_IO_PUSH_OP_H_ +#define SSF_IO_PUSH_OP_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include +#include +#include + +#include + +namespace ssf { +namespace io { + +template +class basic_pending_push_operation BOOST_ASIO_INHERIT_TRACKED_HANDLER { + public: + void complete(const boost::system::error_code& ec) { + auto destroy = false; + func_(this, destroy, ec); + } + + void destroy() { + auto destroy = true; + func_(this, destroy, boost::system::error_code()); + } + + T element() { return element_; } + + protected: + typedef void (*func_type)(basic_pending_push_operation*, bool, + const boost::system::error_code&); + + basic_pending_push_operation(func_type func, T element) + : next_(nullptr), func_(func), element_(std::move(element)) {} + + ~basic_pending_push_operation() {} + + friend class boost::asio::detail::op_queue_access; + basic_pending_push_operation* next_; + func_type func_; + T element_; +}; + +template +class pending_push_operation : public basic_pending_push_operation { + public: + BOOST_ASIO_DEFINE_HANDLER_PTR(pending_push_operation); + + pending_push_operation(Handler handler, T element) + : basic_pending_push_operation(&pending_push_operation::do_complete, + std::move(element)), + handler_(std::move(handler)) {} + + static void do_complete(basic_pending_push_operation* base, bool destroy, + const boost::system::error_code& result_ec) { + boost::system::error_code ec(result_ec); + + pending_push_operation* o(static_cast(base)); + + ptr p = {boost::asio::detail::addressof(o->handler_), o, o}; + + BOOST_ASIO_HANDLER_COMPLETION((o)); + + boost::asio::detail::binder1 handler( + o->handler_, ec); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + if (!destroy) { + boost::asio::detail::fenced_block b( + boost::asio::detail::fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_)); + boost_asio_handler_invoke_helpers::invoke(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + + private: + Handler handler_; +}; + +} // io +} // ssf + +#include + +#endif // SSF_COMMON_IO_PUSH_OP_H_ diff --git a/src/framework/ssf/io/read_op.h b/src/framework/ssf/io/read_op.h new file mode 100644 index 00000000..05f3d6e3 --- /dev/null +++ b/src/framework/ssf/io/read_op.h @@ -0,0 +1,164 @@ +#ifndef SSF_IO_READ_OP_H_ +#define SSF_IO_READ_OP_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ssf/io/op.h" + +#include + +namespace ssf { +namespace io { + +/// Base class for pending read operations +template +class basic_pending_read_operation : public basic_pending_sized_io_operation { + protected: + typedef typename Protocol::endpoint endpoint_type; + typedef std::size_t (*fill_buffer_func_type)( + basic_pending_read_operation*, typename Protocol::ReceiveDatagram&, + boost::system::error_code&); + + protected: + /// Constructor + /** + * @param func The completion handler + * @param fill_buffer_func The fill buffer handler + * @param p_endpoint The remote endpoint + */ + basic_pending_read_operation(basic_pending_sized_io_operation::func_type func, + fill_buffer_func_type fill_buffer_func, + endpoint_type* p_endpoint) + : basic_pending_sized_io_operation(func), + p_endpoint_(p_endpoint), + fill_buffer_func_(fill_buffer_func) {} + + public: + /// Set the remote endpoint of the accepted socket + /** + * @param e The remote endpoint to set + */ + void set_p_endpoint(endpoint_type e) { + if (p_endpoint_) { + *p_endpoint_ = std::move(e); + } + } + + std::size_t fill_buffer(typename Protocol::ReceiveDatagram& datagram, + boost::system::error_code& ec) { + return fill_buffer_func_(this, datagram, ec); + } + + private: + endpoint_type* p_endpoint_; + fill_buffer_func_type fill_buffer_func_; +}; + +/// Class to store read operations +/** +* @tparam MutableBufferSequence The type of the mutable buffer sequence +* @tparam Handler The type of the handler to be called upon completion +* @tparam Protocol Protocol of the read op +*/ +template +class pending_read_operation : public basic_pending_read_operation { + private: + typedef typename basic_pending_read_operation::endpoint_type endpoint_type; + + public: + BOOST_ASIO_DEFINE_HANDLER_PTR(pending_read_operation); + + /// Constructor + /** + * @param buffers The buffers to fill in + * @param handler The handler to call upon completion + * @param p_endpoint The remote endpoint + */ + pending_read_operation(const MutableBufferSequence& buffers, Handler handler, + endpoint_type* p_endpoint) + : basic_pending_read_operation( + &pending_read_operation::do_complete, + &pending_read_operation::do_fill_buffer, p_endpoint), + buffers_(buffers), + handler_(std::move(handler)) {} + + /// Implementation of the completion callback + /** + * @param base A pointer to the base class + * @param destroy A boolean to decide if the op should be destroyed + * @param result_ec The error_code of the operation + */ + static void do_complete(basic_pending_sized_io_operation* base, bool destroy, + const boost::system::error_code& result_ec, + std::size_t length) { + boost::system::error_code ec(result_ec); + + // take ownership of the operation object + pending_read_operation* o(static_cast(base)); + + ptr p = {boost::asio::detail::addressof(o->handler_), o, o}; + + BOOST_ASIO_HANDLER_COMPLETION((o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + boost::asio::detail::binder2 handler(o->handler_, ec, length); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (!destroy) { + boost::asio::detail::fenced_block b( + boost::asio::detail::fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + boost_asio_handler_invoke_helpers::invoke(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + + static std::size_t do_fill_buffer( + basic_pending_read_operation* base, + typename Protocol::ReceiveDatagram& datagram, + boost::system::error_code& ec) { + pending_read_operation* o(static_cast(base)); + + if (boost::asio::buffer_size(o->buffers_) < datagram.payload().GetSize()) { + ec.assign(ssf::error::message_size, ssf::error::get_ssf_category()); + return 0; + } + + auto payload_buffers = datagram.payload().GetConstBuffers(); + + std::size_t copied = boost::asio::buffer_copy(o->buffers_, payload_buffers); + + return copied; + } + + private: + MutableBufferSequence buffers_; + Handler handler_; +}; + +} // io +} // ssf + +#include + +#endif // SSF_IO_READ_OP_H_ diff --git a/src/framework/ssf/io/read_stream_op.h b/src/framework/ssf/io/read_stream_op.h new file mode 100644 index 00000000..b0d7ae33 --- /dev/null +++ b/src/framework/ssf/io/read_stream_op.h @@ -0,0 +1,138 @@ +#ifndef SSF_COMMON_IO_READ_STREAM_OP_H_ +#define SSF_COMMON_IO_READ_STREAM_OP_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ssf/io/op.h" + +#include + +namespace ssf { +namespace io { + +/// Base class for pending read operations +class basic_pending_read_stream_operation + : public basic_pending_sized_io_operation { + protected: + typedef size_t (*fill_buffer_func_type)( + basic_pending_read_stream_operation*, boost::asio::streambuf&); + + protected: + /// Constructor + /** + * @param func The completion handler + */ + basic_pending_read_stream_operation( + basic_pending_sized_io_operation::func_type func, + fill_buffer_func_type fill_buffer_func) + : basic_pending_sized_io_operation(func), + fill_buffer_func_(fill_buffer_func) {} + + public: + size_t fill_buffer(boost::asio::streambuf& stream) { + return fill_buffer_func_(this, stream); + } + + private: + fill_buffer_func_type fill_buffer_func_; +}; + +/// Class to store read operations +/** +* @tparam Handler The type of the handler to be called upon completion +*/ +template +class pending_read_stream_operation + : public basic_pending_read_stream_operation { + public: + BOOST_ASIO_DEFINE_HANDLER_PTR(pending_read_stream_operation); + + /// Constructor + /** + * @param handler The handler to call upon completion + */ + pending_read_stream_operation(const MutableBufferSequence& buffers, + Handler handler) + : basic_pending_read_stream_operation( + &pending_read_stream_operation::do_complete, + &pending_read_stream_operation::do_fill_buffer), + buffers_(buffers), + handler_(std::move(handler)) {} + + /// Implementation of the completion callback + /** + * @param base A pointer to the base class + * @param destroy A boolean to decide if the op should be destroyed + * @param result_ec The error_code of the operation + */ + static void do_complete(basic_pending_sized_io_operation* base, bool destroy, + const boost::system::error_code& result_ec, + std::size_t length) { + boost::system::error_code ec(result_ec); + + // take ownership of the operation object + pending_read_stream_operation* o( + static_cast(base)); + + ptr p = {boost::asio::detail::addressof(o->handler_), o, o}; + + BOOST_ASIO_HANDLER_COMPLETION((o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + boost::asio::detail::binder2 handler(o->handler_, ec, length); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (!destroy) { + boost::asio::detail::fenced_block b( + boost::asio::detail::fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + boost_asio_handler_invoke_helpers::invoke(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + + static size_t do_fill_buffer(basic_pending_read_stream_operation* base, + boost::asio::streambuf& stream) { + pending_read_stream_operation* o( + static_cast(base)); + + auto copied = boost::asio::buffer_copy(o->buffers_, stream.data()); + stream.consume(copied); + + return copied; + } + + private: + MutableBufferSequence buffers_; + Handler handler_; +}; + +} // io +} // ssf + +#include + +#endif // SSF_IO_READ_STREAM_OP_H_ diff --git a/src/framework/ssf/io/write_op.h b/src/framework/ssf/io/write_op.h new file mode 100644 index 00000000..ef9a2837 --- /dev/null +++ b/src/framework/ssf/io/write_op.h @@ -0,0 +1,125 @@ +#ifndef SSF_IO_WRITE_OP_H_ +#define SSF_IO_WRITE_OP_H_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ssf/io/buffers.h" + +#include "ssf/io/op.h" + +#include + +namespace ssf { +namespace io { + +class basic_pending_write_operation : public basic_pending_sized_io_operation { + protected: + typedef io::fixed_const_buffer_sequence (*const_buffers_func_type)( + basic_pending_write_operation*); + + public: + io::fixed_const_buffer_sequence const_buffers() { + return const_buffers_func_(this); + } + + protected: + basic_pending_write_operation( + basic_pending_sized_io_operation::func_type func, + const_buffers_func_type const_buffers_func) + : basic_pending_sized_io_operation(func), + const_buffers_func_(const_buffers_func) {} + + protected: + const_buffers_func_type const_buffers_func_; +}; + +/// Class to store write operations +/** +* @tparam ConstBufferSequence The type of the const buffer sequence (input buffer to write) +* @tparam Handler The type of the handler to be called upon completion +*/ +template +class pending_write_operation : public basic_pending_write_operation { + public: + BOOST_ASIO_DEFINE_HANDLER_PTR(pending_write_operation); + + /// Constructor + /** + * @param buffer The const buffer sequence to write + * @param handler The handler to call upon completion + */ + pending_write_operation(ConstBufferSequence buffers, Handler handler) + : basic_pending_write_operation( + &pending_write_operation::do_complete, + &pending_write_operation::do_const_buffers), + buffers_(std::move(buffers)), + handler_(std::move(handler)) {} + + /// Implementation of the completion callback + /** + * @param base A pointer to the base class + * @param destroy A boolean to decide if the op should be destroyed + * @param result_ec The error_code of the operation + */ + static void do_complete(basic_pending_sized_io_operation* base, bool destroy, + const boost::system::error_code& result_ec, + std::size_t length) { + boost::system::error_code ec(result_ec); + + // take ownership of the operation object + pending_write_operation* o(static_cast(base)); + + ptr p = {boost::asio::detail::addressof(o->handler_), o, o}; + + BOOST_ASIO_HANDLER_COMPLETION((o)); + + // Make a copy of the handler so that the memory can be deallocated before + // the upcall is made. Even if we're not about to make an upcall, a + // sub-object of the handler may be the true owner of the memory associated + // with the handler. Consequently, a local copy of the handler is required + // to ensure that any owning sub-object remains valid until after we have + // deallocated the memory here. + boost::asio::detail::binder2 handler(o->handler_, ec, length); + p.h = boost::asio::detail::addressof(handler.handler_); + p.reset(); + + // Make the upcall if required. + if (!destroy) { + boost::asio::detail::fenced_block b( + boost::asio::detail::fenced_block::half); + BOOST_ASIO_HANDLER_INVOCATION_BEGIN((handler.arg1_, handler.arg2_)); + boost_asio_handler_invoke_helpers::invoke(handler, handler.handler_); + BOOST_ASIO_HANDLER_INVOCATION_END; + } + } + + static io::fixed_const_buffer_sequence do_const_buffers( + basic_pending_write_operation* base) { + pending_write_operation* o(static_cast(base)); + return o->buffers_; + } + + private: + ConstBufferSequence buffers_; + Handler handler_; +}; + +} // io +} // ssf + +#include + +#endif // SSF_IO_WRITE_OP_H_ diff --git a/src/framework/ssf/layer/accept_op.h b/src/framework/ssf/layer/accept_op.h new file mode 100644 index 00000000..d6b0899d --- /dev/null +++ b/src/framework/ssf/layer/accept_op.h @@ -0,0 +1,131 @@ +#ifndef SSF_LAYER_ACCEPT_OP_H_ +#define SSF_LAYER_ACCEPT_OP_H_ + +#include + +#include + +#include + +#include +#include +#include + +namespace ssf { +namespace layer { +namespace detail { + +template +class AcceptOp { + public: + AcceptOp(Acceptor& acceptor, PeerImpl* p_peer_impl, Endpoint* p_peer_endpoint, + AcceptHandler handler) + : coro_(), + acceptor_(acceptor), + p_peer_impl_(p_peer_impl), + p_peer_endpoint_(p_peer_endpoint), + handler_(std::move(handler)) {} + + AcceptOp(const AcceptOp& other) + : coro_(other.coro_), + acceptor_(other.acceptor_), + p_peer_impl_(other.p_peer_impl_), + p_peer_endpoint_(other.p_peer_endpoint_), + handler_(other.handler_) {} + + AcceptOp(AcceptOp&& other) + : coro_(std::move(other.coro_)), + acceptor_(other.acceptor_), + p_peer_impl_(other.p_peer_impl_), + p_peer_endpoint_(other.p_peer_endpoint_), + handler_(std::move(other.handler_)) {} + +#include + void operator()( + const boost::system::error_code& ec = boost::system::error_code()) { + if (!ec) { + reenter(coro_) { + yield acceptor_.async_accept( + *p_peer_impl_->p_next_layer_socket, + p_peer_impl_->p_remote_endpoint->next_layer_endpoint(), + std::move(*this)); + + p_peer_impl_->p_remote_endpoint->set(); + + auto& local_endpoint = *p_peer_impl_->p_local_endpoint; + local_endpoint = + Endpoint(local_endpoint.endpoint_context(), + p_peer_impl_->p_next_layer_socket->local_endpoint()); + + if (p_peer_endpoint_) { + *p_peer_endpoint_ = *p_peer_impl_->p_remote_endpoint; + } + + handler_(ec); + } + } else { + // error + handler_(ec); + } + } +#include + + inline AcceptHandler& handler() { return handler_; } + + private: + boost::asio::coroutine coro_; + Acceptor& acceptor_; + PeerImpl* p_peer_impl_; + Endpoint* p_peer_endpoint_; + AcceptHandler handler_; +}; + +template +inline void* asio_handler_allocate( + std::size_t size, AcceptOp* this_handler) { + return boost_asio_handler_alloc_helpers::allocate(size, + this_handler->handler()); +} + +template +inline void asio_handler_deallocate( + void* pointer, std::size_t size, + AcceptOp* + this_handler) { + boost_asio_handler_alloc_helpers::deallocate(pointer, size, + this_handler->handler()); +} + +template +inline bool asio_handler_is_continuation(AcceptOp< + Protocol, Acceptor, PeerImpl, Endpoint, AcceptHandler>* this_handler) { + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->handler()); +} + +template +inline void asio_handler_invoke(Function& function, + AcceptOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +template +inline void asio_handler_invoke(const Function& function, + AcceptOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +} // detail +} // layer +} // ssf + +#endif // SSF_LAYER_ACCEPT_OP_H_ diff --git a/src/framework/ssf/layer/basic_empty_datagram.h b/src/framework/ssf/layer/basic_empty_datagram.h new file mode 100644 index 00000000..af54d2b6 --- /dev/null +++ b/src/framework/ssf/layer/basic_empty_datagram.h @@ -0,0 +1,318 @@ +#ifndef SSF_LAYER_BASIC_EMPTY_DATAGRAM_H_ +#define SSF_LAYER_BASIC_EMPTY_DATAGRAM_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "ssf/error/error.h" +#include "ssf/io/composed_op.h" + +#include "ssf/layer/basic_impl.h" +#include "ssf/layer/basic_resolver.h" +#include "ssf/layer/basic_endpoint.h" + +#include "ssf/layer/protocol_attributes.h" +#include "ssf/layer/parameters.h" + +#include "ssf/layer/connect_op.h" +#include "ssf/layer/receive_from_op.h" + +namespace ssf { +namespace layer { + +template +class VirtualEmptyDatagramSocket_service; + +template +class VirtualEmptyDatagramProtocol { + public: + enum { + id = 3, + overhead = 0, + facilities = ssf::layer::facilities::datagram, + mtu = NextLayer::mtu - overhead + }; + enum { endpoint_stack_size = NextLayer::endpoint_stack_size }; + + typedef NextLayer next_layer_protocol; + typedef int socket_context; + typedef int endpoint_context_type; + using next_endpoint_type = typename next_layer_protocol::endpoint; + + typedef basic_VirtualLink_endpoint endpoint; + typedef basic_VirtualLink_resolver resolver; + typedef boost::asio::basic_datagram_socket< + VirtualEmptyDatagramProtocol, + VirtualEmptyDatagramSocket_service> socket; + + private: + using query = typename resolver::query; + + public: + static endpoint make_endpoint(boost::asio::io_service& io_service, + typename query::const_iterator parameters_it, + uint32_t, boost::system::error_code& ec) { + return endpoint( + next_layer_protocol::make_endpoint(io_service, parameters_it, id, ec)); + } + + static std::string get_address(const endpoint& endpoint) { + return endpoint.next_layer_endpoint().address().to_string(); + } +}; + +#include + +template +class VirtualEmptyDatagramSocket_service + : public boost::asio::detail::service_base< + VirtualEmptyDatagramSocket_service> { + public: + /// The protocol type. + typedef Protocol protocol_type; + /// The endpoint type. + typedef typename protocol_type::endpoint endpoint_type; + + typedef basic_socket_impl implementation_type; + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + private: + typedef typename protocol_type::next_layer_protocol::socket next_socket_type; + + public: + explicit VirtualEmptyDatagramSocket_service( + boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service) {} + + virtual ~VirtualEmptyDatagramSocket_service() {} + + void construct(implementation_type& impl) { + impl.p_next_layer_socket = + std::make_shared(this->get_io_service()); + } + + void destroy(implementation_type& impl) { + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_socket.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->open( + typename protocol_type::next_layer_protocol(), ec); + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { + return impl.p_next_layer_socket->is_open(); + } + + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_remote_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_remote_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_local_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->close(ec); + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + // boost::system::error_code cancel(implementation_type& impl, + // boost::system::error_code& ec) { + // return impl.p_next_layer_socket->cancel(ec); + //} + + bool at_mark(const implementation_type& impl, + boost::system::error_code& ec) const { + return impl.p_next_layer_socket->at_mark(ec); + } + + std::size_t available(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return 0; + } + + return impl.p_next_layer_socket->available(ec); + } + + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) { + impl.p_local_endpoint = std::make_shared(endpoint); + return impl.p_next_layer_socket->bind(endpoint.next_layer_endpoint(), ec); + } + + boost::system::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + boost::system::error_code& ec) { + impl.p_remote_endpoint = std::make_shared(peer_endpoint); + + impl.p_next_layer_socket->connect(peer_endpoint.next_layer_endpoint(), ec); + + if (!ec) { + impl.p_local_endpoint = std::make_shared( + impl.p_next_layer_socket->local_endpoint(ec)); + } + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + ConnectHandler&& handler) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + impl.p_remote_endpoint = std::make_shared(peer_endpoint); + impl.p_local_endpoint = std::make_shared(0); + + detail::AsyncConnectOp< + protocol_type, next_socket_type, endpoint_type, + typename boost::asio::handler_type< + ConnectHandler, void(boost::system::error_code)>::type> ( + *impl.p_next_layer_socket, impl.p_local_endpoint.get(), peer_endpoint, + init.handler)(); + + return init.result.get(); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + return impl.p_next_layer_socket->async_send( + buffers, std::forward(handler)); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + return impl.p_next_layer_socket->async_send_to( + buffers, destination.next_layer_endpoint(), + std::forward(handler)); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + return impl.p_next_layer_socket->async_receive( + buffers, std::forward(handler)); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + detail::ReceiveFromOp< + protocol_type, next_socket_type, endpoint_type, MutableBufferSequence, + typename boost::asio::handler_type< + ReadHandler, void(boost::system::error_code)>::type> ( + *impl.p_next_layer_socket, &sender_endpoint, buffers, flags, + init.handler)(); + + return init.result.get(); + } + + boost::system::error_code shutdown( + implementation_type& impl, boost::asio::socket_base::shutdown_type what, + boost::system::error_code& ec) { + impl.p_next_layer_socket->shutdown(what, ec); + + return ec; + } + + private: + void shutdown_service() {} +}; + +#include + +template +using VirtualEmptyDatagram_endpoint = + typename VirtualEmptyDatagramProtocol::endpoint; +template +using VirtualEmptyDatagram_socket = + typename VirtualEmptyDatagramProtocol::socket; +template +using VirtualEmptyDatagram_resolver = + typename VirtualEmptyDatagramProtocol::resolver; + +} // layer +} // ssf + +#endif // SSF_LAYER_BASIC_EMPTY_DATAGRAM_H_ diff --git a/src/framework/ssf/layer/basic_empty_stream.h b/src/framework/ssf/layer/basic_empty_stream.h new file mode 100644 index 00000000..55417268 --- /dev/null +++ b/src/framework/ssf/layer/basic_empty_stream.h @@ -0,0 +1,491 @@ +#ifndef SSF_LAYER_BASIC_EMPTY_STREAM_H_ +#define SSF_LAYER_BASIC_EMPTY_STREAM_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ssf/error/error.h" + +#include "ssf/layer/basic_impl.h" +#include "ssf/layer/basic_resolver.h" +#include "ssf/layer/basic_endpoint.h" + +#include "ssf/layer/accept_op.h" +#include "ssf/layer/connect_op.h" +#include "ssf/layer/parameters.h" +#include "ssf/layer/protocol_attributes.h" + +namespace ssf { +namespace layer { + +template +class VirtualEmptyStreamSocket_service; +template +class VirtualEmptyStreamAcceptor_service; + +template +class VirtualEmptyStreamProtocol { + public: + enum { + id = 3, + overhead = 0, + facilities = ssf::layer::facilities::stream, + mtu = NextLayer::mtu - overhead + }; + enum { endpoint_stack_size = NextLayer::endpoint_stack_size }; + + typedef NextLayer next_layer_protocol; + typedef int socket_context; + typedef int acceptor_context; + typedef int endpoint_context_type; + using next_endpoint_type = typename next_layer_protocol::endpoint; + + typedef basic_VirtualLink_endpoint endpoint; + typedef basic_VirtualLink_resolver resolver; + typedef boost::asio::basic_stream_socket< + VirtualEmptyStreamProtocol, + VirtualEmptyStreamSocket_service> socket; + typedef boost::asio::basic_socket_acceptor< + VirtualEmptyStreamProtocol, + VirtualEmptyStreamAcceptor_service> acceptor; + + private: + using query = typename resolver::query; + + public: + static std::string get_name() { return next_layer_protocol::get_name(); } + + static endpoint make_endpoint(boost::asio::io_service& io_service, + typename query::const_iterator parameters_it, + uint32_t, boost::system::error_code& ec) { + return endpoint( + next_layer_protocol::make_endpoint(io_service, parameters_it, id, ec)); + } + + static std::string get_address(const endpoint& endpoint) { + return endpoint.next_layer_endpoint().address().to_string(); + } + + static unsigned short get_port(const endpoint& endpoint) { + return endpoint.next_layer_endpoint().port(); + } + + static void add_params_from_property_tree( + query* p_query, const boost::property_tree::ptree& property_tree, + bool connect, boost::system::error_code& ec) { + next_layer_protocol::add_params_from_property_tree(p_query, property_tree, + connect, ec); + } +}; + +#include + +template +class VirtualEmptyStreamSocket_service + : public boost::asio::detail::service_base< + VirtualEmptyStreamSocket_service> { + public: + /// The protocol type. + typedef Protocol protocol_type; + /// The endpoint type. + typedef typename protocol_type::endpoint endpoint_type; + typedef typename protocol_type::endpoint_context_type endpoint_context_type; + + typedef basic_socket_impl implementation_type; + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + private: + typedef typename protocol_type::next_layer_protocol::socket next_socket_type; + + public: + explicit VirtualEmptyStreamSocket_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service) {} + + virtual ~VirtualEmptyStreamSocket_service() {} + + void construct(implementation_type& impl) { + impl.p_next_layer_socket = + std::make_shared(this->get_io_service()); + } + + void destroy(implementation_type& impl) { + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_socket.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->open( + typename protocol_type::next_layer_protocol(), ec); + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { + return impl.p_next_layer_socket->is_open(); + } + + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_remote_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_remote_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_local_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->close(ec); + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->cancel(ec); + } + + bool at_mark(const implementation_type& impl, + boost::system::error_code& ec) const { + return impl.p_next_layer_socket->at_mark(ec); + } + + std::size_t available(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return 0; + } + + return impl.p_next_layer_socket->available(ec); + } + + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) { + impl.p_local_endpoint = std::make_shared(endpoint); + return impl.p_next_layer_socket->bind(endpoint.next_layer_endpoint(), ec); + } + + boost::system::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + boost::system::error_code& ec) { + impl.p_remote_endpoint = std::make_shared(peer_endpoint); + + impl.p_next_layer_socket->connect(peer_endpoint.next_layer_endpoint(), ec); + + if (!ec) { + impl.p_local_endpoint = std::make_shared( + impl.p_next_layer_socket->local_endpoint(ec)); + } + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + ConnectHandler&& handler) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + impl.p_remote_endpoint = std::make_shared(peer_endpoint); + impl.p_local_endpoint = std::make_shared(0); + + detail::AsyncConnectOp< + protocol_type, next_socket_type, endpoint_type, + typename boost::asio::handler_type< + ConnectHandler, void(boost::system::error_code)>::type> ( + *impl.p_next_layer_socket, impl.p_local_endpoint.get(), peer_endpoint, + init.handler)(); + + return init.result.get(); + } + + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->send(buffers, flags, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + return impl.p_next_layer_socket->async_send( + buffers, std::forward(handler)); + } + + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->receive(buffers, flags, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + return impl.p_next_layer_socket->async_receive( + buffers, std::forward(handler)); + } + + boost::system::error_code shutdown( + implementation_type& impl, boost::asio::socket_base::shutdown_type what, + boost::system::error_code& ec) { + impl.p_next_layer_socket->shutdown(what, ec); + + return ec; + } + + private: + void shutdown_service() {} +}; + +template +class VirtualEmptyStreamAcceptor_service + : public boost::asio::detail::service_base< + VirtualEmptyStreamAcceptor_service> { + public: + /// The protocol type. + typedef Protocol protocol_type; + /// The endpoint type. + typedef typename protocol_type::endpoint endpoint_type; + + typedef basic_acceptor_impl implementation_type; + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + private: + typedef + typename protocol_type::next_layer_protocol::acceptor next_acceptor_type; + + public: + explicit VirtualEmptyStreamAcceptor_service( + boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service) {} + + virtual ~VirtualEmptyStreamAcceptor_service() {} + + void construct(implementation_type& impl) { + impl.p_next_layer_acceptor = + std::make_shared(this->get_io_service()); + } + + void destroy(implementation_type& impl) { + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_acceptor.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->open( + typename protocol_type::next_layer_protocol(), ec); + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { + return impl.p_next_layer_acceptor->is_open(); + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_local_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->close(ec); + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + /// Set a socket option. + template + boost::system::error_code set_option(implementation_type& impl, + const SettableSocketOption& option, + boost::system::error_code& ec) { + if (impl.p_next_layer_acceptor) { + return impl.p_next_layer_acceptor->set_option(option, ec); + } + return ec; + } + + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) { + impl.p_local_endpoint = std::make_shared(endpoint); + return impl.p_next_layer_acceptor->bind(endpoint.next_layer_endpoint(), ec); + } + + boost::system::error_code listen(implementation_type& impl, int backlog, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->listen(backlog, ec); + } + + template + boost::system::error_code accept( + implementation_type& impl, + boost::asio::basic_socket& peer, + endpoint_type* p_peer_endpoint, boost::system::error_code& ec, + typename std::enable_if::value>::type* = 0) { + auto& peer_impl = peer.native_handle(); + peer_impl.p_remote_endpoint = + std::make_shared(); + + impl.p_next_layer_acceptor->accept( + *peer.native_handle().p_next_layer_socket, + peer_impl.p_remote_endpoint->next_layer_endpoint(), ec); + + if (!ec) { + peer_impl.p_local_endpoint = impl.p_local_endpoint; + + // Add current layer endpoint context here (if necessary) + peer_impl.p_remote_endpoint->set(); + + if (p_peer_endpoint) { + *p_peer_endpoint = *(peer_impl.p_remote_endpoint); + } + } + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, void(boost::system::error_code)) + async_accept(implementation_type& impl, + boost::asio::basic_socket& peer, + endpoint_type* p_peer_endpoint, AcceptHandler&& handler, + typename std::enable_if::value>::type* = 0) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + auto& peer_impl = peer.native_handle(); + peer_impl.p_local_endpoint = + std::make_shared(); + peer_impl.p_remote_endpoint = + std::make_shared(); + + detail::AcceptOp< + protocol_type, next_acceptor_type, + typename std::remove_reference::native_handle_type>::type, + endpoint_type, + typename boost::asio::handler_type< + AcceptHandler, void(boost::system::error_code)>::type> ( + *impl.p_next_layer_acceptor, &peer_impl, p_peer_endpoint, + init.handler)(); + + return init.result.get(); + } + + private: + void shutdown_service() {} +}; + +#include + +template +using VirtualEmptyStream_endpoint = + typename VirtualEmptyStreamProtocol::endpoint; +template +using VirtualEmptyStream_socket = + typename VirtualEmptyStreamProtocol::socket; +template +using VirtualEmptyStream_acceptor = + typename VirtualEmptyStreamProtocol::acceptor; +template +using VirtualEmptyStream_resolver = + typename VirtualEmptyStreamProtocol::resolver; + +} // layer +} // ssf + +#endif // SSF_LAYER_BASIC_EMPTY_STREAM_H_ diff --git a/src/framework/ssf/layer/basic_endpoint.h b/src/framework/ssf/layer/basic_endpoint.h new file mode 100644 index 00000000..b61ee548 --- /dev/null +++ b/src/framework/ssf/layer/basic_endpoint.h @@ -0,0 +1,121 @@ +#ifndef SSF_LAYER_BASIC_ENDPOINT_H_ +#define SSF_LAYER_BASIC_ENDPOINT_H_ + +namespace ssf { +namespace layer { + +template +class basic_VirtualLink_endpoint { + public: + typedef Protocol protocol_type; + + typedef typename protocol_type::endpoint_context_type internal_context_type; + using next_layer_endpoint_type = typename protocol_type::next_endpoint_type; + + public: + class basic_address { + public: + basic_address(const std::string& address) : address_(address) {} + std::string to_string() const { return address_; } + + private: + std::string address_; + }; + + public: + template + basic_VirtualLink_endpoint(const Args&... args) + : set_(!!sizeof...(Args)), + internal_context_(), + next_layer_endpoint_(args...) {} + + template + basic_VirtualLink_endpoint(const internal_context_type& context, + Args&&... args) + : set_(!!sizeof...(Args)), + internal_context_(context), + next_layer_endpoint_(std::forward(args)...) {} + + basic_VirtualLink_endpoint(const basic_VirtualLink_endpoint& other) + : set_(other.set_), + internal_context_(other.internal_context_), + next_layer_endpoint_(other.next_layer_endpoint_) {} + + basic_VirtualLink_endpoint& operator=( + const basic_VirtualLink_endpoint& endpoint) { + set_ = endpoint.set_; + internal_context_ = endpoint.internal_context_; + next_layer_endpoint_ = endpoint.next_layer_endpoint_; + + return *this; + } + + bool operator==(const basic_VirtualLink_endpoint& rhs) const { + if (set_ != rhs.set_) { + return false; + } + + if (!set_) { + return true; + } + + return (internal_context_ == rhs.internal_context_) && + (next_layer_endpoint_ == rhs.next_layer_endpoint_); + } + + bool operator!=(const basic_VirtualLink_endpoint& rhs) const { + return !(rhs == *this); + } + + bool operator<(const basic_VirtualLink_endpoint& rhs) const { + if (set_ != rhs.set_) { + return !set_; + } + + if (!set_) { + return false; + } + + if (internal_context_ != rhs.internal_context_) { + return internal_context_ < rhs.internal_context_; + } + + return next_layer_endpoint_ < rhs.next_layer_endpoint_; + } + + bool is_set() const { return set_; } + + void set() { set_ = true; } + + internal_context_type& endpoint_context() { return internal_context_; } + + const internal_context_type& endpoint_context() const { + return internal_context_; + } + + next_layer_endpoint_type& next_layer_endpoint() { return next_layer_endpoint_; } + + const next_layer_endpoint_type& next_layer_endpoint() const { + return next_layer_endpoint_; + } + + protocol_type protocol() const { return protocol_type(); } + + basic_address address() const { + return basic_address(protocol_type::get_address(*this)); + } + + unsigned short port() const { + return protocol_type::get_port(*this); + } + + private: + bool set_; + internal_context_type internal_context_; + next_layer_endpoint_type next_layer_endpoint_; +}; + +} // layer +} // ssf + +#endif // SSF_LAYER_BASIC_ENDPOINT_H_ diff --git a/src/framework/ssf/layer/basic_impl.h b/src/framework/ssf/layer/basic_impl.h new file mode 100644 index 00000000..f0a91ec2 --- /dev/null +++ b/src/framework/ssf/layer/basic_impl.h @@ -0,0 +1,90 @@ +#ifndef SSF_LAYER_BASIC_IMPL_H_ +#define SSF_LAYER_BASIC_IMPL_H_ + +#include + +namespace ssf { +namespace layer { + +template +struct basic_socket_impl_ex { + private: + typedef std::shared_ptr socket_context; + typedef std::shared_ptr endpoint_type; + typedef std::shared_ptr next_layer_socket_type; + + public: + basic_socket_impl_ex() + : p_socket_context(nullptr), + p_local_endpoint(nullptr), + p_remote_endpoint(nullptr), + p_next_layer_socket(nullptr) {} + + basic_socket_impl_ex(basic_socket_impl_ex&& other) + : p_socket_context(std::move(other.p_socket_context)), + p_local_endpoint(std::move(other.p_local_endpoint)), + p_remote_endpoint(std::move(other.p_remote_endpoint)), + p_next_layer_socket(std::move(other.p_next_layer_socket)) {} + + basic_socket_impl_ex& operator=(basic_socket_impl_ex&& other) { + p_socket_context = std::move(other.p_socket_context); + p_local_endpoint = std::move(other.p_local_endpoint); + p_remote_endpoint = std::move(other.p_remote_endpoint); + p_next_layer_socket = std::move(other.p_next_layer_socket); + return *this; + } + + socket_context p_socket_context; + endpoint_type p_local_endpoint; + endpoint_type p_remote_endpoint; + next_layer_socket_type p_next_layer_socket; +}; + +template +using basic_socket_impl = basic_socket_impl_ex< + typename Protocol::socket_context, typename Protocol::endpoint, + typename Protocol::next_layer_protocol::socket>; + +template +struct basic_acceptor_impl_ex { + private: + typedef std::shared_ptr acceptor_context; + typedef std::shared_ptr endpoint_type; + typedef std::shared_ptr next_layer_acceptor_type; + + public: + basic_acceptor_impl_ex() + : p_acceptor_context(nullptr), + p_local_endpoint(nullptr), + p_remote_endpoint(nullptr), + p_next_layer_acceptor(nullptr) {} + + basic_acceptor_impl_ex(basic_acceptor_impl_ex&& other) + : p_acceptor_context(std::move(other.p_acceptor_context)), + p_local_endpoint(std::move(other.p_local_endpoint)), + p_remote_endpoint(std::move(other.p_remote_endpoint)), + p_next_layer_acceptor(std::move(other.p_next_layer_acceptor)) {} + + basic_acceptor_impl_ex& operator=(basic_acceptor_impl_ex&& other) { + p_acceptor_context = std::move(other.p_acceptor_context); + p_local_endpoint = std::move(other.p_local_endpoint); + p_remote_endpoint = std::move(other.p_remote_endpoint); + p_next_layer_acceptor = std::move(other.p_next_layer_acceptor); + return *this; + } + + acceptor_context p_acceptor_context; + endpoint_type p_local_endpoint; + endpoint_type p_remote_endpoint; + next_layer_acceptor_type p_next_layer_acceptor; +}; + +template +using basic_acceptor_impl = basic_acceptor_impl_ex< + typename Protocol::acceptor_context, typename Protocol::endpoint, + typename Protocol::next_layer_protocol::acceptor>; + +} // layer +} // ssf + +#endif // SSF_LAYER_BASIC_IMPL_H_ diff --git a/src/framework/ssf/layer/basic_resolver.h b/src/framework/ssf/layer/basic_resolver.h new file mode 100644 index 00000000..356fa0af --- /dev/null +++ b/src/framework/ssf/layer/basic_resolver.h @@ -0,0 +1,88 @@ +#ifndef SSF_LAYER_BASIC_RESOLVER_H_ +#define SSF_LAYER_BASIC_RESOLVER_H_ + +#include + +#include + +#include + +#include "ssf/error/error.h" + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { + +template +class basic_VirtualLink_resolver { + private: + class EndpointIterator { + public: + EndpointIterator() : endpoints_(1), index_(0) {} + EndpointIterator(std::vector endpoints) + : endpoints_(endpoints), index_(0) {} + + typename Protocol::endpoint& operator*() { return endpoints_[index_]; } + typename Protocol::endpoint* operator->() { return &endpoints_[index_]; } + + typename Protocol::endpoint& operator++() { + ++index_; + return endpoints_[index_]; + } + + typename Protocol::endpoint operator++(int) { + ++index_; + return endpoints_[index_ - 1]; + } + + typename Protocol::endpoint& operator--() { + --index_; + return endpoints_[index_]; + } + + typename Protocol::endpoint operator--(int) { + --index_; + return endpoints_[index_ + 1]; + } + + private: + std::vector endpoints_; + std::size_t index_; + }; + + public: + typedef typename Protocol::endpoint endpoint_type; + typedef EndpointIterator iterator; + using query = ParameterStack; + + public: + basic_VirtualLink_resolver(boost::asio::io_service& io_service) + : io_service_(io_service) {} + + iterator resolve(const query& parameters_list, + boost::system::error_code& ec) { + if (parameters_list.size() < Protocol::endpoint_stack_size) { + ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + return iterator(); + } + + std::vector result; + result.emplace_back(Protocol::make_endpoint( + io_service_, std::begin(parameters_list), 0, ec)); + + if (ec) { + return iterator(); + } + + return iterator(result); + } + + private: + boost::asio::io_service& io_service_; +}; + +} // layer +} // ssf + +#endif // SSF_LAYER_BASIC_RESOLVER_H_ diff --git a/src/framework/ssf/layer/congestion/drop_tail_policy.h b/src/framework/ssf/layer/congestion/drop_tail_policy.h new file mode 100644 index 00000000..a9d1d06b --- /dev/null +++ b/src/framework/ssf/layer/congestion/drop_tail_policy.h @@ -0,0 +1,26 @@ +#ifndef SSF_LAYER_CONGESTION_DROP_TAIL_POLICY_H_ +#define SSF_LAYER_CONGESTION_DROP_TAIL_POLICY_H_ + +namespace ssf { +namespace layer { +namespace congestion { + +template +class DropTailPolicy { + public: + template + bool IsAddable(const Queue& queue, const Packet& packet) { + return queue.size() < MaxSize; + } + + template + bool IsAddable(const Queue& queue) { + return queue.size() < MaxSize; + } +}; + +} // congestion +} // layer +} // ssf + +#endif // SSF_LAYER_CONGESTION_DROP_TAIL_POLICY_H_ diff --git a/src/framework/ssf/layer/connect_op.h b/src/framework/ssf/layer/connect_op.h new file mode 100644 index 00000000..9da2b943 --- /dev/null +++ b/src/framework/ssf/layer/connect_op.h @@ -0,0 +1,141 @@ +#ifndef SSF_LAYER_CONNECT_OP_H_ +#define SSF_LAYER_CONNECT_OP_H_ + +#include + +#include + +#include + +#include +#include +#include + +namespace ssf { +namespace layer { +namespace detail { + +template +class ConnectOp { + public: + ConnectOp(Stream& stream, Endpoint* p_local_endpoint, Endpoint peer_endpoint) + : stream_(stream), + p_local_endpoint_(p_local_endpoint), + peer_endpoint_(std::move(peer_endpoint)) {} + + void operator()(boost::system::error_code& ec) { + stream_.connect(peer_endpoint_.next_layer_endpoint(), ec); + if (!ec) { + auto& next_layer_endpoint = p_local_endpoint_->next_layer_endpoint(); + next_layer_endpoint = stream_.local_endpoint(ec); + p_local_endpoint_->set(); + } + } + + private: + Stream& stream_; + Endpoint* p_local_endpoint_; + Endpoint peer_endpoint_; +}; + +template +class AsyncConnectOp { + public: + AsyncConnectOp(Stream& stream, Endpoint* p_local_endpoint, + Endpoint peer_endpoint, ConnectHandler handler) + : coro_(), + stream_(stream), + p_local_endpoint_(p_local_endpoint), + peer_endpoint_(std::move(peer_endpoint)), + handler_(std::move(handler)) {} + + AsyncConnectOp(const AsyncConnectOp& other) + : coro_(other.coro_), + stream_(other.stream_), + p_local_endpoint_(other.p_local_endpoint_), + peer_endpoint_(other.peer_endpoint_), + handler_(other.handler_) {} + + AsyncConnectOp(AsyncConnectOp&& other) + : coro_(std::move(other.coro_)), + stream_(other.stream_), + p_local_endpoint_(other.p_local_endpoint_), + peer_endpoint_(std::move(other.peer_endpoint_)), + handler_(std::move(other.handler_)) {} + +#include + void operator()( + const boost::system::error_code& ec = boost::system::error_code()) { + if (!ec) { + reenter(coro_) { + yield stream_.async_connect(peer_endpoint_.next_layer_endpoint(), + std::move(*this)); + + boost::system::error_code endpoint_ec; + auto& next_layer_endpoint = p_local_endpoint_->next_layer_endpoint(); + next_layer_endpoint = stream_.local_endpoint(endpoint_ec); + p_local_endpoint_->set(); + + handler_(ec); + } + } else { + // error + handler_(ec); + } + } +#include + + inline ConnectHandler& handler() { return handler_; } + + private: + boost::asio::coroutine coro_; + Stream& stream_; + Endpoint* p_local_endpoint_; + Endpoint peer_endpoint_; + ConnectHandler handler_; +}; + +template +inline void* asio_handler_allocate( + std::size_t size, + AsyncConnectOp* this_handler) { + return boost_asio_handler_alloc_helpers::allocate(size, + this_handler->handler()); +} + +template +inline void asio_handler_deallocate( + void* pointer, std::size_t size, + AsyncConnectOp* this_handler) { + boost_asio_handler_alloc_helpers::deallocate(pointer, size, + this_handler->handler()); +} + +template +inline bool asio_handler_is_continuation( + AsyncConnectOp* this_handler) { + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->handler()); +} + +template +inline void asio_handler_invoke( + Function& function, + AsyncConnectOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +template +inline void asio_handler_invoke( + const Function& function, + AsyncConnectOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +} // detail +} // layer +} // ssf + +#endif // SSF_LAYER_CONNECT_OP_H_ diff --git a/src/framework/ssf/layer/cryptography/basic_crypto_stream.h b/src/framework/ssf/layer/cryptography/basic_crypto_stream.h new file mode 100644 index 00000000..c7a79f21 --- /dev/null +++ b/src/framework/ssf/layer/cryptography/basic_crypto_stream.h @@ -0,0 +1,591 @@ +#ifndef SSF_LAYER_CRYPTOGRAPHY_BASIC_CRYPTO_STREAM_H_ +#define SSF_LAYER_CRYPTOGRAPHY_BASIC_CRYPTO_STREAM_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "ssf/io/handler_helpers.h" +#include "ssf/error/error.h" + +#include "ssf/layer/basic_endpoint.h" +#include "ssf/layer/basic_impl.h" +#include "ssf/layer/basic_resolver.h" + +#include "ssf/layer/cryptography/crypto_stream_op.h" + +#include "ssf/layer/parameters.h" +#include "ssf/layer/protocol_attributes.h" + +namespace ssf { +namespace layer { +namespace cryptography { + +template +class basic_CryptoStreamSocket_service; +template +class basic_CryptoStreamAcceptor_service; + +template class Crypto> +class basic_CryptoStreamProtocol { + public: + using CryptoProtocol = Crypto; + + enum { + id = CryptoProtocol::id, + overhead = CryptoProtocol::overhead, + facilities = ssf::layer::facilities::stream, + mtu = CryptoProtocol::mtu + }; + enum { endpoint_stack_size = CryptoProtocol::endpoint_stack_size }; + + typedef NextLayer next_layer_protocol; + typedef char socket_context; + typedef char acceptor_context; + using endpoint_context_type = typename CryptoProtocol::endpoint_context_type; + using next_endpoint_type = typename next_layer_protocol::endpoint; + + typedef basic_VirtualLink_endpoint endpoint; + typedef basic_VirtualLink_resolver resolver; + typedef boost::asio::basic_stream_socket< + basic_CryptoStreamProtocol, + basic_CryptoStreamSocket_service> socket; + typedef boost::asio::basic_socket_acceptor< + basic_CryptoStreamProtocol, + basic_CryptoStreamAcceptor_service> acceptor; + + private: + using query = typename resolver::query; + + public: + static std::string get_name() { + std::string name = + CryptoProtocol::get_name() + "_" + next_layer_protocol::get_name(); + return name; + } + + static endpoint make_endpoint(boost::asio::io_service& io_service, + typename query::const_iterator parameters_it, + uint32_t, boost::system::error_code& ec) { + auto endpoint_context = CryptoProtocol::make_endpoint_context( + io_service, parameters_it, id, ec); + if (ec) { + return endpoint(); + } + + auto next_endpoint = + next_layer_protocol::make_endpoint(io_service, ++parameters_it, id, ec); + + return endpoint(std::move(endpoint_context), std::move(next_endpoint)); + } + + static void add_params_from_property_tree( + query* p_query, const boost::property_tree::ptree& property_tree, + bool connect, boost::system::error_code& ec) { + auto sublayer = property_tree.get_child_optional("sublayer"); + if (!sublayer) { + ec.assign(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()); + return; + } + + CryptoProtocol::add_params_from_property_tree(p_query, property_tree, + connect, ec); + next_layer_protocol::add_params_from_property_tree(p_query, *sublayer, + connect, ec); + } +}; + +#include + +template +class basic_CryptoStreamSocket_service + : public boost::asio::detail::service_base< + basic_CryptoStreamSocket_service> { + public: + using protocol_type = Protocol; + + using crypto_stream_type = typename CryptoProtocol::Stream; + + using socket_context = typename protocol_type::socket_context; + using endpoint_type = typename protocol_type::endpoint; + + using implementation_type = + basic_socket_impl_ex; + + using native_handle_type = implementation_type&; + using native_type = native_handle_type; + + public: + explicit basic_CryptoStreamSocket_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service) {} + + virtual ~basic_CryptoStreamSocket_service() {} + + void construct(implementation_type& impl) {} + + void destroy(implementation_type& impl) { + impl.p_socket_context.reset(); + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_socket.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return ec; + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { + if (!impl.p_next_layer_socket) { + return false; + } + + return impl.p_next_layer_socket->next_layer().is_open(); + } + + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_remote_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_remote_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_local_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return ec; + } + + // impl.p_next_layer_socket->shutdown(ec); + return impl.p_next_layer_socket->next_layer().close(ec); + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + return ec; + } + + return impl.p_next_layer_socket->next_layer().cancel(ec); + } + + bool at_mark(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + return false; + } + + return impl.p_next_layer_socket->next_layer().at_mark(ec); + } + + std::size_t available(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return 0; + } + + return impl.p_next_layer_socket->next_layer().available(ec); + } + + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) { + impl.p_next_layer_socket = std::make_shared( + this->get_io_service(), endpoint.endpoint_context()); + + impl.p_local_endpoint = std::make_shared(endpoint); + + auto result = impl.p_next_layer_socket->next_layer().open( + protocol_type::next_layer_protocol(), ec); + + if (!ec) { + result = impl.p_next_layer_socket->next_layer().bind( + endpoint.next_layer_endpoint(), ec); + } + + return result; + } + + /// Connect the layer behind the ssl stream + /// Then handshake + boost::system::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + boost::system::error_code& ec) { + impl.p_next_layer_socket = std::make_shared( + this->get_io_service(), peer_endpoint.endpoint_context()); + + impl.p_remote_endpoint = std::make_shared(peer_endpoint); + + impl.p_next_layer_socket->next_layer().connect( + peer_endpoint.next_layer_endpoint(), ec); + + if (!ec) { + impl.p_local_endpoint = std::make_shared( + peer_endpoint.endpoint_context(), + impl.p_next_layer_socket->next_layer().local_endpoint(ec)); + } + + if (!ec) { + impl.p_next_layer_socket->handshake( + CryptoProtocol::handshake_type::client, ec); + } + + return ec; + } + + /// Async connect the layer behind the ssl stream + /// Then async handshake, then async execute handler + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + ConnectHandler&& handler) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + impl.p_next_layer_socket = std::make_shared( + this->get_io_service(), peer_endpoint.endpoint_context()); + + impl.p_remote_endpoint = std::make_shared(peer_endpoint); + impl.p_local_endpoint = + std::make_shared(peer_endpoint.endpoint_context()); + + detail::CryptoStreamConnectOp< + CryptoProtocol, crypto_stream_type, endpoint_type, + decltype(init.handler)> ( + *impl.p_next_layer_socket, impl.p_local_endpoint.get(), peer_endpoint, + init.handler)(); + + return init.result.get(); + } + + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return 0; + } + + return impl.p_next_layer_socket->write_some(buffers, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_next_layer_socket) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::broken_pipe, + ssf::error::get_ssf_category()), + 0); + return init.result.get(); + } + + impl.p_next_layer_socket->async_write_some(buffers, init.handler); + + return init.result.get(); + } + + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return 0; + } + + return impl.p_next_layer_socket->read_some(buffers, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_next_layer_socket) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::broken_pipe, + ssf::error::get_ssf_category()), + 0); + return init.result.get(); + } + + impl.p_next_layer_socket->async_read_some(buffers, init.handler); + + return init.result.get(); + } + + boost::system::error_code shutdown( + implementation_type& impl, boost::asio::socket_base::shutdown_type what, + boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return ec; + } + + return impl.p_next_layer_socket->next_layer().shutdown(what, ec); + } + + private: + void shutdown_service() {} +}; + +template +class basic_CryptoStreamAcceptor_service + : public boost::asio::detail::service_base< + basic_CryptoStreamAcceptor_service> { + public: + using protocol_type = Protocol; + + using endpoint_type = typename protocol_type::endpoint; + + using implementation_type = basic_acceptor_impl; + using native_handle_type = implementation_type&; + using native_type = native_handle_type; + + private: + using next_acceptor_type = + typename protocol_type::next_layer_protocol::acceptor; + using crypto_stream_type = typename CryptoProtocol::Stream; + + public: + explicit basic_CryptoStreamAcceptor_service( + boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service) {} + + virtual ~basic_CryptoStreamAcceptor_service() {} + + void construct(implementation_type& impl) { + impl.p_next_layer_acceptor = + std::make_shared(this->get_io_service()); + } + + void destroy(implementation_type& impl) { + impl.p_acceptor_context.reset(); + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_acceptor.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->open( + typename protocol_type::next_layer_protocol(), ec); + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { + return impl.p_next_layer_acceptor->is_open(); + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_local_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->close(ec); + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + /// Set a socket option. + template + boost::system::error_code set_option(implementation_type& impl, + const SettableSocketOption& option, boost::system::error_code& ec) + { + if (impl.p_next_layer_acceptor) { + return impl.p_next_layer_acceptor->set_option(option, ec); + } + return ec; + } + + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->cancel(ec); + } + + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) { + impl.p_local_endpoint = std::make_shared(endpoint); + return impl.p_next_layer_acceptor->bind(endpoint.next_layer_endpoint(), ec); + } + + boost::system::error_code listen(implementation_type& impl, int backlog, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->listen(backlog, ec); + } + + template + boost::system::error_code accept( + implementation_type& impl, + boost::asio::basic_socket& peer, + endpoint_type* p_peer_endpoint, boost::system::error_code& ec, + typename std::enable_if::value>::type* = 0) { + auto& peer_impl = peer.native_handle(); + peer_impl.p_next_layer_socket = std::make_shared( + this->get_io_service(), impl.p_local_endpoint->endpoint_context()); + peer_impl.p_local_endpoint = impl.p_local_endpoint; + peer_impl.p_remote_endpoint = + std::make_shared( + impl.p_local_endpoint->endpoint_context()); + + impl.p_next_layer_acceptor->accept( + peer_impl.p_next_layer_socket->next_layer(), + peer_impl.p_remote_endpoint->next_layer_endpoint(), ec); + + if (!ec) { + peer_impl.p_remote_endpoint->set(); + + if (p_peer_endpoint) { + *p_peer_endpoint = *(peer_impl.p_remote_endpoint); + } + + peer_impl.p_next_layer_socket->handshake( + CryptoProtocol::handshake_type::server, ec); + } + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, void(boost::system::error_code)) + async_accept(implementation_type& impl, + boost::asio::basic_socket& peer, + endpoint_type* p_peer_endpoint, AcceptHandler&& handler, + typename std::enable_if::value>::type* = 0) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + auto& peer_impl = peer.native_handle(); + peer_impl.p_next_layer_socket = std::make_shared( + this->get_io_service(), impl.p_local_endpoint->endpoint_context()); + peer_impl.p_local_endpoint = std::make_shared( + impl.p_local_endpoint->endpoint_context()); + peer_impl.p_remote_endpoint = + std::make_shared( + impl.p_local_endpoint->endpoint_context()); + + detail::CryptoStreamAcceptOp< + CryptoProtocol, next_acceptor_type, + typename std::remove_reference::native_handle_type>::type, + endpoint_type, + decltype(init.handler)> ( + *impl.p_next_layer_acceptor, &peer_impl, p_peer_endpoint, + init.handler)(); + + return init.result.get(); + } + + private: + void shutdown_service() {} +}; + +#include + +} // cryptography +} // layer +} // ssf + +#endif // SSF_LAYER_CRYPTOGRAPHY_BASIC_CRYPTO_STREAM_H_ diff --git a/src/framework/ssf/layer/cryptography/crypto_stream_op.h b/src/framework/ssf/layer/cryptography/crypto_stream_op.h new file mode 100644 index 00000000..c4d9093a --- /dev/null +++ b/src/framework/ssf/layer/cryptography/crypto_stream_op.h @@ -0,0 +1,240 @@ +#ifndef SSF_LAYER_CRYPTOGRAPHY_CRYPTO_STREAM_OP_H_ +#define SSF_LAYER_CRYPTOGRAPHY_CRYPTO_STREAM_OP_H_ + +#include + +#include +#include +#include +#include + +#include + +namespace ssf { +namespace layer { +namespace cryptography { +namespace detail { + +template +class CryptoStreamConnectOp { + public: + CryptoStreamConnectOp(Stream& stream, Endpoint* p_local_endpoint, + const Endpoint& peer_endpoint, + const ConnectHandler& handler) + : coro_(), + stream_(stream), + p_local_endpoint_(p_local_endpoint), + peer_endpoint_(peer_endpoint), + handler_(handler) {} + + CryptoStreamConnectOp(const CryptoStreamConnectOp& other) + : coro_(other.coro_), + stream_(other.stream_), + p_local_endpoint_(other.p_local_endpoint_), + peer_endpoint_(other.peer_endpoint_), + handler_(other.handler_) {} + + CryptoStreamConnectOp(CryptoStreamConnectOp&& other) + : coro_(std::move(other.coro_)), + stream_(other.stream_), + p_local_endpoint_(std::move(other.p_local_endpoint_)), + peer_endpoint_(std::move(other.peer_endpoint_)), + handler_(std::move(other.handler_)) {} + +#include + void operator()( + const boost::system::error_code& ec = boost::system::error_code()) { + if (!ec) { + reenter(coro_) { + yield stream_.next_layer().async_connect( + peer_endpoint_.next_layer_endpoint(), std::move(*this)); + + boost::system::error_code endpoint_ec; + p_local_endpoint_->next_layer_endpoint() = + stream_.next_layer().local_endpoint(endpoint_ec); + p_local_endpoint_->set(); + + stream_.async_handshake(CryptoProtocol::handshake_type::client, + std::move(handler_)); + } + } else { + // error + handler_(ec); + } + } +#include + + inline ConnectHandler& handler() { return handler_; } + + private: + boost::asio::coroutine coro_; + Stream& stream_; + Endpoint* p_local_endpoint_; + Endpoint peer_endpoint_; + ConnectHandler handler_; +}; + +template +inline void* asio_handler_allocate( + std::size_t size, CryptoStreamConnectOp* this_handler) { + return boost_asio_handler_alloc_helpers::allocate(size, + this_handler->handler()); +} + +template +inline void asio_handler_deallocate( + void* pointer, std::size_t size, + CryptoStreamConnectOp* + this_handler) { + boost_asio_handler_alloc_helpers::deallocate(pointer, size, + this_handler->handler()); +} + +template +inline bool asio_handler_is_continuation(CryptoStreamConnectOp< + CryptoProtocol, Stream, Endpoint, ConnectHandler>* this_handler) { + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->handler()); +} + +template +inline void asio_handler_invoke( + Function& function, CryptoStreamConnectOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +template +inline void asio_handler_invoke( + const Function& function, + CryptoStreamConnectOp* + this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +template +class CryptoStreamAcceptOp { + public: + CryptoStreamAcceptOp(Acceptor& acceptor, PeerImpl* p_peer_impl, + Endpoint* p_peer_endpoint, const AcceptHandler& handler) + : coro_(), + acceptor_(acceptor), + p_peer_impl_(p_peer_impl), + p_peer_endpoint_(p_peer_endpoint), + handler_(handler) {} + + CryptoStreamAcceptOp(const CryptoStreamAcceptOp& other) + : coro_(other.coro_), + acceptor_(other.acceptor_), + p_peer_impl_(other.p_peer_impl_), + p_peer_endpoint_(other.p_peer_endpoint_), + handler_(other.handler_) {} + + CryptoStreamAcceptOp(CryptoStreamAcceptOp&& other) + : coro_(std::move(other.coro_)), + acceptor_(other.acceptor_), + p_peer_impl_(std::move(other.p_peer_impl_)), + p_peer_endpoint_(std::move(other.p_peer_endpoint_)), + handler_(std::move(other.handler_)) {} + +#include + void operator()( + const boost::system::error_code& ec = boost::system::error_code()) { + if (!ec) { + reenter(coro_) { + yield acceptor_.async_accept( + p_peer_impl_->p_next_layer_socket->next_layer(), + p_peer_impl_->p_remote_endpoint->next_layer_endpoint(), + std::move(*this)); + + p_peer_impl_->p_remote_endpoint->set(); + + p_peer_impl_->p_local_endpoint->next_layer_endpoint() = + p_peer_impl_->p_next_layer_socket->next_layer().local_endpoint(); + p_peer_impl_->p_local_endpoint->set(); + + if (p_peer_endpoint_) { + *p_peer_endpoint_ = *p_peer_impl_->p_remote_endpoint; + } + + p_peer_impl_->p_next_layer_socket->async_handshake( + CryptoProtocol::handshake_type::server, std::move(handler_)); + } + } else { + // error + handler_(ec); + } + } +#include + + inline AcceptHandler& handler() { return handler_; } + + private: + boost::asio::coroutine coro_; + Acceptor& acceptor_; + PeerImpl* p_peer_impl_; + Endpoint* p_peer_endpoint_; + AcceptHandler handler_; +}; + +template +inline void* asio_handler_allocate( + std::size_t size, + CryptoStreamAcceptOp* this_handler) { + return boost_asio_handler_alloc_helpers::allocate(size, + this_handler->handler()); +} + +template +inline void asio_handler_deallocate( + void* pointer, std::size_t size, + CryptoStreamAcceptOp* this_handler) { + boost_asio_handler_alloc_helpers::deallocate(pointer, size, + this_handler->handler()); +} + +template +inline bool asio_handler_is_continuation( + CryptoStreamAcceptOp* this_handler) { + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->handler()); +} + +template +inline void asio_handler_invoke( + Function& function, + CryptoStreamAcceptOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +template +inline void asio_handler_invoke( + const Function& function, + CryptoStreamAcceptOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +} // detail +} // cryptography +} // layer +} // ssf + +#endif // SSF_LAYER_CRYPTOGRAPHY_CRYPTO_STREAM_OP_H_ diff --git a/src/framework/ssf/layer/cryptography/tls/OpenSSL/helpers.cpp b/src/framework/ssf/layer/cryptography/tls/OpenSSL/helpers.cpp new file mode 100644 index 00000000..4e51d35b --- /dev/null +++ b/src/framework/ssf/layer/cryptography/tls/OpenSSL/helpers.cpp @@ -0,0 +1,328 @@ +#include "ssf/layer/cryptography/tls/OpenSSL/helpers.h" + +#include + +#include + +#include "ssf/error/error.h" +#include "ssf/log/log.h" +#include "ssf/utils/cleaner.h" +#include "ssf/utils/map_helpers.h" + +namespace ssf { +namespace layer { +namespace cryptography { +namespace detail { + +ExtendedTLSContext::ExtendedTLSContext( + std::shared_ptr p_ctx) + : p_ctx_(std::move(p_ctx)) {} + +boost::asio::ssl::context& ExtendedTLSContext::operator*() { return *p_ctx_; } +std::shared_ptr ExtendedTLSContext::operator->() { + return p_ctx_; +} + +bool ExtendedTLSContext::operator==(const ExtendedTLSContext&) const { + return true; +} + +bool ExtendedTLSContext::operator!=(const ExtendedTLSContext&) const { + return false; +} + +bool ExtendedTLSContext::operator<(const ExtendedTLSContext& other) const { + return p_ctx_ < other.p_ctx_; +} + +bool ExtendedTLSContext::operator!() const { return !p_ctx_; } + +ExtendedTLSContext make_tls_context(boost::asio::io_service& io_service, + const LayerParameters& parameters) { + auto p_ctx = std::make_shared( + boost::asio::ssl::context::tlsv12); + + auto& ctx = *p_ctx; + + // Set the callback to decipher the private key + auto password = helpers::GetField("password", parameters); + auto password_lambda = [password]( + std::size_t size, boost::asio::ssl::context::password_purpose purpose) + ->std::string { + return password; + }; + + ctx.set_password_callback(password_lambda); + + // Set the mutual authentication + ctx.set_verify_mode(boost::asio::ssl::verify_peer | + boost::asio::ssl::verify_fail_if_no_peer_cert); + + boost::system::error_code ec; + + // Set the callback to verify the cetificate chains of the peer + ctx.set_verify_callback(&verify_certificate, ec); + + if (ec) { + SSF_LOG(kLogError) + << "network crypto layer: could not set verify callback"; + return ExtendedTLSContext(nullptr); + } + + // Set various security options + ctx.set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::no_sslv3 | + boost::asio::ssl::context::no_tlsv1 | + boost::asio::ssl::context::single_dh_use); + + SSL_CTX_set_options(ctx.native_handle(), + SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TICKET); + + // [not used] Set compression methods + SSL_COMP_add_compression_method(0, COMP_rle()); + SSL_COMP_add_compression_method(1, COMP_zlib()); + + bool success = true; + + if (!SetCtxCipher(ctx, parameters)) { + SSF_LOG(kLogError) << "network[crypto]: set context cipher suite failed"; + success = false; + } + if (!SetCtxCa(ctx, parameters)) { + SSF_LOG(kLogError) << "network[crypto]: set context CA failed"; + success = false; + } + if (!SetCtxCrt(ctx, parameters, ec)) { + SSF_LOG(kLogError) << "network[crypto]: set context crt failed"; + success = false; + } + if (!SetCtxKey(ctx, parameters, ec)) { + SSF_LOG(kLogError) << "network[crypto]: set context key failed"; + success = false; + } + + success |= SetCtxDhparam(ctx, parameters, ec); + + if (!success) { + SSF_LOG(kLogError) << "network[crypto]: context init failed"; + return ExtendedTLSContext(nullptr); + } + + return ExtendedTLSContext(p_ctx); +} + +bool SetCtxCipher(boost::asio::ssl::context& ctx, + const LayerParameters& parameters) { + auto cipher_suit = + helpers::GetField("set_cipher_suit", parameters); + if (cipher_suit.empty()) { + return !!SSL_CTX_set_cipher_list(ctx.native_handle(), + "DHE-RSA-AES256-GCM-SHA384"); + } else { + return !!SSL_CTX_set_cipher_list(ctx.native_handle(), + cipher_suit.c_str()); + } +} + +bool SetCtxCa(boost::asio::ssl::context& ctx, + const LayerParameters& parameters) { + if (helpers::GetField("ca_src", parameters) == "file") { + // Load the file containing the trusted certificate authorities + return !!SSL_CTX_load_verify_locations( + ctx.native_handle(), + helpers::GetField("ca_file", parameters).c_str(), + NULL); + } else if (helpers::GetField("ca_src", parameters) == "buffer") { + auto ca_vector = get_value_in_vector(parameters, "ca_buffer"); + if (ca_vector.size()) { + X509* x509_ca = NULL; + ScopeCleaner cleaner([&x509_ca]() { + X509_free(x509_ca); + x509_ca = NULL; + }); + + auto p_ca_vector = ca_vector.data(); + auto imported = !!d2i_X509(&x509_ca, (const unsigned char**)&p_ca_vector, + static_cast(ca_vector.size())); + + if (!imported) { + return false; + } + + X509_STORE* store = X509_STORE_new(); + SSL_CTX_set_cert_store(ctx.native_handle(), store); + auto cert_added = !!X509_STORE_add_cert(store, x509_ca); + + if (!cert_added) { + return false; + } + + return true; + } else { + return false; + } + } + + return false; +} + +bool SetCtxCrt(boost::asio::ssl::context& ctx, + const LayerParameters& parameters, + boost::system::error_code& ec) { + if (helpers::GetField("crt_src", parameters) == "file") { + // The certificate used by the local peer + ctx.use_certificate_chain_file( + helpers::GetField("crt_file", parameters), ec); + + return !ec; + } + + if (helpers::GetField("crt_src", parameters) != "buffer") { + ec.assign(ssf::error::no_crt_error, ssf::error::get_ssf_category()); + return false; + } + + auto crt_vector = get_value_in_vector(parameters, "crt_buffer"); + + if (!crt_vector.size()) { + ec.assign(ssf::error::no_crt_error, ssf::error::get_ssf_category()); + return false; + } + + X509* x509_cert = NULL; + ScopeCleaner cleaner([&x509_cert]() { + X509_free(x509_cert); + x509_cert = NULL; + }); + + auto p_crt_vector = crt_vector.data(); + auto imported = !!d2i_X509(&x509_cert, (const unsigned char**)&p_crt_vector, + static_cast(crt_vector.size())); + + if (!imported) { + ec.assign(ssf::error::import_crt_error, ssf::error::get_ssf_category()); + return false; + } + + auto used = !!SSL_CTX_use_certificate(ctx.native_handle(), x509_cert); + + if (!used) { + ec.assign(ssf::error::set_crt_error, ssf::error::get_ssf_category()); + return false; + } + + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return true; +} + +bool SetCtxKey(boost::asio::ssl::context& ctx, + const LayerParameters& parameters, + boost::system::error_code& ec) { + if (helpers::GetField("key_src", parameters) == "file") { + // The private key used by the local peer + ctx.use_private_key_file( + helpers::GetField("key_file", parameters), + boost::asio::ssl::context::pem, ec); + + return !ec; + } + + if (helpers::GetField("key_src", parameters) != "buffer") { + ec.assign(ssf::error::no_key_error, ssf::error::get_ssf_category()); + return false; + } + + auto key_vector = get_value_in_vector(parameters, "key_buffer"); + + if (!key_vector.size()) { + ec.assign(ssf::error::no_key_error, ssf::error::get_ssf_category()); + return false; + } + + EVP_PKEY* RSA_key = NULL; + ScopeCleaner cleaner([&RSA_key]() { + EVP_PKEY_free(RSA_key); + RSA_key = NULL; + }); + + auto p_key_vector = key_vector.data(); + auto imported = !!d2i_PrivateKey(EVP_PKEY_RSA, &RSA_key, + (const unsigned char**)&p_key_vector, + static_cast(key_vector.size())); + + if (!imported) { + ec.assign(ssf::error::import_key_error, ssf::error::get_ssf_category()); + return false; + } + + auto used = !!SSL_CTX_use_PrivateKey(ctx.native_handle(), RSA_key); + + if (!used) { + ec.assign(ssf::error::set_key_error, ssf::error::get_ssf_category()); + return false; + } + + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return true; +} + +bool SetCtxDhparam(boost::asio::ssl::context& ctx, + const LayerParameters& parameters, + boost::system::error_code& ec) { + if (helpers::GetField("dhparam_src", parameters) == "file") { + // The Diffie-Hellman parameter file + ctx.use_tmp_dh_file( + helpers::GetField("dhparam_file", parameters), ec); + + return !ec; + } else { + ec.assign(ssf::error::no_dh_param_error, ssf::error::get_ssf_category()); + return false; + } +} + +bool verify_certificate(bool preverified, + boost::asio::ssl::verify_context& ctx) { + X509_STORE_CTX* cts = ctx.native_handle(); + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); + + char subject_name[256]; + X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); + //std::cout << "Verifying " << subject_name << "\n"; + + X509* issuer = cts->current_issuer; + if (issuer) { + X509_NAME_oneline(X509_get_subject_name(issuer), subject_name, 256); + //std::cout << "Issuer " << subject_name << "\n"; + } + + // More checking ? + return preverified; +} + +std::vector get_deserialized_vector(const std::string& serialized) { + std::istringstream istrs(serialized); + boost::archive::text_iarchive ar(istrs); + std::vector deserialized; + + ar >> BOOST_SERIALIZATION_NVP(deserialized); + + return deserialized; +} + +std::vector get_value_in_vector(const LayerParameters& parameters, + const std::string& key) { + if (parameters.count(key)) { + auto serialized = parameters.find(key)->second; + auto deserialized = get_deserialized_vector(serialized); + return deserialized; + } else { + return std::vector(); + } +} + +} // detail +} // cryptography +} // layer +} // ssf diff --git a/src/framework/ssf/layer/cryptography/tls/OpenSSL/helpers.h b/src/framework/ssf/layer/cryptography/tls/OpenSSL/helpers.h new file mode 100644 index 00000000..74b541dd --- /dev/null +++ b/src/framework/ssf/layer/cryptography/tls/OpenSSL/helpers.h @@ -0,0 +1,62 @@ +#ifndef SSF_LAYER_CRYPTOGRAPHY_TLS_OPENSSL_HELPERS_H_ +#define SSF_LAYER_CRYPTOGRAPHY_TLS_OPENSSL_HELPERS_H_ + +#include + +#include +#include +#include + +#include +#include + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { +namespace cryptography { +namespace detail { + +struct ExtendedTLSContext { + ExtendedTLSContext() : p_ctx_(nullptr) {} + + explicit ExtendedTLSContext(std::shared_ptr p_ctx); + + boost::asio::ssl::context& operator*(); + std::shared_ptr operator->(); + + bool operator==(const ExtendedTLSContext&) const; + bool operator!=(const ExtendedTLSContext&) const; + bool operator<(const ExtendedTLSContext& other) const; + bool operator!() const; + + std::shared_ptr p_ctx_; +}; + +ExtendedTLSContext make_tls_context(boost::asio::io_service& io_service, + const LayerParameters& parameters); +bool SetCtxCipher(boost::asio::ssl::context& ctx, + const LayerParameters& parameters); +bool SetCtxCa(boost::asio::ssl::context& ctx, + const LayerParameters& parameters); +bool SetCtxCrt(boost::asio::ssl::context& ctx, + const LayerParameters& parameters, + boost::system::error_code& ec); +bool SetCtxKey(boost::asio::ssl::context& ctx, + const LayerParameters& parameters, + boost::system::error_code& ec); +bool SetCtxDhparam(boost::asio::ssl::context& ctx, + const LayerParameters& parameters, + boost::system::error_code& ec); +bool verify_certificate(bool preverified, + boost::asio::ssl::verify_context& ctx); +std::vector get_deserialized_vector(const std::string& serialized); +std::vector get_value_in_vector(const LayerParameters& parameters, + const std::string& key); + +} // detail +} // cryptography +} // layer +} // ssf + +#endif // SSF_LAYER_CRYPTOGRAPHY_TLS_OPENSSL_HELPERS_H_ diff --git a/src/framework/ssf/layer/cryptography/tls/OpenSSL/impl.h b/src/framework/ssf/layer/cryptography/tls/OpenSSL/impl.h new file mode 100644 index 00000000..4f73989c --- /dev/null +++ b/src/framework/ssf/layer/cryptography/tls/OpenSSL/impl.h @@ -0,0 +1,705 @@ +#ifndef SSF_LAYER_CRYPTOGRAPHY_TLS_OPENSSL_IMPL_H_ +#define SSF_LAYER_CRYPTOGRAPHY_TLS_OPENSSL_IMPL_H_ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ssf/layer/cryptography/tls/OpenSSL/helpers.h" + +#include "ssf/error/error.h" +#include "ssf/io/read_stream_op.h" + +#include "ssf/layer/basic_endpoint.h" +#include "ssf/layer/protocol_attributes.h" +#include "ssf/layer/parameters.h" + +#include "ssf/log/log.h" + +namespace ssf { +namespace layer { +namespace cryptography { + +namespace detail { + +/// The class in charge of receiving data from a TLS stream into a buffer +template +class TLSStreamBufferer : public std::enable_shared_from_this< + TLSStreamBufferer> { + private: + enum { + lower_queue_size_bound = 1 * 1024 * 1024, + higher_queue_size_bound = 16 * 1024 * 1024, + receive_buffer_size = 50 * 1024 + }; + + private: + typedef boost::asio::ssl::stream tls_stream_type; + typedef std::shared_ptr p_tls_stream_type; + typedef detail::ExtendedTLSContext p_context_type; + typedef boost::asio::io_service::strand strand_type; + typedef std::shared_ptr p_strand_type; + + public: + typedef TLSStreamBufferer puller_type; + typedef std::shared_ptr p_puller_type; + typedef boost::asio::detail::op_queue + op_queue_type; + + public: + TLSStreamBufferer(const TLSStreamBufferer&) = delete; + TLSStreamBufferer& operator=(const TLSStreamBufferer&) = delete; + + ~TLSStreamBufferer() {} + + static p_puller_type create(p_tls_stream_type p_socket, + p_strand_type p_strand, p_context_type p_ctx) { + return p_puller_type(new puller_type(p_socket, p_strand, p_ctx)); + } + + /// Start receiving data + void start_pulling() { + { + boost::recursive_mutex::scoped_lock lock(pulling_mutex_); + if (!pulling_) { + pulling_ = true; + SSF_LOG(kLogDebug) << "network[crypto]: pulling"; + io_service_.post(boost::bind(&TLSStreamBufferer::async_pull_packets, + this->shared_from_this())); + } + } + } + + /// User interface for receiving some data + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + auto buffer_size = boost::asio::buffer_size(buffers); + + if (buffer_size) { + typedef io::pending_read_stream_operation op; + typename op::ptr p = { + boost::asio::detail::addressof(init.handler), + boost_asio_handler_alloc_helpers::allocate(sizeof(op), init.handler), + 0}; + + p.p = new (p.v) op(buffers, init.handler); + + { + boost::recursive_mutex::scoped_lock lock(op_queue_mutex_); + op_queue_.push(p.p); + } + + p.v = p.p = 0; + + io_service_.post(boost::bind(&TLSStreamBufferer::handle_data_n_ops, + this->shared_from_this())); + } else { + io_service_.post( + boost::bind(init.handler, boost::system::error_code(), 0)); + } + + return init.result.get(); + } + + boost::system::error_code cancel(boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock1(data_queue_mutex_); + boost::recursive_mutex::scoped_lock lock2(op_queue_mutex_); + data_queue_.consume(data_queue_.size()); + while (!op_queue_.empty()) { + auto op = op_queue_.front(); + op_queue_.pop(); + auto self = this->shared_from_this(); + auto do_complete = [op, this, self]() { + op->complete(boost::asio::error::make_error_code( + boost::asio::error::basic_errors::operation_aborted), + 0); + }; + io_service_.post(do_complete); + } + + return ec; + } + + private: + TLSStreamBufferer(p_tls_stream_type p_socket, p_strand_type p_strand, + p_context_type p_ctx) + : socket_(*p_socket), + p_socket_(p_socket), + strand_(*p_strand), + p_strand_(p_strand), + p_ctx_(p_ctx), + io_service_(strand_.get_io_service()), + status_(boost::system::error_code()), + pulling_(false) {} + + /// Check if data is available for user requests + void handle_data_n_ops() { + if (!status_) { + { + boost::recursive_mutex::scoped_lock lock(pulling_mutex_); + if ((data_queue_.size() < lower_queue_size_bound) && !pulling_) { + start_pulling(); + } + } + + boost::recursive_mutex::scoped_lock lock1(data_queue_mutex_); + boost::recursive_mutex::scoped_lock lock2(op_queue_mutex_); + if (!op_queue_.empty() && data_queue_.size()) { + auto op = op_queue_.front(); + op_queue_.pop(); + + size_t copied = op->fill_buffer(data_queue_); + + auto do_complete = + [=]() { op->complete(boost::system::error_code(), copied); }; + io_service_.post(do_complete); + + io_service_.dispatch(boost::bind(&TLSStreamBufferer::handle_data_n_ops, + this->shared_from_this())); + } + } else { + boost::recursive_mutex::scoped_lock lock1(data_queue_mutex_); + boost::recursive_mutex::scoped_lock lock2(op_queue_mutex_); + if (!op_queue_.empty()) { + auto op = op_queue_.front(); + op_queue_.pop(); + auto self = this->shared_from_this(); + auto do_complete = + [op, this, self]() { op->complete(this->status_, 0); }; + io_service_.post(do_complete); + + io_service_.dispatch(boost::bind(&TLSStreamBufferer::handle_data_n_ops, + this->shared_from_this())); + } + } + } + + /// Receive some data + void async_pull_packets() { + auto self = this->shared_from_this(); + + auto handler = [this, self](const boost::system::error_code& ec, + size_t length) { + if (!ec) { + { + boost::recursive_mutex::scoped_lock lock1(this->data_queue_mutex_); + this->data_queue_.commit(length); + } + + if (!this->status_) { + this->io_service_.dispatch( + boost::bind(&TLSStreamBufferer::async_pull_packets, + this->shared_from_this())); + } + } else { + if (ec.value() == boost::asio::error::operation_aborted) { + boost::system::error_code cancel_ec; + cancel(cancel_ec); + } else { + this->status_ = ec; + SSF_LOG(kLogDebug) << "network[crypto]: TLS connection terminated (" + << ec.value() << ": " << ec.message() << ")"; + } + } + + this->io_service_.dispatch(boost::bind( + &TLSStreamBufferer::handle_data_n_ops, this->shared_from_this())); + }; + + { + boost::recursive_mutex::scoped_lock lock(data_queue_mutex_); + boost::asio::streambuf::mutable_buffers_type bufs = + data_queue_.prepare(receive_buffer_size); + + if (data_queue_.size() < higher_queue_size_bound) { + auto lambda = [this, bufs, handler, self]() { + this->socket_.async_read_some(bufs, this->strand_.wrap(handler)); + }; + strand_.dispatch(lambda); + } else { + pulling_ = false; + SSF_LOG(kLogDebug) << "network[crypto]: not pulling"; + } + } + } + + /// The TLS stream to receive from + tls_stream_type& socket_; + p_tls_stream_type p_socket_; + + /// The strand to insure that read and write are not concurrent + strand_type& strand_; + p_strand_type p_strand_; + + p_context_type p_ctx_; + + /// The io_service handling asynchronous operations + boost::asio::io_service& io_service_; + + /// Errors during async_read are saved here + boost::system::error_code status_; + + /// Handle the data received + boost::recursive_mutex data_queue_mutex_; + boost::asio::streambuf data_queue_; + + /// Handle pending user operations + boost::recursive_mutex op_queue_mutex_; + op_queue_type op_queue_; + + boost::recursive_mutex pulling_mutex_; + bool pulling_; +}; + +} // detail + +/// The class wrapping a TLS stream to allow buffer optimization, non +/// concurrent io and move operations +template +class basic_buffered_tls_socket { + private: + typedef boost::asio::ssl::stream tls_stream_type; + typedef std::shared_ptr p_tls_stream_type; + typedef detail::ExtendedTLSContext p_context_type; + typedef std::shared_ptr p_streambuf; + typedef detail::TLSStreamBufferer puller_type; + typedef std::shared_ptr p_puller_type; + typedef boost::asio::io_service::strand strand_type; + typedef std::shared_ptr p_strand_type; + + public: + typedef typename tls_stream_type::next_layer_type next_layer_type; + typedef typename tls_stream_type::lowest_layer_type lowest_layer_type; + typedef typename tls_stream_type::handshake_type handshake_type; + + public: + basic_buffered_tls_socket() + : p_ctx_(nullptr), + p_socket_(nullptr), + socket_(), + p_strand_(nullptr), + p_puller_(nullptr) {} + + basic_buffered_tls_socket(p_tls_stream_type p_socket, p_context_type p_ctx) + : p_ctx_(p_ctx), + p_socket_(p_socket), + socket_(*p_socket_), + p_strand_(std::make_shared( + socket_.get().lowest_layer().get_io_service())), + p_puller_(puller_type::create(p_socket_, p_strand_, p_ctx_)) {} + + basic_buffered_tls_socket(boost::asio::io_service& io_service, + p_context_type p_ctx) + : p_ctx_(p_ctx), + p_socket_(new tls_stream_type(io_service, *p_ctx)), + socket_(*p_socket_), + p_strand_(std::make_shared(io_service)), + p_puller_(puller_type::create(p_socket_, p_strand_, p_ctx_)) {} + + basic_buffered_tls_socket(basic_buffered_tls_socket&& other) + : p_ctx_(std::move(other.p_ctx_)), + p_socket_(std::move(other.p_socket_)), + socket_(*p_socket_), + p_strand_(std::move(other.p_strand_)), + p_puller_(std::move(other.p_puller_)) { + other.socket_ = *(other.p_socket_); + } + + ~basic_buffered_tls_socket() {} + + basic_buffered_tls_socket(const basic_buffered_tls_socket&) = delete; + basic_buffered_tls_socket& operator=(const basic_buffered_tls_socket&) = + delete; + + boost::asio::io_service& get_io_service() { + return socket_.get().lowest_layer().get_io_service(); + } + + lowest_layer_type& lowest_layer() { return socket_.get().lowest_layer(); } + next_layer_type& next_layer() { return socket_.get().next_layer(); } + + boost::system::error_code handshake(handshake_type type, + boost::system::error_code& ec) { + socket_.get().handshake(type, ec); + + if (!ec) { + p_puller_->start_pulling(); + } + + return ec; + } + + /// Forward the call to the TLS stream and start pulling packets on + /// completion + template + void async_handshake(handshake_type type, Handler handler) { + auto do_user_handler = + [this, handler](const boost::system::error_code& ec) mutable { + if (!ec) { + this->p_puller_->start_pulling(); + } + handler(ec); + }; + + auto lambda = [this, type, do_user_handler]() { + this->socket_.get().async_handshake(type, + p_strand_->wrap(do_user_handler)); + }; + p_strand_->dispatch(lambda); + } + + template + std::size_t read_some(const MutableBufferSequence& buffers, + boost::system::error_code& ec) { + try { + auto read = async_read_some(buffers, boost::asio::use_future); + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return read.get(); + } catch (const std::exception&) { + ec.assign(ssf::error::io_error, ssf::error::get_ssf_category()); + return 0; + } + } + + /// Forward the call to the TLSStreamBufferer object + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_read_some(const MutableBufferSequence& buffers, + ReadHandler&& handler) { + return p_puller_->async_read_some(buffers, + std::forward(handler)); + } + + template + std::size_t write_some(const ConstBufferSequence& buffers, + boost::system::error_code& ec) { + return socket_.get().write_some(buffers, ec); + } + + /// Forward the call directly to the TLS stream (wrapped in an strand) + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_write_some(const ConstBufferSequence& buffers, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + auto lambda = [this, buffers, init]() { + this->socket_.get().async_write_some(buffers, + this->p_strand_->wrap(init.handler)); + }; + p_strand_->dispatch(lambda); + + return init.result.get(); + } + + /// Forward the call to the lowest layer of the TLS stream + bool is_open() { return socket_.get().lowest_layer().is_open(); } + + void close() { + boost::system::error_code ec; + socket_.get().lowest_layer().close(ec); + } + + boost::system::error_code close(boost::system::error_code& ec) { + return socket_.get().lowest_layer().close(ec); + } + + void shutdown(boost::asio::socket_base::shutdown_type type, + boost::system::error_code& ec) { + socket_.get().lowest_layer().shutdown(type, ec); + } + + boost::asio::ssl::context& context() { return *p_ctx_; } + tls_stream_type& socket() { return socket_; } + strand_type& strand() { return *p_strand_; } + + private: + /// The TLS ctx in a shared_ptr to be able to move it + p_context_type p_ctx_; + + /// The TLS stream in a shared_ptr to be able to move it + p_tls_stream_type p_socket_; + + /// A reference to the TLS stream to avoid dereferencing a pointer + std::reference_wrapper socket_; + + /// The strand in a shared_ptr to be able to move it + p_strand_type p_strand_; + + /// The TLSStreamBufferer in a shared_ptr to be able to move it + p_puller_type p_puller_; +}; + +template +class basic_tls_socket { + private: + typedef boost::asio::ssl::stream tls_stream_type; + typedef std::shared_ptr p_tls_stream_type; + typedef detail::ExtendedTLSContext p_context_type; + typedef std::shared_ptr p_streambuf; + typedef boost::asio::io_service::strand strand_type; + typedef std::shared_ptr p_strand_type; + + public: + typedef typename tls_stream_type::next_layer_type next_layer_type; + typedef typename tls_stream_type::lowest_layer_type lowest_layer_type; + typedef typename tls_stream_type::handshake_type handshake_type; + + public: + basic_tls_socket() + : p_ctx_(nullptr), p_socket_(nullptr), socket_(), p_strand_(nullptr) {} + + basic_tls_socket(p_tls_stream_type p_socket, p_context_type p_ctx) + : p_ctx_(p_ctx), + p_socket_(p_socket), + socket_(*p_socket_), + p_strand_(std::make_shared( + socket_.get().lowest_layer().get_io_service())) {} + + basic_tls_socket(boost::asio::io_service& io_service, p_context_type p_ctx) + : p_ctx_(p_ctx), + p_socket_(new tls_stream_type(io_service, *p_ctx)), + socket_(*p_socket_), + p_strand_(std::make_shared(io_service)) {} + + basic_tls_socket(basic_tls_socket&& other) + : p_ctx_(std::move(other.p_ctx_)), + p_socket_(std::move(other.p_socket_)), + socket_(*p_socket_), + p_strand_(std::move(other.p_strand_)) { + other.socket_ = *(other.p_socket_); + } + + ~basic_tls_socket() {} + + basic_tls_socket(const basic_tls_socket&) = delete; + basic_tls_socket& operator=(const basic_tls_socket&) = delete; + + boost::asio::io_service& get_io_service() { + return socket_.get().lowest_layer().get_io_service(); + } + + lowest_layer_type& lowest_layer() { return socket_.get().lowest_layer(); } + next_layer_type& next_layer() { return socket_.get().next_layer(); } + + boost::system::error_code handshake(handshake_type type, + boost::system::error_code ec) { + socket_.get().handshake(type, ec); + return ec; + } + + /// Forward the call to the TLS stream and start pulling packets on + /// completion + template + void async_handshake(handshake_type type, Handler handler) { + auto lambda = [this, type, handler]() { + this->socket_.get().async_handshake(type, p_strand_->wrap(handler)); + }; + + p_strand_->dispatch(lambda); + } + + template + std::size_t read_some(const MutableBufferSequence& buffers, + boost::system::error_code& ec) { + return socket_.get().read_some(buffers, ec); + } + + /// Forward the call to the TLSStreamBufferer object + template + void async_read_some(const MutableBufferSequence& buffers, + ReadHandler&& handler) { + auto lambda = [this, buffers, handler]() { + this->socket_.get().async_read_some(buffers, + this->p_strand_->wrap(handler)); + }; + p_strand_->dispatch(lambda); + } + + template + std::size_t write_some(const ConstBufferSequence& buffers, + boost::system::error_code& ec) { + return socket_.get().write_some(buffers, ec); + } + + /// Forward the call directly to the TLS stream (wrapped in an strand) + template + void async_write_some(const ConstBufferSequence& buffers, Handler&& handler) { + auto lambda = [this, buffers, handler]() { + this->socket_.get().async_write_some(buffers, + this->p_strand_->wrap(handler)); + }; + p_strand_->dispatch(lambda); + } + + /// Forward the call to the lowest layer of the TLS stream + bool is_open() { return socket_.get().lowest_layer().is_open(); } + + void close() { + boost::system::error_code ec; + socket_.get().lowest_layer().close(ec); + } + + boost::system::error_code close(boost::system::error_code& ec) { + return socket_.get().lowest_layer().close(ec); + } + + void shutdown(boost::asio::socket_base::shutdown_type type, + boost::system::error_code& ec) { + socket_.get().lowest_layer().shutdown(type, ec); + } + + boost::asio::ssl::context& context() { return *p_ctx_; } + tls_stream_type& socket() { return socket_; } + strand_type& strand() { return *p_strand_; } + + private: + /// The TLS ctx in a shared_ptr to be able to move it + p_context_type p_ctx_; + + /// The TLS stream in a shared_ptr to be able to move it + p_tls_stream_type p_socket_; + + /// A reference to the TLS stream to avoid dereferencing a pointer + std::reference_wrapper socket_; + + /// The strand in a shared_ptr to be able to move it + p_strand_type p_strand_; +}; + +template class TLSStreamSocket> +class basic_tls { + public: + enum { + id = 2, + overhead = 0, + facilities = ssf::layer::facilities::stream, + mtu = NextLayer::mtu - overhead + }; + + static const char* NAME; + + enum { endpoint_stack_size = 1 + NextLayer::endpoint_stack_size }; + + using handshake_type = boost::asio::ssl::stream_base::handshake_type; + + using endpoint_context_type = detail::ExtendedTLSContext; + using Stream = TLSStreamSocket; + + private: + using query = ParameterStack; + + public: + static std::string get_name() { return NAME; } + + static endpoint_context_type make_endpoint_context( + boost::asio::io_service& io_service, + typename query::const_iterator parameters_it, uint32_t lower_id, + boost::system::error_code& ec) { + auto context = detail::make_tls_context(io_service, *parameters_it); + if (!context) { + SSF_LOG(kLogError) << "network[crypto]: could not generate context"; + ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + } + + return context; + } + + static void add_params_from_property_tree( + query* p_query, const boost::property_tree::ptree& property_tree, + bool connect, boost::system::error_code& ec) { + LayerParameters params; + auto layer_name = property_tree.get_child_optional("layer"); + if (!layer_name || layer_name.get().data() != NAME) { + ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + return; + } + + auto layer_parameters = property_tree.get_child_optional("parameters"); + if (!layer_parameters) { + ec.assign(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()); + return; + } + + ssf::layer::ptree_entry_to_query(*layer_parameters, "ca_file", ¶ms); + ssf::layer::ptree_entry_to_query(*layer_parameters, "ca_buffer", ¶ms); + if (params.count("ca_file") == 1) { + params["ca_src"] = "file"; + } + if (params.count("ca_buffer") == 1) { + params["ca_src"] = "buffer"; + } + + ssf::layer::ptree_entry_to_query(*layer_parameters, "crt_file", ¶ms); + ssf::layer::ptree_entry_to_query(*layer_parameters, "crt_buffer", ¶ms); + if (params.count("crt_file") == 1) { + params["crt_src"] = "file"; + } + if (params.count("crt_buffer") == 1) { + params["crt_src"] = "buffer"; + } + + ssf::layer::ptree_entry_to_query(*layer_parameters, "key_file", ¶ms); + ssf::layer::ptree_entry_to_query(*layer_parameters, "key_buffer", ¶ms); + if (params.count("key_file") == 1) { + params["key_src"] = "file"; + } + if (params.count("key_buffer") == 1) { + params["key_src"] = "buffer"; + } + + ssf::layer::ptree_entry_to_query(*layer_parameters, "dhparam_file", + ¶ms); + ssf::layer::ptree_entry_to_query(*layer_parameters, "dhparam_buffer", + ¶ms); + if (params.count("dhparam_file") == 1) { + params["dhparam_src"] = "file"; + } + if (params.count("dhparam_buffer") == 1) { + params["dhparam_src"] = "buffer"; + } + + p_query->push_back(params); + } +}; + +template class TLSStreamSocket> +const char* basic_tls::NAME = "TLS"; + +template +using buffered_tls = basic_tls; + +template +using tls = basic_tls; + +} // cryptography +} // layer +} // ssf + +#endif // SSF_LAYER_CRYPTOGRAPHY_TLS_OPENSSL_IMPL_H_ diff --git a/src/framework/ssf/layer/data_link/basic_circuit_acceptor_service.h b/src/framework/ssf/layer/data_link/basic_circuit_acceptor_service.h new file mode 100644 index 00000000..52621544 --- /dev/null +++ b/src/framework/ssf/layer/data_link/basic_circuit_acceptor_service.h @@ -0,0 +1,730 @@ +#ifndef SSF_LAYER_DATA_LINK_BASIC_CIRCUIT_ACCEPTOR_SERVICE_H_ +#define SSF_LAYER_DATA_LINK_BASIC_CIRCUIT_ACCEPTOR_SERVICE_H_ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "ssf/error/error.h" + +#include "ssf/io/accept_op.h" +#include "ssf/io/handler_helpers.h" + +#include "ssf/layer/basic_impl.h" +#include "ssf/layer/parameters.h" +#include "ssf/layer/data_link/helpers.h" +#include "ssf/layer/data_link/circuit_op.h" + +#include "ssf/network/base_session.h" +#include "ssf/network/manager.h" +#include "ssf/network/session_forwarder.h" + +namespace ssf { +namespace layer { +namespace data_link { + +#include + +template +class basic_CircuitAcceptor_service + : public boost::asio::detail::service_base< + basic_CircuitAcceptor_service> { + public: + typedef Prococol protocol_type; + + typedef typename protocol_type::endpoint endpoint_type; + typedef typename protocol_type::resolver resolver_type; + + typedef std::shared_ptr p_endpoint_type; + + typedef basic_acceptor_impl implementation_type; + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + private: + typedef typename protocol_type::circuit_policy circuit_policy; + typedef ssf::ItemManager Manager; + typedef typename protocol_type::socket socket_type; + typedef + typename protocol_type::next_layer_protocol::acceptor next_acceptor_type; + typedef typename protocol_type::next_layer_protocol::socket next_socket_type; + typedef + typename protocol_type::next_layer_protocol::endpoint next_endpoint_type; + typedef std::shared_ptr p_socket_type; + typedef std::shared_ptr p_next_acceptor_type; + typedef std::shared_ptr p_next_socket_type; + typedef std::shared_ptr p_next_endpoint_type; + + typedef std::pair pending_connection; + typedef std::queue connection_queue; + typedef boost::asio::detail::op_queue< + io::basic_pending_accept_operation> op_queue; + + public: + explicit basic_CircuitAcceptor_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service), + next_acceptors_(), + next_local_endpoints_() {} + + virtual ~basic_CircuitAcceptor_service() {} + + void construct(implementation_type& impl) { + impl.p_next_layer_acceptor = + std::make_shared(this->get_io_service()); + } + + void destroy(implementation_type& impl) { + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_acceptor.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->open( + typename protocol_type::next_layer_protocol(), ec); + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { + return impl.p_next_layer_acceptor->is_open(); + } + + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_remote_endpoint) { + return *impl.p_remote_endpoint; + } else { + return endpoint_type(); + } + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_local_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + { + boost::recursive_mutex::scoped_lock lock(bind_mutex_); + + endpoint_type input_endpoint; + for (const auto& pair : input_bindings_) { + if (pair.second == &impl) { + input_endpoint = pair.first; + + { + boost::recursive_mutex::scoped_lock lock1(accept_mutex_); + pending_connections_.erase( + impl.p_local_endpoint->next_layer_endpoint()); + } + + break; + } + } + input_bindings_.erase(input_endpoint); + + endpoint_type forward_endpoint; + for (const auto& pair : forward_bindings_) { + if (pair.second == &impl) { + forward_endpoint = pair.first; + break; + } + } + forward_bindings_.erase(forward_endpoint); + + p_next_acceptor_type p_input_next_acceptor; + auto input_next_acceptor_it = + next_acceptors_.find(input_endpoint.next_layer_endpoint()); + if (input_next_acceptor_it != std::end(next_acceptors_)) { + p_input_next_acceptor = input_next_acceptor_it->second; + next_acceptors_.erase(input_endpoint.next_layer_endpoint()); + } + + p_next_acceptor_type p_forward_next_acceptor; + auto forward_next_acceptor_it = + next_acceptors_.find(forward_endpoint.next_layer_endpoint()); + if (forward_next_acceptor_it != std::end(next_acceptors_)) { + p_forward_next_acceptor = forward_next_acceptor_it->second; + next_acceptors_.erase(forward_endpoint.next_layer_endpoint()); + } + + next_local_endpoints_.erase(p_input_next_acceptor); + next_local_endpoints_.erase(p_forward_next_acceptor); + + listening_.erase(p_input_next_acceptor); + listening_.erase(p_forward_next_acceptor); + + if (p_input_next_acceptor) { + clean_pending_accepts( + input_endpoint.next_layer_endpoint(), + boost::system::error_code(ssf::error::interrupted, + ssf::error::get_ssf_category())); + p_input_next_acceptor->close(ec); + } + + if (p_forward_next_acceptor) { + p_forward_next_acceptor->close(ec); + } + } + + // close forwarding sessions + manager_.stop_all(); + + return ec; + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + /// Set a socket option. + template + boost::system::error_code set_option(implementation_type& impl, + const SettableSocketOption& option, + boost::system::error_code& ec) { + if (impl.p_next_layer_acceptor) { + return impl.p_next_layer_acceptor->set_option(option, ec); + } + return ec; + } + + /// Register given endpoint if not already provided + /// Create connection queue if not a forward endpoint + /// Bind the acceptor of the next layer to the next layer endpoint + /// Link the acceptor of the next layer to the current impl + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(bind_mutex_); + + auto is_forward = detail::is_endpoint_forwarding(endpoint); + + default_parameters_ = unserialize_parameter_stack( + endpoint.endpoint_context().default_parameters); + bool binding_insertion = false; + + if (is_forward) { + // If the endpoint only forwards, the acceptor will not be able + // to accept connections (pending_connections_ not filled) + auto forward_bind = + forward_bindings_.emplace(std::make_pair(endpoint, &impl)); + binding_insertion = forward_bind.second; + } + + if (!is_forward || binding_insertion) { + auto input_bind = + input_bindings_.emplace(std::make_pair(endpoint, &impl)); + binding_insertion = input_bind.second; + + boost::recursive_mutex::scoped_lock lock1(accept_mutex_); + pending_connections_.emplace( + std::make_pair(endpoint.next_layer_endpoint(), connection_queue())); + } + + if (!binding_insertion) { + if (is_forward && + forward_bindings_.find(endpoint) != forward_bindings_.end()) { + forward_bindings_.erase(endpoint); + } + ec.assign(ssf::error::address_in_use, ssf::error::get_ssf_category()); + return ec; + } + + impl.p_local_endpoint = std::make_shared(endpoint); + + auto inserted = next_acceptors_.emplace( + std::make_pair(impl.p_local_endpoint->next_layer_endpoint(), + impl.p_next_layer_acceptor)); + + if (!inserted.second) { + impl.p_next_layer_acceptor = inserted.first->second; + return boost::system::error_code(); + } + + next_local_endpoints_.emplace( + std::make_pair(impl.p_next_layer_acceptor, + impl.p_local_endpoint->next_layer_endpoint())); + + return impl.p_next_layer_acceptor->bind( + impl.p_local_endpoint->next_layer_endpoint(), ec); + } + + /// Wait for new connections from next layer if not already listening + boost::system::error_code listen(implementation_type& impl, int backlog, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(bind_mutex_); + if (listening_.count(impl.p_next_layer_acceptor)) { + return ec; + } + + listening_.insert(impl.p_next_layer_acceptor); + auto listening_ec = impl.p_next_layer_acceptor->listen(backlog, ec); + + if (!ec) { + start_accepting(impl.p_next_layer_acceptor); + } + + return listening_ec; + } + + template + boost::system::error_code accept( + implementation_type& impl, + boost::asio::basic_socket& peer, + endpoint_type* p_peer_endpoint, boost::system::error_code& ec, + typename std::enable_if::value>::type* = 0) { + auto& next_layer_local_endpoint = + impl.p_local_endpoint->next_layer_endpoint(); + connection_queue* p_queue = nullptr; + + { + boost::recursive_mutex::scoped_lock lock(accept_mutex_); + auto queue_it = pending_connections_.find(next_layer_local_endpoint); + + if (queue_it == std::end(pending_connections_)) { + ec.assign(ssf::error::bad_address, ssf::error::get_ssf_category()); + return ec; + } + + p_queue = &queue_it->second; + } + + /// Waiting for new connections from next layer (p_queue is populated + /// asynchronously) + while (true) { + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + boost::recursive_mutex::scoped_lock lock(accept_mutex_); + if (p_queue->empty()) { + continue; + } + + auto& peer_impl = peer.native_handle(); + auto connection = std::move(p_queue->front()); + p_queue->pop(); + peer_impl.p_next_layer_socket = connection.first; + peer_impl.p_remote_endpoint = std::move(connection.second); + break; + } + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, void(boost::system::error_code)) + async_accept(implementation_type& impl, + boost::asio::basic_socket& peer, + endpoint_type* p_peer_endpoint, AcceptHandler&& handler, + typename std::enable_if::value>::type* = 0) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + if (!impl.p_local_endpoint) { + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::identifier_removed, + ssf::error::get_ssf_category())); + return init.result.get(); + } + + auto& peer_impl = peer.native_handle(); + peer_impl.p_local_endpoint = impl.p_local_endpoint; + peer_impl.p_remote_endpoint = + std::make_shared(); + peer_impl.p_remote_endpoint->set(); + + typedef detail::CircuitAcceptOp< + typename boost::asio::basic_socket::native_handle_type, + endpoint_type, + typename boost::asio::handler_type< + AcceptHandler, void(boost::system::error_code)>::type> + CircuitAcceptOp; + CircuitAcceptOp op_handler(peer_impl, p_peer_endpoint, init.handler); + + { + boost::recursive_mutex::scoped_lock lock(accept_mutex_); + // Create and save a new op_queue at + // [impl.p_local_endpoint->next_layer_endpoint()] index + // in pending_accepts + auto& op_queue = + pending_accepts_[impl.p_local_endpoint->next_layer_endpoint()]; + + typedef io::pending_accept_operation op; + typename op::ptr p = { + boost::asio::detail::addressof(op_handler), + boost_asio_handler_alloc_helpers::allocate(sizeof(op), op_handler), + 0}; + + p.p = new (p.v) + op(peer, peer_impl.p_remote_endpoint.get(), std::move(op_handler)); + op_queue.push(p.p); + p.v = p.p = 0; + } + connection_queue_handler(); + + return init.result.get(); + } + + private: + /// Start async accepting new connection on the next layer + void start_accepting(p_next_acceptor_type p_next_layer_acceptor, + p_next_socket_type p_next_layer_socket = nullptr, + p_next_endpoint_type p_next_layer_endpoint = nullptr) { + if (p_next_layer_acceptor->is_open()) { + if (!p_next_layer_socket) { + p_next_layer_socket = std::make_shared< + typename protocol_type::next_layer_protocol::socket>( + this->get_io_service()); + } + + if (!p_next_layer_endpoint) { + p_next_layer_endpoint = std::make_shared< + typename protocol_type::next_layer_protocol::endpoint>(); + } + + p_next_layer_acceptor->async_accept( + *p_next_layer_socket, *p_next_layer_endpoint, + boost::bind(&basic_CircuitAcceptor_service::accepted, this, + p_next_layer_acceptor, p_next_layer_socket, + p_next_layer_endpoint, _1)); + } + } + + /// New connection was accepted by the next layer + void accepted(p_next_acceptor_type p_next_layer_acceptor, + p_next_socket_type p_next_layer_socket, + p_next_endpoint_type p_next_layer_endpoint, + const boost::system::error_code& ec) { + if (ec) { + // TODO : log error + return; + } + + { + boost::recursive_mutex::scoped_lock lock2(bind_mutex_); + boost::recursive_mutex::scoped_lock lock1(accept_mutex_); + + auto next_local_endpoint_it = + next_local_endpoints_.find(p_next_layer_acceptor); + + if (next_local_endpoint_it == std::end(next_local_endpoints_)) { + return; + } + + auto& next_local_endpoint = next_local_endpoint_it->second; + + // Create empty endpoint to store the future remote endpoint received by + // the circuit protocol + auto p_remote_endpoint = + std::make_shared(*p_next_layer_endpoint); + + auto p_received_endpoint = std::make_shared(); + + p_received_endpoint->endpoint_context().default_parameters = + serialize_parameter_stack(default_parameters_); + + circuit_policy::AsyncInitConnection( + *p_next_layer_socket, p_received_endpoint.get(), + circuit_policy::server, + boost::bind( + &basic_CircuitAcceptor_service::connection_initiated_handler, + this, p_next_layer_socket, std::move(p_remote_endpoint), + p_received_endpoint, next_local_endpoint, _1)); + } + + start_accepting(p_next_layer_acceptor); + } + + void connection_initiated_handler(p_next_socket_type p_next_layer_socket, + p_endpoint_type p_remote_endpoint, + p_endpoint_type p_received_endpoint, + next_endpoint_type next_local_endpoint, + const boost::system::error_code& ec) { + if (ec) { + // TODO : log error + return; + } + + if (detail::is_endpoint_forwarding(*p_received_endpoint)) { + // Received endpoint is not the final destination : + // create and connect a new socket to the next endpoint + // and forward its data + do_connection_forward(std::move(p_next_layer_socket), + std::move(p_received_endpoint)); + return; + } + + p_remote_endpoint->endpoint_context() = + p_received_endpoint->endpoint_context(); + + p_remote_endpoint->endpoint_context().id = + p_remote_endpoint->endpoint_context().details; + + boost::recursive_mutex::scoped_lock lock1(accept_mutex_); + + // Connection is only accepted if there is a binding + // of a non forward endpoint link to this next local endpoint + if (pending_connections_.count(next_local_endpoint)) { + circuit_policy::AsyncValidateConnection( + *p_next_layer_socket, p_remote_endpoint.get(), ec.value(), + boost::bind( + &basic_CircuitAcceptor_service::connection_validated_handler, + this, std::move(next_local_endpoint), p_next_layer_socket, + p_remote_endpoint, _1)); + } else { + boost::system::error_code close_ec; + p_next_layer_socket->shutdown(boost::asio::socket_base::shutdown_both, + close_ec); + p_next_layer_socket->close(close_ec); + } + } + + void connection_validated_handler(next_endpoint_type next_local_endpoint, + p_next_socket_type p_next_layer_socket, + p_endpoint_type p_remote_endpoint, + const boost::system::error_code& ec) { + if (ec) { + // TODO : log error + return; + } + + this->do_connection_accept(next_local_endpoint, + std::make_pair(std::move(p_next_layer_socket), + std::move(p_remote_endpoint))); + } + + /// Create and connect a new socket to the remote endpoint + /// and forward its data + void do_connection_forward(p_next_socket_type p_next_socket, + p_endpoint_type p_remote_endpoint) { + auto p_forward_socket = + std::make_shared(this->get_io_service()); + + p_forward_socket->async_connect( + *p_remote_endpoint, + boost::bind(&basic_CircuitAcceptor_service::connected_handler, this, + p_remote_endpoint, std::move(p_next_socket), + p_forward_socket, _1)); + } + + void connected_handler(p_endpoint_type p_remote_endpoint, + p_next_socket_type p_next_socket, + p_socket_type p_forward_socket, + const boost::system::error_code& ec) { + if (ec) { + // TODO : log error + circuit_policy::AsyncValidateConnection( + *p_next_socket, p_remote_endpoint.get(), ec.value(), + [](const boost::system::error_code&) {}); + + boost::system::error_code close_ec; + p_next_socket->shutdown(boost::asio::socket_base::shutdown_both, + close_ec); + p_next_socket->close(close_ec); + p_forward_socket->shutdown(boost::asio::socket_base::shutdown_both, + close_ec); + p_forward_socket->close(close_ec); + return; + } + + circuit_policy::AsyncValidateConnection( + *p_next_socket, p_remote_endpoint.get(), ec.value(), + boost::bind( + &basic_CircuitAcceptor_service::connection_forwarded_handler, this, + p_next_socket, std::move(p_forward_socket), _1)); + } + + void connection_forwarded_handler(p_next_socket_type p_next_socket, + p_socket_type p_forward_socket, + const boost::system::error_code& ec) { + if (ec) { + // TODO : log error + boost::system::error_code close_ec; + p_next_socket->shutdown(boost::asio::socket_base::shutdown_both, + close_ec); + p_next_socket->close(close_ec); + p_forward_socket->shutdown(boost::asio::socket_base::shutdown_both, + close_ec); + p_forward_socket->close(close_ec); + return; + } + + // pipe data between p_next_socket and p_forward_socket + auto p_session = + ssf::SessionForwarder::create( + &this->manager_, + std::move(*p_forward_socket->native_handle().p_next_layer_socket), + std::move(*p_next_socket)); + + boost::system::error_code start_ec; + this->manager_.start(p_session, start_ec); + } + + /// Unqueue accept operation after accepting connection + void do_connection_accept(const next_endpoint_type& next_local_endpoint, + pending_connection connection) { + boost::recursive_mutex::scoped_lock lock(accept_mutex_); + + auto connection_queue_it = pending_connections_.find(next_local_endpoint); + + // Connection is only accepted if there is a binding + // of a non forward endpoint link to this next local endpoint + if (connection_queue_it == std::end(pending_connections_)) { + boost::system::error_code ec; + connection.first->shutdown(boost::asio::socket_base::shutdown_both, ec); + connection.first->close(ec); + return; + } + + auto& connection_queue = connection_queue_it->second; + connection_queue.emplace(std::move(connection)); + + connection_queue_handler(); + } + + void close_next_layer_acceptor(p_next_acceptor_type p_acceptor) { + auto endpoint_it = next_local_endpoints_.find(p_acceptor); + + if (endpoint_it == std::end(next_local_endpoints_)) { + return; + } + + auto acceptor_it = next_acceptors_.find(endpoint_it->second); + + if (acceptor_it != std::end(next_acceptors_)) { + boost::system::error_code ec; + acceptor_it->second->close(ec); + } + + next_local_endpoints_.erase(endpoint_it); + next_acceptors_.erase(acceptor_it); + + return; + } + + /// Execute accept handler after peer connection + void connection_queue_handler( + const boost::system::error_code& ec = boost::system::error_code()) { + boost::recursive_mutex::scoped_lock lock(accept_mutex_); + + for (auto& connection_pair : pending_connections_) { + if (!connection_pair.second.size()) { + continue; + } + + auto accept_queue_it = pending_accepts_.find(connection_pair.first); + + if (accept_queue_it == std::end(pending_accepts_)) { + continue; + } + + auto& connection_queue = connection_pair.second; + auto& accept_queue = accept_queue_it->second; + // auto next_local_endpoint = connection_pair.first; + + if (!connection_queue.empty() && !accept_queue.empty()) { + auto connection = std::move(connection_queue.front()); + connection_queue.pop(); + auto* p_accept_op = accept_queue.front(); + accept_queue.pop(); + + if (!ec) { + p_accept_op->set_p_endpoint(*connection.second); + auto& peer = p_accept_op->peer(); + auto& native_handle = peer.native_handle(); + native_handle.p_next_layer_socket = std::move(connection.first); + native_handle.p_remote_endpoint = connection.second; + } + + auto do_complete = [p_accept_op, ec]() { p_accept_op->complete(ec); }; + this->get_io_service().post(do_complete); + + this->get_io_service().post(boost::bind( + &basic_CircuitAcceptor_service::connection_queue_handler, this, + ec)); + } + } + } + + void clean_pending_accepts(const next_endpoint_type& next_layer_endpoint, + const boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(accept_mutex_); + auto accept_queue_it = pending_accepts_.find(next_layer_endpoint); + + if (accept_queue_it == pending_accepts_.end()) { + return; + } + + auto& accept_queue = accept_queue_it->second; + while (!accept_queue.empty()) { + auto* p_accept_op = accept_queue.front(); + accept_queue.pop(); + p_accept_op->complete(ec); + } + + pending_accepts_.erase(next_layer_endpoint); + } + + void shutdown_service() { manager_.stop_all(); } + + private: + boost::recursive_mutex bind_mutex_; + ParameterStack default_parameters_; + std::map next_acceptors_; + std::map next_local_endpoints_; + std::map input_bindings_; + std::map forward_bindings_; + std::set listening_; + + boost::recursive_mutex accept_mutex_; + std::map pending_accepts_; + std::map pending_connections_; + + Manager manager_; +}; + +#include + +} // data_link +} // layer +} // ssf + +#endif // SSF_LAYER_DATA_LINK_BASIC_CIRCUIT_ACCEPTOR_SERVICE_H_ diff --git a/src/framework/ssf/layer/data_link/basic_circuit_protocol.h b/src/framework/ssf/layer/data_link/basic_circuit_protocol.h new file mode 100644 index 00000000..7fb0d920 --- /dev/null +++ b/src/framework/ssf/layer/data_link/basic_circuit_protocol.h @@ -0,0 +1,170 @@ +#ifndef SSF_LAYER_DATA_LINK_CIRCUIT_PROTOCOL_H_ +#define SSF_LAYER_DATA_LINK_CIRCUIT_PROTOCOL_H_ + +#include + +#include + +#include +#include +#include + +#include + +#include "ssf/layer/basic_resolver.h" +#include "ssf/layer/basic_endpoint.h" + +#include "ssf/layer/data_link/basic_circuit_acceptor_service.h" +#include "ssf/layer/data_link/basic_circuit_socket_service.h" +#include "ssf/layer/data_link/circuit_helpers.h" +#include "ssf/layer/data_link/circuit_endpoint_context.h" + +#include "ssf/layer/parameters.h" +#include "ssf/layer/protocol_attributes.h" + +namespace ssf { +namespace layer { +namespace data_link { + +template class CircuitPolicy> +class basic_CircuitProtocol { + public: + enum { + id = 5, + overhead = 0, + facilities = ssf::layer::facilities::stream, + mtu = NextLayer::mtu - overhead + }; + enum { endpoint_stack_size = 1 + NextLayer::endpoint_stack_size }; + + static const char* NAME; + + typedef CircuitPolicy circuit_policy; + typedef NextLayer next_layer_protocol; + typedef int socket_context; + typedef int acceptor_context; + typedef CircuitEndpointContext endpoint_context_type; + using next_endpoint_type = typename next_layer_protocol::endpoint; + + typedef basic_VirtualLink_endpoint endpoint; + typedef basic_VirtualLink_resolver resolver; + typedef boost::asio::basic_stream_socket< + basic_CircuitProtocol, basic_CircuitSocket_service> + socket; + typedef boost::asio::basic_socket_acceptor< + basic_CircuitProtocol, + basic_CircuitAcceptor_service> acceptor; + + private: + using query = typename resolver::query; + + public: + static std::string get_name() { + std::string name(NAME); + name += "_" + next_layer_protocol::get_name(); + return name; + } + + static endpoint make_endpoint(boost::asio::io_service& io_service, + typename query::const_iterator parameters_it, + uint32_t, boost::system::error_code& ec) { + auto context = detail::make_circuit_context(io_service, *parameters_it); + auto next_layer_endpoint = + next_layer_protocol::make_endpoint(io_service, ++parameters_it, id, ec); + + return endpoint(context, next_layer_endpoint); + } + + static void add_params_from_property_tree( + query* p_query, const boost::property_tree::ptree& property_tree, + bool connect, boost::system::error_code& ec) { + auto layer_name = property_tree.get_child_optional("layer"); + if (!layer_name || layer_name.get().data() != NAME) { + ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + return; + } + + auto layer_parameters = property_tree.get_child_optional("parameters"); + if (!layer_parameters) { + ec.assign(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()); + return; + } + + auto sublayer = property_tree.get_child_optional("sublayer"); + if (!sublayer) { + ec.assign(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()); + return; + } + + bool forward = false; + auto given_forward = (*layer_parameters).get_child_optional("forward"); + forward = given_forward && given_forward.get().get_value(); + + ParameterStack next_layer_parameters; + next_layer_protocol::add_params_from_property_tree(&next_layer_parameters, + *sublayer, connect, ec); + if (ec) { + return; + } + + if (!connect) { + // acceptor query + auto given_local_id = (*layer_parameters).get_child_optional("local_id"); + if (!given_local_id) { + ec.assign(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()); + return; + } + std::string local_id = given_local_id.get().data(); + ParameterStack default_params = {}; + if (!forward) { + *p_query = make_acceptor_parameter_stack(local_id, default_params, + next_layer_parameters); + } else { + *p_query = make_forwarding_acceptor_parameter_stack( + local_id, default_params, next_layer_parameters); + } + } else { + // connect query + auto given_remote_id = + (*layer_parameters).get_child_optional("remote_id"); + if (!given_remote_id) { + ec.assign(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()); + return; + } + std::string remote_id = given_remote_id.get().data(); + NodeParameterList nodes; + + auto given_nodes = (*layer_parameters).get_child_optional("nodes"); + if (given_nodes && given_nodes->size() > 0) { + nodes = nodes_property_tree_to_node_list( + *given_nodes, connect, ec); + } + + if (ec) { + return; + } + + nodes.PushBackNode(); + auto node_stack_end_it = next_layer_parameters.rend(); + for (auto param_node_it = next_layer_parameters.rbegin(); + param_node_it != node_stack_end_it; ++param_node_it) { + nodes.AddTopLayerToBackNode(*param_node_it); + } + + *p_query = make_client_full_circuit_parameter_stack(remote_id, nodes); + } + } +}; + +template class CircuitPolicy> +const char* basic_CircuitProtocol::NAME = "CIRCUIT"; + +} // data_link +} // layer +} // ssf + +#endif // SSF_LAYER_DATA_LINK_CIRCUIT_PROTOCOL_H_ diff --git a/src/framework/ssf/layer/data_link/basic_circuit_socket_service.h b/src/framework/ssf/layer/data_link/basic_circuit_socket_service.h new file mode 100644 index 00000000..53af3965 --- /dev/null +++ b/src/framework/ssf/layer/data_link/basic_circuit_socket_service.h @@ -0,0 +1,276 @@ +#ifndef SSF_LAYER_DATA_LINK_BASIC_CIRCUIT_SOCKET_SERVICE_H_ +#define SSF_LAYER_DATA_LINK_BASIC_CIRCUIT_SOCKET_SERVICE_H_ + +#include + +#include + +#include +#include + +#include "ssf/io/handler_helpers.h" +#include "ssf/error/error.h" + +#include "ssf/layer/basic_impl.h" + +#include "ssf/layer/data_link/circuit_helpers.h" +#include "ssf/layer/data_link/circuit_op.h" + +namespace ssf { +namespace layer { +namespace data_link { + +#include + +template +class basic_CircuitSocket_service + : public boost::asio::detail::service_base< + basic_CircuitSocket_service> { + public: + /// The protocol type. + typedef Protocol protocol_type; + /// The endpoint type. + typedef typename protocol_type::endpoint endpoint_type; + typedef typename protocol_type::resolver resolver_type; + + typedef basic_socket_impl implementation_type; + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + private: + typedef typename protocol_type::circuit_policy circuit_policy; + typedef typename protocol_type::next_layer_protocol::socket next_socket_type; + typedef std::shared_ptr p_next_socket_type; + typedef std::shared_ptr p_endpoint_type; + + public: + explicit basic_CircuitSocket_service( + boost::asio::io_service& io_service) + : boost::asio::detail::service_base< + basic_CircuitSocket_service>(io_service) {} + + virtual ~basic_CircuitSocket_service() {} + + void construct(implementation_type& impl) { + impl.p_next_layer_socket = + std::make_shared(this->get_io_service()); + } + + void destroy(implementation_type& impl) { + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_socket.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->open(typename protocol_type::next_layer_protocol(), + ec); + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + + return ec; + } + + bool is_open(const implementation_type& impl) const { + return impl.p_next_layer_socket->is_open(); + } + + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_remote_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_remote_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_local_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->close(ec); + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + bool at_mark(const implementation_type& impl, + boost::system::error_code& ec) const { + return impl.p_next_layer_socket->at_mark(ec); + } + + std::size_t available(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return 0; + } + + return impl.p_next_layer_socket->available(ec); + } + + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + return ec; + } + + return impl.p_next_layer_socket->cancel(ec); + } + + /// Nothing to bind for this layer, bind the next layer with its endpoint + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) { + impl.p_local_endpoint = std::make_shared(endpoint); + return impl.p_next_layer_socket->bind(endpoint.next_layer_endpoint(), ec); + } + + /// Connect next layer to its endpoint + /// Connect the circuit + boost::system::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + boost::system::error_code& ec) { + impl.p_remote_endpoint = std::make_shared(peer_endpoint); + + impl.p_next_layer_socket->connect(peer_endpoint.next_layer_endpoint(), ec); + + impl.p_local_endpoint = std::make_shared(); + impl.p_local_endpoint->endpoint_context().id = detail::get_local_id(); + auto& next_layer_endpoint = impl.p_local_endpoint->next_layer_endpoint(); + next_layer_endpoint = impl.p_next_layer_socket->local_endpoint(); + impl.p_local_endpoint->set(); + + if (ec) { + return ec; + } + + circuit_policy::InitConnection(*impl.p_next_layer_socket, + impl.p_remote_endpoint.get(), + circuit_policy::client, ec); + + return ec; + } + + /// Async connect next layer to its endpoint + /// Then async connect the circuit with the provided endpoint + /// Then async execute handler + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + ConnectHandler&& handler) { + boost::asio::detail::async_result_init< + ConnectHandler, void(boost::system::error_code)> + init(std::forward(handler)); + + impl.p_remote_endpoint = std::make_shared(peer_endpoint); + impl.p_local_endpoint = std::make_shared(); + impl.p_local_endpoint->endpoint_context().id = detail::get_local_id(); + + detail::CircuitConnectOp< + protocol_type, next_socket_type, endpoint_type, + typename boost::asio::handler_type:: + type>(*impl.p_next_layer_socket, impl.p_local_endpoint.get(), + impl.p_remote_endpoint.get(), init.handler)(); + + return init.result.get(); + } + + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + boost::system::error_code& ec) { + ec.assign(ssf::error::function_not_supported, + ssf::error::get_ssf_category()); + return 0; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + impl.p_next_layer_socket->async_send(buffers, init.handler); + + return init.result.get(); + } + + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + boost::system::error_code& ec) { + ec.assign(ssf::error::function_not_supported, + ssf::error::get_ssf_category()); + return 0; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + impl.p_next_layer_socket->async_receive(buffers, init.handler); + + return init.result.get(); + } + + boost::system::error_code shutdown( + implementation_type& impl, boost::asio::socket_base::shutdown_type what, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->shutdown(what, ec); + } + + private: + void shutdown_service() {} +}; + +#include + +} // data_link +} // layer +} // ssf + +#endif // SSF_LAYER_DATA_LINK_BASIC_CIRCUIT_SOCKET_SERVICE_H_ diff --git a/src/framework/ssf/layer/data_link/circuit_endpoint_context.h b/src/framework/ssf/layer/data_link/circuit_endpoint_context.h new file mode 100644 index 00000000..1da160a7 --- /dev/null +++ b/src/framework/ssf/layer/data_link/circuit_endpoint_context.h @@ -0,0 +1,43 @@ +#ifndef SSF_LAYER_DATA_LINK_CIRCUIT_ENDPOINT_CONTEXT_H_ +#define SSF_LAYER_DATA_LINK_CIRCUIT_ENDPOINT_CONTEXT_H_ + +#include + +#include +#include + +namespace ssf { +namespace layer { +namespace data_link { + +struct CircuitEndpointContext { + using ID = std::string; + using SerializedForwardBlocks = std::string; + using Details = std::string; + using SerializedDefaultParameters = std::string; + + bool operator==(const CircuitEndpointContext& rhs) const { + return id == rhs.id; + } + + bool operator!=(const CircuitEndpointContext& rhs) const { + return !(*this == rhs); + } + + bool operator<(const CircuitEndpointContext& rhs) const { + return id < rhs.id; + } + + bool forward; + // TODO change std::string to uint32_t for id (?) + ID id; + SerializedForwardBlocks forward_blocks; + SerializedDefaultParameters default_parameters; + Details details; +}; + +} // data_link +} // layer +} // ssf + +#endif // SSF_LAYER_DATA_LINK_CIRCUIT_ENDPOINT_CONTEXT_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/data_link/circuit_helpers.cpp b/src/framework/ssf/layer/data_link/circuit_helpers.cpp new file mode 100644 index 00000000..68e0649c --- /dev/null +++ b/src/framework/ssf/layer/data_link/circuit_helpers.cpp @@ -0,0 +1,157 @@ +#include "ssf/layer/data_link/circuit_helpers.h" + +namespace ssf { +namespace layer { +namespace data_link { +namespace detail { + +CircuitEndpointContext::ID get_local_id() { return "-1"; } + +LayerParameters make_next_forward_node_ciruit_layer_parameters( + const ParameterStack &next_node_full_stack) { + LayerParameters circuit_link_parameters; + circuit_link_parameters["forward"] = "1"; + circuit_link_parameters["circuit_id"] = ""; + circuit_link_parameters["circuit_nodes"] = + serialize_parameter_stack(next_node_full_stack); + circuit_link_parameters["details"] = ""; + + return circuit_link_parameters; +} + +ParameterStack make_destination_node_parameter_stack() { + LayerParameters end_parameters; + end_parameters["forward"] = "0"; + end_parameters["circuit_id"] = ""; + end_parameters["circuit_nodes"] = ""; + end_parameters["details"] = get_local_id(); + + ParameterStack end_stack; + end_stack.push_back(std::move(end_parameters)); + + return end_stack; +} + +CircuitEndpointContext make_circuit_context(boost::asio::io_service &io_service, + const LayerParameters ¶meters) { + auto forward_str = helpers::GetField("forward", parameters); + bool forward = false; + + try { + forward = !!std::stoul(forward_str); + } catch (const std::exception &) { + forward = false; + } + + auto id = helpers::GetField("circuit_id", parameters); + auto forward_blocks = + helpers::GetField("circuit_nodes", parameters); + auto details = helpers::GetField("details", parameters); + auto default_parameters = + helpers::GetField("default_parameters", parameters); + + return CircuitEndpointContext( + {forward, id, forward_blocks, default_parameters, details}); +} + +} // detail + +void NodeParameterList::PushFrontNode(ParameterStack new_node_stack) { + nodes_.push_back(std::move(new_node_stack)); +} + +ParameterStack &NodeParameterList::FrontNode() { return nodes_.back(); } +const ParameterStack &NodeParameterList::FrontNode() const { + return nodes_.back(); +} +void NodeParameterList::PopFrontNode() { nodes_.pop_back(); } + +void NodeParameterList::AddTopLayerToFrontNode(LayerParameters top_layer) { + nodes_.back().push_front(std::move(top_layer)); +} + +void NodeParameterList::PushBackNode(ParameterStack new_node_stack) { + nodes_.push_front(std::move(new_node_stack)); +} + +ParameterStack &NodeParameterList::BackNode() { return nodes_.front(); } +const ParameterStack &NodeParameterList::BackNode() const { + return nodes_.front(); +} +void NodeParameterList::PopBackNode() { nodes_.pop_front(); } + +void NodeParameterList::AddTopLayerToBackNode(LayerParameters top_layer) { + nodes_.front().push_front(std::move(top_layer)); +} + +NodeParameterList::NodeList::iterator NodeParameterList::begin() { + return nodes_.begin(); +} +NodeParameterList::NodeList::iterator NodeParameterList::end() { + return nodes_.end(); +} + +NodeParameterList::NodeList::const_iterator NodeParameterList::begin() const { + return nodes_.begin(); +} +NodeParameterList::NodeList::const_iterator NodeParameterList::end() const { + return nodes_.end(); +} + +ParameterStack make_acceptor_parameter_stack( + std::string local_id, ParameterStack default_parameters, + ParameterStack next_layer_parameters) { + LayerParameters circuit_link_parameters; + circuit_link_parameters["forward"] = "0"; + circuit_link_parameters["circuit_id"] = std::move(local_id); + circuit_link_parameters["circuit_nodes"] = ""; + circuit_link_parameters["details"] = ""; + circuit_link_parameters["default_parameters"] = + serialize_parameter_stack(default_parameters); + + ParameterStack acceptor_parameters(std::move(next_layer_parameters)); + acceptor_parameters.push_front(std::move(circuit_link_parameters)); + + return acceptor_parameters; +} + +ParameterStack make_forwarding_acceptor_parameter_stack( + std::string local_id, ParameterStack default_parameters, + ParameterStack next_layer_parameters) { + LayerParameters circuit_link_parameters; + circuit_link_parameters["forward"] = "1"; + circuit_link_parameters["circuit_id"] = std::move(local_id); + circuit_link_parameters["circuit_nodes"] = ""; + circuit_link_parameters["details"] = ""; + circuit_link_parameters["default_parameters"] = + serialize_parameter_stack(default_parameters); + + ParameterStack acceptor_parameters(std::move(next_layer_parameters)); + acceptor_parameters.push_front(std::move(circuit_link_parameters)); + + return acceptor_parameters; +} + +ParameterStack make_client_full_circuit_parameter_stack( + std::string remote_id, const NodeParameterList &nodes) { + auto destination_node_stack = detail::make_destination_node_parameter_stack(); + + auto next_node_stack = std::move(destination_node_stack); + + for (const auto &node : nodes) { + auto current_node_partial_stack = node; + current_node_partial_stack.push_front( + detail::make_next_forward_node_ciruit_layer_parameters( + next_node_stack)); + next_node_stack = std::move(current_node_partial_stack); + } + + next_node_stack.front()["forward"] = "0"; + next_node_stack.front()["circuit_id"] = std::move(remote_id); + + return next_node_stack; +} + +} // data_link +} // layer +} // ssf diff --git a/src/framework/ssf/layer/data_link/circuit_helpers.h b/src/framework/ssf/layer/data_link/circuit_helpers.h new file mode 100644 index 00000000..8b9278d0 --- /dev/null +++ b/src/framework/ssf/layer/data_link/circuit_helpers.h @@ -0,0 +1,106 @@ +#ifndef SSF_LAYER_DATA_LINK_CIRCUIT_HELPERS_H_ +#define SSF_LAYER_DATA_LINK_CIRCUIT_HELPERS_H_ + +#include +#include +#include + +#include + +#include + +#include "ssf/utils/map_helpers.h" + +#include "ssf/layer/parameters.h" + +#include "ssf/layer/data_link/circuit_endpoint_context.h" + +namespace ssf { +namespace layer { +namespace data_link { + +namespace detail { + +CircuitEndpointContext::ID get_local_id(); + +LayerParameters make_next_forward_node_ciruit_layer_parameters( + const ParameterStack &next_node_full_stack); + +ParameterStack make_destination_node_parameter_stack(); + +CircuitEndpointContext make_circuit_context(boost::asio::io_service &io_service, + const LayerParameters ¶meters); + +} // detail + +class NodeParameterList { + private: + typedef std::list NodeList; + + public: + void PushFrontNode(ParameterStack new_node_stack = ParameterStack()); + + ParameterStack &FrontNode(); + const ParameterStack &FrontNode() const; + void PopFrontNode(); + + void AddTopLayerToFrontNode(LayerParameters top_layer); + + void PushBackNode(ParameterStack new_node_stack = ParameterStack()); + + ParameterStack &BackNode(); + const ParameterStack &BackNode() const; + void PopBackNode(); + + void AddTopLayerToBackNode(LayerParameters top_layer); + + NodeList::iterator begin(); + NodeList::iterator end(); + + NodeList::const_iterator begin() const; + NodeList::const_iterator end() const; + + private: + NodeList nodes_; +}; + +ParameterStack make_acceptor_parameter_stack( + std::string local_id, ParameterStack default_parameters, + ParameterStack next_layer_parameters); + +ParameterStack make_forwarding_acceptor_parameter_stack( + std::string local_id, ParameterStack default_parameters, + ParameterStack next_layer_parameters); + +ParameterStack make_client_full_circuit_parameter_stack( + std::string remote_id, const NodeParameterList &nodes); + +template +NodeParameterList nodes_property_tree_to_node_list( + const boost::property_tree::ptree &nodes_tree, bool connect, + boost::system::error_code &ec) { + NodeParameterList nodes; + + for (auto &node : nodes_tree) { + ParameterStack node_stack; + NodeProtocol::add_params_from_property_tree(&node_stack, node.second, + connect, ec); + if (ec) { + return nodes; + } + nodes.PushBackNode(); + auto node_stack_end_it = node_stack.rend(); + for (auto param_node_it = node_stack.rbegin(); + param_node_it != node_stack_end_it; ++param_node_it) { + nodes.AddTopLayerToBackNode(*param_node_it); + } + } + + return nodes; +} + +} // data_link +} // layer +} // ssf + +#endif // SSF_LAYER_DATA_LINK_CIRCUIT_HELPERS_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/data_link/circuit_op.h b/src/framework/ssf/layer/data_link/circuit_op.h new file mode 100644 index 00000000..dd24bc7c --- /dev/null +++ b/src/framework/ssf/layer/data_link/circuit_op.h @@ -0,0 +1,202 @@ +#ifndef SSF_LAYER_DATA_LINK_CIRCUIT_OP_H_ +#define SSF_LAYER_DATA_LINK_CIRCUIT_OP_H_ + +#include + +#include + +#include + +#include +#include +#include + +namespace ssf { +namespace layer { +namespace data_link { +namespace detail { + +template +class CircuitConnectOp { + public: + CircuitConnectOp(Stream& stream, Endpoint* p_local_endpoint, + Endpoint* p_remote_endpoint, ConnectHandler handler) + : coro_(), + stream_(stream), + p_local_endpoint_(p_local_endpoint), + p_remote_endpoint_(p_remote_endpoint), + handler_(std::move(handler)) {} + + CircuitConnectOp(const CircuitConnectOp& other) + : coro_(other.coro_), + stream_(other.stream_), + p_local_endpoint_(other.p_local_endpoint_), + p_remote_endpoint_(other.p_remote_endpoint_), + handler_(other.handler_) {} + + CircuitConnectOp(CircuitConnectOp&& other) + : coro_(std::move(other.coro_)), + stream_(other.stream_), + p_local_endpoint_(other.p_local_endpoint_), + p_remote_endpoint_(other.p_remote_endpoint_), + handler_(std::move(other.handler_)) {} + +#include + void operator()( + const boost::system::error_code& ec = boost::system::error_code()) { + if (!ec) { + reenter(coro_) { + yield stream_.async_connect( + p_remote_endpoint_->next_layer_endpoint(), std::move(*this)); + + boost::system::error_code endpoint_ec; + auto& next_layer_endpoint = p_local_endpoint_->next_layer_endpoint(); + next_layer_endpoint = stream_.local_endpoint(endpoint_ec); + p_local_endpoint_->set(); + + Protocol::circuit_policy::AsyncInitConnection( + stream_, p_remote_endpoint_, Protocol::circuit_policy::client, + std::move(handler_)); + } + } else { + // error + handler_(ec); + } + } +#include + + inline ConnectHandler& handler() { return handler_; } + + private: + boost::asio::coroutine coro_; + Stream& stream_; + Endpoint* p_local_endpoint_; + Endpoint* p_remote_endpoint_; + ConnectHandler handler_; +}; + +template +inline void* asio_handler_allocate( + std::size_t size, CircuitConnectOp* this_handler) { + return boost_asio_handler_alloc_helpers::allocate(size, + this_handler->handler()); +} + +template +inline void asio_handler_deallocate( + void* pointer, std::size_t size, + CircuitConnectOp* + this_handler) { + boost_asio_handler_alloc_helpers::deallocate(pointer, size, + this_handler->handler()); +} + +template +inline bool asio_handler_is_continuation(CircuitConnectOp< + Protocol, Stream, Endpoint, ConnectHandler>* this_handler) { + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->handler()); +} + +template +inline void asio_handler_invoke( + Function& function, CircuitConnectOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +template +inline void asio_handler_invoke( + const Function& function, CircuitConnectOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +template +class CircuitAcceptOp { + public: + CircuitAcceptOp(typename std::remove_reference::type& peer_impl, + Endpoint* p_peer_endpoint, AcceptHandler handler) + : peer_impl_(peer_impl), + p_peer_endpoint_(std::move(p_peer_endpoint)), + handler_(std::move(handler)) {} + + CircuitAcceptOp(const CircuitAcceptOp& other) + : peer_impl_(other.peer_impl_), + p_peer_endpoint_(other.p_peer_endpoint_), + handler_(other.handler_) {} + + CircuitAcceptOp(CircuitAcceptOp&& other) + : peer_impl_(other.peer_impl_), + p_peer_endpoint_(std::move(other.p_peer_endpoint_)), + handler_(std::move(other.handler_)) {} + +#include + void operator()( + const boost::system::error_code& ec = boost::system::error_code()) { + if (p_peer_endpoint_) { + *p_peer_endpoint_ = *peer_impl_.p_remote_endpoint; + } + + handler_(ec); + } +#include + + inline AcceptHandler& handler() { return handler_; } + + private: + PeerImpl& peer_impl_; + Endpoint* p_peer_endpoint_; + AcceptHandler handler_; +}; + +template +inline void* asio_handler_allocate( + std::size_t size, + CircuitAcceptOp* this_handler) { + return boost_asio_handler_alloc_helpers::allocate(size, + this_handler->handler()); +} + +template +inline void asio_handler_deallocate( + void* pointer, std::size_t size, + CircuitAcceptOp* this_handler) { + boost_asio_handler_alloc_helpers::deallocate(pointer, size, + this_handler->handler()); +} + +template +inline bool asio_handler_is_continuation( + CircuitAcceptOp* this_handler) { + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->handler()); +} + +template +inline void asio_handler_invoke( + Function& function, + CircuitAcceptOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +template +inline void asio_handler_invoke( + const Function& function, + CircuitAcceptOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +} // detail +} // data_link_layer +} // layer +} // ssf + +#endif // SSF_LAYER_DATA_LINK_LAYER_CIRCUIT_OP_H_ diff --git a/src/framework/ssf/layer/data_link/helpers.h b/src/framework/ssf/layer/data_link/helpers.h new file mode 100644 index 00000000..9edeb093 --- /dev/null +++ b/src/framework/ssf/layer/data_link/helpers.h @@ -0,0 +1,27 @@ +#ifndef SSF_LAYER_DATA_LINK_HELPERS_H_ +#define SSF_LAYER_DATA_LINK_HELPERS_H_ + +#include + +#include +#include + +#include "ssf/layer/basic_endpoint.h" + +namespace ssf { +namespace layer { +namespace data_link { +namespace detail { + +template +bool is_endpoint_forwarding( + const basic_VirtualLink_endpoint& endpoint) { + return endpoint.is_set() && endpoint.endpoint_context().forward; +} + +} // detail +} // data_link +} // layer +} // ssf + +#endif // SSF_LAYER_DATA_LINK_HELPERS_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/data_link/simple_circuit_policy.h b/src/framework/ssf/layer/data_link/simple_circuit_policy.h new file mode 100644 index 00000000..6b6ca3c0 --- /dev/null +++ b/src/framework/ssf/layer/data_link/simple_circuit_policy.h @@ -0,0 +1,158 @@ +#ifndef SSF_LAYER_DATA_LINK_SIMPLE_CIRCUIT_POLICY_H_ +#define SSF_LAYER_DATA_LINK_SIMPLE_CIRCUIT_POLICY_H_ + +#include + +#include + +#include + +#include "ssf/network/object_io_helpers.h" + +#include "ssf/layer/data_link/circuit_helpers.h" + +namespace ssf { +namespace layer { +namespace data_link { + +template +class CircuitPolicy { +public: + enum { client = 0, server = 1 }; + +private: + using next_socket_type = typename Protocol::next_layer_protocol::socket; + using endpoint_type = typename Protocol::endpoint; + using resolver_type = typename Protocol::resolver; + +public: + static void InitConnection(next_socket_type &next_socket, + endpoint_type *p_remote_endpoint, uint8_t type, + boost::system::error_code &ec) { + ec.assign(ssf::error::function_not_supported, + ssf::error::get_ssf_category()); + return; + } + + template + static void AsyncInitConnection(next_socket_type &next_socket, + endpoint_type *p_remote_endpoint, + uint8_t type, Handler handler) { + if (type == client) { + AsyncInitConnectionClient(next_socket, p_remote_endpoint, handler); + } else { + AsyncInitConnectionServer(next_socket, p_remote_endpoint, handler); + } + } + + template + static void AsyncValidateConnection(next_socket_type &next_socket, + endpoint_type *p_remote_endpoint, + uint32_t ec_value, Handler handler) { + ssf::SendBase(next_socket, ec_value, handler); + } + +private: + /// Send protocol data (circuit nodes), wait validation, + /// execute given handler + template + static void AsyncInitConnectionClient(next_socket_type &next_socket, + endpoint_type *p_remote_endpoint, + Handler handler) { + auto string_sent_lambda = [&next_socket, handler]( + const boost::system::error_code &ec) mutable { + if (!ec) { + auto p_value = std::make_shared(0); + + auto ec_value_received_lambda = [handler, p_value]( + const boost::system::error_code &ec) mutable { + if (!ec) { + handler(boost::system::error_code( + *p_value, boost::system::system_category())); + } else { + handler(ec); + } + }; + + ssf::ReceiveBase(next_socket, p_value.get(), + ec_value_received_lambda); + } else { + handler(ec); + } + }; + + ssf::SendString(next_socket, + p_remote_endpoint->endpoint_context().forward_blocks, + string_sent_lambda); + } + + /// Read the protocol data, extract parameters stack and populate + /// p_remote_endpoint with received data, execute given handler + template + static void AsyncInitConnectionServer(next_socket_type &next_socket, + endpoint_type *p_received_endpoint, + Handler handler) { + auto p_string = std::make_shared(); + + auto string_received_lambda = + [&next_socket, handler, p_string, p_received_endpoint] + (const boost::system::error_code &ec) mutable { + + if (ec) { + handler(ec); + return; + } + + auto stack = unserialize_parameter_stack(*p_string); + + if (stack.size() == 1) { + // final endpoint + p_received_endpoint->endpoint_context() = detail::make_circuit_context( + next_socket.get_io_service(), stack.front()); + handler(ec); + return; + } + + auto default_parameters = unserialize_parameter_stack( + p_received_endpoint->endpoint_context().default_parameters); + + //populate default parameters + if (default_parameters.size() == stack.size()) { + auto default_param_it = default_parameters.begin(); + auto layer_param_it = stack.begin(); + auto end_it = stack.end(); + + while (layer_param_it != end_it) { + if (layer_param_it->count("default") == 1) { + // default parameter requested for this sublayer + *layer_param_it = *default_param_it; + } + ++layer_param_it; + ++default_param_it; + } + } + + resolver_type resolver(next_socket.get_io_service()); + boost::system::error_code resolve_ec; + auto endpoint_it = resolver.resolve(stack, resolve_ec); + + if (resolve_ec) { + handler(resolve_ec); + return; + } + + *p_received_endpoint = *endpoint_it; + + handler(ec); + return; + }; + + ssf::ReceiveString(next_socket, p_string.get(), string_received_lambda); + } +}; + +} // data_link +} // layer +} // ssf + +#endif // SSF_LAYER_DATA_LINK_SIMPLE_CIRCUIT_POLICY_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/datagram/basic_datagram.h b/src/framework/ssf/layer/datagram/basic_datagram.h new file mode 100644 index 00000000..af15c851 --- /dev/null +++ b/src/framework/ssf/layer/datagram/basic_datagram.h @@ -0,0 +1,105 @@ +#ifndef SSF_LAYER_DATAGRAM_BASIC_DATAGRAM_H_ +#define SSF_LAYER_DATAGRAM_BASIC_DATAGRAM_H_ + +#include + +#include + +#include "ssf/io/buffers.h" + +namespace ssf { +namespace layer { + +template +class basic_Datagram { + public: + typedef THeader Header; + typedef TPayload Payload; + typedef TFooter Footer; + + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { size = Header::size + Payload::size + Footer::size }; + + public: + basic_Datagram() : header_(), payload_(), footer_() {} + + basic_Datagram(Header header, Payload payload, Footer footer) + : header_(std::move(header)), + payload_(std::move(payload)), + footer_(std::move(footer)) {} + + basic_Datagram(const basic_Datagram& datagram) + : header_(datagram.header_), + payload_(datagram.payload_), + footer_(datagram.footer_) {} + + basic_Datagram(basic_Datagram&& datagram) + : header_(std::move(datagram.header_)), + payload_(std::move(datagram.payload_)), + footer_(std::move(datagram.footer_)) {} + + basic_Datagram& operator=(const basic_Datagram&& datagram) { + header_ = std::move(datagram.header_); + payload_ = std::move(datagram.payload_); + footer_ = std::move(datagram.footer_); + return *this; + } + + basic_Datagram& operator=(const basic_Datagram& datagram) { + header_ = datagram.header_; + payload_ = datagram.payload_; + footer_ = datagram.footer_; + return *this; + } + + ~basic_Datagram() {} + + ConstBuffers GetConstBuffers() const { + ConstBuffers buffers; + header_.GetConstBuffers(&buffers); + payload_.GetConstBuffers(&buffers); + footer_.GetConstBuffers(&buffers); + return buffers; + } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + header_.GetConstBuffers(p_buffers); + payload_.GetConstBuffers(p_buffers); + footer_.GetConstBuffers(p_buffers); + } + + MutableBuffers GetMutableBuffers() { + MutableBuffers buffers; + header_.GetMutableBuffers(&buffers); + payload_.GetMutableBuffers(&buffers); + footer_.GetMutableBuffers(&buffers); + return buffers; + } + + void GetMutableBuffers(MutableBuffers* p_buffers) { + header_.GetMutableBuffers(p_buffers); + payload_.GetMutableBuffers(p_buffers); + footer_.GetMutableBuffers(p_buffers); + } + + Header& header() { return header_; } + const Header& header() const { return header_; } + + Payload& payload() { return payload_; } + const Payload& payload() const { return payload_; } + + Footer& footer() { return footer_; } + const Footer& footer() const { return footer_; } + + private: + Header header_; + Payload payload_; + Footer footer_; +}; + +} // layer +} // ssf + +#endif // SSF_LAYER_DATAGRAM_BASIC_DATAGRAM_H_ diff --git a/src/framework/ssf/layer/datagram/basic_flags.h b/src/framework/ssf/layer/datagram/basic_flags.h new file mode 100644 index 00000000..080a84d4 --- /dev/null +++ b/src/framework/ssf/layer/datagram/basic_flags.h @@ -0,0 +1,54 @@ +#ifndef SSF_LAYER_DATAGRAM_BASIC_FLAGS_H_ +#define SSF_LAYER_DATAGRAM_BASIC_FLAGS_H_ + +#include + +#include + +namespace ssf { +namespace layer { + +template +class basic_Flags { + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { size = sizeof(BasicType) }; + + public: + basic_Flags() {} + basic_Flags(BasicType raw_flags) : raw_flags_(raw_flags) {} + + ~basic_Flags() {} + + ConstBuffers GetConstBuffers() const { + ConstBuffers buffers; + buffers.push_back(boost::asio::buffer(&raw_flags_, sizeof(raw_flags_))); + return buffers; + } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + p_buffers->push_back(boost::asio::buffer(&raw_flags_, sizeof(raw_flags_))); + } + + MutableBuffers GetMutableBuffers() { + MutableBuffers buffers; + buffers.push_back(boost::asio::buffer(&raw_flags_, sizeof(raw_flags_))); + return buffers; + } + + void GetMutableBuffers(MutableBuffers* p_buffers) { + p_buffers->push_back(boost::asio::buffer(&raw_flags_, sizeof(raw_flags_))); + } + + BasicType raw_flags() const { return raw_flags_ } + void set_raw_flags(BasicType raw_flags) { raw_flags_ = raw_flags; } + + private: + BasicType raw_flags_; +}; + +} // layer +} // ssf + +#endif // SSF_LAYER_DATAGRAM_BASIC_FLAGS_H_ diff --git a/src/framework/ssf/layer/datagram/basic_header.h b/src/framework/ssf/layer/datagram/basic_header.h new file mode 100644 index 00000000..3cdd2311 --- /dev/null +++ b/src/framework/ssf/layer/datagram/basic_header.h @@ -0,0 +1,119 @@ +#ifndef SSF_LAYER_DATAGRAM_BASIC_HEADER_H_ +#define SSF_LAYER_DATAGRAM_BASIC_HEADER_H_ + +#include + +#include + +#include + +namespace ssf { +namespace layer { + +template +struct ResolvePayloadLengthBaseSize { + enum { value = sizeof(T) }; +}; + +struct uint0_t { + template + uint0_t(const T&) {} +}; + +template <> +struct ResolvePayloadLengthBaseSize { + enum { value = 0 }; +}; + +template +class basic_Header { + public: + typedef TVersion Version; + typedef TID ID; + typedef TFlags Flags; + typedef TPayloadLength PayloadLength; + + private: + enum { + payload_length_base_size = + ResolvePayloadLengthBaseSize::value + }; + + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { + size = Version::size + ID::size + Flags::size + payload_length_base_size + }; + + public: + basic_Header() : version_(), id_(), flags_(), payload_length_(0) {} + + basic_Header(Version version, ID id, Flags flags, + PayloadLength payload_length) + : version_(std::move(version)), + id_(std::move(id)), + flags_(std::move(flags)), + payload_length_(std::move(payload_length)) {} + + ~basic_Header() {} + + ConstBuffers GetConstBuffers() const { + ConstBuffers buffers; + version_.GetConstBuffers(&buffers); + id_.GetConstBuffers(&buffers); + flags_.GetConstBuffers(&buffers); + buffers.push_back( + boost::asio::buffer(&payload_length_, payload_length_base_size)); + return buffers; + } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + version_.GetConstBuffers(p_buffers); + id_.GetConstBuffers(p_buffers); + flags_.GetConstBuffers(p_buffers); + p_buffers->push_back( + boost::asio::buffer(&payload_length_, payload_length_base_size)); + } + + MutableBuffers GetMutableBuffers() { + MutableBuffers buffers; + version_.GetMutableBuffers(&buffers); + id_.GetMutableBuffers(&buffers); + flags_.GetMutableBuffers(&buffers); + buffers.push_back( + boost::asio::buffer(&payload_length_, payload_length_base_size)); + return buffers; + } + + void GetMutableBuffers(MutableBuffers* p_buffers) { + version_.GetMutableBuffers(p_buffers); + id_.GetMutableBuffers(p_buffers); + flags_.GetMutableBuffers(p_buffers); + p_buffers->push_back( + boost::asio::buffer(&payload_length_, payload_length_base_size)); + } + + Version& version() { return version_; } + const Version& version() const { return version_; } + + ID& id() { return id_; } + const ID& id() const { return id_; } + + Flags& flags() { return flags_; } + const Flags& flags() const { return flags_; } + + PayloadLength& payload_length() { return payload_length_; } + const PayloadLength& payload_length() const { return payload_length_; } + + private: + Version version_; + ID id_; + Flags flags_; + PayloadLength payload_length_; +}; + +} // layer +} // ssf + +#endif // SSF_LAYER_DATAGRAM_BASIC_HEADER_H_ diff --git a/src/framework/ssf/layer/datagram/basic_payload.h b/src/framework/ssf/layer/datagram/basic_payload.h new file mode 100644 index 00000000..9128ca0d --- /dev/null +++ b/src/framework/ssf/layer/datagram/basic_payload.h @@ -0,0 +1,132 @@ +#ifndef SSF_LAYER_DATAGRAM_BASIC_PAYLOAD_H_ +#define SSF_LAYER_DATAGRAM_BASIC_PAYLOAD_H_ + +#include + +#include + +#include + +#include "ssf/io/buffers.h" + +namespace ssf { +namespace layer { + +class MutablePayload { + public: + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + typedef io::fixed_const_buffer_sequence ConstBuffers; + enum { size = 0 }; + + public: + template + MutablePayload(MutableBufferSequence&& buffers) + : data_(std::forward(buffers)) {} + + ~MutablePayload() {} + + MutableBuffers GetMutableBuffers() const { return data_; } + + void GetMutableBuffers(MutableBuffers* p_buffers) const { + for (const auto& buffer : data_) { + p_buffers->push_back(buffer); + } + } + + ConstBuffers GetConstBuffers() const { return data_; } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + for (const auto& buffer : data_) { + p_buffers->push_back(buffer); + } + } + + std::size_t GetSize() const { return boost::asio::buffer_size(data_); } + + MutableBuffers::iterator begin() { return data_.begin(); } + MutableBuffers::iterator end() { return data_.end(); } + + MutableBuffers::const_iterator begin() const { return data_.begin(); } + MutableBuffers::const_iterator end() const { return data_.end(); } + + private: + MutableBuffers data_; +}; + +class ConstPayload { + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + enum { size = 0 }; + + public: + template + ConstPayload(ConstBufferSequence&& buffers) + : data_(std::forward(buffers)) {} + + ~ConstPayload() {} + + ConstBuffers GetConstBuffers() const { return data_; } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + for (const auto& buffer : data_) { + p_buffers->push_back(buffer); + } + } + + std::size_t GetSize() const { return boost::asio::buffer_size(data_); } + + ConstBuffers::iterator begin() { return data_.begin(); } + ConstBuffers::iterator end() { return data_.end(); } + + ConstBuffers::const_iterator begin() const { return data_.begin(); } + ConstBuffers::const_iterator end() const { return data_.end(); } + + private: + ConstBuffers data_; +}; + +template +class BufferPayload { + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { size = 0 }; + + public: + BufferPayload() : data_(MaxSize) {} + ~BufferPayload() {} + + ConstBuffers GetConstBuffers() const { + return ConstBuffers({boost::asio::buffer(data_)}); + } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + p_buffers->push_back(boost::asio::buffer(data_)); + } + + MutableBuffers GetMutableBuffers() { + return MutableBuffers({boost::asio::buffer(data_)}); + } + + void GetMutableBuffers(MutableBuffers* p_buffers) { + p_buffers->push_back(boost::asio::buffer(data_)); + } + + std::size_t GetSize() const { return data_.size(); } + + void SetSize(std::size_t new_size) { + if (new_size <= MaxSize) { + data_.resize(new_size); + } + } + + void ResetSize() { data_.resize(MaxSize); } + + private: + std::vector data_; +}; + +} // layer +} // ssf + +#endif // SSF_LAYER_DATAGRAM_BASIC_PAYLOAD_H_ diff --git a/src/framework/ssf/layer/datagram/empty_component.h b/src/framework/ssf/layer/datagram/empty_component.h new file mode 100644 index 00000000..d0253ee3 --- /dev/null +++ b/src/framework/ssf/layer/datagram/empty_component.h @@ -0,0 +1,33 @@ +#ifndef SSF_LAYER_DATAGRAM_EMPTY_COMPONENT_H_ +#define SSF_LAYER_DATAGRAM_EMPTY_COMPONENT_H_ + +#include + +#include + +#include + +namespace ssf { +namespace layer { + + class EmptyComponent { + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { size = 0 }; + + public: + EmptyComponent() {} + ~EmptyComponent() {} + + ConstBuffers GetConstBuffers() const { return ConstBuffers(); } + void GetConstBuffers(ConstBuffers* p_buffers) const {} + + MutableBuffers GetMutableBuffers() { return MutableBuffers(); } + void GetMutableBuffers(MutableBuffers* p_buffers) {} + }; + +} // layer +} // ssf + +#endif // SSF_LAYER_DATAGRAM_EMPTY_COMPONENT_H_ diff --git a/src/framework/ssf/layer/interface_layer/basic_interface.h b/src/framework/ssf/layer/interface_layer/basic_interface.h new file mode 100644 index 00000000..ccc5a2e5 --- /dev/null +++ b/src/framework/ssf/layer/interface_layer/basic_interface.h @@ -0,0 +1,82 @@ +#ifndef SSF_LAYER_INTERFACE_BASIC_INTERFACE_H_ +#define SSF_LAYER_INTERFACE_BASIC_INTERFACE_H_ + +#include +#include + +#include + +#include "basic_interface_service.h" + +namespace ssf { +namespace layer { +namespace interface_layer { + +template > +class basic_Interface : public boost::asio::basic_io_object { + private: + typedef typename InterfaceService::raw_next_layer_protocol + raw_next_layer_protocol; + typedef + typename InterfaceService::endpoint_context_type endpoint_context_type; + + public: + basic_Interface(boost::asio::io_service& io_service) + : boost::asio::basic_io_object(io_service) {} + + basic_Interface(const basic_Interface& other) = delete; + + basic_Interface& operator=(const basic_Interface& other) = delete; + + basic_Interface(basic_Interface&& other) + : boost::asio::basic_io_object(std::move(other)) {} + + basic_Interface& operator=(basic_Interface&& other) { + boost::asio::basic_io_object::operator=(std::move(other)); + return *this; + } + + ~basic_Interface() { + boost::system::error_code ec; + close(ec); + } + + /// Async connect a next layer socket to its endpoint + /// and register this socket as interface_name + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(endpoint_context_type interface_name, + const typename raw_next_layer_protocol::endpoint& + next_remote_endpoint, + ConnectHandler handler) { + return this->get_service().async_connect(this->implementation, + std::move(interface_name), + next_remote_endpoint, handler); + } + + /// Async accept connection of a next layer acceptor to its endpoint + /// and register the first connection (next layer socket) as interface_name + template + BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, void(boost::system::error_code)) + async_accept( + const endpoint_context_type& interface_name, + const typename raw_next_layer_protocol::endpoint& next_local_endpoint, + AcceptHandler handler) { + return this->get_service().async_accept( + this->implementation, interface_name, next_local_endpoint, handler); + } + + boost::system::error_code close(boost::system::error_code& ec) { + return this->get_service().close(this->implementation, ec); + } + + bool is_open() { return this->get_service().is_open(this->implementation); } +}; + +} // interface_layer +} // layer +} // ssf + +#endif // SSF_LAYER_INTERFACE_LAYER_BASIC_INTERFACE_H_ diff --git a/src/framework/ssf/layer/interface_layer/basic_interface_manager.h b/src/framework/ssf/layer/interface_layer/basic_interface_manager.h new file mode 100644 index 00000000..8bd226b5 --- /dev/null +++ b/src/framework/ssf/layer/interface_layer/basic_interface_manager.h @@ -0,0 +1,57 @@ +#ifndef SSF_LAYER_INTERFACE_LAYER_BASIC_INTERFACE_MANAGER_H_ +#define SSF_LAYER_INTERFACE_LAYER_BASIC_INTERFACE_MANAGER_H_ + +#include +#include + +#include +#include + +namespace ssf { +namespace layer { +namespace interface_layer { + +template +class basic_InterfaceManager { + public: + typedef std::shared_ptr SocketPtr; + typedef typename Protocol::endpoint_context_type EndpointContext; + + public: + basic_InterfaceManager() : mutex_(), available_sockets_() {} + + std::size_t Count(const EndpointContext& endpoint) { + return available_sockets_.count(endpoint); + } + + boost::optional Find(const EndpointContext& endpoint) { + boost::mutex::scoped_lock lock_sockets(mutex_); + auto p_socket_it = available_sockets_.find(endpoint); + if (p_socket_it != available_sockets_.end()) { + return boost::optional(p_socket_it->second); + } + return boost::optional(); + } + + bool Emplace(const EndpointContext& endpoint, SocketPtr p_socket) { + boost::mutex::scoped_lock lock_sockets(mutex_); + auto inserted = available_sockets_.emplace(endpoint, p_socket); + + return inserted.second; + } + + bool Erase(const EndpointContext& endpoint) { + boost::mutex::scoped_lock lock_sockets(mutex_); + return 1 == available_sockets_.erase(endpoint); + } + + private: + boost::mutex mutex_; + std::map available_sockets_; +}; + +} // interface_layer +} // layer +} // ssf + +#endif // SSF_LAYER_INTERFACE_LAYER_BASIC_INTERFACE_MANAGER_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/interface_layer/basic_interface_protocol.cc b/src/framework/ssf/layer/interface_layer/basic_interface_protocol.cc new file mode 100644 index 00000000..2680bdc8 --- /dev/null +++ b/src/framework/ssf/layer/interface_layer/basic_interface_protocol.cc @@ -0,0 +1,12 @@ +#include "ssf/layer/interface_layer/basic_interface_protocol.h" + +namespace ssf { +namespace layer { +namespace interface_layer { + +basic_InterfaceProtocol::interface_manager_type + basic_InterfaceProtocol::interface_manager_; + +} // interface_layer +} // layer +} // ssf diff --git a/src/framework/ssf/layer/interface_layer/basic_interface_protocol.h b/src/framework/ssf/layer/interface_layer/basic_interface_protocol.h new file mode 100644 index 00000000..5d228095 --- /dev/null +++ b/src/framework/ssf/layer/interface_layer/basic_interface_protocol.h @@ -0,0 +1,131 @@ +#ifndef SSF_LAYER_INTERFACE_LAYER_BASIC_INTERFACE_PROTOCOL_H_ +#define SSF_LAYER_INTERFACE_LAYER_BASIC_INTERFACE_PROTOCOL_H_ + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include "ssf/layer/basic_endpoint.h" +#include "ssf/layer/basic_impl.h" +#include "ssf/layer/basic_resolver.h" + +#include "ssf/layer/interface_layer/basic_interface.h" +#include "ssf/layer/interface_layer/basic_interface_service.h" +#include "ssf/layer/interface_layer/basic_interface_manager.h" + +#include "ssf/layer/datagram/basic_datagram.h" +#include "ssf/layer/datagram/basic_header.h" +#include "ssf/layer/datagram/basic_payload.h" +#include "ssf/layer/datagram/empty_component.h" + +#include "ssf/layer/protocol_attributes.h" +#include "ssf/layer/parameters.h" + +#include "ssf/utils/map_helpers.h" + +#include "ssf/layer/interface_layer/generic_interface_socket.h" + +#define INTERFACE_MTU 65535 + +namespace ssf { +namespace layer { +namespace interface_layer { + +class basic_InterfaceProtocol { + private: + typedef generic_interface_socket socket_type; + typedef std::shared_ptr p_socket_type; + + public: + using PayloadSize = uint16_t; + /*static_assert(std::numeric_limits::max() >= INTERFACE_MTU, + "MTU too big for payload size field");*/ + typedef basic_Header Header; + typedef EmptyComponent Footer; + + typedef BufferPayload + ReceivePayload; + typedef basic_Datagram ReceiveDatagram; + + typedef ConstPayload SendPayload; + typedef basic_Datagram SendDatagram; + + enum { + id = 16, + overhead = SendDatagram::size, + facilities = ssf::layer::facilities::datagram, + mtu = INTERFACE_MTU - overhead + }; + enum { endpoint_stack_size = 1 }; + + typedef std::string endpoint_context_type; + using next_endpoint_type = int; + + typedef basic_InterfaceManager + interface_manager_type; + + typedef basic_VirtualLink_endpoint endpoint; + typedef basic_VirtualLink_resolver resolver; + + typedef socket_type socket; + + typedef int socket_context; + + private: + using query = resolver::query; + + public: + static endpoint make_endpoint(boost::asio::io_service& io_service, + query::const_iterator parameters_it, uint32_t, + boost::system::error_code& ec) { + int next_layer_endpoint = 0; + auto context = + helpers::GetField("interface_id", *parameters_it); + + return endpoint(context, next_layer_endpoint); + } + + static p_socket_type ResolveNextLocalToGateway( + const endpoint_context_type& next_local_endpoint) { + auto p_next_socket_optional = get_interface_manager().Find(next_local_endpoint); + + if (!p_next_socket_optional) { + return nullptr; + } + + return *p_next_socket_optional; + } + + template + static SendDatagram make_datagram(const ConstBufferSequence& buffers) { + Header dgr_header; + auto& data_size = dgr_header.payload_length(); + data_size = + static_cast(boost::asio::buffer_size(buffers)); + SendPayload payload(buffers); + + return SendDatagram(dgr_header, payload, Footer()); + } + + static interface_manager_type& get_interface_manager() { + return interface_manager_; + } + + private: + static interface_manager_type interface_manager_; +}; + +} // interface_layer +} // layer +} // ssf + +#endif // SSF_LAYER_INTERFACE_LAYER_BASIC_INTERFACE_PROTOCOL_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/interface_layer/basic_interface_service.h b/src/framework/ssf/layer/interface_layer/basic_interface_service.h new file mode 100644 index 00000000..cbd61d98 --- /dev/null +++ b/src/framework/ssf/layer/interface_layer/basic_interface_service.h @@ -0,0 +1,288 @@ +#ifndef SSF_LAYER_INTERFACE_LAYER_BASIC_INTERFACE_SERVICE_H_ +#define SSF_LAYER_INTERFACE_LAYER_BASIC_INTERFACE_SERVICE_H_ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "ssf/error/error.h" +#include "ssf/io/composed_op.h" + +#include "ssf/layer/interface_layer/generic_interface_socket.h" +#include "ssf/layer/interface_layer/specific_interface_socket.h" + +namespace ssf { +namespace layer { +namespace interface_layer { + +#include + +template +class basic_Interface_service + : public boost::asio::detail::service_base< + basic_Interface_service> { + public: + typedef Protocol protocol_type; + typedef NextLayerProtocol raw_next_layer_protocol; + typedef typename protocol_type::endpoint_context_type endpoint_context_type; + typedef typename raw_next_layer_protocol::socket next_socket_type; + typedef typename raw_next_layer_protocol::acceptor next_acceptor_type; + typedef std::shared_ptr p_next_socket_type; + typedef std::shared_ptr p_next_acceptor_type; + + struct implementation_type { + endpoint_context_type interface_name; + p_next_socket_type p_next_socket; + p_next_acceptor_type p_next_acceptor; + }; + + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + public: + explicit basic_Interface_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base(io_service) { + } + + virtual ~basic_Interface_service() {} + + void construct(implementation_type& impl) {} + + void destroy(implementation_type& impl) { + protocol_type::get_interface_manager().Erase(impl.interface_name); + + impl.interface_name.clear(); + impl.p_next_socket.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + /// Create a socket of the next layer type, + /// async connect it to its given endpoint and + /// register it as the interface name + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(implementation_type& impl, + endpoint_context_type interface_name, + const typename raw_next_layer_protocol::endpoint& + next_remote_endpoint, + ConnectHandler&& handler) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + impl.interface_name = interface_name; + bool interface_already_exists = true; + if (!protocol_type::get_interface_manager().Find(interface_name)) { + interface_already_exists = false; + impl.p_next_socket = + std::make_shared( + this->get_io_service()); + } + + if (impl.p_next_socket->is_open()) { + this->get_io_service().post(boost::bind( + init.handler, + boost::system::error_code(ssf::error::device_or_resource_busy, + ssf::error::get_ssf_category()))); + return init.result.get(); + } + + auto connected_lambda = [this, init, &impl, interface_already_exists]( + const boost::system::error_code& ec) { + auto updated_ec = ec; + if (!ec) { + if (!interface_already_exists) { + // encapsulate next layer socket in specific interface socket + auto p_specific_socket = + specific_interface_socket::Create( + impl.p_next_socket); + + auto endpoint_inserted = + protocol_type::get_interface_manager().Emplace( + impl.interface_name, p_specific_socket); + + if (!endpoint_inserted) { + updated_ec.assign(ssf::error::device_or_resource_busy, + ssf::error::get_ssf_category()); + } + } else { + auto p_socket_optional = + protocol_type::get_interface_manager().Find(impl.interface_name); + if (p_socket_optional) { + boost::system::error_code ec; + (*p_socket_optional)->connect(ec); + } + } + } else { + boost::system::error_code close_ec; + impl.p_next_socket->close(close_ec); + } + + init.handler(updated_ec); + }; + + return impl.p_next_socket->async_connect( + next_remote_endpoint, + io::ComposedOp< + decltype(connected_lambda), + typename boost::asio::handler_type< + ConnectHandler, void(boost::system::error_code)>::type>( + std::move(connected_lambda), init.handler)); + } + + /// Create an acceptor of the next layer type, + /// make it listening to its given endpoint, async accept connection and + /// register the first connection (next layer socket) as the interface name + template + BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, void(boost::system::error_code)) + async_accept( + implementation_type& impl, + const endpoint_context_type& interface_name, + const typename raw_next_layer_protocol::endpoint& next_local_endpoint, + AcceptHandler&& handler) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + impl.interface_name = interface_name; + bool interface_already_exists = true; + + if (!impl.p_next_socket) { + interface_already_exists = false; + impl.p_next_socket = + std::make_shared( + this->get_io_service()); + } + + if (impl.p_next_socket->is_open()) { + this->get_io_service().post(boost::bind( + init.handler, + boost::system::error_code(ssf::error::device_or_resource_busy, + ssf::error::get_ssf_category()))); + return init.result.get(); + } + + if (!impl.p_next_acceptor) { + impl.p_next_acceptor = + std::make_shared( + this->get_io_service()); + boost::system::error_code ec; + impl.p_next_acceptor->open(next_local_endpoint.protocol()); + impl.p_next_acceptor->set_option( + boost::asio::socket_base::reuse_address(true), ec); + impl.p_next_acceptor->bind(next_local_endpoint, ec); + + if (ec) { + this->get_io_service().post(boost::bind(init.handler, ec)); + return init.result.get(); + } + + impl.p_next_acceptor->listen(); + } + + auto p_next_remote_endpoint = + std::make_shared(); + auto p_next_acceptor = impl.p_next_acceptor; + auto p_next_socket = impl.p_next_socket; + auto accepted_lambda = [this, &impl, p_next_remote_endpoint, init, + p_next_acceptor, p_next_socket, + interface_already_exists]( + const boost::system::error_code& ec) mutable { + auto updated_ec = ec; + if (!ec) { + if (!interface_already_exists) { + // encapsulate next layer socket in specific interface socket + auto p_specific_socket = + specific_interface_socket::Create( + impl.p_next_socket); + auto endpoint_inserted = + protocol_type::get_interface_manager().Emplace( + impl.interface_name, p_specific_socket); + + if (!endpoint_inserted) { + updated_ec.assign(ssf::error::device_or_resource_busy, + ssf::error::get_ssf_category()); + } + } else { + auto p_socket_optional = + protocol_type::get_interface_manager().Find(impl.interface_name); + if (p_socket_optional) { + boost::system::error_code ec; + (*p_socket_optional)->connect(ec); + } + } + } else { + boost::system::error_code close_ec; + impl.p_next_socket->close(close_ec); + } + + if (p_next_acceptor) { + boost::system::error_code other_ec; + p_next_acceptor->close(other_ec); + p_next_acceptor.reset(); + impl.p_next_acceptor.reset(); + } + + init.handler(updated_ec); + }; + + return impl.p_next_acceptor->async_accept( + *impl.p_next_socket, *p_next_remote_endpoint, + io::ComposedOp< + decltype(accepted_lambda), + typename boost::asio::handler_type< + AcceptHandler, void(boost::system::error_code)>::type>( + std::move(accepted_lambda), init.handler)); + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + protocol_type::get_interface_manager().Erase(impl.interface_name); + + if (impl.p_next_acceptor) { + impl.p_next_acceptor->close(ec); + } + + if (!impl.p_next_socket) { + return ec; + } + + impl.p_next_socket->shutdown(boost::asio::socket_base::shutdown_both, ec); + return impl.p_next_socket->close(ec); + } + + bool is_open(implementation_type& impl) const { + if (impl.p_next_acceptor) { + return true; + } + if (!impl.p_next_socket) { + return false; + } + return impl.p_next_socket->is_open(); + } + + private: + void shutdown_service() {} +}; + +#include + +} // interface_layer +} // layer +} // ssf + +#endif // SSF_LAYER_INTERFACE_LAYER_BASIC_INTERFACE_SERVICE_H_ diff --git a/src/framework/ssf/layer/interface_layer/generic_interface_socket.h b/src/framework/ssf/layer/interface_layer/generic_interface_socket.h new file mode 100644 index 00000000..a526ffdf --- /dev/null +++ b/src/framework/ssf/layer/interface_layer/generic_interface_socket.h @@ -0,0 +1,47 @@ +#ifndef SSF_LAYER_INTERFACE_LAYER_GENERIC_INTERFACE_SOCKET_H_ +#define SSF_LAYER_INTERFACE_LAYER_GENERIC_INTERFACE_SOCKET_H_ + +#include +#include + +#include +#include + +#include + +#include "ssf/layer/io_handler.h" +#include "ssf/layer/interface_layer/interface_buffers.h" + +namespace ssf { +namespace layer { +namespace interface_layer { + +template +class generic_interface_socket { + public: + typedef Protocol protocol_type; + + private: + typedef typename protocol_type::endpoint endpoint_type; + + public: + virtual std::size_t available(boost::system::error_code& ec) = 0; + + virtual boost::system::error_code cancel(boost::system::error_code& ec) = 0; + + virtual void close(boost::system::error_code& ec) = 0; + + virtual void connect(boost::system::error_code& ec) = 0; + + virtual void async_receive(interface_mutable_buffers buffers, + ssf::layer::WrappedIOHandler handler) = 0; + + virtual void async_send(interface_const_buffers buffers, + ssf::layer::WrappedIOHandler handler) = 0; +}; + +} // interface_layer +} // layer +} // ssf + +#endif // SSF_LAYER_INTERFACE_LAYER_GENERIC_INTERFACE_SOCKET_H_ diff --git a/src/framework/ssf/layer/interface_layer/interface_buffers.h b/src/framework/ssf/layer/interface_layer/interface_buffers.h new file mode 100644 index 00000000..b22b7665 --- /dev/null +++ b/src/framework/ssf/layer/interface_layer/interface_buffers.h @@ -0,0 +1,17 @@ +#ifndef SSF_LAYER_INTERFACE_LAYER_INTERFACE_BUFFERS_H_ +#define SSF_LAYER_INTERFACE_LAYER_INTERFACE_BUFFERS_H_ + +#include "ssf/io/buffers.h" + +namespace ssf { +namespace layer { +namespace interface_layer { + +typedef io::fixed_mutable_buffer_sequence interface_mutable_buffers; +typedef io::fixed_const_buffer_sequence interface_const_buffers; + +} // interface_layer +} // layer +} // ssf + +#endif // SSF_LAYER_INTERFACE_LAYER_INTERFACE_BUFFERS_H_ diff --git a/src/framework/ssf/layer/interface_layer/specific_interface_socket.h b/src/framework/ssf/layer/interface_layer/specific_interface_socket.h new file mode 100644 index 00000000..2a304bd5 --- /dev/null +++ b/src/framework/ssf/layer/interface_layer/specific_interface_socket.h @@ -0,0 +1,342 @@ +#ifndef SSF_LAYER_INTERFACE_LAYER_SPECIFIC_INTERFACE_SOCKET_H_ +#define SSF_LAYER_INTERFACE_LAYER_SPECIFIC_INTERFACE_SOCKET_H_ + +#include + +#include +#include + +#include + +#include + +#include "ssf/layer/interface_layer/generic_interface_socket.h" +#include "ssf/layer/interface_layer/interface_buffers.h" + +#include "ssf/error/error.h" +#include "ssf/io/composed_op.h" +#include "ssf/io/handler_helpers.h" +#include "ssf/io/read_op.h" +#include "ssf/io/write_op.h" + +#include "ssf/layer/protocol_attributes.h" + +namespace ssf { +namespace layer { +namespace interface_layer { + +template +class specific_interface_socket + : public generic_interface_socket, + public std::enable_shared_from_this< + specific_interface_socket> { + private: + using protocol_type = Protocol; + using internal_socket_type = typename NextLayerProtocol::socket; + using p_internal_socket_type = std::shared_ptr; + using endpoint_type = typename protocol_type::endpoint; + + using receive_datagram_type = typename protocol_type::ReceiveDatagram; + using receive_op_queue_type = boost::asio::detail::op_queue< + io::basic_pending_read_operation>; + + using send_datagram_type = typename protocol_type::SendDatagram; + using send_buffer_type = std::vector; + using send_op_queue_type = + boost::asio::detail::op_queue; + + public: + static std::shared_ptr Create( + p_internal_socket_type p_internal_socket) { + return std::shared_ptr( + new specific_interface_socket(std::move(p_internal_socket))); + } + + virtual std::size_t available(boost::system::error_code& ec) { + auto available_size = p_internal_socket_->available(ec); + if (ec) { + boost::system::error_code close_ec; + p_internal_socket_->close(close_ec); + } + + return available_size; + } + + virtual boost::system::error_code cancel(boost::system::error_code& ec) { + { + boost::recursive_mutex::scoped_lock lock(send_mutex_); + while (!send_op_queue_.empty()) { + auto op = std::move(send_op_queue_.front()); + send_op_queue_.pop(); + + p_internal_socket_->get_io_service().post([op]() { + op->complete(boost::asio::error::make_error_code( + boost::asio::error::operation_aborted), + 0); + }); + } + } + + { + boost::recursive_mutex::scoped_lock lock(receive_mutex_); + + while (!receive_op_queue_.empty()) { + auto op = std::move(receive_op_queue_.front()); + receive_op_queue_.pop(); + + p_internal_socket_->get_io_service().post([op]() { + op->complete(boost::asio::error::make_error_code( + boost::asio::error::operation_aborted), + 0); + }); + } + } + + return ec; + } + + virtual void close(boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock_closed_(closed_mutex_); + if (!closed_) { + closed_ = true; + p_internal_socket_->close(ec); + } + } + + virtual void connect(boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock_closed_(closed_mutex_); + if (closed_) { + closed_ = false; + } + + p_internal_socket_->get_io_service().dispatch( + [this]() { this->do_async_receive(); }); + p_internal_socket_->get_io_service().dispatch( + [this]() { this->do_async_send(); }); + } + + virtual void async_receive(interface_mutable_buffers buffers, + ssf::layer::WrappedIOHandler handler) { + boost::recursive_mutex::scoped_lock lock(receive_mutex_); + + typedef io::pending_read_operation op; + typename op::ptr p = { + boost::asio::detail::addressof(handler), + boost_asio_handler_alloc_helpers::allocate(sizeof(op), handler), 0}; + + p.p = new (p.v) op(buffers, handler, nullptr); + receive_op_queue_.push(p.p); + p.v = p.p = 0; + if (!receive_pending_) { + do_async_receive(); + } + } + + virtual void async_send(interface_const_buffers buffers, + ssf::layer::WrappedIOHandler handler) { + boost::recursive_mutex::scoped_lock lock(send_mutex_); + + typedef io::pending_write_operation op; + typename op::ptr p = { + boost::asio::detail::addressof(handler), + boost_asio_handler_alloc_helpers::allocate(sizeof(op), handler), 0}; + + p.p = new (p.v) op(buffers, handler); + send_op_queue_.push(p.p); + p.v = p.p = 0; + + if (!send_pending_) { + do_async_send(); + } + } + + private: + specific_interface_socket(p_internal_socket_type p_internal_socket) + : p_internal_socket_(std::move(p_internal_socket)), + closed_mutex_(), + closed_(false), + receive_mutex_(), + receive_pending_(false), + internal_receive_datagram_(), + receive_op_queue_(), + send_mutex_(), + send_pending_(false), + send_buffer_(protocol_type::mtu), + internal_send_datagram_( + protocol_type::make_datagram(boost::asio::buffer(send_buffer_))), + send_op_queue_() {} + + void handle_received(const boost::system::error_code& ec, + std::size_t length) { + if (ec) { + // Close socket if any error happened on reading + boost::system::error_code close_ec; + this->close(close_ec); + return; + } + + { + boost::recursive_mutex::scoped_lock lock(receive_mutex_); + + if (!receive_op_queue_.empty()) { + auto op = std::move(receive_op_queue_.front()); + receive_op_queue_.pop(); + + boost::system::error_code fill_ec; + auto copied = op->fill_buffer(internal_receive_datagram_, fill_ec); + + if (fill_ec) { + auto do_complete = + [op, fill_ec, copied]() { op->complete(fill_ec, copied); }; + p_internal_socket_->get_io_service().post(std::move(do_complete)); + } else { + // check copied == length - receive_datagram_type::size ?? + auto do_complete = [op, ec, copied]() { op->complete(ec, copied); }; + p_internal_socket_->get_io_service().post(std::move(do_complete)); + } + } + } + + { + boost::recursive_mutex::scoped_lock lock_closed(closed_mutex_); + if (!closed_) { + p_internal_socket_->get_io_service().dispatch( + [this]() { this->do_async_receive(); }); + } + } + } + + void handle_sent(const boost::system::error_code& ec, std::size_t length) { + if (ec) { + // Close socket if any error happened on sending + boost::system::error_code close_ec; + this->close(close_ec); + return; + } + + { + boost::recursive_mutex::scoped_lock lock(send_mutex_); + + if (!send_op_queue_.empty()) { + auto op = std::move(send_op_queue_.front()); + send_op_queue_.pop(); + + length -= send_datagram_type::size; + + auto do_complete = [op, ec, length]() { op->complete(ec, length); }; + p_internal_socket_->get_io_service().post(std::move(do_complete)); + } + } + + { + boost::recursive_mutex::scoped_lock lock_closed(closed_mutex_); + if (!closed_) { + p_internal_socket_->get_io_service().dispatch( + [this]() { this->do_async_send(); }); + } + } + } + + void do_async_receive() { + { + boost::recursive_mutex::scoped_lock lock_closed(closed_mutex_); + if (closed_) { + return; + } + } + + { + boost::recursive_mutex::scoped_lock lock(receive_mutex_); + + if (receive_op_queue_.empty()) { + receive_pending_ = false; + return; + } + + receive_pending_ = true; + } + + auto self = this->shared_from_this(); + AsyncReceiveDatagram( + *p_internal_socket_, &internal_receive_datagram_, + [self, this](const boost::system::error_code& ec, std::size_t length) { + this->handle_received(ec, length); + }); + } + + void do_async_send() { + { + boost::recursive_mutex::scoped_lock lock_closed(closed_mutex_); + if (closed_) { + return; + } + } + + { + boost::recursive_mutex::scoped_lock lock(send_mutex_); + + if (send_op_queue_.empty()) { + send_pending_ = false; + return; + } + + send_pending_ = true; + + auto op = send_op_queue_.front(); + auto buffers = op->const_buffers(); + + if (boost::asio::buffer_size(buffers) > protocol_type::mtu) { + send_op_queue_.pop(); + auto do_complete = [op]() { + op->complete( + boost::system::error_code(ssf::error::message_size, + ssf::error::get_ssf_category()), + 0); + }; + p_internal_socket_->get_io_service().post(std::move(do_complete)); + + p_internal_socket_->get_io_service().post( + [this]() { this->do_async_send(); }); + return; + } + + send_buffer_.resize(boost::asio::buffer_size(buffers)); + boost::asio::buffer_copy(boost::asio::buffer(send_buffer_), buffers); + } + + internal_send_datagram_ = + std::move(protocol_type::make_datagram(boost::asio::buffer(send_buffer_))); + + auto self = this->shared_from_this(); + AsyncSendDatagram( + *p_internal_socket_, internal_send_datagram_, + [self, this](const boost::system::error_code& ec, std::size_t length) { + this->handle_sent(ec, length); + }); + } + + private: + p_internal_socket_type p_internal_socket_; + boost::recursive_mutex closed_mutex_; + bool closed_; + + boost::recursive_mutex receive_mutex_; + bool receive_pending_; + receive_datagram_type internal_receive_datagram_; + receive_op_queue_type receive_op_queue_; + + boost::recursive_mutex send_mutex_; + bool send_pending_; + send_buffer_type send_buffer_; + send_datagram_type internal_send_datagram_; + send_op_queue_type send_op_queue_; +}; + +} // interface_layer +} // layer +} // ssf + +#endif // SSF_LAYER_INTERFACE_LAYER_SPECIFIC_INTERFACE_SOCKET_H_ diff --git a/src/framework/ssf/layer/io_handler.h b/src/framework/ssf/layer/io_handler.h new file mode 100644 index 00000000..44fda0ae --- /dev/null +++ b/src/framework/ssf/layer/io_handler.h @@ -0,0 +1,74 @@ +#ifndef SSF_LAYER_IO_HANDLER_H_ +#define SSF_LAYER_IO_HANDLER_H_ + +#include + +#include + +namespace ssf { +namespace layer { + +class BaseIOHandler { + public: + virtual ~BaseIOHandler() {} + virtual void operator()(const boost::system::error_code&, std::size_t) = 0; + virtual void operator()(const boost::system::error_code&, std::size_t) const = 0; +}; + +template +class IOHandler : public BaseIOHandler { + public: + IOHandler() {} + IOHandler(SubHandler handler) : handler_(std::move(handler)) {} + + void operator()(const boost::system::error_code& ec, std::size_t length) { + handler_(ec, length); + } + + void operator()(const boost::system::error_code& ec, std::size_t length) const { + handler_(ec, length); + } + + private: + SubHandler handler_; +}; + +typedef std::shared_ptr BaseIOHandlerPtr; + +class WrappedIOHandler { +public: + WrappedIOHandler() : p_handler_(nullptr) {} + + template + WrappedIOHandler(Handler handler) + : p_handler_(std::make_shared>(std::move(handler))) {} + + WrappedIOHandler(const WrappedIOHandler& other) + : p_handler_(other.p_handler_) {} + + WrappedIOHandler(WrappedIOHandler&& other) + : p_handler_(std::move(other.p_handler_)) {} + + ~WrappedIOHandler() {} + + void operator()(const boost::system::error_code& ec, std::size_t length) { + if (p_handler_) { + (*p_handler_)(ec, length); + } + } + + void operator()(const boost::system::error_code& ec, + std::size_t length) const { + if (p_handler_) { + (*p_handler_)(ec, length); + } + } + +private: + BaseIOHandlerPtr p_handler_; +}; + +} // layer +} // ssf + +#endif // SSF_LAYER_IO_HANDLER_H_ diff --git a/src/framework/ssf/layer/multiplexing/basic_demultiplexer.h b/src/framework/ssf/layer/multiplexing/basic_demultiplexer.h new file mode 100644 index 00000000..756d28ff --- /dev/null +++ b/src/framework/ssf/layer/multiplexing/basic_demultiplexer.h @@ -0,0 +1,282 @@ +#ifndef SSF_LAYER_MULTIPLEXING_BASIC_DEMULTIPLEXER_H_ +#define SSF_LAYER_MULTIPLEXING_BASIC_DEMULTIPLEXER_H_ + +#include +#include +#include + +#include + +#include "ssf/error/error.h" + +namespace ssf { +namespace layer { +namespace multiplexing { + +template +class basic_Demultiplexer : public std::enable_shared_from_this< + basic_Demultiplexer > +{ + private: + typedef typename Protocol::next_layer_protocol::socket NextSocket; + typedef std::shared_ptr NextSocketPtr; + typedef typename Protocol::socket_context SocketContext; + typedef std::shared_ptr SocketContextPtr; + typedef typename Protocol::endpoint_context_type EndpointContext; + typedef typename Protocol::next_layer_protocol::endpoint NextEndpoint; + typedef std::shared_ptr NextEndpointPtr; + typedef typename Protocol::ReceiveDatagram ReceiveDatagram; + typedef std::shared_ptr ReceiveDatagramPtr; + typedef std::shared_ptr CongestionPolicyPtr; + typedef std::pair ContextPtrCongestionPair; + + public: + static std::shared_ptr Create(NextSocketPtr p_socket) { + return std::shared_ptr( + new basic_Demultiplexer(p_socket)); + } + + void Start() { + reading_ = true; + AsyncReadHeader(); + } + + void Stop() { reading_ = false; } + + bool Bind(SocketContextPtr p_socket_context) { + boost::recursive_mutex::scoped_lock lock(mutex_); + auto& local_multiplexed_map = + multiplexed_maps_[p_socket_context->local_id]; + + auto socket_context_inserted = local_multiplexed_map.emplace( + p_socket_context->remote_id, + ContextPtrCongestionPair(p_socket_context, + std::make_shared())); + + return socket_context_inserted.second; + } + + bool Unbind(SocketContextPtr p_socket_context) { + boost::recursive_mutex::scoped_lock lock(mutex_); + auto& local_multiplexed_map = + multiplexed_maps_[p_socket_context->local_id]; + + local_multiplexed_map.erase(p_socket_context->remote_id); + + if (local_multiplexed_map.empty()) { + multiplexed_maps_.erase(p_socket_context->local_id); + } + + return true; + } + + bool IsBound(SocketContextPtr p_socket_context) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + auto local_multiplexed_map_it = + multiplexed_maps_.find(p_socket_context->local_id); + + if (local_multiplexed_map_it == std::end(multiplexed_maps_)) { + return false; + } + + auto& local_multiplexed_map = local_multiplexed_map_it->second; + auto p_context_it = + local_multiplexed_map.find(p_socket_context->remote_id); + + return (p_context_it != std::end(local_multiplexed_map)) && + (p_socket_context == p_context_it->second.first); + } + + void Read(SocketContextPtr p_socket_context) { + HandleQueues(p_socket_context); + } + + private: + basic_Demultiplexer(NextSocketPtr p_socket) + : p_socket_(p_socket), + multiplexed_maps_(), + mutex_(), + reading_(false) {} + + /// Async read receive_datagram_type + void AsyncReadHeader(NextEndpointPtr p_next_endpoint = nullptr, + ReceiveDatagramPtr p_datagram = nullptr) { + if (!reading_) { + return; + } + + if (!p_next_endpoint) { + p_next_endpoint = std::make_shared(); + } + + if (!p_datagram) { + p_datagram = std::make_shared(); + } + + p_datagram->payload().ResetSize(); + + AsyncReceiveDatagram(*p_socket_, p_datagram.get(), *p_next_endpoint, + boost::bind(&basic_Demultiplexer::DispatchDatagram, + this->shared_from_this(), p_next_endpoint, + p_datagram, _1, _2)); + } + + /// Dispatch the datagram through its corresponding queue + void DispatchDatagram(NextEndpointPtr p_next_endpoint, + ReceiveDatagramPtr p_datagram, + const boost::system::error_code& ec, + std::size_t length) { + if (ec) { + boost::system::error_code close_ec; + p_socket_->close(close_ec); + return; + } + + { + boost::recursive_mutex::scoped_lock lock(mutex_); + + auto& header = p_datagram->header(); + // Second half header represents the local id + auto local_id = header.id().GetSecondHalfId(); + + auto local_multiplexed_it = multiplexed_maps_.find(local_id); + + if (local_multiplexed_it == std::end(multiplexed_maps_)) { + return; + } + + // The local id is multiplexed + auto& local_multiplexed_map = local_multiplexed_it->second; + + SocketContextPtr p_context = nullptr; + CongestionPolicyPtr p_congestion_policy = nullptr; + + // First half represents the remote id + auto remote_id = header.id().GetFirstHalfId(); + + auto p_context_it = local_multiplexed_map.find(remote_id); + + if (p_context_it != std::end(local_multiplexed_map)) { + // There is a socket which is bound on this particular id + p_context = p_context_it->second.first; + p_congestion_policy = p_context_it->second.second; + } else { + // There is a socket which is bound on any remote id + auto p_bound_context_it = + local_multiplexed_map.find(EndpointContext()); + + if (p_bound_context_it != std::end(local_multiplexed_map)) { + p_context = p_bound_context_it->second.first; + p_congestion_policy = p_bound_context_it->second.second; + } + } + + // Enqueue the datagram in the socket context queue. If none, drop it + if (p_context) { + + { + boost::recursive_mutex::scoped_lock lock(p_context->mutex); + + auto& datagram_queue = p_context->datagram_queue; + auto& payload = p_datagram->payload(); + + // Drop packet if not addable + if (p_congestion_policy->IsAddable(datagram_queue, payload)) { + datagram_queue.push(std::move(*p_datagram)); + auto& next_endpoint_queue = p_context->next_endpoint_queue; + next_endpoint_queue.push(std::move(*p_next_endpoint)); + } + } + + HandleQueues(p_context); + } + } + + // Continue reading datagram + AsyncReadHeader(std::move(p_next_endpoint), + std::move(p_datagram)); + } + + void HandleQueues(SocketContextPtr p_context, + const boost::system::error_code& ec = boost::system::error_code()) { + boost::recursive_mutex::scoped_lock lock(p_context->mutex); + auto& read_op_queue = p_context->read_op_queue; + auto& datagram_queue = p_context->datagram_queue; + auto& next_endpoint_queue = p_context->next_endpoint_queue; + + if (ec) { + while (!read_op_queue.empty()) { + auto read_op = std::move(read_op_queue.front()); + read_op_queue.pop(); + + auto do_complete = [read_op, ec]() { read_op->complete(ec, 0); }; + + p_socket_->get_io_service().post(std::move(do_complete)); + } + return; + } + + if (datagram_queue.empty() || read_op_queue.empty()) { + return; + } + + auto read_op = std::move(read_op_queue.front()); + read_op_queue.pop(); + + auto copied = read_op->fill_buffer(datagram_queue.front()); + + if (copied == std::numeric_limits::max()) { + auto do_complete = [read_op]() { + read_op->complete( + boost::system::error_code(ssf::error::message_size, + ssf::error::get_ssf_category()), + 0); + }; + + p_socket_->get_io_service().post(std::move(do_complete)); + return; + } + + auto datagram = std::move(datagram_queue.front()); + datagram_queue.pop(); + auto& id = datagram.header().id(); + auto half_id = Protocol::id_type::MakeHalfRemoteID(id); + + auto src_next_endpoint = std::move(next_endpoint_queue.front()); + next_endpoint_queue.pop(); + + read_op->set_p_endpoint( + Protocol::endpoint(std::move(half_id), std::move(src_next_endpoint))); + + auto do_complete = [read_op, ec, copied]() { + read_op->complete(ec, copied); + }; + + p_socket_->get_io_service().post(std::move(do_complete)); + } + + private: + typedef typename Protocol::endpoint_context_type LocalEndpointContext; + typedef typename Protocol::endpoint_context_type RemoteEndpointContext; + typedef std::map + LocalMultiplexedMaps; + typedef std::map + MultiplexedMaps; + + private: + NextSocketPtr p_socket_; + MultiplexedMaps multiplexed_maps_; + boost::recursive_mutex mutex_; + std::atomic reading_; +}; + +template +using basic_DemultiplexerPtr = + std::shared_ptr>; + +} // multiplexing +} // layer +} // ssf + +#endif // SSF_LAYER_MULTIPLEXING_BASIC_DEMULTIPLEXER_H_ diff --git a/src/framework/ssf/layer/multiplexing/basic_multiplexer.h b/src/framework/ssf/layer/multiplexing/basic_multiplexer.h new file mode 100644 index 00000000..65a3cae0 --- /dev/null +++ b/src/framework/ssf/layer/multiplexing/basic_multiplexer.h @@ -0,0 +1,139 @@ +#ifndef SSF_LAYER_MULTIPLEXING_BASIC_MULTIPLEXER_H_ +#define SSF_LAYER_MULTIPLEXING_BASIC_MULTIPLEXER_H_ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ssf/error/error.h" + +#include "ssf/layer/io_handler.h" +#include "ssf/layer/protocol_attributes.h" + +namespace ssf { +namespace layer { +namespace multiplexing { + +template +class basic_Multiplexer + : public std::enable_shared_from_this< + basic_Multiplexer> { + private: + typedef ssf::layer::BaseIOHandler BaseIOHandler; + typedef ssf::layer::BaseIOHandlerPtr BaseIOHandlerPtr; + typedef std::pair, BaseIOHandlerPtr> + QueueElement; + typedef std::queue Queue; + + public: + static std::shared_ptr Create(SocketPtr p_socket) { + return std::shared_ptr( + new basic_Multiplexer(std::move(p_socket))); + } + + template + bool Send(Datagram datagram, const Endpoint& destination, Handler handler) { + if (!ready_) { + return false; + } + + boost::recursive_mutex::scoped_lock lock(mutex_); + + if (!congestion_policy_.IsAddable(pending_datagrams_, datagram)) { + // drop packet and notify handler + p_socket_->get_io_service().post([handler]() { + handler(boost::system::error_code(ssf::error::no_buffer_space, + ssf::error::get_ssf_category()), + 0); + }); + return false; + } + + pending_datagrams_.emplace(std::make_pair( + std::make_pair(destination, std::move(datagram)), + std::make_shared>(std::move(handler)))); + + if (!popping_) { + StartPopping(); + } + + return true; + } + + void Stop() { ready_ = false; } + + private: + basic_Multiplexer(SocketPtr p_socket) + : ready_(true), + popping_(false), + p_socket_(std::move(p_socket)), + mutex_(), + pending_datagrams_(), + congestion_policy_() {} + + /// Get the first element of the datagram queue and async send it + void StartPopping() { + boost::recursive_mutex::scoped_lock lock(mutex_); + + if (pending_datagrams_.empty()) { + popping_ = false; + return; + } + + popping_ = true; + auto& datagram = pending_datagrams_.front(); + + AsyncSendDatagram( + *p_socket_, datagram.first.second, datagram.first.first, + boost::bind(&basic_Multiplexer::DatagramSent, this->shared_from_this(), _1, _2)); + } + + /// Pop an element from the datagram queue + void DatagramSent(const boost::system::error_code& ec, std::size_t length) { + { + boost::recursive_mutex::scoped_lock lock(mutex_); + auto datagram = std::move(pending_datagrams_.front()); + pending_datagrams_.pop(); + auto p_handler = std::move(datagram.second); + p_socket_->get_io_service().post([p_handler, ec, length]() { + (*p_handler)(ec, length); + }); + } + + if (!ec) { + StartPopping(); + } else { + ready_ = false; + popping_ = false; + } + } + + private: + std::atomic ready_; + std::atomic popping_; + SocketPtr p_socket_; + boost::recursive_mutex mutex_; + Queue pending_datagrams_; + CongestionPolicy congestion_policy_; +}; + +template +using basic_MultiplexerPtr = std::shared_ptr< + basic_Multiplexer>; + +} // multiplexing +} // layer +} // ssf + +#endif // SSF_LAYER_MULTIPLEXING_BASIC_MULTIPLEXER_H_ diff --git a/src/framework/ssf/layer/multiplexing/basic_multiplexer_protocol.h b/src/framework/ssf/layer/multiplexing/basic_multiplexer_protocol.h new file mode 100644 index 00000000..80d1cfe8 --- /dev/null +++ b/src/framework/ssf/layer/multiplexing/basic_multiplexer_protocol.h @@ -0,0 +1,166 @@ +#ifndef SSF_LAYER_MULTIPLEXING_BASIC_MULTIPLEXER_PROTOCOL_H_ +#define SSF_LAYER_MULTIPLEXING_BASIC_MULTIPLEXER_PROTOCOL_H_ + +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "ssf/io/read_op.h" + +#include "ssf/layer/basic_resolver.h" +#include "ssf/layer/basic_endpoint.h" + +#include "ssf/layer/protocol_attributes.h" +#include "ssf/layer/parameters.h" + +#include "ssf/layer/datagram/basic_datagram.h" +#include "ssf/layer/datagram/basic_header.h" +#include "ssf/layer/datagram/basic_payload.h" +#include "ssf/layer/datagram/empty_component.h" + +#include "ssf/layer/multiplexing/basic_multiplexer_socket_service.h" + +#include "ssf/utils/map_helpers.h" + +namespace ssf { +namespace layer { +namespace multiplexing { + +template +class basic_MultiplexedProtocol { + private: + typedef typename NextLayer::socket next_socket_type; + + public: + typedef MultiplexID id_type; + typedef basic_Header Header; + typedef EmptyComponent Footer; + + typedef BufferPayload + ReceivePayload; + typedef basic_Datagram ReceiveDatagram; + + typedef ConstPayload SendPayload; + typedef basic_Datagram SendDatagram; + + enum { + id = 16, + overhead = SendDatagram::size, + facilities = ssf::layer::facilities::datagram, + mtu = NextLayer::mtu - overhead + }; + enum { endpoint_stack_size = NextLayer::endpoint_stack_size + 1 }; + + typedef NextLayer next_layer_protocol; + typedef typename id_type::HalfID endpoint_context_type; + using next_endpoint_type = typename next_layer_protocol::endpoint; + + typedef CongestionPolicy congestion_policy_type; + + typedef basic_VirtualLink_endpoint endpoint; + typedef basic_VirtualLink_resolver resolver; + typedef boost::asio::basic_datagram_socket< + basic_MultiplexedProtocol, + basic_MultiplexedSocket_service> socket; + + struct socket_context { + boost::recursive_mutex mutex; + + endpoint_context_type local_id; + endpoint_context_type remote_id; + + std::queue datagram_queue; + + std::queue next_endpoint_queue; + + boost::asio::detail::op_queue> read_op_queue; + }; + +private: + using query = typename resolver::query; + + public: + static endpoint make_endpoint( + boost::asio::io_service& io_service, + typename query::const_iterator parameters_it, uint32_t id, + boost::system::error_code& ec) { + auto context = id_type::MakeHalfID(io_service, *parameters_it, id); + if (!id) { + id = basic_MultiplexedProtocol::id; + } + + auto next_layer_endpoint = next_layer_protocol::make_endpoint( + io_service, ++parameters_it, id, ec); + + return endpoint(context, next_layer_endpoint); + } + + static endpoint remote_to_local_endpoint( + const endpoint& remote_endpoint, + const std::set& used_local_endpoints) { + std::set used_local_next_endpoints; + for (const auto& endpoint : used_local_endpoints) { + used_local_next_endpoints.insert(endpoint.next_layer_endpoint()); + } + + auto next_endpoint = next_layer_protocol::remote_to_local_endpoint( + remote_endpoint.next_layer_endpoint(), used_local_next_endpoints); + + if (next_endpoint == next_layer_protocol::endpoint()) { + return endpoint(); + } + + auto context = FindAvailableContext(next_endpoint, remote_endpoint, + used_local_endpoints); + + return endpoint(context, next_endpoint); + } + + template + static SendDatagram make_datagram(const ConstBufferSequence& buffers, + const endpoint& source, + const endpoint& destination) { + Header dgr_header; + auto& id = dgr_header.id(); + id = Header::ID(source.endpoint_context(), destination.endpoint_context()); + /*auto& data_size = dgr_header.payload_length(); + data_size = boost::asio::buffer_size(buffers);*/ + SendPayload payload(buffers); + + return SendDatagram(dgr_header, payload, Footer()); + } + + private: + static endpoint_context_type FindAvailableContext( + const next_endpoint_type& next_endpoint, const endpoint& remote_endpoint, + const std::set& used_local_endpoints) { + std::set context_set; + + // Find local endpoint contexts whose next layer is identic to + // the remote next layer endpoint context + for (const auto& endpoint : used_local_endpoints) { + if (endpoint.next_layer_endpoint() == next_endpoint) { + context_set.insert(endpoint.endpoint_context()); + } + } + + return id_type::LocalHalfIDFromRemote(remote_endpoint.endpoint_context(), + context_set); + } +}; + +} // multiplexing +} // layer +} // ssf + +#endif // SSF_LAYER_MULTIPLEXING_BASIC_MULTIPLEXER_PROTOCOL_H_ diff --git a/src/framework/ssf/layer/multiplexing/basic_multiplexer_socket_service.h b/src/framework/ssf/layer/multiplexing/basic_multiplexer_socket_service.h new file mode 100644 index 00000000..fe6d5628 --- /dev/null +++ b/src/framework/ssf/layer/multiplexing/basic_multiplexer_socket_service.h @@ -0,0 +1,591 @@ +#ifndef SSF_LAYER_MULTIPLEXING_BASIC_MULTIPLEXER_SOCKET_SERVICE_H_ +#define SSF_LAYER_MULTIPLEXING_BASIC_MULTIPLEXER_SOCKET_SERVICE_H_ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "ssf/error/error.h" +#include "ssf/io/read_op.h" +#include "ssf/io/composed_op.h" + +#include "ssf/layer/basic_impl.h" + +#include "ssf/layer/protocol_attributes.h" + +#include "ssf/layer/multiplexing/basic_multiplexer.h" +#include "ssf/layer/multiplexing/multiplexer_manager.h" +#include "ssf/layer/multiplexing/basic_demultiplexer.h" +#include "ssf/layer/multiplexing/demultiplexer_manager.h" + +namespace ssf { +namespace layer { +namespace multiplexing { + +#include + +template +class basic_MultiplexedSocket_service + : public boost::asio::detail::service_base< + basic_MultiplexedSocket_service> { + public: + /// The protocol type. + typedef Protocol protocol_type; + /// The endpoint type. + typedef typename protocol_type::endpoint endpoint_type; + typedef typename protocol_type::resolver resolver_type; + typedef typename protocol_type::id_type id_type; + typedef typename protocol_type::SendDatagram send_datagram_type; + typedef typename protocol_type::ReceiveDatagram receive_datagram_type; + typedef std::shared_ptr p_receive_datagram_type; + + typedef basic_socket_impl implementation_type; + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + private: + typedef typename protocol_type::next_layer_protocol::socket next_socket_type; + typedef std::shared_ptr p_next_socket_type; + typedef typename protocol_type::next_layer_protocol::endpoint + next_endpoint_type; + typedef std::shared_ptr p_next_endpoint_type; + typedef typename protocol_type::endpoint_context_type endpoint_context_type; + typedef typename protocol_type::socket_context socket_context_type; + typedef std::shared_ptr p_socket_context_type; + typedef typename protocol_type::congestion_policy_type congestion_policy_type; + + public: + explicit basic_MultiplexedSocket_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service) {} + + virtual ~basic_MultiplexedSocket_service() {} + + void construct(implementation_type& impl) {} + + void destroy(implementation_type& impl) { + impl.p_socket_context.reset(); + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_socket.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return ec; + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + + return ec; + } + + bool is_open(const implementation_type& impl) const { + return !!impl.p_next_layer_socket; + } + + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_remote_endpoint) { + return *impl.p_remote_endpoint; + } else { + return endpoint_type(); + } + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + return *impl.p_local_endpoint; + } else { + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + { + boost::recursive_mutex::scoped_lock lock(mutex_); + + // Unbind context in demultiplexer + demultiplexer_manager_.Unbind(impl.p_next_layer_socket, + impl.p_socket_context); + + if (impl.p_local_endpoint) { + local_endpoints_.erase(*(impl.p_local_endpoint)); + auto& next_endpoint = impl.p_local_endpoint->next_layer_endpoint(); + + auto pair_p_next_socket_it = + next_endpoint_to_next_socket_.find(next_endpoint); + + if (pair_p_next_socket_it != std::end(next_endpoint_to_next_socket_)) { + // Decrease usage counter + --(pair_p_next_socket_it->second.second); + + if (!pair_p_next_socket_it->second.second) { + // Next socket layer unused + StopMultiplexer(pair_p_next_socket_it->second.first); + StopDemultiplexer(pair_p_next_socket_it->second.first); + next_endpoint_to_next_socket_.erase(next_endpoint); + impl.p_next_layer_socket->close(ec); + } + } + } + } + impl.p_socket_context.reset(); + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_socket.reset(); + + return ec; + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + bool at_mark(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + return false; + } + + return impl.p_next_layer_socket->at_mark(ec); + } + + /// Size in bytes of the next datagram payload + std::size_t available(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_socket_context) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return 0; + } + + boost::recursive_mutex::scoped_lock lock(impl.p_socket_context->mutex); + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + if (impl.p_socket_context->datagram_queue.empty()) { + return 0; + } + + return impl.p_socket_context->datagram_queue.front().payload().GetSize(); + } + + boost::system::error_code bind(implementation_type& impl, + endpoint_type local_endpoint, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + // Get the next layer socket which is linked to the next local endpoint + auto pair_p_next_socket_it = next_endpoint_to_next_socket_.find( + local_endpoint.next_layer_endpoint()); + + if (pair_p_next_socket_it == std::end(next_endpoint_to_next_socket_)) { + // Next layer endpoint unknown => create a new socket for this endpoint, + // create its multiplexer and start it + impl.p_next_layer_socket = + std::make_shared(this->get_io_service()); + impl.p_next_layer_socket->bind(local_endpoint.next_layer_endpoint(), ec); + + if (ec) { + return ec; + } + auto next_endpoint_inserted = next_endpoint_to_next_socket_.emplace( + local_endpoint.next_layer_endpoint(), + std::make_pair(impl.p_next_layer_socket, 1)); + + StartDemultiplexer(impl.p_next_layer_socket); + StartMultiplexer(impl.p_next_layer_socket); + } else { + // Increase usage counter + ++(pair_p_next_socket_it->second.second); + impl.p_next_layer_socket = pair_p_next_socket_it->second.first; + } + + if (!impl.p_socket_context) { + impl.p_socket_context = + std::make_shared(); + } + + impl.p_socket_context->local_id = local_endpoint.endpoint_context(); + + if (!demultiplexer_manager_.Bind(impl.p_next_layer_socket, + impl.p_socket_context)) { + ec.assign(ssf::error::address_in_use, ssf::error::get_ssf_category()); + return ec; + } + local_endpoints_.insert(local_endpoint); + impl.p_local_endpoint = + std::make_shared(std::move(local_endpoint)); + local_endpoints_.insert(local_endpoint); + return ec; + } + + boost::system::error_code connect(implementation_type& impl, + endpoint_type remote_endpoint, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + if (!impl.p_socket_context) { + impl.p_socket_context = + std::make_shared(); + } + + if (!impl.p_local_endpoint) { + impl.p_remote_endpoint = + std::make_shared(std::move(remote_endpoint)); + impl.p_socket_context->remote_id = remote_endpoint.endpoint_context(); + + auto local_endpoint = protocol_type::remote_to_local_endpoint( + remote_endpoint, local_endpoints_); + + if (local_endpoint == endpoint_type()) { + ec.assign(ssf::error::address_not_available, + ssf::error::get_ssf_category()); + return ec; + } + + bind(impl, std::move(local_endpoint), ec); + } else { + if (!impl.p_remote_endpoint) { + impl.p_remote_endpoint = + std::make_shared(); + } + + auto binding_changed = ChangeBinding(impl, remote_endpoint); + + if (!binding_changed) { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return ec; + } + } + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& remote_endpoint, + ConnectHandler&& handler) { + boost::asio::detail::async_result_init< + ConnectHandler, void(const boost::system::error_code&)> + init(std::forward(handler)); + + auto connect_lambda = [this, &impl, remote_endpoint, init]() { + boost::system::error_code ec; + this->connect(impl, remote_endpoint, ec); + init.handler(ec); + }; + + this->get_io_service().post( + io::ComposedOp( + std::move(connect_lambda), init.handler)); + + return init.result.get(); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_remote_endpoint) { + this->get_io_service().post(boost::asio::detail::binder2< + decltype(init.handler), boost::system::error_code, std::size_t>( + init.handler, + boost::system::error_code(ssf::error::destination_address_required, + ssf::error::get_ssf_category()), + 0)); + return init.result.get(); + } + + return async_send_to(impl, buffers, *impl.p_remote_endpoint, flags, + init.handler); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_next_layer_socket || !impl.p_socket_context || + !impl.p_local_endpoint) { + this->get_io_service().post(boost::asio::detail::binder2< + decltype(init.handler), boost::system::error_code, std::size_t>( + init.handler, + boost::system::error_code(ssf::error::destination_address_required, + ssf::error::get_ssf_category()), + 0)); + return init.result.get(); + } + + if (boost::asio::buffer_size(buffers) > protocol_type::mtu) { + this->get_io_service().post( + boost::asio::detail::binder2( + init.handler, + boost::system::error_code(ssf::error::message_size, + ssf::error::get_ssf_category()), + 0)); + return init.result.get(); + } + + auto datagram = protocol_type::make_datagram( + buffers, *impl.p_local_endpoint, destination); + + auto complete_lambda = [init](const boost::system::error_code& ec, + std::size_t length) { + if (length < send_datagram_type::size) { + init.handler(boost::system::error_code(ssf::error::broken_pipe, + ssf::error::get_ssf_category()), + 0); + return; + } + + init.handler(ec, length - send_datagram_type::size); + }; + + multiplexer_manager_.Send( + impl.p_next_layer_socket, std::move(datagram), + destination.next_layer_endpoint(), + io::ComposedOp( + std::move(complete_lambda), init.handler)); + + return init.result.get(); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_remote_endpoint) { + this->get_io_service().post(boost::asio::detail::binder2< + decltype(init.handler), boost::system::error_code, std::size_t>( + init.handler, + boost::system::error_code(ssf::error::destination_address_required, + ssf::error::get_ssf_category()), + 0)); + return init.result.get(); + } + + return async_receive_from(impl, buffers, *impl.p_remote_endpoint, flags, + handler); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& sender_endpoint, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_next_layer_socket || !impl.p_socket_context || + !impl.p_local_endpoint) { + this->get_io_service().post(boost::asio::detail::binder2< + decltype(init.handler), boost::system::error_code, std::size_t>( + init.handler, + boost::system::error_code(ssf::error::destination_address_required, + ssf::error::get_ssf_category()), + 0)); + return init.result.get(); + } + + if (!demultiplexer_manager_.IsBound(impl.p_next_layer_socket, + impl.p_socket_context)) { + this->get_io_service().post( + boost::asio::detail::binder2( + init.handler, + boost::system::error_code(ssf::error::network_down, + ssf::error::get_ssf_category()), + 0)); + return init.result.get(); + } + + { + boost::recursive_mutex::scoped_lock lock(impl.p_socket_context->mutex); + auto& op_queue = impl.p_socket_context->read_op_queue; + + typedef io::pending_read_operation< + MutableBufferSequence, decltype(init.handler), protocol_type> op; + typename op::ptr p = { + boost::asio::detail::addressof(init.handler), + boost_asio_handler_alloc_helpers::allocate(sizeof(op), init.handler), + 0}; + + p.p = new (p.v) op(buffers, init.handler, &sender_endpoint); + op_queue.push(p.p); + p.v = p.p = 0; + } + + demultiplexer_manager_.Read(impl.p_next_layer_socket, impl.p_socket_context); + + return init.result.get(); + } + + boost::system::error_code shutdown( + implementation_type& impl, boost::asio::socket_base::shutdown_type what, + boost::system::error_code& ec) { + return ec; + } + + /// Link a demultiplexer to the next layer socket + void StartDemultiplexer(p_next_socket_type p_socket) { + demultiplexer_manager_.Start(p_socket); + } + + void StopDemultiplexer(p_next_socket_type p_socket) { + demultiplexer_manager_.Stop(p_socket); + } + + static void StopDemultiplexer() { demultiplexer_manager_.Stop(); } + + /// Link a multiplexer to the next layer socket + void StartMultiplexer(p_next_socket_type p_socket) { + multiplexer_manager_.Start(p_socket); + } + + void StopMultiplexer(p_next_socket_type p_socket) { + multiplexer_manager_.Stop(p_socket); + } + + static void StopMultiplexer() { multiplexer_manager_.Stop(); } + + private: + void shutdown_service() {} + + private: + bool ChangeBinding(implementation_type& impl, endpoint_type remote_endpoint) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + if (!impl.p_socket_context || !impl.p_next_layer_socket || + !impl.p_local_endpoint || !impl.p_remote_endpoint) { + return false; + } + + auto pair_p_next_socket_it = next_endpoint_to_next_socket_.find( + impl.p_next_layer_socket->local_endpoint()); + + if (pair_p_next_socket_it == std::end(next_endpoint_to_next_socket_)) { + return false; + } + + if (demultiplexer_manager_.Unbind(impl.p_next_layer_socket, + impl.p_socket_context)) { + --pair_p_next_socket_it->second.second; + } + + impl.p_remote_endpoint = + std::make_shared(std::move(remote_endpoint)); + impl.p_socket_context->remote_id = + impl.p_remote_endpoint->endpoint_context(); + impl.p_socket_context->local_id = impl.p_local_endpoint->endpoint_context(); + + auto context_bound = demultiplexer_manager_.Bind(impl.p_next_layer_socket, + impl.p_socket_context); + + pair_p_next_socket_it->second.second += !!context_bound; + + return context_bound; + } + + private: + typedef uint32_t usage_counter_type; + + static boost::recursive_mutex mutex_; + static std::map> + next_endpoint_to_next_socket_; + static std::set local_endpoints_; + static multiplexing::MultiplexerManager< + p_next_socket_type, send_datagram_type, next_endpoint_type, + congestion_policy_type> multiplexer_manager_; + + static multiplexing::DemultiplexerManager< + protocol_type, congestion_policy_type> demultiplexer_manager_; +}; + +template +boost::recursive_mutex basic_MultiplexedSocket_service::mutex_; + +template +std::map::next_endpoint_type, + std::pair::p_next_socket_type, + typename basic_MultiplexedSocket_service< + Protocol >::usage_counter_type> > + basic_MultiplexedSocket_service::next_endpoint_to_next_socket_; + +template +std::set + basic_MultiplexedSocket_service::local_endpoints_; + +template +multiplexing::MultiplexerManager< + typename basic_MultiplexedSocket_service::p_next_socket_type, + typename basic_MultiplexedSocket_service::send_datagram_type, + typename basic_MultiplexedSocket_service::next_endpoint_type, + typename basic_MultiplexedSocket_service::congestion_policy_type> + basic_MultiplexedSocket_service::multiplexer_manager_; + +template +multiplexing::DemultiplexerManager< + Protocol, + typename basic_MultiplexedSocket_service::congestion_policy_type> + basic_MultiplexedSocket_service::demultiplexer_manager_; + +#include + +} // multiplexing +} // layer +} // ssf + +#endif // SSF_LAYER_MULTIPLEXING_BASIC_MULTIPLEXER_SOCKET_SERVICE_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/multiplexing/demultiplexer_manager.h b/src/framework/ssf/layer/multiplexing/demultiplexer_manager.h new file mode 100644 index 00000000..2aab333c --- /dev/null +++ b/src/framework/ssf/layer/multiplexing/demultiplexer_manager.h @@ -0,0 +1,112 @@ +#ifndef SSF_LAYER_MULTIPLEXING_DEMULTIPLEXER_MANAGER_H_ +#define SSF_LAYER_MULTIPLEXING_DEMULTIPLEXER_MANAGER_H_ + +#include + +#include + +#include "ssf/layer/multiplexing/basic_demultiplexer.h" + +namespace ssf { +namespace layer { +namespace multiplexing { + +template +class DemultiplexerManager { + private: + typedef basic_Demultiplexer Demultiplexer; + typedef basic_DemultiplexerPtr DemultiplexerPtr; + typedef typename Protocol::next_layer_protocol::socket NextSocket; + typedef std::shared_ptr NextSocketPtr; + typedef typename Protocol::socket_context SocketContext; + typedef std::shared_ptr SocketContextPtr; + + public: + bool Start(NextSocketPtr p_socket) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + auto inserted = + demultiplexers_.insert(std::make_pair(p_socket, Demultiplexer::Create(p_socket))); + inserted.first->second->Start(); + return inserted.second; + } + + void Stop(NextSocketPtr p_socket) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + auto demultiplexer_it = demultiplexers_.find(p_socket); + + if (demultiplexer_it != std::end(demultiplexers_)) { + demultiplexer_it->second->Stop(); + demultiplexers_.erase(demultiplexer_it); + } + } + + void Stop() { + boost::recursive_mutex::scoped_lock lock(mutex_); + + for (auto& pair : demultiplexers_) { + pair.second->Stop(); + } + + demultiplexers_.clear(); + } + + bool Bind(NextSocketPtr p_socket, SocketContextPtr p_socket_context) { + boost::recursive_mutex::scoped_lock lock(mutex_); + // Get the demultiplexer linked to the given socket + auto demultiplexer_it = demultiplexers_.find(p_socket); + + if (demultiplexer_it == std::end(demultiplexers_)) { + return false; + } + + return demultiplexer_it->second->Bind(p_socket_context); + } + + bool Unbind(NextSocketPtr p_socket, SocketContextPtr p_socket_context) { + boost::recursive_mutex::scoped_lock lock(mutex_); + // Get the demultiplexer linked to the given socket + auto demultiplexer_it = demultiplexers_.find(p_socket); + + if (demultiplexer_it == std::end(demultiplexers_)) { + return false; + } + + return demultiplexer_it->second->Unbind(p_socket_context); + } + + bool IsBound(NextSocketPtr p_socket, SocketContextPtr p_socket_context) { + boost::recursive_mutex::scoped_lock lock(mutex_); + // Get the demultiplexer linked to the given socket + auto demultiplexer_it = demultiplexers_.find(p_socket); + + if (demultiplexer_it == std::end(demultiplexers_)) { + return false; + } + + return demultiplexer_it->second->IsBound(p_socket_context); + } + + void Read(NextSocketPtr p_socket, SocketContextPtr p_socket_context) { + boost::recursive_mutex::scoped_lock lock(mutex_); + // Get the demultiplexer linked to the given socket + auto demultiplexer_it = demultiplexers_.find(p_socket); + + if (demultiplexer_it == std::end(demultiplexers_)) { + return; + } + + return demultiplexer_it->second->Read(p_socket_context); + } + + private: + boost::recursive_mutex mutex_; + std::map demultiplexers_; +}; + +} // multiplexing +} // layer +} // ssf + +#endif // SSF_LAYER_MULTIPLEXING_DEMULTIPLEXER_MANAGER_H_ diff --git a/src/framework/ssf/layer/multiplexing/multiplexer_manager.h b/src/framework/ssf/layer/multiplexing/multiplexer_manager.h new file mode 100644 index 00000000..a7b0ca77 --- /dev/null +++ b/src/framework/ssf/layer/multiplexing/multiplexer_manager.h @@ -0,0 +1,79 @@ +#ifndef SSF_LAYER_MULTIPLEXING_MULTIPLEXER_MANAGER_H_ +#define SSF_LAYER_MULTIPLEXING_MULTIPLEXER_MANAGER_H_ + +#include + +#include + +#include "ssf/layer/multiplexing/basic_multiplexer.h" + +namespace ssf { +namespace layer { +namespace multiplexing { + +template +class MultiplexerManager { + private: + typedef basic_Multiplexer Multiplexer; + typedef basic_MultiplexerPtr MultiplexerPtr; + + public: + MultiplexerManager() : mutex_(), multiplexers_() {} + ~MultiplexerManager() {} + + bool Start(SocketPtr p_socket) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + auto inserted = + multiplexers_.insert(std::make_pair(p_socket, Multiplexer::Create(p_socket))); + + return inserted.second; + } + + template + bool Send(SocketPtr p_socket, Datagram datagram, const Endpoint& destination, + Handler handler) { + boost::recursive_mutex::scoped_lock lock(mutex_); + // Get the multiplexer linked to the given socket and send datagram through it + auto multiplexer_it = multiplexers_.find(p_socket); + + if (multiplexer_it == std::end(multiplexers_)) { + return false; + } + + return multiplexer_it->second->Send(std::move(datagram), destination, + std::move(handler)); + } + + void Stop(SocketPtr p_socket) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + auto multiplexer_it = multiplexers_.find(p_socket); + + if (multiplexer_it != std::end(multiplexers_)) { + multiplexer_it->second->Stop(); + multiplexers_.erase(multiplexer_it); + } + } + + void Stop() { + boost::recursive_mutex::scoped_lock lock(mutex_); + + for (auto& pair : multiplexers_) { + pair.second->Stop(); + } + + multiplexers_.clear(); + } + + private: + boost::recursive_mutex mutex_; + std::map multiplexers_; +}; + +} // multiplexing +} // layer +} // ssf + +#endif // SSF_LAYER_MULTIPLEXING_MULTIPLEXER_MANAGER_H_ diff --git a/src/framework/ssf/layer/multiplexing/port_multiplex_id.h b/src/framework/ssf/layer/multiplexing/port_multiplex_id.h new file mode 100644 index 00000000..47b7f871 --- /dev/null +++ b/src/framework/ssf/layer/multiplexing/port_multiplex_id.h @@ -0,0 +1,163 @@ +#ifndef SSF_LAYER_MULTIPLEXING_PORT_MULTIPLEX_ID_H_ +#define SSF_LAYER_MULTIPLEXING_PORT_MULTIPLEX_ID_H_ + +#include + +#include + +#include + +#include "ssf/utils/map_helpers.h" + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { +namespace multiplexing { + +class PortID { + private: + typedef uint16_t ID; + + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { size = sizeof(ID) }; + + public: + PortID() : id_(0) {} + PortID(ID id) : id_(id) {} + ~PortID() {} + + bool operator==(const PortID& rhs) const { return id_ == rhs.id_; } + bool operator!=(const PortID& rhs) const { return id_ != rhs.id_; } + bool operator<(const PortID& rhs) const { return id_ < rhs.id_; } + bool operator!() const { return !id_; } + + ConstBuffers GetConstBuffers() const { + return ConstBuffers({boost::asio::buffer(&id_, sizeof(id_))}); + } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + p_buffers->push_back(boost::asio::buffer(&id_, sizeof(id_))); + } + + MutableBuffers GetMutableBuffers() { + return MutableBuffers({boost::asio::buffer(&id_, sizeof(id_))}); + } + + void GetMutableBuffers(MutableBuffers* p_buffers) { + p_buffers->push_back(boost::asio::buffer(&id_, sizeof(id_))); + } + + ID& id() { return id_; } + const ID& id() const { return id_; } + + private: + ID id_; +}; + +class PortPairID { + private: + typedef PortID ID; + + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { size = 2 * ID::size }; + + public: + PortPairID() : left_id_(0), right_id_(0) {} + PortPairID(ID l_id, ID r_id) : left_id_(l_id), right_id_(r_id) {} + ~PortPairID() {} + + bool operator==(const PortPairID& rhs) const { + return (left_id_ == rhs.left_id_) && (right_id_ == rhs.right_id_); + } + + bool operator!=(const PortPairID& rhs) const { return !(rhs == *this); } + + bool operator<(const PortPairID& rhs) const { + return (left_id_ < rhs.left_id_) || + ((left_id_ == rhs.left_id_) && (right_id_ < rhs.right_id_)); + } + + ConstBuffers GetConstBuffers() const { + ConstBuffers result; + left_id_.GetConstBuffers(&result); + right_id_.GetConstBuffers(&result); + return result; + } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + left_id_.GetConstBuffers(p_buffers); + right_id_.GetConstBuffers(p_buffers); + } + + MutableBuffers GetMutableBuffers() { + MutableBuffers result; + left_id_.GetMutableBuffers(&result); + right_id_.GetMutableBuffers(&result); + return result; + } + + void GetMutableBuffers(MutableBuffers* p_buffers) { + left_id_.GetMutableBuffers(p_buffers); + right_id_.GetMutableBuffers(p_buffers); + } + + ID& left_id() { return left_id_; } + const ID& left_id() const { return left_id_; } + + ID& right_id() { return right_id_; } + const ID& right_id() const { return right_id_; } + + ID GetFirstHalfId() const { return left_id_; } + ID GetSecondHalfId() const { return right_id_; } + + private: + ID left_id_; + ID right_id_; +}; + +class PortMultiplexID { + public: + typedef PortID HalfID; + typedef PortPairID FullID; + enum { size = FullID::size }; + + public: + static HalfID MakeHalfRemoteID(FullID full_id) { + return full_id.GetFirstHalfId(); + } + + static HalfID MakeHalfID(boost::asio::io_service& io_service, + const LayerParameters& parameters, uint32_t id) { + auto id_string = helpers::GetField("port", parameters); + + try { + return HalfID((uint16_t)std::stoul(id_string)); + } + catch (const std::exception&) { + return HalfID(); + } + } + + static HalfID LocalHalfIDFromRemote(const HalfID& remote_id, + const std::set& used_local_ids) { + for (uint16_t i = 1; i < std::numeric_limits::max(); ++i) { + auto half_id = HalfID(i); + if (!used_local_ids.count(half_id)) { + return half_id; + } + } + + return HalfID(0); + } +}; + +} // multiplexing +} // layer +} // ssf + +#endif // SSF_LAYER_MULTIPLEXING_PORT_MULTIPLEX_ID_H_ diff --git a/src/framework/ssf/layer/multiplexing/protocol_and_port_multiplex_id.h b/src/framework/ssf/layer/multiplexing/protocol_and_port_multiplex_id.h new file mode 100644 index 00000000..f88e8cbf --- /dev/null +++ b/src/framework/ssf/layer/multiplexing/protocol_and_port_multiplex_id.h @@ -0,0 +1,197 @@ +#ifndef SSF_LAYER_MULTIPLEXING_PROTOCOL_N_PORT_MULTIPLEX_ID_H_ +#define SSF_LAYER_MULTIPLEXING_PROTOCOL_N_PORT_MULTIPLEX_ID_H_ + +#include + +#include +#include +#include + +#include +#include + +#include "common/utils/map_helpers.h" + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { +namespace multiplexing { + +class ProtocolAndPortID { + private: + typedef uint8_t ProtocolID; + typedef uint16_t PortID; + + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { size = sizeof(ProtocolID) + sizeof(PortID) }; + + public: + ProtocolAndPortID() : protocol_id_(0), port_id_(0) {} + ProtocolAndPortID(ProtocolID protocol_id, PortID port_id) + : protocol_id_(protocol_id), port_id_(port_id) {} + ~ProtocolAndPortID() {} + + bool operator==(const ProtocolAndPortID& rhs) const { + return ((protocol_id_ == rhs.protocol_id_) && (port_id_ == rhs.port_id_)); + } + + bool operator!=(const ProtocolAndPortID& rhs) const { + return !(*this == rhs); + } + + bool operator<(const ProtocolAndPortID& rhs) const { + return (protocol_id_ < rhs.protocol_id_) || + ((protocol_id_ == rhs.protocol_id_) && (port_id_ < rhs.port_id_)); + } + + bool operator!() const { return !(!!protocol_id_ && !!port_id_); } + + ConstBuffers GetConstBuffers() const { + ConstBuffers result; + result.push_back(boost::asio::buffer(&protocol_id_, sizeof(protocol_id_))); + result.push_back(boost::asio::buffer(&port_id_, sizeof(port_id_))); + return result; + } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + p_buffers->push_back( + boost::asio::buffer(&protocol_id_, sizeof(protocol_id_))); + p_buffers->push_back(boost::asio::buffer(&port_id_, sizeof(port_id_))); + } + + MutableBuffers GetMutableBuffers() { + MutableBuffers result; + result.push_back(boost::asio::buffer(&protocol_id_, sizeof(protocol_id_))); + result.push_back(boost::asio::buffer(&port_id_, sizeof(port_id_))); + return result; + } + + void GetMutableBuffers(MutableBuffers* p_buffers) { + p_buffers->push_back( + boost::asio::buffer(&protocol_id_, sizeof(protocol_id_))); + p_buffers->push_back(boost::asio::buffer(&port_id_, sizeof(port_id_))); + } + + ProtocolID& protocol_id() { return protocol_id_; } + const ProtocolID& protocol_id() const { return protocol_id_; } + + PortID& port_id() { return port_id_; } + const PortID& port_id() const { return port_id_; } + + private: + ProtocolID protocol_id_; + PortID port_id_; +}; + +class ProtocolAndPortPairID { + private: + typedef ProtocolAndPortID ID; + + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { size = 2 * ID::size }; + + public: + ProtocolAndPortPairID() : left_id_(), right_id_() {} + ProtocolAndPortPairID(ID l_id, ID r_id) : left_id_(l_id), right_id_(r_id) {} + ~ProtocolAndPortPairID() {} + + bool operator==(const ProtocolAndPortPairID& rhs) const { + return (left_id_ == rhs.left_id_) && (right_id_ == rhs.right_id_); + } + + bool operator!=(const ProtocolAndPortPairID& rhs) const { + return !(rhs == *this); + } + + bool operator<(const ProtocolAndPortPairID& rhs) const { + return (left_id_ < rhs.left_id_) || + ((left_id_ == rhs.left_id_) && (right_id_ < rhs.right_id_)); + } + + ConstBuffers GetConstBuffers() const { + ConstBuffers result; + left_id_.GetConstBuffers(&result); + right_id_.GetConstBuffers(&result); + return result; + } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + left_id_.GetConstBuffers(p_buffers); + right_id_.GetConstBuffers(p_buffers); + } + + MutableBuffers GetMutableBuffers() { + MutableBuffers result; + left_id_.GetMutableBuffers(&result); + right_id_.GetMutableBuffers(&result); + return result; + } + + void GetMutableBuffers(MutableBuffers* p_buffers) { + left_id_.GetMutableBuffers(p_buffers); + right_id_.GetMutableBuffers(p_buffers); + } + + ID& left_id() { return left_id_; } + const ID& left_id() const { return left_id_; } + + ID& right_id() { return right_id_; } + const ID& right_id() const { return right_id_; } + + ID GetFirstHalfId() const { return left_id_; } + ID GetSecondHalfId() const { return right_id_; } + + private: + ID left_id_; + ID right_id_; +}; + +class ProtocolAndPortMultiplexID { + public: + typedef ProtocolAndPortID HalfID; + typedef ProtocolAndPortPairID FullID; + enum { size = FullID::size }; + + public: + static HalfID MakeHalfRemoteID(FullID full_id) { + return full_id.GetFirstHalfId(); + } + + static HalfID MakeHalfID(boost::asio::io_service& io_service, + const LayerParameters& parameters, uint32_t id) { + auto protocol_string = + helpers::GetField("protocol", parameters); + auto port_string = helpers::GetField("port", parameters); + + try { + return HalfID((uint8_t)std::stoul(protocol_string), + (uint16_t)std::stoul(port_string)); + } + catch (const std::exception&) { + return HalfID(); + } + } + + static HalfID LocalHalfIDFromRemote(const HalfID& remote_id, + const std::set& used_local_ids) { + for (uint16_t i = 1; i < std::numeric_limits::max(); ++i) { + auto half_id = HalfID(remote_id.protocol_id(), i); + if (!used_local_ids.count(half_id)) { + return half_id; + } + } + + return HalfID(); + } +}; + +} // multiplexing +} // layer +} // ssf + +#endif // SSF_LAYER_MULTIPLEXING_PROTOCOL_N_PORT_MULTIPLEX_ID_H_ diff --git a/src/framework/ssf/layer/multiplexing/protocol_multiplex_id.h b/src/framework/ssf/layer/multiplexing/protocol_multiplex_id.h new file mode 100644 index 00000000..23a7e22d --- /dev/null +++ b/src/framework/ssf/layer/multiplexing/protocol_multiplex_id.h @@ -0,0 +1,100 @@ +#ifndef SSF_LAYER_MULTIPLEXING_PROTOCOL_MULTIPLEX_ID_H_ +#define SSF_LAYER_MULTIPLEXING_PROTOCOL_MULTIPLEX_ID_H_ + +#include + +#include + +#include + +#include "ssf/utils/map_helpers.h" + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { +namespace multiplexing { + +class ProtocolID { + private: + typedef uint8_t ID; + + public: + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { size = sizeof(ID) }; + + public: + ProtocolID() : id_(0) {} + ProtocolID(ID id) : id_(id) {} + ProtocolID(ProtocolID id1, ProtocolID id2) : id_(id1.id_) {} + ~ProtocolID() {} + + bool operator==(const ProtocolID& rhs) const { return id_ == rhs.id_; } + bool operator!=(const ProtocolID& rhs) const { return id_ != rhs.id_; } + bool operator<(const ProtocolID& rhs) const { return id_ < rhs.id_; } + bool operator!() const { return !id_; } + + ConstBuffers GetConstBuffers() const { + return ConstBuffers({boost::asio::buffer(&id_, sizeof(id_))}); + } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + p_buffers->push_back(boost::asio::buffer(&id_, sizeof(id_))); + } + + MutableBuffers GetMutableBuffers() { + return MutableBuffers({boost::asio::buffer(&id_, sizeof(id_))}); + } + + void GetMutableBuffers(MutableBuffers* p_buffers) { + p_buffers->push_back(boost::asio::buffer(&id_, sizeof(id_))); + } + + ID& id() { return id_; } + const ID& id() const { return id_; } + + ID GetFirstHalfId() const { return id_; } + ID GetSecondHalfId() const { return id_; } + + private: + ID id_; +}; + +class ProtocolMultiplexID { + public: + typedef ProtocolID HalfID; + typedef ProtocolID FullID; + enum { size = ProtocolID::size }; + + public: + static HalfID MakeHalfRemoteID(FullID full_id) { return full_id; } + + static HalfID MakeHalfID(boost::asio::io_service& io_service, + const LayerParameters& parameters, uint32_t id) { + if (!id) { + auto id_string = + helpers::GetField("protocol_id", parameters); + + try { + return HalfID((uint8_t)std::stoul(id_string)); + } + catch (const std::exception&) { + return HalfID(); + } + } else { + return HalfID((uint8_t)id); + } + } + + static HalfID LocalHalfIDFromRemote(const HalfID& remote_id, + const std::set& used_local_ids) { + return remote_id; + } +}; + +} // multiplexing +} // layer +} // ssf + +#endif // SSF_LAYER_MULTIPLEXING_PROTOCOL_MULTIPLEX_ID_H_ diff --git a/src/framework/ssf/layer/network/basic_network_protocol.h b/src/framework/ssf/layer/network/basic_network_protocol.h new file mode 100644 index 00000000..4e601f9f --- /dev/null +++ b/src/framework/ssf/layer/network/basic_network_protocol.h @@ -0,0 +1,145 @@ +#ifndef SSF_LAYER_NETWORK_BASIC_NETWORK_PROTOCOL_H_ +#define SSF_LAYER_NETWORK_BASIC_NETWORK_PROTOCOL_H_ + +#include + +#include +#include +#include + +#include +#include + +#include + +#include "ssf/error/error.h" + +#include "ssf/layer/basic_endpoint.h" +#include "ssf/layer/basic_resolver.h" + +#include "ssf/layer/datagram/basic_datagram.h" +#include "ssf/layer/datagram/basic_header.h" +#include "ssf/layer/datagram/basic_payload.h" +#include "ssf/layer/datagram/empty_component.h" + +#include "ssf/layer/parameters.h" +#include "ssf/layer/protocol_attributes.h" + +#include "ssf/layer/network/basic_network_socket_service.h" +#include "ssf/layer/network/basic_network_raw_socket_service.h" +#include "ssf/layer/network/network_id.h" + +namespace ssf { +namespace layer { +namespace network { + +template +class basic_NetworkProtocol { + public: + using PayloadSize = uint16_t; + typedef basic_Header + Header; + typedef EmptyComponent Footer; + + typedef BufferPayload + ReceivePayload; + typedef basic_Datagram ReceiveDatagram; + + typedef ConstPayload SendPayload; + typedef basic_Datagram SendDatagram; + + enum { + id = 13, + overhead = SendDatagram::size, + facilities = ssf::layer::facilities::datagram, + mtu = NextLayer::mtu - overhead + }; + enum { endpoint_stack_size = NextLayer::endpoint_stack_size + 1 }; + + typedef NextLayer next_layer_protocol; + + struct socket_context { + std::shared_ptr p_cancelled; + }; + + typedef NetworkID::HalfID endpoint_context_type; + using next_endpoint_type = typename next_layer_protocol::endpoint; + + typedef basic_VirtualLink_endpoint endpoint; + typedef basic_VirtualLink_resolver resolver; + typedef boost::asio::basic_datagram_socket< + basic_NetworkProtocol, basic_NetworkSocket_service> + socket; + typedef boost::asio::basic_datagram_socket< + basic_NetworkProtocol, + basic_NetworkRawSocket_service> raw_socket; + + private: + using query = typename resolver::query; + + public: + static endpoint make_endpoint(boost::asio::io_service& io_service, + typename query::const_iterator parameters_it, + uint32_t, boost::system::error_code& ec) { + endpoint_context_type context = + NetworkID::MakeHalfID(io_service, *parameters_it, id); + + if (context) { + return endpoint(context, next_layer_protocol::make_endpoint( + io_service, ++parameters_it, id, ec)); + } else { + ec.assign(ssf::error::bad_address, ssf::error::get_ssf_category()); + return endpoint(); + } + } + + template + static SendDatagram make_datagram(const ConstBufferSequence& buffers, + const endpoint& source, + const endpoint& destination) { + Header dgr_header; + auto& id = dgr_header.id(); + id = Header::ID(source.endpoint_context(), destination.endpoint_context()); + auto& payload_length = dgr_header.payload_length(); + payload_length = boost::asio::buffer_size(buffers); + + SendPayload payload(buffers); + + return SendDatagram(dgr_header, payload, Footer()); + } + + static boost::recursive_mutex bind_mutex_; + static std::set used_interface_endpoints_; + static std::set bounds_; + static std::map + network_to_interface_; + static std::map + interface_to_network_; +}; + +template +boost::recursive_mutex basic_NetworkProtocol::bind_mutex_; + +template +std::set::next_endpoint_type> + basic_NetworkProtocol::used_interface_endpoints_; + +template +std::set::endpoint_context_type> + basic_NetworkProtocol::bounds_; + +template +std::map::endpoint_context_type, + typename basic_NetworkProtocol::next_endpoint_type> + basic_NetworkProtocol::network_to_interface_; + +template +std::map::next_endpoint_type, + typename basic_NetworkProtocol::endpoint_context_type> + basic_NetworkProtocol::interface_to_network_; + +} // network +} // layer +} // ssf + +#endif // SSF_LAYER_NETWORK_BASIC_NETWORK_PROTOCOL_H_ diff --git a/src/framework/ssf/layer/network/basic_network_raw_socket_service.h b/src/framework/ssf/layer/network/basic_network_raw_socket_service.h new file mode 100644 index 00000000..b2cdd04a --- /dev/null +++ b/src/framework/ssf/layer/network/basic_network_raw_socket_service.h @@ -0,0 +1,405 @@ +#ifndef SSF_LAYER_NETWORK_BASIC_NETWORK_RAW_SOCKET_SERVICE_H_ +#define SSF_LAYER_NETWORK_BASIC_NETWORK_RAW_SOCKET_SERVICE_H_ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "ssf/error/error.h" +#include "ssf/io/composed_op.h" +#include "ssf/io/handler_helpers.h" + +#include "ssf/layer/basic_impl.h" +#include "ssf/layer/protocol_attributes.h" + +namespace ssf { +namespace layer { +namespace network { + +#include + +template +class basic_NetworkRawSocket_service + : public boost::asio::detail::service_base< + basic_NetworkRawSocket_service> { + public: + typedef Protocol protocol_type; + typedef typename protocol_type::endpoint endpoint_type; + + typedef basic_socket_impl implementation_type; + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + private: + typedef + typename protocol_type::next_layer_protocol::endpoint next_endpoint_type; + typedef typename protocol_type::next_layer_protocol::socket next_socket_type; + typedef typename protocol_type::endpoint_context_type endpoint_context_type; + + public: + explicit basic_NetworkRawSocket_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service), + p_worker_(nullptr), + usage_count_(0), + p_alive_(std::make_shared(true)) {} + + virtual ~basic_NetworkRawSocket_service() {} + + void construct(implementation_type& impl) { + impl.p_socket_context = + std::make_shared(); + impl.p_socket_context->p_cancelled = std::make_shared(false); + } + + void destroy(implementation_type& impl) { + impl.p_socket_context.reset(); + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_socket.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return ec; + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { return true; } + + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_remote_endpoint) { + return *impl.p_remote_endpoint; + } else { + return endpoint_type(); + } + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + return *impl.p_local_endpoint; + } else { + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + if (impl.p_local_endpoint) { + boost::recursive_mutex::scoped_lock lock(protocol_type::bind_mutex_); + protocol_type::network_to_interface_.erase( + impl.p_local_endpoint->endpoint_context()); + protocol_type::interface_to_network_.erase( + impl.p_local_endpoint->next_layer_endpoint()); + protocol_type::bounds_.erase(impl.p_local_endpoint->endpoint_context()); + protocol_type::used_interface_endpoints_.erase( + impl.p_local_endpoint->next_layer_endpoint()); + } + + *(impl.p_socket_context->p_cancelled) = true; + impl.p_next_layer_socket->cancel(ec); + impl.p_next_layer_socket.reset(); + return ec; + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->cancel(ec); + } + + bool at_mark(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + return false; + } else { + return impl.p_next_layer_socket->at_mark(ec); + } + } + + std::size_t available(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return 0; + } + + return impl.p_next_layer_socket->available(ec); + } + + /// Bind a network id to an interface (one to one relationship) + /// Set interface and network as used + /// Fail if network or interface is already used + boost::system::error_code bind(implementation_type& impl, + endpoint_type local_endpoint, + boost::system::error_code& ec) { + const auto& local_endpoint_context = local_endpoint.endpoint_context(); + const auto& local_interface_endpoint = local_endpoint.next_layer_endpoint(); + + boost::recursive_mutex::scoped_lock lock1(protocol_type::bind_mutex_); + if (protocol_type::used_interface_endpoints_.count( + local_interface_endpoint) || + protocol_type::bounds_.count(local_endpoint_context)) { + ec.assign(ssf::error::address_in_use, ssf::error::get_ssf_category()); + return ec; + } + + auto p_socket_interface_optional = + protocol_type::next_layer_protocol::get_interface_manager().Find( + local_endpoint.next_layer_endpoint().endpoint_context()); + + if (!p_socket_interface_optional) { + ec.assign(ssf::error::address_not_available, + ssf::error::get_ssf_category()); + return ec; + } + + protocol_type::network_to_interface_.insert( + std::make_pair(local_endpoint_context, local_interface_endpoint)); + protocol_type::interface_to_network_.insert( + std::make_pair(local_interface_endpoint, local_endpoint_context)); + protocol_type::bounds_.insert(std::move(local_endpoint_context)); + protocol_type::used_interface_endpoints_.insert( + std::move(local_interface_endpoint)); + + impl.p_next_layer_socket = *p_socket_interface_optional; + impl.p_local_endpoint = + std::make_shared(std::move(local_endpoint)); + + return ec; + } + + boost::system::error_code connect(implementation_type& impl, + const endpoint_type& remote_endpoint, + boost::system::error_code& ec) { + ec.assign(ssf::error::function_not_supported, + ssf::error::get_ssf_category()); + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& remote_endpoint, + ConnectHandler&& handler) { + boost::asio::detail::async_result_init< + ConnectHandler, void(const boost::system::error_code&)> + init(std::forward(handler)); + + auto connect_lambda = [this, &impl, remote_endpoint, init]() { + boost::system::error_code ec; + this->connect(impl, remote_endpoint, ec); + init.handler(ec); + }; + + this->get_io_service().post( + io::ComposedOp( + std::move(connect_lambda), init.handler)); + + return init.result.get(); + } + + /// Send buffers through the interface socket without any encapsulation + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_next_layer_socket || !impl.p_local_endpoint) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::identifier_removed, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + register_async_op(); + auto p_socket_cancelled = impl.p_socket_context->p_cancelled; + auto p_alive = p_alive_; + auto user_handler = [this, p_alive, p_socket_cancelled, init]( + const boost::system::error_code& ec, std::size_t length) { + if (!(*p_alive)) { + return; + } + if (!*p_socket_cancelled) { + init.handler(ec, length); + } + + this->unregister_async_op(); + }; + + impl.p_next_layer_socket->async_send(buffers, std::move(user_handler)); + + return init.result.get(); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::function_not_supported, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + /// Receive datagram from the bound interface socket + /// and put it in buffers (without any decapsulation) + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_next_layer_socket || !impl.p_local_endpoint) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::identifier_removed, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + register_async_op(); + auto p_socket_cancelled = impl.p_socket_context->p_cancelled; + auto p_alive = p_alive_; + auto user_handler = [this, buffers, p_alive, p_socket_cancelled, init]( + const boost::system::error_code& ec, std::size_t length) { + if (!(*p_alive)) { + return; + } + + if (!(*p_socket_cancelled)) { + init.handler(ec, length); + } + + this->unregister_async_op(); + }; + + impl.p_next_layer_socket->async_receive(buffers, std::move(user_handler)); + + return init.result.get(); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& source, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::function_not_supported, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + boost::system::error_code shutdown( + implementation_type& impl, boost::asio::socket_base::shutdown_type what, + boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::not_connected, ssf::error::get_ssf_category()); + + return ec; + } + + impl.p_next_layer_socket->shutdown(what, ec); + + return ec; + } + + private: + void shutdown_service() { + *p_alive_ = false; + p_worker_.reset(); + } + + void register_async_op() { + boost::recursive_mutex::scoped_lock lock(mutex_); + if (usage_count_ == 0) { + p_worker_.reset(new boost::asio::io_service::work(this->get_io_service())); + } + ++usage_count_; + } + + void unregister_async_op() { + boost::recursive_mutex::scoped_lock lock(mutex_); + --usage_count_; + if (usage_count_ == 0) { + p_worker_.reset(); + } + } + + private: + std::unique_ptr p_worker_; + uint64_t usage_count_; + std::shared_ptr p_alive_; + boost::recursive_mutex mutex_; +}; + +#include + +} // network +} // layer +} // ssf + +#endif // SSF_LAYER_NETWORK_BASIC_NETWORK_RAW_SOCKET_SERVICE_H_ diff --git a/src/framework/ssf/layer/network/basic_network_socket_service.h b/src/framework/ssf/layer/network/basic_network_socket_service.h new file mode 100644 index 00000000..b238c548 --- /dev/null +++ b/src/framework/ssf/layer/network/basic_network_socket_service.h @@ -0,0 +1,444 @@ +#ifndef SSF_LAYER_NETWORK_BASIC_NETWORK_SOCKET_SERVICE_H_ +#define SSF_LAYER_NETWORK_BASIC_NETWORK_SOCKET_SERVICE_H_ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "ssf/io/composed_op.h" +#include "ssf/io/handler_helpers.h" +#include "ssf/error/error.h" + +#include "ssf/layer/basic_impl.h" +#include "ssf/layer/protocol_attributes.h" + +namespace ssf { +namespace layer { +namespace network { + +#include + +template +class basic_NetworkSocket_service : public boost::asio::detail::service_base< + basic_NetworkSocket_service> { + public: + typedef Protocol protocol_type; + typedef typename protocol_type::endpoint endpoint_type; + + typedef basic_socket_impl implementation_type; + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + private: + typedef + typename protocol_type::next_layer_protocol::endpoint next_endpoint_type; + typedef typename protocol_type::next_layer_protocol::socket next_socket_type; + typedef typename protocol_type::endpoint_context_type endpoint_context_type; + + public: + explicit basic_NetworkSocket_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service), + p_worker_(nullptr), + usage_count_(0), + p_alive_(std::make_shared(true)) {} + + virtual ~basic_NetworkSocket_service() {} + + void construct(implementation_type& impl) { + impl.p_socket_context = + std::make_shared(); + impl.p_socket_context->p_cancelled = std::make_shared(false); + } + + void destroy(implementation_type& impl) { + impl.p_socket_context.reset(); + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_socket.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return ec; + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { return true; } + + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_remote_endpoint) { + return *impl.p_remote_endpoint; + } else { + return endpoint_type(); + } + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + return *impl.p_local_endpoint; + } else { + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + if (impl.p_local_endpoint) { + boost::recursive_mutex::scoped_lock lock(protocol_type::bind_mutex_); + protocol_type::network_to_interface_.erase( + impl.p_local_endpoint->endpoint_context()); + protocol_type::interface_to_network_.erase( + impl.p_local_endpoint->next_layer_endpoint()); + protocol_type::bounds_.erase(impl.p_local_endpoint->endpoint_context()); + protocol_type::used_interface_endpoints_.erase( + impl.p_local_endpoint->next_layer_endpoint()); + } + + *(impl.p_socket_context->p_cancelled) = true; + impl.p_next_layer_socket->cancel(ec); + impl.p_next_layer_socket.reset(); + return ec; + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->cancel(ec); + } + + bool at_mark(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + return false; + } else { + return impl.p_next_layer_socket->at_mark(ec); + } + } + + std::size_t available(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return 0; + } + + return impl.p_next_layer_socket->available(ec); + } + + /// Bind a network id to an interface (one to one relationship) + /// Set interface and network as used + /// Fail if network or interface is already used + boost::system::error_code bind(implementation_type& impl, + endpoint_type local_endpoint, + boost::system::error_code& ec) { + const auto& local_endpoint_context = local_endpoint.endpoint_context(); + const auto& local_interface_endpoint = local_endpoint.next_layer_endpoint(); + + boost::recursive_mutex::scoped_lock lock1(protocol_type::bind_mutex_); + if (protocol_type::used_interface_endpoints_.count( + local_interface_endpoint) || + protocol_type::bounds_.count(local_endpoint_context)) { + ec.assign(ssf::error::address_in_use, ssf::error::get_ssf_category()); + return ec; + } + + auto p_socket_interface_optional = + protocol_type::next_layer_protocol::get_interface_manager().Find( + local_interface_endpoint.endpoint_context()); + + if (!p_socket_interface_optional) { + ec.assign(ssf::error::address_not_available, + ssf::error::get_ssf_category()); + return ec; + } + + protocol_type::network_to_interface_.insert( + std::make_pair(local_endpoint_context, local_interface_endpoint)); + protocol_type::interface_to_network_.insert( + std::make_pair(local_interface_endpoint, local_endpoint_context)); + protocol_type::bounds_.insert(std::move(local_endpoint_context)); + protocol_type::used_interface_endpoints_.insert( + std::move(local_interface_endpoint)); + + impl.p_next_layer_socket = *p_socket_interface_optional; + impl.p_local_endpoint = + std::make_shared(std::move(local_endpoint)); + + return ec; + } + + boost::system::error_code connect(implementation_type& impl, + const endpoint_type& remote_endpoint, + boost::system::error_code& ec) { + ec.assign(ssf::error::function_not_supported, + ssf::error::get_ssf_category()); + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& remote_endpoint, + ConnectHandler&& handler) { + boost::asio::detail::async_result_init< + ConnectHandler, void(const boost::system::error_code&)> + init(std::forward(handler)); + + auto connect_lambda = [this, &impl, remote_endpoint, init]() { + boost::system::error_code ec; + this->connect(impl, remote_endpoint, ec); + init.handler(ec); + }; + + this->get_io_service().post( + io::ComposedOp( + std::move(connect_lambda), init.handler)); + + return init.result.get(); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::function_not_supported, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + /// Encapsulate buffers in NetworkDatagram ([[source, destination][buffers]]) + /// Async send it through interface socket (next layer socket) + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_next_layer_socket || !impl.p_local_endpoint) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::identifier_removed, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + if (boost::asio::buffer_size(buffers) > protocol_type::mtu) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::message_size, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + auto p_datagram = std::make_shared( + protocol_type::make_datagram(buffers, *impl.p_local_endpoint, + destination)); + + register_async_op(); + auto p_socket_cancelled = impl.p_socket_context->p_cancelled; + auto p_alive = p_alive_; + auto user_handler = [this, p_datagram, p_alive, p_socket_cancelled, init]( + const boost::system::error_code& ec, std::size_t length) { + if (!(*p_alive)) { + return; + } + if (!*p_socket_cancelled) { + if (!ec) { + if (length < protocol_type::overhead) { + init.handler( + boost::system::error_code(ssf::error::broken_pipe, + ssf::error::get_ssf_category()), + 0); + } else { + init.handler(ec, length - protocol_type::overhead); + } + } else { + init.handler(ec, 0); + } + } + + this->unregister_async_op(); + }; + + AsyncSendDatagram(*impl.p_next_layer_socket, *p_datagram, + std::move(user_handler)); + + return init.result.get(); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::function_not_supported, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + /// Async read data from interface socket + /// Fill in given source endpoint with datagram headers + /// Fill in buffers with datagram payload + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& source, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + auto p_datagram = + std::make_shared(); + + register_async_op(); + auto p_socket_cancelled = impl.p_socket_context->p_cancelled; + auto p_alive = p_alive_; + auto user_handler = + [this, buffers, p_datagram, &source, p_alive, p_socket_cancelled, init]( + const boost::system::error_code& ec, std::size_t length) { + if (!(*p_alive)) { + return; + } + if (!(*p_socket_cancelled)) { + if (!ec) { + if (boost::asio::buffer_size(buffers) < + length - protocol_type::overhead) { + init.handler( + boost::system::error_code(ssf::error::message_size, + ssf::error::get_ssf_category()), + 0); + } else { + source = typename protocol_type::endpoint( + p_datagram->header().id().left_id()); + auto copied = boost::asio::buffer_copy( + buffers, p_datagram->payload().GetConstBuffers()); + init.handler(ec, copied); + } + } else { + init.handler(ec, 0); + } + } + + this->unregister_async_op(); + }; + + AsyncReceiveDatagram(*impl.p_next_layer_socket, p_datagram.get(), + std::move(user_handler)); + + return init.result.get(); + } + + boost::system::error_code shutdown( + implementation_type& impl, boost::asio::socket_base::shutdown_type what, + boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::not_connected, ssf::error::get_ssf_category()); + + return ec; + } + + impl.p_next_layer_socket->shutdown(what, ec); + + return ec; + } + + private: + void shutdown_service() { + *p_alive_ = false; + p_worker_.reset(); + } + + void register_async_op() { + boost::recursive_mutex::scoped_lock lock(mutex_); + if (usage_count_ == 0) { + p_worker_ = std::unique_ptr( + new boost::asio::io_service::work(this->get_io_service())); + + ++usage_count_; + } + } + + void unregister_async_op() { + boost::recursive_mutex::scoped_lock lock(mutex_); + --usage_count_; + if (usage_count_ == 0) { + p_worker_.reset(); + } + } + + private: + std::unique_ptr p_worker_; + uint64_t usage_count_; + std::shared_ptr p_alive_; + boost::recursive_mutex mutex_; +}; + +#include + +} // network +} // layer +} // ssf + +#endif // SSF_LAYER_NETWORK_BASIC_NETWORK_SOCKET_SERVICE_H_ diff --git a/src/framework/ssf/layer/network/network_id.h b/src/framework/ssf/layer/network/network_id.h new file mode 100644 index 00000000..92b53c66 --- /dev/null +++ b/src/framework/ssf/layer/network/network_id.h @@ -0,0 +1,86 @@ +#ifndef SSF_LAYER_NETWORK_NETWORK_ID_H_ +#define SSF_LAYER_NETWORK_NETWORK_ID_H_ + +#include + +#include +#include +#include + +#include "ssf/io/buffers.h" +#include "ssf/utils/map_helpers.h" + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { +namespace network { + +class NetworkID { + public: + typedef uint16_t HalfID; + + typedef io::fixed_const_buffer_sequence ConstBuffers; + typedef io::fixed_mutable_buffer_sequence MutableBuffers; + enum { size = 2 * sizeof(HalfID) }; + + public: + NetworkID() : local_id_(0), remote_id_(0) {} + + NetworkID(HalfID local, HalfID remote) + : local_id_(local), remote_id_(remote) {} + + ConstBuffers GetConstBuffers() const { + ConstBuffers buffers; + buffers.push_back(boost::asio::buffer(&local_id_, sizeof(HalfID))); + buffers.push_back(boost::asio::buffer(&remote_id_, sizeof(HalfID))); + return buffers; + } + + void GetConstBuffers(ConstBuffers* p_buffers) const { + p_buffers->push_back(boost::asio::buffer(&local_id_, sizeof(HalfID))); + p_buffers->push_back(boost::asio::buffer(&remote_id_, sizeof(HalfID))); + } + + MutableBuffers GetMutableBuffers() { + MutableBuffers buffers; + buffers.push_back(boost::asio::buffer(&local_id_, sizeof(HalfID))); + buffers.push_back(boost::asio::buffer(&remote_id_, sizeof(HalfID))); + return buffers; + } + + void GetMutableBuffers(MutableBuffers* p_buffers) { + p_buffers->push_back(boost::asio::buffer(&local_id_, sizeof(HalfID))); + p_buffers->push_back(boost::asio::buffer(&remote_id_, sizeof(HalfID))); + } + + HalfID& left_id() { return local_id_; } + const HalfID& left_id() const { return local_id_; } + + HalfID& right_id() { return remote_id_; } + const HalfID& right_id() const { return remote_id_; } + + HalfID GetFirstHalfId() const { return local_id_; } + HalfID GetSecondHalfId() const { return remote_id_; } + + static HalfID MakeHalfID(boost::asio::io_service& io_service, + const LayerParameters& parameters, uint32_t) { + auto id_string = helpers::GetField("network_id", parameters); + + try { + return HalfID(std::stoul(id_string)); + } catch (const std::exception&) { + return 0; + } + } + + private: + HalfID local_id_; + HalfID remote_id_; +}; + +} // network +} // layer +} // ssf + +#endif // SSF_LAYER_NETWORK_NETWORK_ID_H_ diff --git a/src/framework/ssf/layer/parameters.cpp b/src/framework/ssf/layer/parameters.cpp new file mode 100644 index 00000000..1e39f503 --- /dev/null +++ b/src/framework/ssf/layer/parameters.cpp @@ -0,0 +1,47 @@ +#include "ssf/layer/parameters.h" + +#include +#include + +#include +#include + +namespace ssf { +namespace layer { + +std::string serialize_parameter_stack( + const ParameterStack &stack) { + std::ostringstream ostrs; + boost::archive::text_oarchive ar(ostrs); + + ar << stack; + + return ostrs.str(); +} + +ParameterStack unserialize_parameter_stack( + const std::string& serialized_stack) { + try { + std::istringstream istrs(serialized_stack); + boost::archive::text_iarchive ar(istrs); + ParameterStack deserialized; + + ar >> deserialized; + + return deserialized; + } catch (const std::exception &) { + return ParameterStack(); + } +} + +void ptree_entry_to_query(const boost::property_tree::ptree& ptree, + const std::string& entry_name, + LayerParameters* p_params) { + auto given_entry = ptree.get_child_optional(entry_name); + if (given_entry) { + (*p_params)[entry_name] = given_entry.get().data(); + } +} + +} // layer +} // ssf diff --git a/src/framework/ssf/layer/parameters.h b/src/framework/ssf/layer/parameters.h new file mode 100644 index 00000000..8c8f85cf --- /dev/null +++ b/src/framework/ssf/layer/parameters.h @@ -0,0 +1,27 @@ +#ifndef SSF_LAYER_PARAMETERS_H_ +#define SSF_LAYER_PARAMETERS_H_ + +#include +#include +#include + +#include + +namespace ssf { +namespace layer { + +using LayerParameters = std::map; +using ParameterStack = std::list; + +std::string serialize_parameter_stack(const ParameterStack& stack); + +ParameterStack unserialize_parameter_stack(const std::string& serialized_stack); + +void ptree_entry_to_query(const boost::property_tree::ptree& ptree, + const std::string& entry_name, + LayerParameters* p_params); + +} // layer +} // ssf + +#endif // SSF_LAYER_PARAMETERS_H_ diff --git a/src/framework/ssf/layer/physical/tcp.cpp b/src/framework/ssf/layer/physical/tcp.cpp new file mode 100644 index 00000000..ffdd66fc --- /dev/null +++ b/src/framework/ssf/layer/physical/tcp.cpp @@ -0,0 +1,11 @@ +#include "ssf/layer/physical/tcp.h" + +namespace ssf { +namespace layer { +namespace physical { + +const char* tcp::NAME = "TCP"; + +} // physical +} // layer +} // ssf diff --git a/src/framework/ssf/layer/physical/tcp.h b/src/framework/ssf/layer/physical/tcp.h new file mode 100644 index 00000000..cf5df41d --- /dev/null +++ b/src/framework/ssf/layer/physical/tcp.h @@ -0,0 +1,99 @@ +#ifndef SSF_LAYER_PHYSICAL_TCP_H_ +#define SSF_LAYER_PHYSICAL_TCP_H_ + +#include + +#include + +#include +#include + +#include +#include + +#include "ssf/layer/basic_empty_stream.h" +#include "ssf/layer/parameters.h" +#include "ssf/layer/physical/tcp_helpers.h" +#include "ssf/layer/protocol_attributes.h" + +namespace ssf { +namespace layer { +namespace physical { + +class tcp { + public: + enum { + id = 1, + overhead = 0, + facilities = ssf::layer::facilities::stream, + mtu = 65535 - overhead + }; + enum { endpoint_stack_size = 1 }; + + static const char* NAME; + + typedef int socket_context; + typedef int acceptor_context; + typedef boost::asio::ip::tcp::socket socket; + typedef boost::asio::ip::tcp::acceptor acceptor; + typedef boost::asio::ip::tcp::resolver resolver; + typedef boost::asio::ip::tcp::endpoint endpoint; + + private: + using query = ParameterStack; + using ptree = boost::property_tree::ptree; + + public: + operator boost::asio::ip::tcp() { return boost::asio::ip::tcp::v4(); } + + static std::string get_name() { + return NAME; + } + + static endpoint make_endpoint(boost::asio::io_service& io_service, + query::const_iterator parameters_it, uint32_t, + boost::system::error_code& ec) { + return ssf::layer::physical::detail::make_tcp_endpoint( + io_service, *parameters_it, ec); + } + + + static std::string get_address(const endpoint& endpoint) { + return endpoint.address().to_string(); + } + + static unsigned short get_port(const endpoint& endpoint) { + return endpoint.port(); + } + + static void add_params_from_property_tree( + query* p_query, const boost::property_tree::ptree& property_tree, + bool connect, boost::system::error_code& ec) { + auto layer_name = property_tree.get_child_optional("layer"); + if (!layer_name || layer_name.get().data() != NAME) { + ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + return; + } + + LayerParameters params; + auto layer_parameters = property_tree.get_child_optional("parameters"); + if (!layer_parameters) { + ec.assign(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()); + return; + } + + ssf::layer::ptree_entry_to_query(*layer_parameters, "port", ¶ms); + ssf::layer::ptree_entry_to_query(*layer_parameters, "addr", ¶ms); + + p_query->push_back(params); + } +}; + +using TCPPhysicalLayer = VirtualEmptyStreamProtocol; + +} // physical +} // layer +} // ssf + +#endif // SSF_LAYER_PHYSICAL_TCP_H_ diff --git a/src/framework/ssf/layer/physical/tcp_helpers.cpp b/src/framework/ssf/layer/physical/tcp_helpers.cpp new file mode 100644 index 00000000..2dd76aa2 --- /dev/null +++ b/src/framework/ssf/layer/physical/tcp_helpers.cpp @@ -0,0 +1,51 @@ +#include "ssf/layer/physical/tcp_helpers.h" + +#include + +#include +#include + +#include "ssf/error/error.h" +#include "ssf/utils/map_helpers.h" + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { +namespace physical { +namespace detail { + +boost::asio::ip::tcp::endpoint make_tcp_endpoint( + boost::asio::io_service& io_service, const LayerParameters& parameters, + boost::system::error_code& ec) { + auto addr = ssf::helpers::GetField("addr", parameters); + auto port = ssf::helpers::GetField("port", parameters); + + if (!port.empty()) { + if (!addr.empty()) { + boost::asio::ip::tcp::resolver resolver(io_service); + boost::asio::ip::tcp::resolver::query query(addr, port); + boost::asio::ip::tcp::resolver::iterator iterator( + resolver.resolve(query, ec)); + + if (!ec) { + return boost::asio::ip::tcp::endpoint(*iterator); + } + } else { + try { + return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), + (uint16_t)std::stoul(port)); + } catch (const std::exception&) { + ec.assign(ssf::error::bad_address, ssf::error::get_ssf_category()); + return boost::asio::ip::tcp::endpoint(); + } + } + } + + return boost::asio::ip::tcp::endpoint(); +} + +} // detail +} // physical +} // layer +} // ssf diff --git a/src/framework/ssf/layer/physical/tcp_helpers.h b/src/framework/ssf/layer/physical/tcp_helpers.h new file mode 100644 index 00000000..69291e25 --- /dev/null +++ b/src/framework/ssf/layer/physical/tcp_helpers.h @@ -0,0 +1,25 @@ +#ifndef SSF_LAYER_PHYSICAL_TCP_HELPERS_H_ +#define SSF_LAYER_PHYSICAL_TCP_HELPERS_H_ + +#include +#include + +#include + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { +namespace physical { +namespace detail { + +boost::asio::ip::tcp::endpoint make_tcp_endpoint( + boost::asio::io_service& io_service, const LayerParameters& parameters, + boost::system::error_code& ec); + +} // detail +} // physical +} // layer +} // ssf + +#endif // SSF_LAYER_PHYSICAL_TCP_HELPERS_H_ diff --git a/src/framework/ssf/layer/physical/tlsotcp.h b/src/framework/ssf/layer/physical/tlsotcp.h new file mode 100644 index 00000000..200584bb --- /dev/null +++ b/src/framework/ssf/layer/physical/tlsotcp.h @@ -0,0 +1,23 @@ +#ifndef SSF_LAYER_PHYSICAL_TLSOTCP_H_ +#define SSF_LAYER_PHYSICAL_TLSOTCP_H_ + +#include "ssf/layer/cryptography/basic_crypto_stream.h" +#include "ssf/layer/cryptography/tls/OpenSSL/impl.h" + +#include "ssf/layer/physical/tcp.h" + +namespace ssf { +namespace layer { +namespace physical { + +using TLSboTCPPhysicalLayer = cryptography::basic_CryptoStreamProtocol< + tcp, cryptography::buffered_tls>; + +using TLSoTCPPhysicalLayer = + cryptography::basic_CryptoStreamProtocol; + +} // physical +} // layer +} // ssf + +#endif // SSF_LAYER_PHYSICAL_TLSOTCP_H_ diff --git a/src/framework/ssf/layer/physical/udp.h b/src/framework/ssf/layer/physical/udp.h new file mode 100644 index 00000000..4fde712a --- /dev/null +++ b/src/framework/ssf/layer/physical/udp.h @@ -0,0 +1,60 @@ +#ifndef SSF_LAYER_PHYSICAL_UDP_H_ +#define SSF_LAYER_PHYSICAL_UDP_H_ + +#include + +#include + +#include +#include + +#include "ssf/layer/basic_empty_datagram.h" +#include "ssf/layer/parameters.h" +#include "ssf/layer/physical/udp_helpers.h" +#include "ssf/layer/protocol_attributes.h" + +namespace ssf { +namespace layer { +namespace physical { + +class udp { + public: + enum { + id = 11, + overhead = 0, + facilities = ssf::layer::facilities::datagram, + mtu = 1500 - overhead + }; + enum { endpoint_stack_size = 1 }; + + typedef int socket_context; + typedef int acceptor_context; + typedef boost::asio::ip::udp::socket socket; + typedef boost::asio::ip::udp::resolver resolver; + typedef boost::asio::ip::udp::endpoint endpoint; + +private: + using query = ParameterStack; + + public: + operator boost::asio::ip::udp() { return boost::asio::ip::udp::v4(); } + + static endpoint make_endpoint(boost::asio::io_service& io_service, + query::const_iterator parameters_it, uint32_t, + boost::system::error_code& ec) { + return endpoint(ssf::layer::physical::detail::make_udp_endpoint( + io_service, *parameters_it, ec)); + } + + static std::string get_address(const endpoint& endpoint) { + return endpoint.address().to_string(); + } +}; + +using UDPPhysicalLayer = VirtualEmptyDatagramProtocol; + +} // physical +} // layer +} // ssf + +#endif // SSF_LAYER_PHYSICAL_UDP_H_ diff --git a/src/framework/ssf/layer/physical/udp_helpers.cpp b/src/framework/ssf/layer/physical/udp_helpers.cpp new file mode 100644 index 00000000..9d2bf3db --- /dev/null +++ b/src/framework/ssf/layer/physical/udp_helpers.cpp @@ -0,0 +1,53 @@ +#include "ssf/layer/physical/udp_helpers.h" + +#include + +#include +#include + +#include + +#include "ssf/error/error.h" +#include "ssf/utils/map_helpers.h" + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { +namespace physical { +namespace detail { + +boost::asio::ip::udp::endpoint make_udp_endpoint( + boost::asio::io_service& io_service, const LayerParameters& parameters, + boost::system::error_code& ec) { + auto addr = helpers::GetField("addr", parameters); + auto port = helpers::GetField("port", parameters); + + if (!port.empty()) { + if (!addr.empty()) { + boost::asio::ip::udp::resolver resolver(io_service); + boost::asio::ip::udp::resolver::query query(addr, port); + boost::asio::ip::udp::resolver::iterator iterator( + resolver.resolve(query, ec)); + + if (!ec) { + return boost::asio::ip::udp::endpoint(*iterator); + } + } else { + try { + return boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), + (uint16_t)std::stoul(port)); + } catch (const std::exception&) { + ec.assign(ssf::error::bad_address, ssf::error::get_ssf_category()); + return boost::asio::ip::udp::endpoint(); + } + } + } + + return boost::asio::ip::udp::endpoint(); +} + +} // detail +} // physical +} // layer +} // ssf diff --git a/src/framework/ssf/layer/physical/udp_helpers.h b/src/framework/ssf/layer/physical/udp_helpers.h new file mode 100644 index 00000000..eb562500 --- /dev/null +++ b/src/framework/ssf/layer/physical/udp_helpers.h @@ -0,0 +1,25 @@ +#ifndef SSF_LAYER_PHYSICAL_UDP_HELPERS_H_ +#define SSF_LAYER_PHYSICAL_UDP_HELPERS_H_ + +#include +#include + +#include + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { +namespace physical { +namespace detail { + +boost::asio::ip::udp::endpoint make_udp_endpoint( + boost::asio::io_service& io_service, const LayerParameters& parameters, + boost::system::error_code& ec); + +} // detail +} // physical +} // layer +} // ssf + +#endif // SSF_LAYER_PHYSICAL_UDP_HELPERS_H_ diff --git a/src/framework/ssf/layer/protocol_attributes.h b/src/framework/ssf/layer/protocol_attributes.h new file mode 100644 index 00000000..75250aa6 --- /dev/null +++ b/src/framework/ssf/layer/protocol_attributes.h @@ -0,0 +1,165 @@ +#ifndef SSF_LAYER_PROTOCOL_ATTRIBUTES_H_ +#define SSF_LAYER_PROTOCOL_ATTRIBUTES_H_ + +#include + +#include + +#include +#include + +namespace ssf { +namespace layer { + +enum overhead { multiplexed = 1 << 1, header = 1 << 2, footer = 1 << 3 }; +enum facilities { + raw = 1 << 1, + promiscuous = 1 << 2, + datagram = 1 << 3, + stream = 1 << 4 +}; + +template +struct IsStream { + enum { value = !!(Socket::protocol_type::facilities & facilities::stream) }; +}; + +template +struct IsDatagram { + enum { value = !!(Socket::protocol_type::facilities & facilities::datagram) }; +}; + +template +void AsyncSendDatagram( + Socket& socket, const Datagram& datagram, const Endpoint& destination, + Handler handler, + typename std::enable_if::value, Socket>::type* = nullptr) { + boost::asio::async_write(socket, datagram.GetConstBuffers(), + std::move(handler)); +} + +template +void AsyncSendDatagram( + Socket& socket, const Datagram& datagram, Handler handler, + typename std::enable_if::value, Socket>::type* = nullptr) { + boost::asio::async_write(socket, datagram.GetConstBuffers(), + std::move(handler)); +} + +template +void AsyncReceiveDatagram( + Socket& socket, Datagram* p_datagram, Endpoint& source, + Handler handler, + typename std::enable_if::value, Socket>::type* = nullptr) { + source = socket.remote_endpoint(); + AsyncReceiveDatagram(socket, p_datagram, std::move(handler)); +} + +template +void AsyncReceiveDatagram( + Socket& socket, Datagram* p_datagram, + const Handler& handler, + typename std::enable_if::value, Socket>::type* = nullptr) { + auto footer_received_lambda = [p_datagram, handler]( + std::size_t total_length, const boost::system::error_code& ec, + std::size_t length) { handler(ec, total_length + length); }; + + auto payload_received_lambda = [&socket, p_datagram, footer_received_lambda]( + std::size_t total_length, const boost::system::error_code& ec, + std::size_t length) { + if (!ec) { + boost::asio::async_read( + socket, p_datagram->footer().GetMutableBuffers(), + std::move(boost::bind(std::move(footer_received_lambda), + total_length + length, _1, _2))); + } else { + footer_received_lambda(total_length + length, ec, 0); + } + }; + + auto header_received_lambda = [&socket, p_datagram, payload_received_lambda]( + const boost::system::error_code& ec, std::size_t length) { + if (!ec) { + p_datagram->payload().SetSize(p_datagram->header().payload_length()); + boost::asio::async_read( + socket, p_datagram->payload().GetMutableBuffers(), + std::move(boost::bind(std::move(payload_received_lambda), + length, _1, _2))); + } else { + payload_received_lambda(length, ec, 0); + } + }; + + boost::asio::async_read(socket, p_datagram->header().GetMutableBuffers(), + std::move(header_received_lambda)); +} + +template +void AsyncSendDatagram( + Socket& socket, const Datagram& datagram, const Endpoint& destination, + Handler handler, + typename std::enable_if::value, Socket>::type* = nullptr) { + socket.async_send_to(datagram.GetConstBuffers(), destination, + std::move(handler)); +} + +template +void AsyncSendDatagram( + Socket& socket, const Datagram& datagram, + Handler handler, + typename std::enable_if::value, Socket>::type* = nullptr) { + socket.async_send(datagram.GetConstBuffers(), std::move(handler)); +} + +template +void AsyncReceiveDatagram( + Socket& socket, Datagram* p_datagram, Endpoint& source, + const Handler& handler, + typename std::enable_if::value, Socket>::type* = nullptr) { + auto p_buffer = std::make_shared>( + Datagram::size + Socket::protocol_type::mtu); + + auto datagram_received_lambda = [p_buffer, p_datagram, handler]( + const boost::system::error_code& ec, std::size_t length) { + if (!ec) { + p_datagram->payload().SetSize(length - Datagram::size); + boost::asio::buffer_copy(p_datagram->GetMutableBuffers(), + boost::asio::buffer(*p_buffer)); + handler(ec, length); + } else { + handler(ec, 0); + } + }; + + socket.async_receive_from(boost::asio::buffer(*p_buffer), source, + std::move(datagram_received_lambda)); +} + +template +void AsyncReceiveDatagram( + Socket& socket, Datagram* p_datagram, + const Handler& handler, + typename std::enable_if::value, Socket>::type* = nullptr) { + auto p_buffer = std::make_shared>( + Datagram::size + Socket::protocol_type::mtu); + + auto datagram_received_lambda = [p_buffer, p_datagram, handler]( + const boost::system::error_code& ec, std::size_t length) { + if (!ec) { + p_datagram->payload().SetSize(length - Datagram::size); + boost::asio::buffer_copy(p_datagram->GetMutableBuffers(), + boost::asio::buffer(*p_buffer)); + handler(ec, length); + } else { + handler(ec, 0); + } + }; + + socket.async_receive(boost::asio::buffer(*p_buffer), + std::move(datagram_received_lambda)); +} + +} // layer +} // ssf + +#endif // SSF_LAYER_PROTOCOL_ATTRIBUTES_H_ diff --git a/src/framework/ssf/layer/proxy/auth_strategy.cpp b/src/framework/ssf/layer/proxy/auth_strategy.cpp new file mode 100644 index 00000000..37d266ff --- /dev/null +++ b/src/framework/ssf/layer/proxy/auth_strategy.cpp @@ -0,0 +1,34 @@ +#include + +#include "ssf/layer/proxy/auth_strategy.h" + +namespace ssf { +namespace layer { +namespace proxy { + +std::string AuthStrategy::ExtractAuthToken(const HttpResponse& response) const { + std::string challenge_str; + auto auth_name = AuthName(); + + auto proxy_authenticate_hdr = response.Header( + proxy_authentication() ? "Proxy-Authenticate" : "WWW-Authenticate"); + for (auto& hdr : proxy_authenticate_hdr) { + // find negotiate auth header + if (hdr.find(auth_name) == 0) { + challenge_str = hdr.substr(auth_name.length()); + break; + } + } + + if (challenge_str.empty()) { + return ""; + } + + boost::trim(challenge_str); + + return challenge_str; +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/auth_strategy.h b/src/framework/ssf/layer/proxy/auth_strategy.h new file mode 100644 index 00000000..86406ac0 --- /dev/null +++ b/src/framework/ssf/layer/proxy/auth_strategy.h @@ -0,0 +1,58 @@ +#ifndef SSF_LAYER_PROXY_AUTH_STRATEGY_H_ +#define SSF_LAYER_PROXY_AUTH_STRATEGY_H_ + +#include + +#include "ssf/layer/proxy/http_request.h" +#include "ssf/layer/proxy/http_response.h" +#include "ssf/layer/proxy/proxy_endpoint_context.h" + +namespace ssf { +namespace layer { +namespace proxy { + +class AuthStrategy { + public: + enum Status : int { + kAuthenticationFailure = -1, + kAuthenticating = 0, + kAuthenticated = 1 + }; + + public: + virtual ~AuthStrategy() {} + + virtual std::string AuthName() const = 0; + + virtual bool Support(const HttpResponse& response) const = 0; + + virtual void ProcessResponse(const HttpResponse& response) = 0; + + virtual void PopulateRequest(HttpRequest* p_request) = 0; + + inline Status status() const { return status_; } + + protected: + AuthStrategy(const Proxy& proxy_ctx, Status status) + : proxy_ctx_(proxy_ctx), status_(status), proxy_authentication_(false) {} + + inline bool proxy_authentication() const { return proxy_authentication_; } + inline void set_proxy_authentication(const HttpResponse& response) { + proxy_authentication_ = !response.Header("Proxy-Authenticate").empty(); + } + + std::string ExtractAuthToken(const HttpResponse& response) const; + + protected: + Proxy proxy_ctx_; + Status status_; + + private: + bool proxy_authentication_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_AUTH_STRATEGY__H \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/base64.cpp b/src/framework/ssf/layer/proxy/base64.cpp new file mode 100644 index 00000000..dd72ec93 --- /dev/null +++ b/src/framework/ssf/layer/proxy/base64.cpp @@ -0,0 +1,78 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "ssf/layer/proxy/base64.h" + +namespace ssf { +namespace layer { +namespace proxy { + +std::string Base64::Encode(const std::string& input) { + return Encode(Buffer(input.begin(), input.end())); +} + +std::string Base64::Encode(const Base64::Buffer& input) { + using namespace boost::archive::iterators; + using Base64EncodeIterator = + base64_from_binary >; + + std::stringstream ss_encoded_input; + + std::copy(Base64EncodeIterator(input.begin()), + Base64EncodeIterator(input.end()), + ostream_iterator(ss_encoded_input)); + + // pad with '=' + switch (input.size() % 3) { + case 1: + ss_encoded_input << "=="; + break; + case 2: + ss_encoded_input << '='; + break; + default: + break; + } + + return ss_encoded_input.str(); +} + +Base64::Buffer Base64::Decode(const std::string& input) { + using namespace boost::archive::iterators; + using Base64DecodeIterator = + transform_width >, + 8, 6>; + + if (input.empty()) { + return Buffer(); + } + + // compute padding size + std::size_t padding_size = 0; + while (input[input.size() - 1 - padding_size] == '=') { + ++padding_size; + } + + Buffer buf(Base64DecodeIterator(input.c_str()), + Base64DecodeIterator(input.c_str() + input.size())); + + // remove padding + buf.resize(buf.size() - padding_size); + + return buf; +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/base64.h b/src/framework/ssf/layer/proxy/base64.h new file mode 100644 index 00000000..2d2569df --- /dev/null +++ b/src/framework/ssf/layer/proxy/base64.h @@ -0,0 +1,27 @@ +#ifndef SSF_LAYER_PROXY_BASE64_H_ +#define SSF_LAYER_PROXY_BASE64_H_ + +#include + +#include +#include + +namespace ssf { +namespace layer { +namespace proxy { + +class Base64 { + public: + using Buffer = std::vector; + + public: + static std::string Encode(const std::string& input); + static std::string Encode(const Buffer& input); + static Buffer Decode(const std::string& input); +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_BASE64_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/basic_auth_strategy.cpp b/src/framework/ssf/layer/proxy/basic_auth_strategy.cpp new file mode 100644 index 00000000..47abc832 --- /dev/null +++ b/src/framework/ssf/layer/proxy/basic_auth_strategy.cpp @@ -0,0 +1,50 @@ +#include "ssf/layer/proxy/base64.h" +#include "ssf/layer/proxy/basic_auth_strategy.h" + +namespace ssf { +namespace layer { +namespace proxy { + +BasicAuthStrategy::BasicAuthStrategy(const Proxy& proxy_ctx) + : AuthStrategy(proxy_ctx, Status::kAuthenticating), + request_populated_(false) {} + +std::string BasicAuthStrategy::AuthName() const { return "Basic"; } + +bool BasicAuthStrategy::Support(const HttpResponse& response) const { + auto auth_name = AuthName(); + return !request_populated_ && + (response.HeaderValueBeginWith("Proxy-Authenticate", auth_name) || + response.HeaderValueBeginWith("WWW-Authenticate", auth_name)); +} + +void BasicAuthStrategy::ProcessResponse(const HttpResponse& response) { + if (response.Success()) { + status_ = Status::kAuthenticated; + return; + } + + if (!Support(response)) { + status_ = Status::kAuthenticationFailure; + return; + } + + set_proxy_authentication(response); +} + +void BasicAuthStrategy::PopulateRequest(HttpRequest* p_request) { + std::stringstream ss_credentials, header_value; + ss_credentials << proxy_ctx_.username << ":" << proxy_ctx_.password; + + header_value << AuthName() << " " << Base64::Encode(ss_credentials.str()); + + p_request->AddHeader( + proxy_authentication() ? "Proxy-Authorization" : "Authorization", + header_value.str()); + + request_populated_ = true; +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/basic_auth_strategy.h b/src/framework/ssf/layer/proxy/basic_auth_strategy.h new file mode 100644 index 00000000..5b87a59d --- /dev/null +++ b/src/framework/ssf/layer/proxy/basic_auth_strategy.h @@ -0,0 +1,32 @@ +#ifndef SSF_LAYER_PROXY_BASIC_AUTH_STRATEGY_H_ +#define SSF_LAYER_PROXY_BASIC_AUTH_STRATEGY_H_ + +#include "ssf/layer/proxy/auth_strategy.h" + +namespace ssf { +namespace layer { +namespace proxy { + +class BasicAuthStrategy : public AuthStrategy { + public: + BasicAuthStrategy(const Proxy& proxy); + + virtual ~BasicAuthStrategy(){}; + + std::string AuthName() const override; + + bool Support(const HttpResponse& response) const override; + + void ProcessResponse(const HttpResponse& response) override; + + void PopulateRequest(HttpRequest* p_request) override; + + private: + bool request_populated_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_BASIC_AUTH_STRATEGY_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/basic_proxy_acceptor_service.h b/src/framework/ssf/layer/proxy/basic_proxy_acceptor_service.h new file mode 100644 index 00000000..390e374a --- /dev/null +++ b/src/framework/ssf/layer/proxy/basic_proxy_acceptor_service.h @@ -0,0 +1,215 @@ +#ifndef SSF_LAYER_PROXY_BASIC_PROXY_ACCEPTOR_SERVICE_H_ +#define SSF_LAYER_PROXY_BASIC_PROXY_ACCEPTOR_SERVICE_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "ssf/error/error.h" + +#include "ssf/layer/accept_op.h" +#include "ssf/layer/basic_impl.h" +#include "ssf/layer/basic_resolver.h" +#include "ssf/layer/parameters.h" + +#include "ssf/network/base_session.h" +#include "ssf/network/manager.h" +#include "ssf/network/session_forwarder.h" + +namespace ssf { +namespace layer { +namespace proxy { + +#include + +template +class basic_ProxyAcceptor_service : public boost::asio::detail::service_base< + basic_ProxyAcceptor_service> { + public: + /// The protocol type. + typedef Protocol protocol_type; + /// The endpoint type. + typedef typename protocol_type::endpoint endpoint_type; + + typedef basic_acceptor_impl implementation_type; + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + private: + typedef + typename protocol_type::next_layer_protocol::acceptor next_acceptor_type; + + public: + explicit basic_ProxyAcceptor_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service) {} + + virtual ~basic_ProxyAcceptor_service() {} + + void construct(implementation_type& impl) { + impl.p_next_layer_acceptor = + std::make_shared(this->get_io_service()); + } + + void destroy(implementation_type& impl) { + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_acceptor.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->open( + typename protocol_type::next_layer_protocol(), ec); + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { + return impl.p_next_layer_acceptor->is_open(); + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_local_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->close(ec); + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + /// Set a socket option. + template + boost::system::error_code set_option(implementation_type& impl, + const SettableSocketOption& option, + boost::system::error_code& ec) { + if (impl.p_next_layer_acceptor) { + return impl.p_next_layer_acceptor->set_option(option, ec); + } + return ec; + } + + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) { + impl.p_local_endpoint = std::make_shared(endpoint); + return impl.p_next_layer_acceptor->bind(endpoint.next_layer_endpoint(), ec); + } + + boost::system::error_code listen(implementation_type& impl, int backlog, + boost::system::error_code& ec) { + return impl.p_next_layer_acceptor->listen(backlog, ec); + } + + template + boost::system::error_code accept( + implementation_type& impl, + boost::asio::basic_socket& peer, + endpoint_type* p_peer_endpoint, boost::system::error_code& ec, + typename std::enable_if::value>::type* = 0) { + auto& peer_impl = peer.native_handle(); + peer_impl.p_remote_endpoint = + std::make_shared(); + + impl.p_next_layer_acceptor->accept( + *peer.native_handle().p_next_layer_socket, + peer_impl.p_remote_endpoint->next_layer_endpoint(), ec); + + if (!ec) { + peer_impl.p_local_endpoint = impl.p_local_endpoint; + + // Add current layer endpoint context here (if necessary) + peer_impl.p_remote_endpoint->set(); + + if (p_peer_endpoint) { + *p_peer_endpoint = *(peer_impl.p_remote_endpoint); + } + } + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, void(boost::system::error_code)) + async_accept(implementation_type& impl, + boost::asio::basic_socket& peer, + endpoint_type* p_peer_endpoint, AcceptHandler&& handler, + typename std::enable_if::value>::type* = 0) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + auto& peer_impl = peer.native_handle(); + peer_impl.p_local_endpoint = + std::make_shared(); + peer_impl.p_remote_endpoint = + std::make_shared(); + + ssf::layer::detail::AcceptOp< + protocol_type, next_acceptor_type, + typename std::remove_reference::native_handle_type>::type, + endpoint_type, + typename boost::asio::handler_type< + AcceptHandler, void(boost::system::error_code)>::type> ( + *impl.p_next_layer_acceptor, &peer_impl, p_peer_endpoint, + init.handler)(); + + return init.result.get(); + } + + private: + void shutdown_service() {} +}; + +#include + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_BASIC_PROXY_ACCEPTOR_SERVICE_H_ diff --git a/src/framework/ssf/layer/proxy/basic_proxy_protocol.h b/src/framework/ssf/layer/proxy/basic_proxy_protocol.h new file mode 100644 index 00000000..1c79ed15 --- /dev/null +++ b/src/framework/ssf/layer/proxy/basic_proxy_protocol.h @@ -0,0 +1,88 @@ +#ifndef SSF_LAYER_BASIC_PROXY_PROTOCOL_H_ +#define SSF_LAYER_BASIC_PROXY_PROTOCOL_H_ + +#include +#include + +#include "ssf/error/error.h" + +#include "ssf/layer/basic_endpoint.h" +#include "ssf/layer/basic_impl.h" +#include "ssf/layer/basic_resolver.h" + +#include "ssf/layer/parameters.h" +#include "ssf/layer/protocol_attributes.h" + +#include "ssf/layer/proxy/proxy_endpoint_context.h" +#include "ssf/layer/proxy/basic_proxy_acceptor_service.h" +#include "ssf/layer/proxy/basic_proxy_socket_service.h" + +#include "ssf/layer/proxy/proxy_helpers.h" + +namespace ssf { +namespace layer { +namespace proxy { + +template +class basic_ProxyProtocol { + public: + enum { + id = 6, + overhead = 0, + facilities = ssf::layer::facilities::stream, + mtu = NextLayer::mtu - overhead + }; + enum { endpoint_stack_size = 1 + NextLayer::endpoint_stack_size }; + + static const char* NAME; + + typedef NextLayer next_layer_protocol; + typedef int socket_context; + typedef int acceptor_context; + typedef ProxyEndpointContext endpoint_context_type; + using next_endpoint_type = typename next_layer_protocol::endpoint; + + typedef basic_VirtualLink_endpoint endpoint; + typedef basic_VirtualLink_resolver resolver; + typedef boost::asio::basic_stream_socket< + basic_ProxyProtocol, basic_ProxySocket_service> + socket; + typedef boost::asio::basic_socket_acceptor< + basic_ProxyProtocol, basic_ProxyAcceptor_service> + acceptor; + + private: + using query = typename resolver::query; + + static std::string get_name() { + std::string name(NAME); + name += "_" + next_layer_protocol::get_name(); + return name; + } + + public: + static endpoint make_endpoint(boost::asio::io_service& io_service, + typename query::const_iterator parameters_it, + uint32_t, boost::system::error_code& ec) { + auto context = MakeProxyContext(io_service, *parameters_it, ec); + if (ec) { + return endpoint(); + } + + return endpoint(context, next_layer_protocol::make_endpoint( + io_service, ++parameters_it, id, ec)); + } + + static void add_params_from_property_tree( + query* p_query, const boost::property_tree::ptree& property_tree, + bool connect, boost::system::error_code& ec) {} +}; + +template +const char* basic_ProxyProtocol::NAME = "PROXY"; + +} // proxy +} // layer +} // layer + +#endif // SSF_LAYER_BASIC_PROXY_PROTOCOL_H_ diff --git a/src/framework/ssf/layer/proxy/basic_proxy_socket_service.h b/src/framework/ssf/layer/proxy/basic_proxy_socket_service.h new file mode 100644 index 00000000..46eddb46 --- /dev/null +++ b/src/framework/ssf/layer/proxy/basic_proxy_socket_service.h @@ -0,0 +1,240 @@ +#ifndef SSF_LAYER_PROXY_BASIC_PROXY_SOCKET_SERVICE_H_ +#define SSF_LAYER_PROXY_BASIC_PROXY_SOCKET_SERVICE_H_ + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "ssf/io/handler_helpers.h" +#include "ssf/error/error.h" + +#include "ssf/layer/basic_impl.h" + +#include "ssf/layer/proxy/connect_op.h" + +namespace ssf { +namespace layer { +namespace proxy { + +#include + +template +class basic_ProxySocket_service : public boost::asio::detail::service_base< + basic_ProxySocket_service> { + public: + /// The protocol type. + typedef Protocol protocol_type; + /// The endpoint type. + typedef typename protocol_type::endpoint endpoint_type; + typedef typename protocol_type::endpoint_context_type endpoint_context_type; + + typedef basic_socket_impl implementation_type; + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + private: + typedef typename protocol_type::next_layer_protocol::socket next_socket_type; + + public: + explicit basic_ProxySocket_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service) {} + + virtual ~basic_ProxySocket_service() {} + + void construct(implementation_type& impl) { + impl.p_next_layer_socket = + std::make_shared(this->get_io_service()); + } + + void destroy(implementation_type& impl) { + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_socket.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->open( + typename protocol_type::next_layer_protocol(), ec); + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { + return impl.p_next_layer_socket->is_open(); + } + + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_remote_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_remote_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return *impl.p_local_endpoint; + } else { + ec.assign(ssf::error::no_link, ssf::error::get_ssf_category()); + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->close(ec); + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->cancel(ec); + } + + bool at_mark(const implementation_type& impl, + boost::system::error_code& ec) const { + return impl.p_next_layer_socket->at_mark(ec); + } + + std::size_t available(const implementation_type& impl, + boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return 0; + } + + return impl.p_next_layer_socket->available(ec); + } + + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) { + impl.p_local_endpoint = std::make_shared(endpoint); + return impl.p_next_layer_socket->bind(endpoint.next_layer_endpoint(), ec); + } + + boost::system::error_code connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + boost::system::error_code& ec) { + impl.p_remote_endpoint = std::make_shared(peer_endpoint); + impl.p_local_endpoint = std::make_shared(); + + ConnectOp (*impl.p_next_layer_socket, + impl.p_local_endpoint.get(), + peer_endpoint)(ec); + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + ConnectHandler&& handler) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + impl.p_remote_endpoint = std::make_shared(peer_endpoint); + impl.p_local_endpoint = std::make_shared(); + + AsyncConnectOp::type> ( + *impl.p_next_layer_socket, impl.p_local_endpoint.get(), peer_endpoint, + init.handler)(); + + return init.result.get(); + } + + template + std::size_t send(implementation_type& impl, + const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->send(buffers, flags, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + return impl.p_next_layer_socket->async_send( + buffers, std::forward(handler)); + } + + template + std::size_t receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + boost::system::error_code& ec) { + return impl.p_next_layer_socket->receive(buffers, flags, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + return impl.p_next_layer_socket->async_receive( + buffers, std::forward(handler)); + } + + boost::system::error_code shutdown( + implementation_type& impl, boost::asio::socket_base::shutdown_type what, + boost::system::error_code& ec) { + impl.p_next_layer_socket->shutdown(what, ec); + + return ec; + } + + private: + void shutdown_service() {} +}; + +#include + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_BASIC_PROXY_SOCKET_SERVICE_H_ diff --git a/src/framework/ssf/layer/proxy/connect_op.h b/src/framework/ssf/layer/proxy/connect_op.h new file mode 100644 index 00000000..9a7331c8 --- /dev/null +++ b/src/framework/ssf/layer/proxy/connect_op.h @@ -0,0 +1,89 @@ +#ifndef SSF_LAYER_PROXY_CONNECT_OP_H_ +#define SSF_LAYER_PROXY_CONNECT_OP_H_ + +#include + +#include "ssf/error/error.h" +#include "ssf/layer/connect_op.h" +#include "ssf/layer/proxy/http_connect_op.h" + +namespace ssf { +namespace layer { +namespace proxy { + +template +class ConnectOp { + public: + ConnectOp(Stream& stream, Endpoint* p_local_endpoint, Endpoint peer_endpoint) + : stream_(stream), + p_local_endpoint_(p_local_endpoint), + peer_endpoint_(std::move(peer_endpoint)) {} + + void operator()(boost::system::error_code& ec) { + auto& context = peer_endpoint_.endpoint_context(); + + if (!context.IsProxyEnabled()) { + ssf::layer::detail::ConnectOp( + stream_, p_local_endpoint_, std::move(peer_endpoint_))(ec); + return; + } + + if (context.HttpProxyEnabled()) { + HttpConnectOp(stream_, p_local_endpoint_, + std::move(peer_endpoint_))(ec); + return; + } + } + + private: + Stream& stream_; + Endpoint* p_local_endpoint_; + Endpoint peer_endpoint_; +}; + +template +class AsyncConnectOp { + public: + AsyncConnectOp(Stream& stream, Endpoint* p_local_endpoint, + Endpoint peer_endpoint, ConnectHandler handler) + : stream_(stream), + p_local_endpoint_(p_local_endpoint), + peer_endpoint_(std::move(peer_endpoint)), + handler_(std::move(handler)) {} + + void operator()() { + auto& context = peer_endpoint_.endpoint_context(); + + if (!context.IsProxyEnabled()) { + ssf::layer::detail::AsyncConnectOp< + Protocol, Stream, Endpoint, + typename boost::asio::handler_type< + ConnectHandler, void(boost::system::error_code)>::type>( + stream_, p_local_endpoint_, std::move(peer_endpoint_), + std::move(handler_))(); + return; + } + + if (context.HttpProxyEnabled()) { + AsyncHttpConnectOp< + Protocol, Stream, Endpoint, + typename boost::asio::handler_type< + ConnectHandler, void(boost::system::error_code)>::type>( + stream_, p_local_endpoint_, std::move(peer_endpoint_), + std::move(handler_))(); + return; + } + } + + private: + Stream& stream_; + Endpoint* p_local_endpoint_; + Endpoint peer_endpoint_; + ConnectHandler handler_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_CONNECT_OP_H_ diff --git a/src/framework/ssf/layer/proxy/digest_auth_strategy.cpp b/src/framework/ssf/layer/proxy/digest_auth_strategy.cpp new file mode 100644 index 00000000..16167e08 --- /dev/null +++ b/src/framework/ssf/layer/proxy/digest_auth_strategy.cpp @@ -0,0 +1,290 @@ +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "ssf/layer/proxy/digest_auth_strategy.h" + +#ifndef BOOST_SPIRIT_USE_PHOENIX_V3 +#define BOOST_SPIRIT_USE_PHOENIX_V3 1 +#endif +#include +#include +#include + +namespace ssf { +namespace layer { +namespace proxy { + +DigestAuthStrategy::DigestAuthStrategy(const Proxy& proxy_ctx) + : AuthStrategy(proxy_ctx, Status::kAuthenticating), + request_populated_(false), + qop_(Qop::kNone), + cnonce_(GenerateRandomString(32)), + nonce_count_(0) {} + +std::string DigestAuthStrategy::AuthName() const { return "Digest"; } + +bool DigestAuthStrategy::Support(const HttpResponse& response) const { + auto auth_name = AuthName(); + return !request_populated_ && + (response.HeaderValueBeginWith("WWW-Authenticate", auth_name) || + response.HeaderValueBeginWith("Proxy-Authenticate", auth_name)); +} + +void DigestAuthStrategy::ProcessResponse(const HttpResponse& response) { + if (response.Success()) { + status_ = Status::kAuthenticated; + return; + } + + if (!Support(response)) { + status_ = Status::kAuthenticationFailure; + return; + } + + set_proxy_authentication(response); + + ParseDigestChallenge(response); + + if (status_ != Status::kAuthenticating) { + return; + } + + // mandatory challenge fields + if (challenge_.count("realm") == 0 || challenge_.count("nonce") == 0) { + status_ = Status::kAuthenticationFailure; + return; + } + + // extract qop type + if (challenge_.count("qop")) { + std::set qops; + boost::split(qops, challenge_["qop"], boost::is_any_of(",")); + if (qops.count("auth")) { + qop_ = Qop::kAuth; + } else if (qops.count("auth-int")) { + qop_ = Qop::kAuthInt; + } + } +} + +void DigestAuthStrategy::PopulateRequest(HttpRequest* p_request) { + ++nonce_count_; + + // construct challenge response fields + std::map chal_resp; + chal_resp["username"] = '"' + proxy_ctx_.username + '"'; + chal_resp["realm"] = '"' + challenge_["realm"] + '"'; + chal_resp["nonce"] = '"' + challenge_["nonce"] + '"'; + chal_resp["uri"] = '"' + p_request->uri() + '"'; + chal_resp["response"] = '"' + GenerateResponseDigest(*p_request) + '"'; + if (qop_ != Qop::kNone) { + chal_resp["cnonce"] = '"' + cnonce_ + '"'; + chal_resp["nc"] = (boost::format("%08x") % nonce_count_).str(); + chal_resp["qop"] = ((qop_ == Qop::kAuth) ? "auth" : "auth-int"); + } + if (challenge_.count("algorithm")) { + if (boost::iequals(challenge_["algorithm"], "md5-sess")) { + chal_resp["algorithm"] = challenge_["algorithm"]; + } else { + chal_resp["algorithm"] = "MD5"; + } + } + if (challenge_.count("opaque")) { + chal_resp["opaque"] = '"' + challenge_["opaque"] + '"'; + } + + // construct header + std::stringstream ss_auth_hdr; + ss_auth_hdr << AuthName() << " "; + auto cur_it = chal_resp.begin(); + auto end_it = chal_resp.end(); + while (cur_it != end_it) { + ss_auth_hdr << cur_it->first << '=' << cur_it->second; + ++cur_it; + if (cur_it != end_it) { + ss_auth_hdr << ", "; + } + } + + p_request->AddHeader( + proxy_authentication() ? "Proxy-Authorization" : "Authorization", + ss_auth_hdr.str()); + + request_populated_ = true; +} + +void DigestAuthStrategy::ParseDigestChallenge(const HttpResponse& response) { + using boost::spirit::qi::int_; + using boost::spirit::qi::alnum; + using boost::spirit::qi::char_; + using boost::spirit::qi::string; + using boost::spirit::qi::lexeme; + using boost::spirit::ascii::space_type; + + using boost::spirit::qi::rule; + using str_it = std::string::const_iterator; + + auto challenge_str = ExtractAuthToken(response); + + std::map result; + + rule option_pattern = + (string("realm") | string("domain") | string("nonce") | string("opaque") | + string("stale") | string("algorithm") | string("qop") | + lexeme[*(~char_('"'))]); + + rule str_pattern = *(~char_('"')); + + rule quoted_str_pattern = + '"' >> lexeme[*(~char_('"'))] >> '"'; + rule(), space_type> item = + option_pattern >> '=' >> (quoted_str_pattern | str_pattern); + + str_it begin = challenge_str.begin(), end = challenge_str.end(); + bool parsed = boost::spirit::qi::phrase_parse( + begin, end, item % ',', boost::spirit::qi::ascii::space, challenge_); + + if (!parsed) { + status_ = Status::kAuthenticationFailure; + return; + } +} + +std::string DigestAuthStrategy::GenerateResponseDigest( + const HttpRequest& request) { + auto a1_hash = GenerateA1Hash(); + auto a2_hash = GenerateA2Hash(request); + + Md5Digest response_digest = {{0}}; + MD5_CTX md5_context; + MD5_Init(&md5_context); + + // md5 a1_hash:nonce: + MD5_Update(&md5_context, a1_hash.c_str(), a1_hash.size()); + MD5_Update(&md5_context, ":", 1); + MD5_Update(&md5_context, challenge_["nonce"].c_str(), + challenge_["nonce"].size()); + MD5_Update(&md5_context, ":", 1); + + if (qop_ != Qop::kNone) { + auto nonce_str = (boost::format("%08x") % nonce_count_).str(); + std::string qop_str = ((qop_ == Qop::kAuth) ? "auth" : "auth-int"); + + // nonce:cnonce:qop + MD5_Update(&md5_context, nonce_str.c_str(), nonce_str.size()); + MD5_Update(&md5_context, ":", 1); + MD5_Update(&md5_context, cnonce_.c_str(), cnonce_.size()); + MD5_Update(&md5_context, ":", 1); + MD5_Update(&md5_context, qop_str.c_str(), qop_str.size()); + MD5_Update(&md5_context, ":", 1); + } + // a2_hash + MD5_Update(&md5_context, a2_hash.c_str(), a2_hash.size()); + MD5_Final(response_digest.data(), &md5_context); + + return BufferToHex(response_digest.data(), response_digest.size()); +} + +std::string DigestAuthStrategy::GenerateA1Hash() { + std::stringstream ss_a1; + auto alg_it = challenge_.find("algorithm"); + + Md5Digest a1_hash = {{0}}; + // md5 of username:realm:password + MD5_CTX md5_context; + MD5_Init(&md5_context); + MD5_Update(&md5_context, proxy_ctx_.username.c_str(), + proxy_ctx_.username.size()); + MD5_Update(&md5_context, ":", 1); + MD5_Update(&md5_context, challenge_["realm"].c_str(), + challenge_["realm"].size()); + MD5_Update(&md5_context, ":", 1); + MD5_Update(&md5_context, proxy_ctx_.password.c_str(), + proxy_ctx_.password.size()); + MD5_Final(a1_hash.data(), &md5_context); + + if (alg_it != challenge_.end() && + boost::iequals(alg_it->second, "md5-sess")) { + // MD5-sess : a1_hash:nonce:cnonce + MD5_Init(&md5_context); + MD5_Update(&md5_context, a1_hash.data(), a1_hash.size()); + MD5_Update(&md5_context, ":", 1); + MD5_Update(&md5_context, challenge_["nonce"].c_str(), + challenge_["nonce"].size()); + MD5_Update(&md5_context, ":", 1); + MD5_Update(&md5_context, cnonce_.c_str(), cnonce_.size()); + MD5_Final(a1_hash.data(), &md5_context); + } + + return BufferToHex(a1_hash.data(), a1_hash.size()); +} + +std::string DigestAuthStrategy::GenerateA2Hash(const HttpRequest& request) { + std::stringstream ss_a2; + + Md5Digest a2_hash = {{0}}; + MD5_CTX md5_context; + MD5_Init(&md5_context); + + // md5 method:uri + MD5_Update(&md5_context, request.method().c_str(), request.method().size()); + MD5_Update(&md5_context, ":", 1); + MD5_Update(&md5_context, request.uri().c_str(), request.uri().size()); + + if (qop_ == Qop::kAuthInt) { + // include request body in md5 + MD5_Update(&md5_context, ":", 1); + MD5_Update(&md5_context, request.body().c_str(), request.body().size()); + } + MD5_Final(a2_hash.data(), &md5_context); + + return BufferToHex(a2_hash.data(), a2_hash.size()); +} + +std::string DigestAuthStrategy::GenerateRandomString(std::size_t strlen) { + std::string random_string(strlen, '0'); + std::string characters( + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "1234567890"); + random_string.reserve(strlen); + boost::random::mt19937 rng; + // TODO improve randomness + rng.seed(static_cast(std::time(0))); + boost::random::uniform_int_distribution<> index_dist(0, + characters.size() - 1); + for (std::size_t i = 0; i < strlen; ++i) { + random_string[i] = characters[index_dist(rng)]; + } + + return random_string; +} + +std::string DigestAuthStrategy::BufferToHex(unsigned char* buffer, + std::size_t buffer_len) { + std::string buffer_hex(buffer_len * 2, '0'); + unsigned char j; + + for (std::size_t i = 0; i < buffer_len; ++i) { + j = (buffer[i] >> 4) & 0xf; + buffer_hex[i * 2] = (j <= 9) ? (j + '0') : (j + 'a' - 10); + + j = buffer[i] & 0xf; + buffer_hex[(i * 2) + 1] = (j <= 9) ? (j + '0') : (j + 'a' - 10); + } + + return buffer_hex; +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/digest_auth_strategy.h b/src/framework/ssf/layer/proxy/digest_auth_strategy.h new file mode 100644 index 00000000..b6b84d0b --- /dev/null +++ b/src/framework/ssf/layer/proxy/digest_auth_strategy.h @@ -0,0 +1,57 @@ +#ifndef SSF_LAYER_PROXY_DIGEST_AUTH_STRATEGY_H_ +#define SSF_LAYER_PROXY_DIGEST_AUTH_STRATEGY_H_ + +#include + +#include +#include +#include + +#include "ssf/layer/proxy/auth_strategy.h" + +namespace ssf { +namespace layer { +namespace proxy { + +class DigestAuthStrategy : public AuthStrategy { + private: + using Md5Digest = std::array; + + enum Qop { kNone, kAuth, kAuthInt }; + + public: + DigestAuthStrategy(const Proxy& proxy_ctx); + + virtual ~DigestAuthStrategy(){}; + + std::string AuthName() const override; + + inline void set_cnonce(const std::string& cnonce) { cnonce_ = cnonce; } + + bool Support(const HttpResponse& response) const override; + + void ProcessResponse(const HttpResponse& response) override; + + void PopulateRequest(HttpRequest* p_request) override; + + private: + void ParseDigestChallenge(const HttpResponse& response); + std::string GenerateResponseDigest(const HttpRequest& request); + std::string GenerateA1Hash(); + std::string GenerateA2Hash(const HttpRequest& request); + static std::string GenerateRandomString(std::size_t strlen); + static std::string BufferToHex(unsigned char* buffer, std::size_t buffer_len); + + private: + std::map challenge_; + bool request_populated_; + Qop qop_; + std::string cnonce_; + uint32_t nonce_count_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_DIGEST_AUTH_STRATEGY_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/http_connect_op.h b/src/framework/ssf/layer/proxy/http_connect_op.h new file mode 100644 index 00000000..0cd3a891 --- /dev/null +++ b/src/framework/ssf/layer/proxy/http_connect_op.h @@ -0,0 +1,323 @@ +#ifndef SSF_LAYER_PROXY_HTTP_CONNECT_OP_H_ +#define SSF_LAYER_PROXY_HTTP_CONNECT_OP_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "ssf/layer/proxy/http_response_builder.h" +#include "ssf/layer/proxy/http_session_initializer.h" + +namespace ssf { +namespace layer { +namespace proxy { + +template +class HttpConnectOp { + public: + using Buffer = std::array; + + public: + HttpConnectOp(Stream& stream, Endpoint* p_local_endpoint, + Endpoint peer_endpoint) + : stream_(stream), + p_local_endpoint_(p_local_endpoint), + peer_endpoint_(std::move(peer_endpoint)) {} + + void operator()(boost::system::error_code& ec) { + auto& endpoint_context = peer_endpoint_.endpoint_context(); + + stream_.connect( + endpoint_context.http_proxy.ToTcpEndpoint(stream_.get_io_service()), + ec); + + if (ec) { + return; + } + + p_local_endpoint_->set(); + + boost::system::error_code close_ec; + boost::system::error_code connect_ec; + + try { + HttpSessionInitializer session_initializer; + HttpResponseBuilder response_builder; + Buffer buffer; + std::size_t bytes_read; + auto& next_layer_remote_endpoint = peer_endpoint_.next_layer_endpoint(); + + session_initializer.Reset( + next_layer_remote_endpoint.address().to_string(), + std::to_string(next_layer_remote_endpoint.port()), endpoint_context); + + HttpRequest http_request; + + // session initialization (connect request + auth) + while (session_initializer.status() == + HttpSessionInitializer::Status::kContinue) { + if ((session_initializer.stage() == + HttpSessionInitializer::Stage::kConnect)) { + if (stream_.is_open()) { + stream_.shutdown(boost::asio::socket_base::shutdown_both, ec); + stream_.close(ec); + } + stream_.connect(endpoint_context.http_proxy.ToTcpEndpoint( + stream_.get_io_service())); + } + + // send request + session_initializer.PopulateRequest(&http_request, connect_ec); + if (connect_ec.value() != 0) { + SSF_LOG(kLogError) << "network[proxy]: session initializer could not " + "generate connect request"; + break; + } + + auto request = http_request.GenerateRequest(); + boost::asio::write(stream_, boost::asio::buffer(request), connect_ec); + if (connect_ec.value() != 0) { + SSF_LOG(kLogError) << "network[proxy]: session initializer could not " + "process connect response"; + break; + } + + response_builder.Reset(); + + // read response + while (!response_builder.complete()) { + bytes_read = stream_.receive(boost::asio::buffer(buffer)); + response_builder.ProcessInput(buffer.data(), bytes_read); + } + + session_initializer.ProcessResponse(response_builder.Get(), connect_ec); + if (connect_ec.value() != 0) { + SSF_LOG(kLogError) << "network[proxy]: session initializer could not " + "process connect response"; + break; + } + } + + if (session_initializer.status() == + HttpSessionInitializer::Status::kSuccess && + connect_ec.value() == 0) { + return; + } + + SSF_LOG(kLogError) << "network[proxy]: connection through proxy failed"; + stream_.close(close_ec); + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + } catch (const std::exception&) { + SSF_LOG(kLogError) << "network[proxy]: connection through proxy failed"; + stream_.close(close_ec); + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return; + } + } + + private: + Stream& stream_; + Endpoint* p_local_endpoint_; + Endpoint peer_endpoint_; +}; + +template +class AsyncHttpConnectOp { + private: + using Buffer = std::array; + + public: + AsyncHttpConnectOp(Stream& stream, Endpoint* p_local_endpoint, + Endpoint peer_endpoint, ConnectHandler handler) + : coro_(), + stream_(stream), + p_local_endpoint_(p_local_endpoint), + peer_endpoint_(std::move(peer_endpoint)), + handler_(std::move(handler)), + p_request_(new std::string()), + p_buffer_(new Buffer()), + p_response_builder_(new HttpResponseBuilder()), + p_session_initializer_(new HttpSessionInitializer()) {} + + AsyncHttpConnectOp(const AsyncHttpConnectOp& other) + : coro_(other.coro_), + stream_(other.stream_), + p_local_endpoint_(other.p_local_endpoint_), + peer_endpoint_(other.peer_endpoint_), + handler_(other.handler_), + p_request_(other.p_request_), + p_buffer_(other.p_buffer_), + p_response_builder_(other.p_response_builder_), + p_session_initializer_(other.p_session_initializer_) {} + + AsyncHttpConnectOp(AsyncHttpConnectOp&& other) + : coro_(std::move(other.coro_)), + stream_(other.stream_), + p_local_endpoint_(other.p_local_endpoint_), + peer_endpoint_(std::move(other.peer_endpoint_)), + handler_(std::move(other.handler_)), + p_request_(other.p_request_), + p_buffer_(other.p_buffer_), + p_response_builder_(other.p_response_builder_), + p_session_initializer_(other.p_session_initializer_) {} + +#include + void operator()( + const boost::system::error_code& ec = boost::system::error_code(), + std::size_t size = 0) { + if (ec) { + // error + handler_(ec); + return; + } + + boost::system::error_code connect_ec; + auto& next_layer_remote_endpoint = peer_endpoint_.next_layer_endpoint(); + auto& endpoint_context = peer_endpoint_.endpoint_context(); + + HttpRequest http_request; + + reenter(coro_) { + p_session_initializer_->Reset( + next_layer_remote_endpoint.address().to_string(), + std::to_string(next_layer_remote_endpoint.port()), endpoint_context); + + yield stream_.async_connect( + endpoint_context.http_proxy.ToTcpEndpoint(stream_.get_io_service()), + std::move(*this)); + + p_local_endpoint_->set(); + + // session initialization (connect request + auth) + while (p_session_initializer_->status() == + HttpSessionInitializer::Status::kContinue) { + if ((p_session_initializer_->stage() == + HttpSessionInitializer::Stage::kConnect)) { + if (stream_.is_open()) { + stream_.shutdown(boost::asio::socket_base::shutdown_both, + close_ec_); + stream_.close(close_ec_); + } + yield stream_.async_connect(endpoint_context.http_proxy.ToTcpEndpoint( + stream_.get_io_service()), + std::move(*this)); + } + + // send request + p_session_initializer_->PopulateRequest(&http_request, connect_ec); + if (connect_ec.value() != 0) { + SSF_LOG(kLogError) << "network[proxy]: session initializer could not " + "generate connect request"; + break; + } + + *p_request_ = http_request.GenerateRequest(); + + yield boost::asio::async_write( + stream_, boost::asio::buffer(*p_request_), std::move(*this)); + + // read response + p_response_builder_->Reset(); + while (!p_response_builder_->complete()) { + yield stream_.async_receive(boost::asio::buffer(*p_buffer_), + std::move(*this)); + p_response_builder_->ProcessInput(p_buffer_->data(), size); + } + + p_session_initializer_->ProcessResponse(p_response_builder_->Get(), + connect_ec); + if (connect_ec.value() != 0) { + SSF_LOG(kLogError) << "network[proxy]: session initializer could not " + "process connect response"; + break; + } + } + + // initialization finished + if (p_session_initializer_->status() == + HttpSessionInitializer::Status::kSuccess && + connect_ec.value() == 0) { + handler_(close_ec_); + return; + } + + SSF_LOG(kLogError) << "network[proxy]: connection through proxy failed"; + stream_.close(close_ec_); + close_ec_.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + handler_(close_ec_); + } + } +#include + + inline ConnectHandler& handler() { return handler_; } + + private: + boost::asio::coroutine coro_; + Stream& stream_; + Endpoint* p_local_endpoint_; + Endpoint peer_endpoint_; + ConnectHandler handler_; + + std::shared_ptr p_request_; + std::shared_ptr p_buffer_; + std::shared_ptr p_response_builder_; + std::shared_ptr p_session_initializer_; + boost::system::error_code close_ec_; +}; + +template +inline void* asio_handler_allocate( + std::size_t size, AsyncHttpConnectOp* this_handler) { + return boost_asio_handler_alloc_helpers::allocate(size, + this_handler->handler()); +} + +template +inline void asio_handler_deallocate( + void* pointer, std::size_t size, + AsyncHttpConnectOp* + this_handler) { + boost_asio_handler_alloc_helpers::deallocate(pointer, size, + this_handler->handler()); +} + +template +inline bool asio_handler_is_continuation(AsyncHttpConnectOp< + Protocol, Stream, Endpoint, ConnectHandler>* this_handler) { + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->handler()); +} + +template +inline void asio_handler_invoke( + Function& function, AsyncHttpConnectOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +template +inline void asio_handler_invoke( + const Function& function, + AsyncHttpConnectOp* + this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_HTTP_CONNECT_OP_H_ diff --git a/src/framework/ssf/layer/proxy/http_request.cpp b/src/framework/ssf/layer/proxy/http_request.cpp new file mode 100644 index 00000000..fee66400 --- /dev/null +++ b/src/framework/ssf/layer/proxy/http_request.cpp @@ -0,0 +1,50 @@ +#include + +#include "ssf/layer/proxy/http_request.h" + +namespace ssf { +namespace layer { +namespace proxy { + +HttpRequest::HttpRequest() : method_(), uri_(), headers_() {} + +void HttpRequest::Reset(const std::string& method, const std::string& uri) { + method_ = method; + uri_ = uri; + headers_.clear(); + body_.clear(); + AddHeader("Connection", "keep-alive"); +} + +void HttpRequest::AddHeader(const std::string& name, const std::string& value) { + headers_[name] = value; +} + +std::string HttpRequest::Header(const std::string& name) { + HeadersMap::const_iterator it = headers_.find(name); + if (it == headers_.end()) { + // header not found + return ""; + } + + return it->second; +} + +std::string HttpRequest::GenerateRequest() const { + std::stringstream ss_request; + std::string eol("\r\n"); + + ss_request << "CONNECT " << uri_ << " HTTP/1.1" << eol; + + for (const auto& header : headers_) { + ss_request << header.first << ": " << header.second << eol; + } + ss_request << body_; + ss_request << eol; + + return ss_request.str(); +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/http_request.h b/src/framework/ssf/layer/proxy/http_request.h new file mode 100644 index 00000000..b3354a54 --- /dev/null +++ b/src/framework/ssf/layer/proxy/http_request.h @@ -0,0 +1,43 @@ +#ifndef SSF_LAYER_PROXY_HTTP_REQUEST_H_ +#define SSF_LAYER_PROXY_HTTP_REQUEST_H_ + +#include +#include +#include + +namespace ssf { +namespace layer { +namespace proxy { + +class HttpRequest { + private: + using HeadersMap = std::map; + + public: + HttpRequest(); + + inline std::string method() const { return method_; } + inline std::string uri() const { return uri_; } + inline std::string body() const { return body_; } + inline void set_body(const std::string& body) { body_ = body; } + + void Reset(const std::string& method, const std::string& uri); + + void AddHeader(const std::string& name, const std::string& value); + + std::string Header(const std::string& name); + + std::string GenerateRequest() const; + + private: + std::string method_; + std::string uri_; + HeadersMap headers_; + std::string body_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_HTTP_REQUEST_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/http_response.cpp b/src/framework/ssf/layer/proxy/http_response.cpp new file mode 100644 index 00000000..3db0211d --- /dev/null +++ b/src/framework/ssf/layer/proxy/http_response.cpp @@ -0,0 +1,76 @@ +#include + +#include "ssf/layer/proxy/http_response.h" + +namespace ssf { +namespace layer { +namespace proxy { + +HttpResponse::HttpResponse() : status_code_(0), headers_() {} + +bool HttpResponse::Success() const { return status_code_ == StatusCode::kOk; } + +bool HttpResponse::Redirected() const { + return (status_code_ == StatusCode::kMovedTemporarily || + status_code_ == StatusCode::kMovedPermanently) && + (headers_.find("location") != headers_.end()); +} + +bool HttpResponse::AuthenticationRequired() const { + return (status_code_ == StatusCode::kUnauthorized || + status_code_ == StatusCode::kProxyAuthenticationRequired) && + ((headers_.find("proxy-authenticate") != headers_.end()) || + (headers_.find("www-authenticate") != headers_.end())); +} + +bool HttpResponse::HeaderValueBeginWith(const std::string& header_name, + const std::string& begin_with) const { + auto header = Header(header_name); + if (header.empty()) { + return false; + } + + for (const auto& value : header) { + if (value.find(begin_with) == 0) { + return true; + } + } + + return false; +} + +void HttpResponse::AddHeader(const std::string& name, + const std::string& value) { + auto name_lower = name; + std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(), + ::tolower); + auto it = headers_.find(name_lower); + if (it == headers_.end()) { + headers_[name_lower] = {}; + } + + headers_[name_lower].push_back(value); +} + +void HttpResponse::Reset() { + status_code_ = 0; + headers_.clear(); + body_.clear(); +} + +std::list HttpResponse::Header(const std::string& name) const { + auto name_lower = name; + std::transform(name_lower.begin(), name_lower.end(), name_lower.begin(), + ::tolower); + HeadersMap::const_iterator it = headers_.find(name_lower); + if (it == headers_.end()) { + // header not found + return {}; + } + + return it->second; +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/http_response.h b/src/framework/ssf/layer/proxy/http_response.h new file mode 100644 index 00000000..628ee08c --- /dev/null +++ b/src/framework/ssf/layer/proxy/http_response.h @@ -0,0 +1,57 @@ +#ifndef SSF_LAYER_PROXY_HTTP_RESPONSE_H_ +#define SSF_LAYER_PROXY_HTTP_RESPONSE_H_ + +#include +#include +#include + +namespace ssf { +namespace layer { +namespace proxy { + +class HttpResponse { + public: + enum StatusCode : int { + kOk = 200, + kMovedPermanently = 301, + kMovedTemporarily = 302, + kUnauthorized = 401, + kProxyAuthenticationRequired = 407 + }; + + private: + using HeadersMap = std::map>; + + public: + HttpResponse(); + + inline int status_code() const { return status_code_; } + inline void set_status_code(int status_code) { status_code_ = status_code; } + + inline std::string body() { return body_; } + inline void set_body(const std::string& body) { body_ = body; } + + std::list Header(const std::string& name) const; + void AddHeader(const std::string& name, const std::string& value); + + void Reset(); + + bool Success() const; + bool Redirected() const; + + bool HeaderValueBeginWith(const std::string& header_name, + const std::string& begin_with) const; + + bool AuthenticationRequired() const; + + private: + int status_code_; + HeadersMap headers_; + std::string body_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_HTTP_RESPONSE_H_ diff --git a/src/framework/ssf/layer/proxy/http_response_builder.cpp b/src/framework/ssf/layer/proxy/http_response_builder.cpp new file mode 100644 index 00000000..3dc0eb38 --- /dev/null +++ b/src/framework/ssf/layer/proxy/http_response_builder.cpp @@ -0,0 +1,166 @@ +#include + +#include + +#include "ssf/layer/proxy/http_response_builder.h" + +#define HTTP_PARSER_C_CALLBACK_IMPL(method) \ + int HttpResponseBuilder::HTTP_PARSER_C_CALLBACK_NAME(method)(http_parser * \ + p_parser) { \ + HttpResponseBuilder* p_response_builder = \ + static_cast(p_parser->data); \ + return p_response_builder->method(p_parser); \ + \ +} + +#define HTTP_PARSER_C_DATA_CALLBACK_IMPL(method) \ + int HttpResponseBuilder::HTTP_PARSER_C_CALLBACK_NAME(method)( \ + http_parser * p_parser, const char* data, size_t len) { \ + HttpResponseBuilder* p_response_builder = \ + static_cast(p_parser->data); \ + return p_response_builder->method(p_parser, data, len); \ + } + +namespace ssf { +namespace layer { +namespace proxy { + +HttpResponseBuilder::HttpResponseBuilder() + : response_(), + complete_(false), + processing_header_name_(true), + current_header_name_(), + current_header_value_(), + ss_body_() { + http_parser_init(&parser_, HTTP_RESPONSE); + parser_.data = this; + + http_parser_settings_init(&parser_settings_); + parser_settings_.on_message_begin = + HTTP_PARSER_C_CALLBACK_NAME(OnMessageBegin); + parser_settings_.on_status = HTTP_PARSER_C_CALLBACK_NAME(OnStatus); + parser_settings_.on_header_field = HTTP_PARSER_C_CALLBACK_NAME(OnHeaderName); + parser_settings_.on_header_value = HTTP_PARSER_C_CALLBACK_NAME(OnHeaderValue); + parser_settings_.on_headers_complete = + HTTP_PARSER_C_CALLBACK_NAME(OnHeadersComplete); + parser_settings_.on_body = HTTP_PARSER_C_CALLBACK_NAME(OnBody); + parser_settings_.on_message_complete = + HTTP_PARSER_C_CALLBACK_NAME(OnMessageComplete); +} + +HttpResponse HttpResponseBuilder::Get() { + auto response = response_; + + Reset(); + + return response; +} + +void HttpResponseBuilder::Reset() { + parser_.data = this; + complete_ = false; + processing_header_name_ = true; + current_header_name_.clear(); + current_header_value_.clear(); + ss_body_.clear(); + + http_parser_init(&parser_, HTTP_RESPONSE); + + response_.Reset(); +} + +HttpResponseBuilder::ParserStatus HttpResponseBuilder::ProcessInput( + const char* p_data, std::size_t size) { + std::size_t parsed_size = + http_parser_execute(&parser_, &parser_settings_, p_data, size); + + if (parser_.upgrade) { + // not implemented + return kParserError; + } + + if (parsed_size != size) { + return kParserError; + } + + return kParserOk; +} + +HttpResponseBuilder::ParserStatus HttpResponseBuilder::OnMessageBegin( + http_parser* p_parser) { + return kParserOk; +} + +HttpResponseBuilder::ParserStatus HttpResponseBuilder::OnStatus( + http_parser* p_parser, const char* at, size_t length) { + response_.set_status_code(p_parser->status_code); + return kParserOk; +} + +HttpResponseBuilder::ParserStatus HttpResponseBuilder::OnHeaderName( + http_parser* p_parser, const char* at, size_t length) { + if (!processing_header_name_) { + response_.AddHeader(current_header_name_, current_header_value_); + current_header_name_.clear(); + current_header_value_.clear(); + processing_header_name_ = true; + } + current_header_name_.append(std::string(at, length)); + return kParserOk; +} + +HttpResponseBuilder::ParserStatus HttpResponseBuilder::OnHeaderValue( + http_parser* p_parser, const char* at, size_t length) { + if (processing_header_name_) { + std::transform(current_header_name_.begin(), current_header_name_.end(), + current_header_name_.begin(), ::tolower); + processing_header_name_ = false; + } + current_header_value_.append(std::string(at, length)); + return kParserOk; +} + +HttpResponseBuilder::ParserStatus HttpResponseBuilder::OnHeadersComplete( + http_parser* p_parser) { + if (!processing_header_name_) { + response_.AddHeader(current_header_name_, current_header_value_); + current_header_name_.clear(); + current_header_value_.clear(); + processing_header_name_ = true; + } + + if (response_.Header("content-length").empty()) { + return kParserNoBody; + } + + return kParserOk; +} + +HttpResponseBuilder::ParserStatus HttpResponseBuilder::OnBody( + http_parser* p_parser, const char* at, size_t length) { + ss_body_.write(at, length); + + return kParserOk; +} + +HttpResponseBuilder::ParserStatus HttpResponseBuilder::OnMessageComplete( + http_parser* p_parser) { + response_.set_body(ss_body_.str()); + + ss_body_.clear(); + complete_ = true; + + return kParserOk; +} + +HTTP_PARSER_C_CALLBACK_IMPL(OnMessageBegin) +HTTP_PARSER_C_DATA_CALLBACK_IMPL(OnStatus) +HTTP_PARSER_C_DATA_CALLBACK_IMPL(OnHeaderName) +HTTP_PARSER_C_DATA_CALLBACK_IMPL(OnHeaderValue) +HTTP_PARSER_C_CALLBACK_IMPL(OnHeadersComplete) +HTTP_PARSER_C_DATA_CALLBACK_IMPL(OnBody) +HTTP_PARSER_C_CALLBACK_IMPL(OnMessageComplete) + +} // proxy +} // layer +} // ssf diff --git a/src/framework/ssf/layer/proxy/http_response_builder.h b/src/framework/ssf/layer/proxy/http_response_builder.h new file mode 100644 index 00000000..f24374ae --- /dev/null +++ b/src/framework/ssf/layer/proxy/http_response_builder.h @@ -0,0 +1,79 @@ +#ifndef SSF_LAYER_PROXY_HTTP_RESPONSE_BUILDER_H_ +#define SSF_LAYER_PROXY_HTTP_RESPONSE_BUILDER_H_ + +#include +#include +#include + +#include + +#include "ssf/layer/proxy/http_response.h" + +#define HTTP_PARSER_C_CALLBACK_NAME(method) C##method##Cb + +#define HTTP_PARSER_C_CALLBACK_DEF(method) \ + static int HTTP_PARSER_C_CALLBACK_NAME(method)(http_parser * p_parser) + +#define HTTP_PARSER_C_DATA_CALLBACK_DEF(method) \ + static int HTTP_PARSER_C_CALLBACK_NAME(method)(http_parser*, const char* at, \ + size_t length) + +namespace ssf { +namespace layer { +namespace proxy { + +class HttpResponseBuilder { + private: + enum ParserStatus : int { + kParserError = -1, + kParserOk = 0, + kParserNoBody = 1 + }; + + public: + HttpResponseBuilder(); + + inline bool complete() { return complete_; } + + ParserStatus ProcessInput(const char* p_data, std::size_t size); + + HttpResponse Get(); + + void Reset(); + + private: + ParserStatus OnMessageBegin(http_parser* p_parser); + ParserStatus OnStatus(http_parser*, const char* at, size_t length); + ParserStatus OnHeaderName(http_parser*, const char* at, size_t length); + ParserStatus OnHeaderValue(http_parser*, const char* at, size_t length); + ParserStatus OnHeadersComplete(http_parser* p_parser); + ParserStatus OnBody(http_parser*, const char* at, size_t length); + ParserStatus OnMessageComplete(http_parser* p_parser); + + // C parser callbacks + HTTP_PARSER_C_CALLBACK_DEF(OnMessageBegin); + HTTP_PARSER_C_DATA_CALLBACK_DEF(OnStatus); + HTTP_PARSER_C_DATA_CALLBACK_DEF(OnHeaderName); + HTTP_PARSER_C_DATA_CALLBACK_DEF(OnHeaderValue); + HTTP_PARSER_C_CALLBACK_DEF(OnHeadersComplete); + HTTP_PARSER_C_DATA_CALLBACK_DEF(OnBody); + HTTP_PARSER_C_CALLBACK_DEF(OnMessageComplete); + + private: + http_parser parser_; + http_parser_settings parser_settings_; + + HttpResponse response_; + + bool complete_; + bool processing_header_name_; + std::string current_header_name_; + std::string current_header_value_; + std::stringstream ss_body_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_HTTP_RESPONSE_BUILDER_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/http_session_initializer.cpp b/src/framework/ssf/layer/proxy/http_session_initializer.cpp new file mode 100644 index 00000000..343e3c09 --- /dev/null +++ b/src/framework/ssf/layer/proxy/http_session_initializer.cpp @@ -0,0 +1,133 @@ +#include + +#include "ssf/error/error.h" +#include "ssf/layer/proxy/http_session_initializer.h" +#include "ssf/layer/proxy/basic_auth_strategy.h" +#include "ssf/layer/proxy/digest_auth_strategy.h" +#include "ssf/layer/proxy/ntlm_auth_strategy.h" +#include "ssf/layer/proxy/negotiate_auth_strategy.h" +#include "ssf/log/log.h" + +namespace ssf { +namespace layer { +namespace proxy { + +HttpSessionInitializer::HttpSessionInitializer() + : status_(Status::kContinue), + stage_(Stage::kConnect), + target_host_(""), + target_port_(""), + proxy_ep_ctx_(), + auth_strategies_(), + p_current_auth_strategy_(nullptr) {} + +void HttpSessionInitializer::Reset(const std::string& target_host, + const std::string& target_port, + const ProxyEndpointContext& proxy_ep_ctx) { + status_ = Status::kContinue; + stage_ = Stage::kConnect; + target_host_ = target_host; + target_port_ = target_port; + proxy_ep_ctx_ = proxy_ep_ctx; + + // instantiate auth strategies + p_current_auth_strategy_ = nullptr; + auth_strategies_.clear(); + auth_strategies_.emplace_back( + new NegotiateAuthStrategy(proxy_ep_ctx_.http_proxy)); + auth_strategies_.emplace_back(new NtlmAuthStrategy(proxy_ep_ctx_.http_proxy)); + auth_strategies_.emplace_back( + new DigestAuthStrategy(proxy_ep_ctx_.http_proxy)); + auth_strategies_.emplace_back( + new BasicAuthStrategy(proxy_ep_ctx_.http_proxy)); +} + +void HttpSessionInitializer::PopulateRequest(HttpRequest* p_request, + boost::system::error_code& ec) { + if (status_ != Status::kContinue) { + ec.assign(ssf::error::interrupted, ssf::error::get_ssf_category()); + } + + p_request->Reset("CONNECT", target_host_ + ':' + target_port_); + + if (stage_ == kProcessing) { + if (p_current_auth_strategy_ != nullptr) { + p_current_auth_strategy_->PopulateRequest(p_request); + } + } +} + +void HttpSessionInitializer::ProcessResponse(const HttpResponse& response, + boost::system::error_code& ec) { + if (response.Success()) { + SSF_LOG(kLogInfo) << "network[proxy]: connected (auth: " + << ((p_current_auth_strategy_ != nullptr) + ? (p_current_auth_strategy_->AuthName()) + : "None") << ")"; + status_ = Status::kSuccess; + return; + } + + if (!response.AuthenticationRequired()) { + // other behaviours (e.g. redirection) not implemented + status_ = Status::kError; + return; + } + + // find auth strategy + bool reconnect = false; + if (p_current_auth_strategy_ == nullptr || + p_current_auth_strategy_->status() == + AuthStrategy::kAuthenticationFailure) { + p_current_auth_strategy_ = nullptr; + if (stage_ == kProcessing) { + stage_ = kConnect; + reconnect = true; + } else { + SetAuthStrategy(response); + } + } + + if (reconnect) { + return; + } + + if (p_current_auth_strategy_ == nullptr) { + SSF_LOG(kLogError) << "network[proxy]: authentication strategies failed"; + status_ = Status::kError; + return; + } + + stage_ = Stage::kProcessing; + + if (p_current_auth_strategy_ != nullptr) { + p_current_auth_strategy_->ProcessResponse(response); + switch (p_current_auth_strategy_->status()) { + case AuthStrategy::Status::kAuthenticating: + case AuthStrategy::Status::kAuthenticationFailure: + break; + case AuthStrategy::Status::kAuthenticated: + status_ = Status::kSuccess; + break; + default: + status_ = Status::kError; + break; + } + return; + } +} + +void HttpSessionInitializer::SetAuthStrategy(const HttpResponse& response) { + for (const auto& p_auth_strategy : auth_strategies_) { + if (p_auth_strategy->Support(response)) { + p_current_auth_strategy_ = p_auth_strategy.get(); + SSF_LOG(kLogDebug) << "network[proxy]: try " + << p_current_auth_strategy_->AuthName() << " auth"; + break; + } + } +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/http_session_initializer.h b/src/framework/ssf/layer/proxy/http_session_initializer.h new file mode 100644 index 00000000..9653a300 --- /dev/null +++ b/src/framework/ssf/layer/proxy/http_session_initializer.h @@ -0,0 +1,58 @@ +#ifndef SSF_LAYER_PROXY_HTTP_SESSION_INITIALIZER_H_ +#define SSF_LAYER_PROXY_HTTP_SESSION_INITIALIZER_H_ + +#include +#include +#include + +#include + +#include "ssf/layer/proxy/auth_strategy.h" +#include "ssf/layer/proxy/http_response.h" +#include "ssf/layer/proxy/proxy_endpoint_context.h" + +namespace ssf { +namespace layer { +namespace proxy { + +class HttpSessionInitializer { + private: + using AuthList = std::list>; + + public: + enum Status : int { kError = -1, kSuccess = 0, kContinue = 1 }; + enum Stage : int { kConnect = 1, kProcessing = 2 }; + + public: + HttpSessionInitializer(); + + void Reset(const std::string& target_host, const std::string& target_port, + const ProxyEndpointContext& proxy_ep_ctx); + + inline Status status() { return status_; } + + inline Stage stage() { return stage_; } + + void PopulateRequest(HttpRequest* p_request, boost::system::error_code& ec); + + void ProcessResponse(const HttpResponse& response, + boost::system::error_code& ec); + + private: + void SetAuthStrategy(const HttpResponse& response); + + private: + Status status_; + Stage stage_; + std::string target_host_; + std::string target_port_; + ProxyEndpointContext proxy_ep_ctx_; + AuthList auth_strategies_; + AuthStrategy* p_current_auth_strategy_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_HTTP_SESSION_INITIALIZER_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/negotiate_auth_strategy.cpp b/src/framework/ssf/layer/proxy/negotiate_auth_strategy.cpp new file mode 100644 index 00000000..f59f9ab7 --- /dev/null +++ b/src/framework/ssf/layer/proxy/negotiate_auth_strategy.cpp @@ -0,0 +1,85 @@ +#include "ssf/layer/proxy/base64.h" +#include "ssf/layer/proxy/negotiate_auth_strategy.h" +#include "ssf/log/log.h" + +#if defined(WIN32) +#include "ssf/layer/proxy/windows/sspi_auth_impl.h" +#else +#include "ssf/layer/proxy/unix/gssapi_auth_impl.h" +#endif + +namespace ssf { +namespace layer { +namespace proxy { + +NegotiateAuthStrategy::NegotiateAuthStrategy(const Proxy& proxy_ctx) + : AuthStrategy(proxy_ctx, Status::kAuthenticating) { +#if defined(WIN32) + p_impl_.reset(new SSPIAuthImpl(SSPIAuthImpl::kNegotiate, proxy_ctx)); +#else + p_impl_.reset(new GSSAPIAuthImpl(proxy_ctx)); +#endif + if (p_impl_.get() != nullptr) { + if (!p_impl_->Init()) { + SSF_LOG(kLogDebug) << "network[proxy]: negotiate: could not initialize " + << "platform impl"; + status_ = Status::kAuthenticationFailure; + } + } +} + +std::string NegotiateAuthStrategy::AuthName() const { return "Negotiate"; } + +bool NegotiateAuthStrategy::Support(const HttpResponse& response) const { + auto auth_name = AuthName(); + return p_impl_.get() != nullptr && + status_ != Status::kAuthenticationFailure && + (response.HeaderValueBeginWith("Proxy-Authenticate", auth_name) || + response.HeaderValueBeginWith("WWW-Authenticate", auth_name)); +} + +void NegotiateAuthStrategy::ProcessResponse(const HttpResponse& response) { + if (response.Success()) { + status_ = Status::kAuthenticated; + return; + } + + if (!Support(response) || p_impl_.get() == nullptr) { + status_ = Status::kAuthenticationFailure; + return; + } + + set_proxy_authentication(response); + + auto server_token = Base64::Decode(ExtractAuthToken(response)); + + if (!p_impl_->ProcessServerToken(server_token)) { + SSF_LOG(kLogDebug) + << "network[proxy]: negotiate: could not process server token"; + status_ = Status::kAuthenticationFailure; + return; + } +} + +void NegotiateAuthStrategy::PopulateRequest(HttpRequest* p_request) { + if (p_impl_.get() == nullptr) { + status_ = Status::kAuthenticationFailure; + return; + } + + auto auth_token = p_impl_->GetAuthToken(); + if (auth_token.empty()) { + SSF_LOG(kLogDebug) << "network[proxy]: negotiate: response token empty"; + status_ = Status::kAuthenticationFailure; + return; + } + + std::string negotiate_value = AuthName() + " " + Base64::Encode(auth_token); + p_request->AddHeader( + proxy_authentication() ? "Proxy-Authorization" : "Authorization", + negotiate_value); +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/negotiate_auth_strategy.h b/src/framework/ssf/layer/proxy/negotiate_auth_strategy.h new file mode 100644 index 00000000..de33e214 --- /dev/null +++ b/src/framework/ssf/layer/proxy/negotiate_auth_strategy.h @@ -0,0 +1,36 @@ +#ifndef SSF_LAYER_PROXY_NEGOTIATE_AUTH_STRATEGY_H_ +#define SSF_LAYER_PROXY_NEGOTIATE_AUTH_STRATEGY_H_ + +#include +#include + +#include "ssf/layer/proxy/auth_strategy.h" +#include "ssf/layer/proxy/platform_auth_impl.h" + +namespace ssf { +namespace layer { +namespace proxy { + +class NegotiateAuthStrategy : public AuthStrategy { + public: + NegotiateAuthStrategy(const Proxy& proxy_ctx); + + virtual ~NegotiateAuthStrategy(){}; + + std::string AuthName() const override; + + bool Support(const HttpResponse& response) const override; + + void ProcessResponse(const HttpResponse& response) override; + + void PopulateRequest(HttpRequest* p_request) override; + + private: + std::unique_ptr p_impl_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_NEGOTIATE_AUTH_STRATEGY_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/ntlm_auth_strategy.cpp b/src/framework/ssf/layer/proxy/ntlm_auth_strategy.cpp new file mode 100644 index 00000000..1f71af4d --- /dev/null +++ b/src/framework/ssf/layer/proxy/ntlm_auth_strategy.cpp @@ -0,0 +1,85 @@ +#include "ssf/layer/proxy/base64.h" +#include "ssf/layer/proxy/ntlm_auth_strategy.h" +#include "ssf/log/log.h" + +#if defined(WIN32) +#include "ssf/layer/proxy/windows/sspi_auth_impl.h" +#endif + +namespace ssf { +namespace layer { +namespace proxy { + +NtlmAuthStrategy::NtlmAuthStrategy(const Proxy& proxy_ctx) + : AuthStrategy(proxy_ctx, Status::kAuthenticating), p_impl_(nullptr) { +#if defined(WIN32) + p_impl_.reset(new SSPIAuthImpl(SSPIAuthImpl::kNTLM, proxy_ctx)); +#endif + + if (p_impl_.get() != nullptr) { + if (!p_impl_->Init()) { + SSF_LOG(kLogDebug) << "network[proxy]: ntlm: could not initialize " + << "platform impl"; + status_ = Status::kAuthenticationFailure; + } + } else { + SSF_LOG(kLogDebug) << "network[proxy]: ntlm: no platform impl found"; + status_ = Status::kAuthenticationFailure; + } +} + +std::string NtlmAuthStrategy::AuthName() const { return "NTLM"; } + +bool NtlmAuthStrategy::Support(const HttpResponse& response) const { + auto auth_name = AuthName(); + return p_impl_.get() != nullptr && + status_ != Status::kAuthenticationFailure && + (response.HeaderValueBeginWith("Proxy-Authenticate", auth_name) || + response.HeaderValueBeginWith("WWW-Authenticate", auth_name)); +} + +void NtlmAuthStrategy::ProcessResponse(const HttpResponse& response) { + if (response.Success()) { + status_ = Status::kAuthenticated; + return; + } + + if (!Support(response)) { + status_ = Status::kAuthenticationFailure; + return; + } + + set_proxy_authentication(response); + + auto server_token = Base64::Decode(ExtractAuthToken(response)); + + if (!p_impl_->ProcessServerToken(server_token)) { + SSF_LOG(kLogDebug) + << "network[proxy]: ntlm: could not process server token"; + status_ = Status::kAuthenticationFailure; + return; + } +} + +void NtlmAuthStrategy::PopulateRequest(HttpRequest* p_request) { + if (p_impl_.get() == nullptr) { + status_ = Status::kAuthenticationFailure; + return; + } + + auto auth_token = p_impl_->GetAuthToken(); + if (auth_token.empty()) { + SSF_LOG(kLogDebug) << "network[proxy]: ntlm: response token empty"; + status_ = Status::kAuthenticationFailure; + return; + } + + std::string ntlm_value = AuthName() + " " + Base64::Encode(auth_token); + p_request->AddHeader( + proxy_authentication() ? "Proxy-Authorization" : "Authorization", + ntlm_value); +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/ntlm_auth_strategy.h b/src/framework/ssf/layer/proxy/ntlm_auth_strategy.h new file mode 100644 index 00000000..05d4a931 --- /dev/null +++ b/src/framework/ssf/layer/proxy/ntlm_auth_strategy.h @@ -0,0 +1,33 @@ +#ifndef SSF_LAYER_PROXY_NTLM_AUTH_STRATEGY_H_ +#define SSF_LAYER_PROXY_NTLM_AUTH_STRATEGY_H_ + +#include "ssf/layer/proxy/auth_strategy.h" +#include "ssf/layer/proxy/platform_auth_impl.h" + +namespace ssf { +namespace layer { +namespace proxy { + +class NtlmAuthStrategy : public AuthStrategy { + public: + NtlmAuthStrategy(const Proxy& proxy_ctx); + + virtual ~NtlmAuthStrategy(){}; + + std::string AuthName() const override; + + bool Support(const HttpResponse& response) const override; + + void ProcessResponse(const HttpResponse& response) override; + + void PopulateRequest(HttpRequest* p_request) override; + + private: + std::unique_ptr p_impl_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_NTLM_AUTH_STRATEGY_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/platform_auth_impl.h b/src/framework/ssf/layer/proxy/platform_auth_impl.h new file mode 100644 index 00000000..5a13c02f --- /dev/null +++ b/src/framework/ssf/layer/proxy/platform_auth_impl.h @@ -0,0 +1,39 @@ +#ifndef SSF_LAYER_PROXY_PLATFORM_AUTH_IMPL_H_ +#define SSF_LAYER_PROXY_PLATFORM_AUTH_IMPL_H_ + +#include + +#include + +#include "ssf/layer/proxy/proxy_endpoint_context.h" + +namespace ssf { +namespace layer { +namespace proxy { + +class PlatformAuthImpl { + public: + enum State { kFailure, kInit, kContinue, kSuccess }; + using Token = std::vector; + + public: + virtual ~PlatformAuthImpl() {} + + virtual bool Init() = 0; + virtual bool ProcessServerToken(const Token& server_token) = 0; + virtual Token GetAuthToken() = 0; + + protected: + PlatformAuthImpl(const Proxy& proxy_ctx) + : state_(kInit), proxy_ctx_(proxy_ctx) {} + + protected: + State state_; + Proxy proxy_ctx_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_PLATFORM_AUTH_IMPL_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/proxy_endpoint_context.cpp b/src/framework/ssf/layer/proxy/proxy_endpoint_context.cpp new file mode 100644 index 00000000..7acccd96 --- /dev/null +++ b/src/framework/ssf/layer/proxy/proxy_endpoint_context.cpp @@ -0,0 +1,41 @@ +#include "ssf/layer/proxy/proxy_endpoint_context.h" + +namespace ssf { +namespace layer { +namespace proxy { + +Proxy::Proxy() : host(""), port("") {} + +boost::asio::ip::tcp::endpoint Proxy::ToTcpEndpoint( + boost::asio::io_service& io_service) { + boost::asio::ip::tcp::resolver resolver(io_service); + auto endpoint_it = resolver.resolve({host, port}); + return *endpoint_it; +} + +ProxyEndpointContext::ProxyEndpointContext() + : proxy_enabled(false), http_proxy() {} + +bool ProxyEndpointContext::IsProxyEnabled() const { return proxy_enabled; } + +bool ProxyEndpointContext::HttpProxyEnabled() const { + return proxy_enabled && !http_proxy.host.empty() && !http_proxy.port.empty(); +} + +bool ProxyEndpointContext::operator==(const ProxyEndpointContext& rhs) const { + return proxy_enabled == rhs.proxy_enabled && + http_proxy.host == rhs.http_proxy.host && + http_proxy.port == rhs.http_proxy.port; +} + +bool ProxyEndpointContext::operator!=(const ProxyEndpointContext& rhs) const { + return !(*this == rhs); +} + +bool ProxyEndpointContext::operator<(const ProxyEndpointContext& rhs) const { + return http_proxy.host < rhs.http_proxy.host; +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/proxy_endpoint_context.h b/src/framework/ssf/layer/proxy/proxy_endpoint_context.h new file mode 100644 index 00000000..7edb7fa5 --- /dev/null +++ b/src/framework/ssf/layer/proxy/proxy_endpoint_context.h @@ -0,0 +1,48 @@ +#ifndef SSF_LAYER_PROXY_PROXY_ENDPOINT_CONTEXT_H_ +#define SSF_LAYER_PROXY_PROXY_ENDPOINT_CONTEXT_H_ + +#include + +#include + +namespace ssf { +namespace layer { +namespace proxy { + +struct Proxy { + Proxy(); + + boost::asio::ip::tcp::endpoint ToTcpEndpoint( + boost::asio::io_service& io_service); + + std::string host; + std::string port; + std::string username; + std::string password; + std::string domain; + bool reuse_ntlm; + bool reuse_kerb; +}; + +struct ProxyEndpointContext { + ProxyEndpointContext(); + + bool IsProxyEnabled() const; + + bool HttpProxyEnabled() const; + + bool operator==(const ProxyEndpointContext& rhs) const; + + bool operator!=(const ProxyEndpointContext& rhs) const; + + bool operator<(const ProxyEndpointContext& rhs) const; + + bool proxy_enabled; + Proxy http_proxy; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_PROXY_ENDPOINT_CONTEXT_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/proxy_helpers.cpp b/src/framework/ssf/layer/proxy/proxy_helpers.cpp new file mode 100644 index 00000000..0199c4af --- /dev/null +++ b/src/framework/ssf/layer/proxy/proxy_helpers.cpp @@ -0,0 +1,61 @@ +#include +#include + +#include "ssf/error/error.h" +#include "ssf/layer/proxy/proxy_helpers.h" +#include "ssf/log/log.h" +#include "ssf/utils/map_helpers.h" + +namespace ssf { +namespace layer { +namespace proxy { + +bool ValidateIPTarget(boost::asio::io_service& io_service, + const std::string& addr, const std::string& port) { + boost::system::error_code ec; + boost::asio::ip::tcp::resolver resolver(io_service); + boost::asio::ip::tcp::resolver::query query(addr, port); + resolver.resolve(query, ec); + + return ec.value() == 0; +} + +ProxyEndpointContext MakeProxyContext(boost::asio::io_service& io_service, + const LayerParameters& parameters, + boost::system::error_code& ec) { + ProxyEndpointContext context; + context.proxy_enabled = false; + auto http_host = ssf::helpers::GetField("http_host", parameters); + auto http_port = ssf::helpers::GetField("http_port", parameters); + if (http_port.empty() || http_host.empty()) { + return context; + } + + if (!ValidateIPTarget(io_service, http_host, http_port)) { + ec.assign(ssf::error::bad_address, ssf::error::get_ssf_category()); + SSF_LOG(kLogError) << "network[proxy]: could not resolve target address <" + << http_host << ":" << http_port << ">"; + return context; + } + + context.proxy_enabled = true; + context.http_proxy.host = http_host; + context.http_proxy.port = http_port; + context.http_proxy.username = + ssf::helpers::GetField("http_username", parameters); + context.http_proxy.domain = + ssf::helpers::GetField("http_domain", parameters); + context.http_proxy.password = + ssf::helpers::GetField("http_password", parameters); + context.http_proxy.reuse_ntlm = + (ssf::helpers::GetField("http_reuse_ntlm", parameters) == + "true"); + context.http_proxy.reuse_kerb = + (ssf::helpers::GetField("http_reuse_kerb", parameters) == + "true"); + return context; +} + +} // proxy +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/proxy_helpers.h b/src/framework/ssf/layer/proxy/proxy_helpers.h new file mode 100644 index 00000000..c60bf9a3 --- /dev/null +++ b/src/framework/ssf/layer/proxy/proxy_helpers.h @@ -0,0 +1,24 @@ +#ifndef SSF_LAYER_PROXY_PROXY_HELPERS_H_ +#define SSF_LAYER_PROXY_PROXY_HELPERS_H_ + +#include + +#include + +#include "ssf/layer/parameters.h" + +#include "ssf/layer/proxy/proxy_endpoint_context.h" + +namespace ssf { +namespace layer { +namespace proxy { + +ProxyEndpointContext MakeProxyContext(boost::asio::io_service& io_service, + const LayerParameters& parameters, + boost::system::error_code& ec); + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_PROXY_HELPERS_H_ diff --git a/src/framework/ssf/layer/proxy/unix/gssapi_auth_impl.cpp b/src/framework/ssf/layer/proxy/unix/gssapi_auth_impl.cpp new file mode 100644 index 00000000..2a560314 --- /dev/null +++ b/src/framework/ssf/layer/proxy/unix/gssapi_auth_impl.cpp @@ -0,0 +1,239 @@ +#include + +#include + +#include "ssf/log/log.h" +#include "ssf/layer/proxy/unix/gssapi_auth_impl.h" + +static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_VAL{ + 10, const_cast("\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04")}; +static gss_OID_desc GSS_C_NT_HOSTBASED_SERVICE_X_VAL{ + 6, const_cast("\x2b\x06\x01\x05\x06\x02")}; +static gss_OID_desc GSS_SPNEGO_MECH_OID_VAL{ + 6, const_cast("\x2b\x06\x01\x05\x05\x02")}; + +#undef GSS_C_NT_HOST_BASED_SERVICE +#undef GSS_C_NT_HOST_BASED_SERVICE_X + +gss_OID GSS_C_NT_HOSTBASED_SERVICE = &GSS_C_NT_HOSTBASED_SERVICE_VAL; +gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = &GSS_C_NT_HOSTBASED_SERVICE_X_VAL; +gss_OID GSS_SPNEGO_MECH_OID = &GSS_SPNEGO_MECH_OID_VAL; + +#define GSS_DL_SYM(lib, func) \ + fct_gss_##func##_t func = reinterpret_cast( \ + dlsym(lib, "gss_" #func)); \ + if (func == NULL) { \ + state_ = kFailure; \ + return false; \ + } + +namespace ssf { +namespace layer { +namespace proxy { + +GSSAPIAuthImpl::GSSAPIAuthImpl(const Proxy& proxy_ctx) + : PlatformAuthImpl(proxy_ctx), + h_gss_api_(NULL), + h_sec_ctx_(GSS_C_NO_CONTEXT), + server_name_(GSS_C_NO_NAME), + auth_token_(), + fct_gss_init_sec_context_(nullptr), + fct_gss_import_name_(nullptr), + fct_gss_release_buffer_(nullptr), + fct_gss_delete_sec_context_(nullptr), + fct_gss_release_name_(nullptr) {} + +GSSAPIAuthImpl::~GSSAPIAuthImpl() { + OM_uint32 minor_status; + + if (server_name_ != GSS_C_NO_NAME) { + fct_gss_release_name_(&minor_status, &server_name_); + } + + if (h_sec_ctx_ != GSS_C_NO_CONTEXT) { + fct_gss_delete_sec_context_(&minor_status, &h_sec_ctx_, GSS_C_NO_BUFFER); + } + + fct_gss_init_sec_context_ = nullptr; + fct_gss_import_name_ = nullptr; + fct_gss_release_buffer_ = nullptr; + fct_gss_delete_sec_context_ = nullptr; + fct_gss_release_name_ = nullptr; + + if (h_gss_api_ != NULL) { + dlclose(h_gss_api_); + h_gss_api_ = NULL; + } +} + +bool GSSAPIAuthImpl::Init() { + if (!InitLibrary()) { + SSF_LOG(kLogDebug) << "network[proxy]: gssapi: " + << "could not init gssapi library"; + state_ = kFailure; + return false; + } + + std::string service_name = "HTTP@" + proxy_ctx_.host; + OM_uint32 minor_status; + gss_buffer_desc spn_name; + spn_name.length = service_name.size(); + spn_name.value = const_cast(service_name.c_str()); + server_name_ = GSS_C_NO_NAME; + if (GSS_S_COMPLETE != fct_gss_import_name_(&minor_status, &spn_name, + GSS_C_NT_HOSTBASED_SERVICE, + &server_name_)) { + SSF_LOG(kLogDebug) << "network[proxy]: gssapi: " + << "could not generate gssapi server name"; + state_ = kFailure; + return false; + } + + return true; +} + +bool GSSAPIAuthImpl::ProcessServerToken(const Token& server_token) { + if (state_ == kFailure) { + return false; + } + + OM_uint32 minor_status; + OM_uint32 req_flags = 0; + + gss_buffer_desc output_token; + + gss_buffer_desc input_token; + input_token.length = server_token.size(); + input_token.value = const_cast(server_token.data()); + + if (state_ == State::kInit && server_token.empty()) { + // initialize context + state_ = State::kContinue; + } + + OM_uint32 maj_status = fct_gss_init_sec_context_( + &minor_status, GSS_C_NO_CREDENTIAL, &h_sec_ctx_, server_name_, + GSS_SPNEGO_MECH_OID, req_flags, GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, &input_token, nullptr, &output_token, 0, + nullptr); + + if (GSS_ERROR(maj_status)) { + LogError(maj_status); + state_ = kFailure; + return false; + } + + auth_token_.resize(output_token.length); + std::copy(static_cast(output_token.value), + static_cast(output_token.value) + output_token.length, + auth_token_.begin()); + + fct_gss_release_buffer_(&minor_status, &output_token); + + return true; +} + +GSSAPIAuthImpl::Token GSSAPIAuthImpl::GetAuthToken() { + if (state_ == kFailure) { + return {}; + } + + return auth_token_; +} + +bool GSSAPIAuthImpl::InitLibrary() { + std::list lib_names; +#if defined(__APPLE__) + lib_names.emplace_back( + "/System/Library/Frameworks/Kerberos.framework/Kerberos"); +#else + lib_names.emplace_back("libgssapi.so"); + lib_names.emplace_back("libgssapi_krb5.so.2"); + lib_names.emplace_back("libgssapi_krb5.so.4"); + lib_names.emplace_back("libgssapi.so.2"); + lib_names.emplace_back("libgssapi.so.1"); +#endif + for (const auto& lib_name : lib_names) { + h_gss_api_ = dlopen(lib_name.c_str(), RTLD_LAZY); + if (h_gss_api_ != NULL) { + break; + } + } + + if (h_gss_api_ == NULL) { + state_ = kFailure; + return false; + } + + GSS_DL_SYM(h_gss_api_, init_sec_context); + GSS_DL_SYM(h_gss_api_, import_name); + GSS_DL_SYM(h_gss_api_, release_buffer); + GSS_DL_SYM(h_gss_api_, delete_sec_context); + GSS_DL_SYM(h_gss_api_, release_name); + + fct_gss_init_sec_context_ = init_sec_context; + fct_gss_import_name_ = import_name; + fct_gss_release_buffer_ = release_buffer; + fct_gss_delete_sec_context_ = delete_sec_context; + fct_gss_release_name_ = release_name; + + return true; +} + +void GSSAPIAuthImpl::LogError(OM_uint32 major_status) { + if (major_status == GSS_S_COMPLETE || major_status == GSS_S_CONTINUE_NEEDED) { + return; + } + + std::string error_msg; + + if (GSS_CALLING_ERROR(major_status)) { + SSF_LOG(kLogDebug) << "network[proxy]: gssapi: calling error"; + return; + } + + auto routine_status = GSS_ROUTINE_ERROR(major_status); + switch (routine_status) { + case GSS_S_DEFECTIVE_TOKEN: + error_msg = "defective token"; + break; + case GSS_S_DEFECTIVE_CREDENTIAL: + error_msg = "defective credential"; + break; + case GSS_S_BAD_SIG: + error_msg = "bad sig"; + break; + case GSS_S_NO_CRED: + error_msg = "no cred"; + break; + case GSS_S_CREDENTIALS_EXPIRED: + error_msg = "credentials expired"; + break; + case GSS_S_BAD_BINDINGS: + error_msg = "bad bindings"; + break; + case GSS_S_NO_CONTEXT: + error_msg = "no context"; + break; + case GSS_S_BAD_NAMETYPE: + error_msg = "bad nametype"; + break; + case GSS_S_BAD_NAME: + error_msg = "bad name"; + break; + case GSS_S_BAD_MECH: + error_msg = "bad mech"; + break; + case GSS_S_FAILURE: + error_msg = "failure"; + break; + default: + error_msg = "unknown error"; + break; + } + SSF_LOG(kLogDebug) << "network[proxy]: gssapi: " << error_msg; +} + +} // proxy +} // layer +} // ssf diff --git a/src/framework/ssf/layer/proxy/unix/gssapi_auth_impl.h b/src/framework/ssf/layer/proxy/unix/gssapi_auth_impl.h new file mode 100644 index 00000000..b8b0abc4 --- /dev/null +++ b/src/framework/ssf/layer/proxy/unix/gssapi_auth_impl.h @@ -0,0 +1,62 @@ +#ifndef SSF_LAYER_PROXY_UNIX_GSSAPI_AUTH_IMPL_H_ +#define SSF_LAYER_PROXY_UNIX_GSSAPI_AUTH_IMPL_H_ + +#if defined(__APPLE__) +#include +#if defined(MAC_OS_X_VERSION_10_9) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 +#define GSSKRB_APPLE_DEPRECATED(x) +#endif +#endif + +#if defined(__APPLE__) +#include +#else +#include +#endif + +#include "ssf/layer/proxy/platform_auth_impl.h" + +namespace ssf { +namespace layer { +namespace proxy { + +class GSSAPIAuthImpl : public PlatformAuthImpl { + private: + using fct_gss_init_sec_context_t = decltype(&gss_init_sec_context); + using fct_gss_import_name_t = decltype(&gss_import_name); + using fct_gss_release_buffer_t = decltype(&gss_release_buffer); + using fct_gss_delete_sec_context_t = decltype(&gss_delete_sec_context); + using fct_gss_release_name_t = decltype(&gss_release_name); + + public: + GSSAPIAuthImpl(const Proxy& proxy_ctx); + + virtual ~GSSAPIAuthImpl(); + + bool Init() override; + bool ProcessServerToken(const Token& server_token) override; + Token GetAuthToken() override; + + private: + bool InitLibrary(); + void LogError(OM_uint32 major_status); + + private: + void* h_gss_api_; + gss_ctx_id_t h_sec_ctx_; + gss_name_t server_name_; + Token auth_token_; + + fct_gss_init_sec_context_t fct_gss_init_sec_context_; + fct_gss_import_name_t fct_gss_import_name_; + fct_gss_release_buffer_t fct_gss_release_buffer_; + fct_gss_delete_sec_context_t fct_gss_delete_sec_context_; + fct_gss_release_name_t fct_gss_release_name_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_UNIX_GSSAPI_AUTH_IMPL_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/proxy/windows/sspi_auth_impl.cpp b/src/framework/ssf/layer/proxy/windows/sspi_auth_impl.cpp new file mode 100644 index 00000000..fd9573ae --- /dev/null +++ b/src/framework/ssf/layer/proxy/windows/sspi_auth_impl.cpp @@ -0,0 +1,196 @@ +#include + +#include "ssf/layer/proxy/windows/sspi_auth_impl.h" +#include "ssf/log/log.h" + +namespace ssf { +namespace layer { +namespace proxy { + +SSPIAuthImpl::SecPackageNames SSPIAuthImpl::sec_package_names_ = {"NTLM", + "Negotiate"}; + +SSPIAuthImpl::SSPIAuthImpl(SecurityPackage sec_package, const Proxy& proxy_ctx) + : PlatformAuthImpl(proxy_ctx), + sec_package_(sec_package), + h_cred_(), + h_sec_ctx_(), + output_token_(), + output_token_length_(0), + service_name_() { + memset(&h_sec_ctx_, 0, sizeof(CtxtHandle)); + memset(&h_cred_, 0, sizeof(CredHandle)); +} + +SSPIAuthImpl::~SSPIAuthImpl() { Clear(); } + +void SSPIAuthImpl::Clear() { + if (IsSecurityContextSet()) { + ::DeleteSecurityContext(&h_sec_ctx_); + memset(&h_sec_ctx_, 0, sizeof(CtxtHandle)); + } + if (h_cred_.dwLower != 0 || h_cred_.dwUpper != 0) { + ::FreeCredentialsHandle(&h_cred_); + memset(&h_cred_, 0, sizeof(CredHandle)); + } +} + +bool SSPIAuthImpl::Init() { + // determine max output token buffer size + PSecPkgInfoA p_win_sec_package; + TimeStamp expiry; + + auto status = ::QuerySecurityPackageInfoA( + const_cast(GenerateSecurityPackageName(sec_package_).c_str()), + &p_win_sec_package); + if (status != SEC_E_OK) { + SSF_LOG(kLogDebug) << "network[proxy]: sspi[" + << sec_package_names_[sec_package_] + << "]: could not query security package"; + state_ = State::kFailure; + return false; + } + + output_token_.resize(p_win_sec_package->cbMaxToken); + service_name_ = GenerateServiceName(sec_package_); + + SEC_WINNT_AUTH_IDENTITY_A identity; + memset(&identity, 0, sizeof(SEC_WINNT_AUTH_IDENTITY_A)); + bool use_identity = false; + if ((SecurityPackage::kNTLM == sec_package_ && !proxy_ctx_.reuse_ntlm) || + (SecurityPackage::kNegotiate == sec_package_ && !proxy_ctx_.reuse_kerb)) { + use_identity = true; + identity.Domain = reinterpret_cast( + const_cast(proxy_ctx_.domain.c_str())); + identity.DomainLength = proxy_ctx_.domain.size(); + identity.User = reinterpret_cast( + const_cast(proxy_ctx_.username.c_str())); + identity.UserLength = proxy_ctx_.username.size(); + identity.Password = reinterpret_cast( + const_cast(proxy_ctx_.password.c_str())); + identity.PasswordLength = proxy_ctx_.password.size(); + identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; + } + + auto cred_status = ::AcquireCredentialsHandleA( + NULL, p_win_sec_package->Name, SECPKG_CRED_OUTBOUND, NULL, + (use_identity ? &identity : NULL), NULL, NULL, &h_cred_, &expiry); + + ::FreeContextBuffer(p_win_sec_package); + + if (cred_status != SEC_E_OK) { + SSF_LOG(kLogDebug) << "network[proxy]: sspi[" + << sec_package_names_[sec_package_] + << "]: could not acquire credentials"; + state_ = State::kFailure; + Clear(); + return false; + } + + return true; +} + +bool SSPIAuthImpl::ProcessServerToken(const Token& server_token) { + TimeStamp expiry; + SecBufferDesc in_sec_buf_desc; + SecBuffer in_sec_buff; + SecBufferDesc out_sec_buff_desc; + SecBuffer out_sec_buff; + + out_sec_buff_desc.ulVersion = SECBUFFER_VERSION; + out_sec_buff_desc.cBuffers = 1; + out_sec_buff_desc.pBuffers = &out_sec_buff; + out_sec_buff.BufferType = SECBUFFER_TOKEN; + out_sec_buff.pvBuffer = output_token_.data(); + out_sec_buff.cbBuffer = output_token_.size(); + + // input token + in_sec_buf_desc.ulVersion = SECBUFFER_VERSION; + in_sec_buf_desc.cBuffers = 1; + in_sec_buf_desc.pBuffers = &in_sec_buff; + in_sec_buff.BufferType = SECBUFFER_TOKEN; + in_sec_buff.pvBuffer = const_cast(server_token.data()); + in_sec_buff.cbBuffer = server_token.size(); + + // update security context + unsigned long attrs; + + auto status = ::InitializeSecurityContextA( + &h_cred_, (IsSecurityContextSet() ? &h_sec_ctx_ : NULL), + const_cast(service_name_.c_str()), 0, 0, SECURITY_NATIVE_DREP, + server_token.empty() ? NULL : &in_sec_buf_desc, 0, &h_sec_ctx_, + &out_sec_buff_desc, &attrs, &expiry); + + switch (status) { + case SEC_E_OK: + state_ = State::kSuccess; + break; + case SEC_I_COMPLETE_AND_CONTINUE: + case SEC_I_COMPLETE_NEEDED: { + auto complete_status = + ::CompleteAuthToken(&h_sec_ctx_, &out_sec_buff_desc); + state_ = complete_status == SEC_E_OK ? State::kContinue : State::kFailure; + break; + } + case SEC_I_CONTINUE_NEEDED: + state_ = State::kContinue; + break; + default: + SSF_LOG(kLogDebug) << "network[proxy]: sspi[" + << sec_package_names_[sec_package_] + << "]: error initializing security context"; + state_ = State::kFailure; + } + + output_token_length_ = out_sec_buff.cbBuffer; + + return state_ != kFailure; +} + +SSPIAuthImpl::Token SSPIAuthImpl::GetAuthToken() { + if (state_ == kFailure) { + return {}; + } + + auto begin_it = output_token_.begin(); + auto end_it = begin_it + output_token_length_; + + return Token(begin_it, end_it); +} + +std::string SSPIAuthImpl::GenerateSecurityPackageName( + SecurityPackage sec_package) { + switch (sec_package) { + case kNTLM: + case kNegotiate: + return sec_package_names_[sec_package]; + default: + return ""; + } +} + +bool SSPIAuthImpl::IsSecurityContextSet() { + return h_sec_ctx_.dwLower != 0 || h_sec_ctx_.dwUpper != 0; +} + +std::string SSPIAuthImpl::GenerateServiceName(SecurityPackage sec_package) { + std::stringstream ss_name; + ss_name << "HTTP/"; + + switch (sec_package) { + case kNTLM: + ss_name << proxy_ctx_.host << ":" << proxy_ctx_.port; + break; + case kNegotiate: + ss_name << proxy_ctx_.host; + break; + default: + break; + } + + return ss_name.str(); +} + +} // proxy +} // layer +} // ssf diff --git a/src/framework/ssf/layer/proxy/windows/sspi_auth_impl.h b/src/framework/ssf/layer/proxy/windows/sspi_auth_impl.h new file mode 100644 index 00000000..358b684b --- /dev/null +++ b/src/framework/ssf/layer/proxy/windows/sspi_auth_impl.h @@ -0,0 +1,53 @@ +#ifndef SSF_LAYER_PROXY_WINDOWS_SSPI_AUTH_IMPL_H_ +#define SSF_LAYER_PROXY_WINDOWS_SSPI_AUTH_IMPL_H_ + +#include +#include + +#include "ssf/layer/proxy/platform_auth_impl.h" + +#define SECURITY_WIN32 +#include +#include + +namespace ssf { +namespace layer { +namespace proxy { + +class SSPIAuthImpl : public PlatformAuthImpl { + private: + using SecPackageNames = std::array; + + public: + enum SecurityPackage { kNTLM = 0, kNegotiate }; + + public: + SSPIAuthImpl(SecurityPackage sec_package, const Proxy& proxy_ctx); + + virtual ~SSPIAuthImpl(); + + bool Init() override; + bool ProcessServerToken(const Token& server_token) override; + Token GetAuthToken() override; + + private: + static std::string GenerateSecurityPackageName(SecurityPackage sec_package); + bool IsSecurityContextSet(); + std::string GenerateServiceName(SecurityPackage sec_package); + void Clear(); + + private: + static SecPackageNames sec_package_names_; + SecurityPackage sec_package_; + CredHandle h_cred_; + CtxtHandle h_sec_ctx_; + Token output_token_; + std::size_t output_token_length_; + std::string service_name_; +}; + +} // proxy +} // layer +} // ssf + +#endif // SSF_LAYER_PROXY_WINDOWS_SSPI_AUTH_IMPL_H_ \ No newline at end of file diff --git a/src/framework/ssf/layer/queue/active_item.h b/src/framework/ssf/layer/queue/active_item.h new file mode 100644 index 00000000..974754bb --- /dev/null +++ b/src/framework/ssf/layer/queue/active_item.h @@ -0,0 +1,49 @@ +#ifndef SSF_LAYER_QUEUE_ACTIVE_ITEM_H_ +#define SSF_LAYER_QUEUE_ACTIVE_ITEM_H_ + +#include + +namespace ssf { +namespace layer { +namespace queue { + +template +class ActiveItem { + public: + ActiveItem(Item item) : active_(true), item_(std::move(item)) {} + + bool IsActive() const { return active_; } + + void Disactivate() { active_ = false; } + + const Item& item() const { return item_; } + Item& item() { return item_; } + + private: + bool active_; + Item item_; +}; + +template +using ActiveItemPtr = std::shared_ptr>; + +template +ActiveItem::type> make_active( + Item&& item) { + return ActiveItem::type>( + std::forward(item)); +} + +template +ActiveItemPtr::type> make_shared_active( + Item&& item) { + return std::make_shared< + ActiveItem::type>>( + std::forward); +} + +} // queue +} // layer +} // ssf + +#endif // SSF_LAYER_QUEUE_ACTIVE_ITEM_H_ diff --git a/src/framework/ssf/layer/queue/async_queue.h b/src/framework/ssf/layer/queue/async_queue.h new file mode 100644 index 00000000..3e57d606 --- /dev/null +++ b/src/framework/ssf/layer/queue/async_queue.h @@ -0,0 +1,95 @@ +#ifndef SSF_LAYER_QUEUE_ASYNC_QUEUE_H_ +#define SSF_LAYER_QUEUE_ASYNC_QUEUE_H_ + +#include + +#include + +#include +#include +#include + +#include + +#include "ssf/layer/queue/async_queue_service.h" + +namespace ssf { +namespace layer { +namespace queue { + +template , + uint32_t QueueMaxSize = boost::integer_traits::const_max, + uint32_t OPQueueMaxSize = boost::integer_traits::const_max, + class Service = basic_async_queue_service> +class basic_async_queue : public boost::asio::basic_io_object { + private: + typedef Ttype T; + + public: + typedef typename Service::value_type value_type; + typedef typename Service::container_type container_type; + enum { + kQueueMaxSize = Service::kQueueMaxSize, + kOPQueueMaxSize = Service::kOPQueueMaxSize + }; + + public: + basic_async_queue(boost::asio::io_service& io_service) + : boost::asio::basic_io_object(io_service) {} + + basic_async_queue(const basic_async_queue&) = delete; + basic_async_queue& operator=(const basic_async_queue&) = delete; + + basic_async_queue(basic_async_queue&& other) + : boost::asio::basic_io_object(std::move(other)) {} + + basic_async_queue& operator=(basic_async_queue&& other){ + boost::asio::basic_io_object::operator=(std::move(other)); + return *this; + } + + ~basic_async_queue() {} + + boost::system::error_code push(T element, boost::system::error_code& ec) { + return this->get_service().push(this->implementation, + std::move(element), ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(boost::system::error_code)) + async_push(T element, Handler&& handler) { + return this->get_service().async_push(this->implementation, + std::move(element), + std::forward(handler)); + } + + T get(boost::system::error_code& ec) { + return this->get_service().get(this->implementation, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(boost::system::error_code, + T)) async_get(Handler&& handler) { + return this->get_service().async_get(this->implementation, + std::forward(handler)); + } + + bool empty() const { return this->get_service().empty(this->implementation); } + + std::size_t size() const { + return this->get_service().size(this->implementation); + } + + void clear() { return this->get_service().clear(this->implementation); } + + boost::system::error_code close(boost::system::error_code& ec) { + return this->get_service().close(this->implementation, ec); + } +}; + +} // queue +} // layer +} // ssf + +#endif // SSF_LAYER_QUEUE_ASYNC_QUEUE_H_ diff --git a/src/framework/ssf/layer/queue/async_queue_service.h b/src/framework/ssf/layer/queue/async_queue_service.h new file mode 100644 index 00000000..e100c064 --- /dev/null +++ b/src/framework/ssf/layer/queue/async_queue_service.h @@ -0,0 +1,431 @@ +#ifndef SSF_LAYER_QUEUE_ASYNC_QUEUE_SERVICE_H_ +#define SSF_LAYER_QUEUE_ASYNC_QUEUE_SERVICE_H_ + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include "ssf/error/error.h" + +#include "ssf/io/get_op.h" +#include "ssf/io/push_op.h" +#include "ssf/io/handler_helpers.h" + +namespace ssf { +namespace layer { +namespace queue { + +template +class basic_async_queue_service + : public boost::asio::detail::service_base> { + private: + typedef Ttype T; + typedef TContainer Container; + + public: + typedef T value_type; + typedef Container container_type; + enum { kQueueMaxSize = QueueMaxSize, kOPQueueMaxSize = OPQueueMaxSize }; + + struct implementation_type { + std::shared_ptr> p_valid; + std::shared_ptr> p_open; + + mutable std::unique_ptr p_container_mutex; + Container container; + + mutable std::unique_ptr p_get_op_queue_mutex; + std::unique_ptr>> p_get_op_queue; + uint32_t get_op_queue_size; + std::unique_ptr p_get_work; + + mutable std::unique_ptr p_push_op_queue_mutex; + std::unique_ptr>> p_push_op_queue; + uint32_t push_op_queue_size; + std::unique_ptr p_push_work; + }; + + public: + explicit basic_async_queue_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service) {} + + virtual ~basic_async_queue_service() {} + + void construct(implementation_type& impl) { + impl.p_valid = std::make_shared>(true); + impl.p_open = std::make_shared>(true); + impl.p_container_mutex = + std::unique_ptr(new boost::recursive_mutex()); + impl.p_get_op_queue_mutex = + std::unique_ptr(new boost::recursive_mutex()); + impl.p_push_op_queue_mutex = + std::unique_ptr(new boost::recursive_mutex()); + impl.p_get_op_queue = std::unique_ptr< + boost::asio::detail::op_queue>>( + new boost::asio::detail::op_queue< + io::basic_pending_get_operation>()); + impl.p_push_op_queue = std::unique_ptr< + boost::asio::detail::op_queue>>( + new boost::asio::detail::op_queue< + io::basic_pending_push_operation>()); + impl.push_op_queue_size = 0; + impl.get_op_queue_size = 0; + } + + void destroy(implementation_type& impl) { + *impl.p_valid = false; + *impl.p_open = false; + + { + boost::recursive_mutex::scoped_lock lock1(*impl.p_container_mutex); + while (!impl.container.empty()) { + impl.container.pop(); + } + } + { + boost::recursive_mutex::scoped_lock lock2(*impl.p_push_op_queue_mutex); + while (!impl.p_push_op_queue->empty()) { + impl.p_push_op_queue->pop(); + } + impl.p_push_op_queue.reset(); + impl.push_op_queue_size = 0; + impl.p_push_work.reset(); + } + { + boost::recursive_mutex::scoped_lock lock3(*impl.p_get_op_queue_mutex); + while (!impl.p_get_op_queue->empty()) { + impl.p_get_op_queue->pop(); + } + impl.p_get_op_queue.reset(); + impl.get_op_queue_size = 0; + impl.p_get_work.reset(); + } + + impl.p_container_mutex.reset(); + impl.p_get_op_queue_mutex.reset(); + impl.p_push_op_queue_mutex.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, + basic_async_queue_service& other_service, + implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code push(implementation_type& impl, T element, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(*impl.p_container_mutex); + + if (!*impl.p_open) { + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return ec; + } + + if (impl.container.size() >= QueueMaxSize) { + ec.assign(ssf::error::buffer_is_full_error, + ssf::error::get_ssf_category()); + return ec; + } + + impl.container.push(std::move(element)); + + this->get_io_service().post( + boost::bind(&basic_async_queue_service::HandleGetQueues, this, &impl, + impl.p_valid)); + + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(boost::system::error_code)) + async_push(implementation_type& impl, T element, Handler&& handler) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + if (!*impl.p_open) { + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::broken_pipe, + ssf::error::get_ssf_category())); + + return init.result.get(); + } + + if (impl.push_op_queue_size >= OPQueueMaxSize) { + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::buffer_is_full_error, + ssf::error::get_ssf_category())); + + return init.result.get(); + } + + typedef io::pending_push_operation< + typename ::boost::asio::handler_type< + Handler, void(boost::system::error_code)>::type, + T> op; + typename op::ptr p = { + boost::asio::detail::addressof(init.handler), + boost_asio_handler_alloc_helpers::allocate(sizeof(op), init.handler), + 0}; + p.p = new (p.v) op(init.handler, std::move(element)); + + { + boost::recursive_mutex::scoped_lock lock(*impl.p_push_op_queue_mutex); + + impl.p_push_op_queue->push(p.p); + ++(impl.push_op_queue_size); + if (!impl.p_push_work) { + impl.p_push_work = std::unique_ptr( + new boost::asio::io_service::work(this->get_io_service())); + } + } + + p.v = p.p = 0; + + this->get_io_service().post( + boost::bind(&basic_async_queue_service::HandlePushQueues, this, &impl, + impl.p_valid)); + + return init.result.get(); + } + + T get(implementation_type& impl, boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(*impl.p_container_mutex); + + if (!*impl.p_open) { + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return T(); + } + + if (impl.container.empty()) { + ec.assign(ssf::error::io_error, ssf::error::get_ssf_category()); + return T(); + } + + auto element = std::move(impl.container.front()); + impl.container.pop(); + + this->get_io_service().post( + boost::bind(&basic_async_queue_service::HandlePushQueues, this, &impl, + impl.p_valid)); + + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return element; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(boost::system::error_code, T)) + async_get(implementation_type& impl, Handler&& handler) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + if (!*impl.p_open) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::broken_pipe, + ssf::error::get_ssf_category()), + T()); + + return init.result.get(); + } + + if ((impl.get_op_queue_size) >= OPQueueMaxSize) { + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::buffer_is_full_error, + ssf::error::get_ssf_category()), + T()); + + return init.result.get(); + } + + typedef io::pending_get_operation< + typename ::boost::asio::handler_type< + Handler, void(boost::system::error_code, T)>::type, + T> op; + typename op::ptr p = { + boost::asio::detail::addressof(init.handler), + boost_asio_handler_alloc_helpers::allocate(sizeof(op), init.handler), + 0}; + p.p = new (p.v) op(init.handler); + + { + boost::recursive_mutex::scoped_lock lock(*impl.p_get_op_queue_mutex); + + impl.p_get_op_queue->push(p.p); + ++(impl.get_op_queue_size); + if (!impl.p_get_work) { + impl.p_get_work = std::unique_ptr( + new boost::asio::io_service::work(this->get_io_service())); + } + } + + p.v = p.p = 0; + + this->get_io_service().post( + boost::bind(&basic_async_queue_service::HandleGetQueues, this, &impl, + impl.p_valid)); + + return init.result.get(); + } + + bool empty(const implementation_type& impl) const { + boost::recursive_mutex::scoped_lock lock(*impl.p_container_mutex); + return impl.container.empty(); + } + + std::size_t size(const implementation_type& impl) const { + boost::recursive_mutex::scoped_lock lock(*impl.p_container_mutex); + return impl.container.size(); + } + + void clear(implementation_type& impl) { + boost::recursive_mutex::scoped_lock lock(*impl.p_container_mutex); + while (!impl.container.empty()) { + impl.container.pop(); + } + + HandlePushQueues(&impl, impl.p_valid); + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + *impl.p_open = false; + + { + boost::recursive_mutex::scoped_lock lock1(*impl.p_get_op_queue_mutex); + while (!impl.p_get_op_queue->empty()) { + auto op = impl.p_get_op_queue->front(); + impl.p_get_op_queue->pop(); + --(impl.get_op_queue_size); + + op->complete(boost::system::error_code(ssf::error::operation_canceled, + ssf::error::get_ssf_category()), + T()); + } + + impl.p_get_work.reset(); + } + + { + boost::recursive_mutex::scoped_lock lock1(*impl.p_push_op_queue_mutex); + while (!impl.p_push_op_queue->empty()) { + auto op = impl.p_push_op_queue->front(); + impl.p_push_op_queue->pop(); + --(impl.push_op_queue_size); + + op->complete(boost::system::error_code(ssf::error::operation_canceled, + ssf::error::get_ssf_category())); + } + + impl.p_push_work.reset(); + } + + clear(impl); + + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return ec; + } + + private: + void HandlePushQueues(implementation_type* p_impl, + std::shared_ptr> p_valid) { + if (!*p_valid) { + return; + } + + boost::recursive_mutex::scoped_lock lock1(*p_impl->p_container_mutex); + boost::recursive_mutex::scoped_lock lock2(*p_impl->p_push_op_queue_mutex); + + if (!*p_impl->p_open) { + return; + } + + if ((p_impl->container.size() >= QueueMaxSize) || + p_impl->p_push_op_queue->empty()) { + return; + } + + auto op = std::move(p_impl->p_push_op_queue->front()); + p_impl->p_push_op_queue->pop(); + --(p_impl->push_op_queue_size); + + auto element = op->element(); + p_impl->container.push(std::move(element)); + + auto do_complete = + [op]() mutable { op->complete(boost::system::error_code()); }; + this->get_io_service().post(do_complete); + + if (p_impl->p_push_op_queue->empty()) { + p_impl->p_push_work.reset(); + } + + HandleGetQueues(p_impl, p_valid); + } + + void HandleGetQueues(implementation_type* p_impl, + std::shared_ptr> p_valid) { + if (!*p_valid) { + return; + } + + boost::recursive_mutex::scoped_lock lock1(*p_impl->p_container_mutex); + boost::recursive_mutex::scoped_lock lock2(*p_impl->p_get_op_queue_mutex); + + if (!*p_impl->p_open) { + return; + } + + if (p_impl->container.empty() || p_impl->p_get_op_queue->empty()) { + HandlePushQueues(p_impl, p_valid); + return; + } + + auto element = std::move(p_impl->container.front()); + p_impl->container.pop(); + + auto op = std::move(p_impl->p_get_op_queue->front()); + p_impl->p_get_op_queue->pop(); + --(p_impl->get_op_queue_size); + + auto do_complete = [element, op]() mutable { + op->complete(boost::system::error_code(), std::move(element)); + }; + this->get_io_service().post(do_complete); + + if (p_impl->p_get_op_queue->empty()) { + p_impl->p_get_work.reset(); + } + + HandlePushQueues(p_impl, p_valid); + } + + void shutdown_service() {} +}; + +} // queue +} // layer +} // ssf + +#endif // SSF_LAYER_QUEUE_ASYNC_QUEUE_SERVICE_H_ diff --git a/src/framework/ssf/layer/queue/commutator.h b/src/framework/ssf/layer/queue/commutator.h new file mode 100644 index 00000000..b3231dfa --- /dev/null +++ b/src/framework/ssf/layer/queue/commutator.h @@ -0,0 +1,214 @@ +#ifndef SSF_LAYER_QUEUE_COMMUTATOR_H_ +#define SSF_LAYER_QUEUE_COMMUTATOR_H_ + +#include +#include +#include + +#include +#include +#include + +#include "ssf/error/error.h" + +#include "ssf/layer/queue/active_item.h" +#include "ssf/layer/queue/tagged_item.h" + +#include "ssf/log/log.h" + +namespace ssf { +namespace layer { +namespace queue { + +template +class Commutator : public std::enable_shared_from_this< + Commutator> { + public: + typedef TIdentifier Identifier; + typedef TElement Element; + typedef TSelector Selector; + + typedef TaggedItem TaggedElement; + + typedef std::function + InputHandler; + typedef std::function InputCallback; + typedef TaggedItemPtr> + TaggedActiveInputCallbackPtr; + + typedef std::function OutputHandler; + typedef std::function OutputCallback; + + typedef std::map InputCallbackMap; + typedef std::map OutputCallbackMap; + + public: + template + static std::shared_ptr Create(Args&&... args) { + return std::shared_ptr( + new Commutator(std::forward(args)...)); + } + + ~Commutator() {} + + bool RegisterInput(Identifier id, InputCallback input_callback) { + boost::recursive_mutex::scoped_lock lock(input_callbacks_mutex_); + + auto active_item = make_active(std::move(input_callback)); + auto p_tagged_active_item = make_shared_tagged(id, std::move(active_item)); + + auto inserted = input_callbacks_.insert( + std::make_pair(std::move(id), p_tagged_active_item)); + + StartInputLoop(std::move(p_tagged_active_item)); + + return inserted.second; + } + + bool UnregisterInput(const Identifier& id) { + boost::recursive_mutex::scoped_lock lock(input_callbacks_mutex_); + + auto p_tagged_active_item_it = input_callbacks_.find(id); + + if (p_tagged_active_item_it == std::end(input_callbacks_)) { + return false; + } + + p_tagged_active_item_it->second->item.Disactivate(); + input_callbacks_.erase(p_tagged_active_item_it); + + return true; + } + + bool RegisterOutput(Identifier id, OutputCallback output_callback) { + boost::recursive_mutex::scoped_lock lock(output_callbacks_mutex_); + + auto inserted = output_callbacks_.insert( + std::make_pair(std::move(id), std::move(output_callback))); + + return inserted.second; + } + + bool UnregisterOutput(const Identifier& id) { + boost::recursive_mutex::scoped_lock lock(output_callbacks_mutex_); + + auto p_output_it = output_callbacks_.find(id); + + if (p_output_it == std::end(output_callbacks_)) { + return false; + } + + output_callbacks_.erase(p_output_it); + + return true; + } + + boost::system::error_code close(boost::system::error_code& ec) { + { + boost::recursive_mutex::scoped_lock lock(input_callbacks_mutex_); + + for (auto& input_callback : input_callbacks_) { + input_callback.second->item.Disactivate(); + } + + input_callbacks_.clear(); + } + + { + boost::recursive_mutex::scoped_lock lock(output_callbacks_mutex_); + output_callbacks_.clear(); + } + + return ec; + } + + private: + Commutator(boost::asio::io_service& io_service, const Selector& selector) + : io_service_(io_service), + selector_(selector), + input_callbacks_mutex_(), + input_callbacks_(), + output_callbacks_mutex_(), + output_callbacks_() {} + + private: + void StartInputLoop(TaggedActiveInputCallbackPtr p_input_callback) { + p_input_callback->item.item()(boost::bind(&Commutator::InputReceived, + this->shared_from_this(), + p_input_callback, _1, _2)); + } + + void InputReceived(TaggedActiveInputCallbackPtr p_input_callback, + const boost::system::error_code& ec, Element element) { + if (ec) { + p_input_callback->item.Disactivate(); + SSF_LOG(kLogTrace) << " * Deactivate input received callback"; + // TODO : erase from map? + // continue routing when socket is disconnected ? + + return; + } + + AsyncCommute( + p_input_callback->tag, std::move(element), + boost::bind(&Commutator::OutputSent, this->shared_from_this(), _1)); + + StartInputLoop(std::move(p_input_callback)); + } + + void AsyncCommute(Identifier id, Element element, OutputHandler handler) { + auto tagged_element = make_tagged(std::move(id), std::move(element)); + + io_service_.post(boost::bind(&Commutator::Select, this->shared_from_this(), + std::move(tagged_element), + std::move(handler))); + } + + void Select(TaggedElement tagged_element, OutputHandler handler) { + auto selected = selector_(&tagged_element.tag, &tagged_element.item); + + if (!selected) { + handler(boost::system::error_code(ssf::error::not_connected, + ssf::error::get_ssf_category())); + return; + } + DoOutput(std::move(tagged_element), std::move(handler)); + } + + void DoOutput(TaggedElement tagged_element, OutputHandler handler) { + boost::recursive_mutex::scoped_lock lock(output_callbacks_mutex_); + + auto output_it = output_callbacks_.find(tagged_element.tag); + + if (output_it == std::end(output_callbacks_)) { + handler(boost::system::error_code(ssf::error::not_connected, + ssf::error::get_ssf_category())); + return; + } + + output_it->second(std::move(tagged_element.item), std::move(handler)); + } + + void OutputSent(const boost::system::error_code& ec) {} + + private: + boost::asio::io_service& io_service_; + + const Selector& selector_; + + boost::recursive_mutex input_callbacks_mutex_; + InputCallbackMap input_callbacks_; + + boost::recursive_mutex output_callbacks_mutex_; + OutputCallbackMap output_callbacks_; +}; + +template +using CommutatorPtr = + std::shared_ptr>; + +} // queue +} // layer +} // ssf + +#endif // SSF_LAYER_QUEUE_COMMUTATOR_H_ diff --git a/src/framework/ssf/layer/queue/send_queued_datagram_socket.h b/src/framework/ssf/layer/queue/send_queued_datagram_socket.h new file mode 100644 index 00000000..f1aa6417 --- /dev/null +++ b/src/framework/ssf/layer/queue/send_queued_datagram_socket.h @@ -0,0 +1,248 @@ +#ifndef SSF_LAYER_QUEUE_SEND_QUEUED_DATAGRAM_SOCKET_H_ +#define SSF_LAYER_QUEUE_SEND_QUEUED_DATAGRAM_SOCKET_H_ + +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "ssf/io/write_op.h" + +#include + +namespace ssf { +namespace layer { +namespace queue { + +template +class basic_send_queued_datagram_socket { + public: + typedef typename DatagramSocket::protocol_type protocol_type; + + typedef DatagramSocket type; + + /// The type of the next layer. + typedef DatagramSocket next_layer_type; + + /// The type of the lowest layer. + typedef typename DatagramSocket::lowest_layer_type lowest_layer_type; + + enum { kMaxQueueSize = max_queue_size }; + + private: + typedef boost::asio::detail::op_queue + OpQueue; + + public: + /// Construct, passing the specified argument to initialise the next layer. + template + explicit basic_send_queued_datagram_socket(Args&&... args) + : socket_(std::forward(args)...), + op_queue_mutex_(), + op_queue_(), + op_queue_size_(0), + popping_mutex_(), + popping_(false) {} + + basic_send_queued_datagram_socket(const basic_send_queued_datagram_socket&) = + delete; + + basic_send_queued_datagram_socket& operator=( + const basic_send_queued_datagram_socket&) = delete; + + /// Get a reference to the next layer. + next_layer_type& next_layer() { return socket_; } + + /// Get a reference to the lowest layer. + lowest_layer_type& lowest_layer() { return socket_.lowest_layer(); } + + /// Get a const reference to the lowest layer. + const lowest_layer_type& lowest_layer() const { + return socket_.lowest_layer(); + } + + /// Get the io_service associated with the object. + boost::asio::io_service& get_io_service() { return socket_.get_io_service(); } + + /// Close the stream. + void close() { socket_.close(); } + + /// Close the stream. + boost::system::error_code close(boost::system::error_code& ec) { + return socket_.close(ec); + } + + /// Write the given data to the stream. Returns the number of bytes written. + /// Throws an exception on failure. + template + std::size_t send(const ConstBufferSequence& buffers) { + boost::system::error_code ec; + auto length = send(buffers, ec); + boost::asio::detail::throw_error(ec, "write_some"); + return length; + } + + /// Write the given data to the stream. Returns the number of bytes written, + /// or 0 if an error occurred and the error handler did not throw. + template + std::size_t send(const ConstBufferSequence& buffers, + boost::system::error_code& ec) { + try { + ec.clear(); + auto future_value = async_send(buffers, boost::asio::use_future); + return future_value.get(); + } catch (const std::system_error& e) { + ec.assign(e.code().value(), ssf::error::get_ssf_category()); + return 0; + } + } + + /// Start an asynchronous write. The data being written must be valid for the + /// lifetime of the asynchronous operation. + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(const ConstBufferSequence& buffers, WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (op_queue_size_.load() > kMaxQueueSize) { + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::buffer_is_full_error, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + typedef io::pending_write_operation op; + typename op::ptr p = { + boost::asio::detail::addressof(init.handler), + boost_asio_handler_alloc_helpers::allocate(sizeof(op), init.handler), + 0}; + + p.p = new (p.v) op(buffers, init.handler); + + { + boost::recursive_mutex::scoped_lock lock(op_queue_mutex_); + ++op_queue_size_; + op_queue_.push(p.p); + } + + p.v = p.p = 0; + + StartPopping(); + + return init.result.get(); + } + + /// Read some data from the stream. Returns the number of bytes read. Throws + /// an exception on failure. + template + std::size_t receive(const MutableBufferSequence& buffers) { + return socket_.receive(buffers); + } + + /// Read some data from the stream. Returns the number of bytes read or 0 if + /// an error occurred. + template + std::size_t receive(const MutableBufferSequence& buffers, + boost::system::error_code& ec) { + return socket_.receive(buffers, ec); + } + + /// Start an asynchronous read. The buffer into which the data will be read + /// must be valid for the lifetime of the asynchronous operation. + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(const MutableBufferSequence& buffers, + ReadHandler&& handler) { + return socket_.async_receive(buffers, std::forward(handler)); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read. + /// Throws an exception on failure. + template + std::size_t peek(const MutableBufferSequence& buffers) { + return socket_.peek(buffers); + } + + /// Peek at the incoming data on the stream. Returns the number of bytes read, + /// or 0 if an error occurred. + template + std::size_t peek(const MutableBufferSequence& buffers, + boost::system::error_code& ec) { + return socket_.peek(buffers, ec); + } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail() { return socket_.in_avail(); } + + /// Determine the amount of data that may be read without blocking. + std::size_t in_avail(boost::system::error_code& ec) { + return socket_.in_avail(ec); + } + + private: + void StartPopping() { + { + boost::recursive_mutex::scoped_lock lock(popping_mutex_); + if (popping_) { + return; + } + + popping_ = true; + } + + Pop(); + } + + void Pop() { + boost::recursive_mutex::scoped_lock lock(op_queue_mutex_); + + if (op_queue_.empty()) { + popping_ = false; + return; + } + + auto op = op_queue_.front(); + --op_queue_size_; + op_queue_.pop(); + + // TODO : Change lambda to specific handler + auto complete_lambda = + [op, this](const boost::system::error_code& ec, std::size_t length) { + this->socket_.get_io_service().post([this]() { this->Pop(); }); + this->socket_.get_io_service().post( + [op, ec, length]() { op->complete(ec, length); }); + }; + + socket_.async_send(op->const_buffers(), std::move(complete_lambda)); + } + + private: + DatagramSocket socket_; + boost::recursive_mutex op_queue_mutex_; + OpQueue op_queue_; + std::atomic op_queue_size_; + boost::recursive_mutex popping_mutex_; + bool popping_; +}; + +} // queue +} // layer +} // ssf + +#include + +#endif // SSF_LAYER_QUEUE_SEND_QUEUED_DATAGRAM_SOCKET_H_ diff --git a/src/framework/ssf/layer/queue/tagged_item.h b/src/framework/ssf/layer/queue/tagged_item.h new file mode 100644 index 00000000..5155cf5c --- /dev/null +++ b/src/framework/ssf/layer/queue/tagged_item.h @@ -0,0 +1,44 @@ +#ifndef SSF_LAYER_QUEUE_TAGGED_ITEM_H_ +#define SSF_LAYER_QUEUE_TAGGED_ITEM_H_ + +#include + +namespace ssf { +namespace layer { +namespace queue { + +template +struct TaggedItem { + TaggedItem(Tag t, Item i) : tag(std::move(t)), item(std::move(i)) {} + + Tag tag; + Item item; +}; + +template +using TaggedItemPtr = std::shared_ptr>; + +template +TaggedItem::type, + typename std::remove_reference::type> +make_tagged(Tag&& tag, Item&& item) { + return TaggedItem::type, + typename std::remove_reference::type>{ + std::forward(tag), std::forward(item)}; +} + +template +TaggedItemPtr::type, + typename std::remove_reference::type> +make_shared_tagged(Tag&& tag, Item&& item) { + return std::make_shared< + TaggedItem::type, + typename std::remove_reference::type>>( + std::forward(tag), std::forward(item)); +} + +} // queue +} // layer +} // ssf + +#endif // SSF_LAYER_QUEUE_TAGGED_ITEM_H_ diff --git a/src/framework/ssf/layer/receive_from_op.h b/src/framework/ssf/layer/receive_from_op.h new file mode 100644 index 00000000..286b685a --- /dev/null +++ b/src/framework/ssf/layer/receive_from_op.h @@ -0,0 +1,134 @@ +#ifndef SSF_LAYER_RECEIVE_FROM_OP_H_ +#define SSF_LAYER_RECEIVE_FROM_OP_H_ + +#include + +#include + +#include + +#include +#include +#include + +#include + +namespace ssf { +namespace layer { +namespace detail { + +template +class ReceiveFromOp { + public: + ReceiveFromOp(Socket& socket, Endpoint* p_remote_endpoint, + MutableBufferSequence buffers, + boost::asio::socket_base::message_flags flags, + ReadHandler handler) + : coro_(), + socket_(socket), + p_remote_endpoint_(p_remote_endpoint), + buffers_(std::move(buffers)), + flags_(std::move(flags)), + handler_(std::move(handler)) {} + + ReceiveFromOp(const ReceiveFromOp& other) + : coro_(other.coro_), + socket_(other.socket_), + p_remote_endpoint_(other.p_remote_endpoint_), + buffers_(other.buffers_), + flags_(other.flags_), + handler_(other.handler_) {} + + ReceiveFromOp(ReceiveFromOp&& other) + : coro_(std::move(other.coro_)), + socket_(other.socket_), + p_remote_endpoint_(other.p_remote_endpoint_), + buffers_(std::move(other.buffers_)), + flags_(std::move(other.flags_)), + handler_(std::move(other.handler_)) {} + +#include + void operator()( + const boost::system::error_code& ec = boost::system::error_code(), + std::size_t length = 0) { + if (!ec) { + reenter(coro_) { + yield socket_.async_receive_from( + buffers_, p_remote_endpoint_->next_layer_endpoint(), flags_, + std::move(*this)); + + p_remote_endpoint_->set(); + + handler_(ec, length); + } + } else { + // error + handler_(ec, length); + } + } +#include + + inline ReadHandler& handler() { return handler_; } + + private: + boost::asio::coroutine coro_; + Socket& socket_; + Endpoint* p_remote_endpoint_; + MutableBufferSequence buffers_; + boost::asio::socket_base::message_flags flags_; + ReadHandler handler_; +}; + +template +inline void* asio_handler_allocate( + std::size_t size, + ReceiveFromOp* this_handler) { + return boost_asio_handler_alloc_helpers::allocate(size, + this_handler->handler()); +} + +template +inline void asio_handler_deallocate( + void* pointer, std::size_t size, + ReceiveFromOp* this_handler) { + boost_asio_handler_alloc_helpers::deallocate(pointer, size, + this_handler->handler()); +} + +template +inline bool asio_handler_is_continuation( + ReceiveFromOp* this_handler) { + return boost_asio_handler_cont_helpers::is_continuation( + this_handler->handler()); +} + +template +inline void asio_handler_invoke( + Function& function, + ReceiveFromOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +template +inline void asio_handler_invoke( + const Function& function, + ReceiveFromOp* this_handler) { + boost_asio_handler_invoke_helpers::invoke(function, this_handler->handler()); +} + +} // detail +} // layer +} // ssf + +#endif // SSF_LAYER_RECEIVE_FROM_OP_H_ diff --git a/src/framework/ssf/layer/routing/basic_routed_protocol.h b/src/framework/ssf/layer/routing/basic_routed_protocol.h new file mode 100644 index 00000000..577287e2 --- /dev/null +++ b/src/framework/ssf/layer/routing/basic_routed_protocol.h @@ -0,0 +1,222 @@ +#ifndef SSF_LAYER_ROUTING_BASIC_ROUTED_PROTOCOL_H_ +#define SSF_LAYER_ROUTING_BASIC_ROUTED_PROTOCOL_H_ + +#include +#include +#include + +#include +#include + +#include + +#include "ssf/error/error.h" +#include "ssf/utils/map_helpers.h" + +#include "ssf/layer/basic_resolver.h" +#include "ssf/layer/basic_endpoint.h" + +#include "ssf/layer/datagram/basic_datagram.h" +#include "ssf/layer/datagram/basic_header.h" +#include "ssf/layer/datagram/basic_payload.h" +#include "ssf/layer/datagram/empty_component.h" + +#include "ssf/layer/protocol_attributes.h" + +#include "ssf/layer/routing/basic_router.h" +#include "ssf/layer/routing/basic_routed_socket_service.h" + +namespace ssf { +namespace layer { +namespace routing { + +template +class basic_RoutedProtocol { + public: + using next_layer_protocol = NextLayer; + + using ReceiveDatagram = typename next_layer_protocol::ReceiveDatagram; + using SendDatagram = typename next_layer_protocol::SendDatagram; + + using network_address_type = + typename next_layer_protocol::endpoint_context_type; + using prefix_type = network_address_type; + + using router_type = basic_Router; + + enum { + id = 568, + overhead = SendDatagram::size, + facilities = ssf::layer::facilities::datagram, + mtu = NextLayer::mtu + }; + enum { endpoint_stack_size = 1 }; + + struct socket_context { + std::shared_ptr p_cancelled; + network_address_type network_address; + std::shared_ptr p_router; + typename router_type::ReceiveQueue* p_receive_queue; + }; + + struct endpoint_context_type { + network_address_type network_addr; + std::shared_ptr p_router; + + bool operator<(const endpoint_context_type& rhs) const { + return network_addr < rhs.network_addr; + } + + bool operator!=(const endpoint_context_type& rhs) const { + return network_addr != rhs.network_addr; + } + + bool operator==(const endpoint_context_type& rhs) const { + return network_addr == rhs.network_addr; + } + }; + + using next_endpoint_type = char; + + using endpoint = basic_VirtualLink_endpoint; + using resolver = basic_VirtualLink_resolver; + using socket = boost::asio::basic_datagram_socket< + basic_RoutedProtocol, basic_RoutedSocket_service>; + // using raw_socket = boost::asio::basic_datagram_socket< + // basic_RoutedProtocol, + // basic_RoutedRawSocket_service>; + + private: + using query = typename resolver::query; + + public: + static endpoint make_endpoint(boost::asio::io_service& io_service, + typename query::const_iterator parameters_it, + uint32_t, boost::system::error_code& ec) { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + + auto network_address_str = + helpers::GetField("network_address", *parameters_it); + auto router_str = helpers::GetField("router", *parameters_it); + + if (network_address_str.empty()) { + ec.assign(ssf::error::bad_address, ssf::error::get_ssf_category()); + return endpoint(); + } + + auto p_router = get_router(router_str); + if (!p_router) { + ec.assign(ssf::error::identifier_removed, ssf::error::get_ssf_category()); + return endpoint(); + } + + auto network_address = ResolveToNetworkAddress(network_address_str, ec); + + if (!ec) { + p_router->resolve(network_address, ec); + } + + if (ec) { + return endpoint(); + } + + endpoint_context_type context; + context.p_router = p_router; + context.network_addr = network_address; + + return endpoint(context, 0); + } + + static network_address_type ResolveToNetworkAddress( + const std::string& network_address_str, boost::system::error_code& ec) { + try { + auto address = std::stoul(network_address_str); + return network_address_type(address); + } catch (...) { + ec.assign(ssf::error::bad_address, ssf::error::get_ssf_category()); + return network_address_type(); + } + } + + static std::string get_address(const endpoint& endpoint) { + return std::to_string(endpoint.endpoint_context().network_addr); + } + + template + static SendDatagram make_datagram(const ConstBufferSequence& buffers, + const endpoint& source, + const endpoint& destination) { + typename SendDatagram::Header dgr_header; + auto& id = dgr_header.id(); + id = typename SendDatagram::Header::ID( + source.endpoint_context().network_addr, + destination.endpoint_context().network_addr); + auto& payload_length = dgr_header.payload_length(); + payload_length = static_cast( + boost::asio::buffer_size(buffers)); + + return SendDatagram(std::move(dgr_header), + typename SendDatagram::Payload(buffers), + typename SendDatagram::Footer()); + } + + static void StartRouter(const std::string& router_name, + boost::asio::io_service& io_service) { + boost::mutex::scoped_lock lock_router(router_mutex_); + auto p_router_it = p_routers_.find(router_name); + if (p_router_it == p_routers_.end()) { + p_routers_.emplace(router_name, + std::make_shared(io_service)); + } + } + + static void StopRouter(const std::string& router_name, + boost::system::error_code& ec) { + boost::mutex::scoped_lock lock_router(router_mutex_); + auto p_router_it = p_routers_.find(router_name); + if (p_router_it != p_routers_.end()) { + p_router_it->second->close(ec); + } + p_routers_.erase(p_router_it); + } + + static void StopAllRouters() { + boost::mutex::scoped_lock lock_router(router_mutex_); + auto p_router_it = p_routers_.begin(); + auto end_router_it = p_routers_.end(); + + while (p_router_it != end_router_it) { + boost::system::error_code ec; + p_router_it->second->close(ec); + p_router_it = p_routers_.erase(p_router_it); + } + } + + static std::shared_ptr get_router( + const std::string& router_name) { + boost::mutex::scoped_lock lock_router(router_mutex_); + auto p_router_it = p_routers_.find(router_name); + if (p_router_it != p_routers_.end()) { + return p_router_it->second; + } + return nullptr; + } + + private: + static boost::mutex router_mutex_; + static std::map> p_routers_; +}; + +template +boost::mutex basic_RoutedProtocol::router_mutex_; + +template +std::map::router_type>> + basic_RoutedProtocol::p_routers_; + +} // routing +} // layer +} // ssf + +#endif // SSF_LAYER_ROUTING_BASIC_ROUTED_PROTOCOL_H_ diff --git a/src/framework/ssf/layer/routing/basic_routed_socket_service.h b/src/framework/ssf/layer/routing/basic_routed_socket_service.h new file mode 100644 index 00000000..901fddf8 --- /dev/null +++ b/src/framework/ssf/layer/routing/basic_routed_socket_service.h @@ -0,0 +1,427 @@ +#ifndef SSF_CORE_VIRTUAL_ROUTING_BASIC_ROUTED_SOCKET_SERVICE_H_ +#define SSF_CORE_VIRTUAL_ROUTING_BASIC_ROUTED_SOCKET_SERVICE_H_ +#include + +#include +#include "boost/asio/detail/bind_handler.hpp" +#include +#include +#include + +#include +#include + +#include "ssf/layer/basic_impl.h" + +#include "ssf/io/composed_op.h" + +#include "ssf/error/error.h" +#include "ssf/io/handler_helpers.h" +#include "ssf/io/write_op.h" + +#include "ssf/layer/protocol_attributes.h" + +namespace ssf { +namespace layer { +namespace routing { + +#include + +template +class basic_RoutedSocket_service : public boost::asio::detail::service_base< + basic_RoutedSocket_service> { + public: + /// The protocol type. + using protocol_type = Protocol; + /// The endpoint type. + using endpoint_type = typename protocol_type::endpoint; + + using implementation_type = basic_socket_impl; + using native_handle_type = implementation_type&; + using native_type = native_handle_type; + + private: + using next_endpoint_type = + typename protocol_type::next_layer_protocol::endpoint; + using next_socket_type = typename protocol_type::next_layer_protocol::socket; + using endpoint_context_type = typename protocol_type::endpoint_context_type; + + public: + explicit basic_RoutedSocket_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base( + io_service), + p_worker_(nullptr), + usage_count_(0), + p_alive_(std::make_shared(true)) {} + + virtual ~basic_RoutedSocket_service() { *p_alive_ = false; } + + void construct(implementation_type& impl) { + impl.p_socket_context = + std::make_shared(); + impl.p_socket_context->p_cancelled = std::make_shared(false); + impl.p_socket_context->p_receive_queue = nullptr; + impl.p_socket_context->p_router = nullptr; + } + + void destroy(implementation_type& impl) { + impl.p_socket_context.reset(); + impl.p_local_endpoint.reset(); + impl.p_remote_endpoint.reset(); + impl.p_next_layer_socket.reset(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return ec; + } + + boost::system::error_code assign(implementation_type& impl, + const protocol_type& protocol, + native_handle_type& native_socket, + boost::system::error_code& ec) { + impl = native_socket; + return ec; + } + + bool is_open(const implementation_type& impl) const { + return !!impl.p_local_endpoint; + } + + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_remote_endpoint) { + return *impl.p_remote_endpoint; + } else { + return endpoint_type(); + } + } + + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + if (impl.p_local_endpoint) { + return *impl.p_local_endpoint; + } else { + return endpoint_type(); + } + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + if (!impl.p_local_endpoint) { + ec.assign(ssf::error::device_or_resource_busy, + ssf::error::get_ssf_category()); + return ec; + } + + impl.p_socket_context->p_receive_queue = nullptr; + + boost::recursive_mutex::scoped_lock lock(bounds_mutex_); + bounds_.erase(*(impl.p_local_endpoint)); + + return ec; + } + + native_type native(implementation_type& impl) { return impl; } + + native_handle_type native_handle(implementation_type& impl) { return impl; } + + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) { + ec.assign(ssf::error::function_not_supported, + ssf::error::get_ssf_category()); + return ec; + } + + bool at_mark(const implementation_type& impl, + boost::system::error_code& ec) const { + ec.assign(ssf::error::function_not_supported, + ssf::error::get_ssf_category()); + return false; + } + + std::size_t available(const implementation_type& impl, + boost::system::error_code& ec) const { + ec.assign(ssf::error::function_not_supported, + ssf::error::get_ssf_category()); + return false; + } + + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& local_endpoint, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(bounds_mutex_); + + if (bounds_.count(local_endpoint)) { + ec.assign(ssf::error::device_or_resource_busy, + ssf::error::get_ssf_category()); + return ec; + } + + auto endpoint_context = local_endpoint.endpoint_context(); + impl.p_socket_context->network_address = endpoint_context.network_addr; + + if (endpoint_context.p_router) { + impl.p_socket_context->p_router = endpoint_context.p_router; + } else { + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return ec; + } + + if (!ec) { + impl.p_socket_context->p_receive_queue = + impl.p_socket_context->p_router->get_network_receive_queue( + endpoint_context.network_addr, ec); + } + + if (!ec) { + bounds_.insert(local_endpoint); + impl.p_local_endpoint = + std::make_shared(std::move(local_endpoint)); + } + + return ec; + } + + boost::system::error_code connect(implementation_type& impl, + const endpoint_type& remote_endpoint, + boost::system::error_code& ec) { + ec.assign(ssf::error::function_not_supported, + ssf::error::get_ssf_category()); + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& remote_endpoint, + ConnectHandler&& handler) { + boost::asio::detail::async_result_init< + ConnectHandler, void(const boost::system::error_code&)> + init(std::forward(handler)); + + auto connect_lambda = [this, &impl, remote_endpoint, init]() { + boost::system::error_code ec; + this->connect(impl, remote_endpoint, ec); + init.handler(ec); + }; + + this->get_io_service().post( + io::ComposedOp( + std::move(connect_lambda), + init.handler)); + + return init.result.get(); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, const ConstBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::function_not_supported, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + /// Async send datagram in router + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send_to(implementation_type& impl, + const ConstBufferSequence& buffers, + const endpoint_type& destination, + boost::asio::socket_base::message_flags flags, + WriteHandler&& handler) { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_socket_context->p_router) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::no_link, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + if (boost::asio::buffer_size(buffers) > protocol_type::mtu) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::message_size, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + register_async_op(); + impl.p_socket_context->p_router->async_send( + protocol_type::make_datagram(buffers, *impl.p_local_endpoint, + destination), + [this, init](const boost::system::error_code& ec, std::size_t length) { + init.handler(ec, length); + this->unregister_async_op(); + }); + + return init.result.get(); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::function_not_supported, + ssf::error::get_ssf_category()), + 0); + + return init.result.get(); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive_from(implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& source, + boost::asio::socket_base::message_flags flags, + ReadHandler&& handler) { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_socket_context->p_receive_queue) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::no_link, + ssf::error::get_ssf_category()), + 0); + return init.result.get(); + } + + register_async_op(); + impl.p_socket_context->p_receive_queue->async_get( + [this, &impl, buffers, &source, init]( + const boost::system::error_code& ec, + const typename protocol_type::ReceiveDatagram& datagram) { + this->DatagramReceived(ec, datagram, impl, buffers, source, + init.handler); + this->unregister_async_op(); + }); + + return init.result.get(); + } + + boost::system::error_code shutdown( + implementation_type& impl, boost::asio::socket_base::shutdown_type what, + boost::system::error_code& ec) { + if (!impl.p_socket_context->p_router || + !impl.p_socket_context->p_receive_queue) { + ec.assign(ssf::error::not_connected, ssf::error::get_ssf_category()); + + return ec; + } + + return ec; + } + + private: + void shutdown_service() { + *p_alive_ = false; + p_worker_.reset(); + } + + template + void DatagramReceived(const boost::system::error_code& ec, + const typename protocol_type::ReceiveDatagram& datagram, + implementation_type& impl, + const MutableBufferSequence& buffers, + endpoint_type& source, ReadHandler handler) { + if (ec) { + handler(ec, 0); + return; + } + + if (datagram.header().payload_length() > + boost::asio::buffer_size(buffers)) { + handler(boost::system::error_code(ssf::error::message_size, + ssf::error::get_ssf_category()), + 0); + return; + } + + auto network_source_id = datagram.header().id().left_id(); + + auto& source_context = source.endpoint_context(); + source.set(); + source_context.network_addr = network_source_id; + source_context.p_router = impl.p_local_endpoint->endpoint_context().p_router; + + auto copied = + boost::asio::buffer_copy(buffers, datagram.payload().GetConstBuffers()); + + handler(ec, copied); + return; + } + + void register_async_op() { + boost::recursive_mutex::scoped_lock lock(mutex_); + if (usage_count_ == 0) { + p_worker_ = std::unique_ptr( + new boost::asio::io_service::work(this->get_io_service())); + } + ++usage_count_; + } + + void unregister_async_op() { + boost::recursive_mutex::scoped_lock lock(mutex_); + --usage_count_; + if (usage_count_ == 0) { + p_worker_.reset(); + } + } + + private: + std::unique_ptr p_worker_; + boost::recursive_mutex mutex_; + uint64_t usage_count_; + std::shared_ptr p_alive_; + + boost::recursive_mutex bounds_mutex_; + std::set bounds_; +}; + +#include + +} // routing +} // layer +} // ssf + +#endif // SSF_CORE_VIRTUAL_ROUTING_BASIC_ROUTED_SOCKET_SERVICE_H_ diff --git a/src/framework/ssf/layer/routing/basic_router.h b/src/framework/ssf/layer/routing/basic_router.h new file mode 100644 index 00000000..9d39d809 --- /dev/null +++ b/src/framework/ssf/layer/routing/basic_router.h @@ -0,0 +1,121 @@ +#ifndef SSF_LAYER_ROUTING_BASIC_ROUTER_H_ +#define SSF_LAYER_ROUTING_BASIC_ROUTER_H_ + +#include + +#include +#include +#include + +#include + +#include "ssf/layer/routing/basic_routing_table.h" +#include "ssf/layer/routing/basic_router_service.h" + +namespace ssf { +namespace layer { +namespace routing { + +template , + class RouterService = + basic_Router_service> +class basic_Router : public boost::asio::basic_io_object { + public: + typedef typename RouterService::network_address_type network_address_type; + typedef typename RouterService::prefix_type prefix_type; + + typedef typename RouterService::next_endpoint_type next_endpoint_type; + + using SendDatagram = typename RouterService::SendDatagram; + using SendQueue = typename RouterService::SendQueue; + using ReceiveQueue = typename RouterService::ReceiveQueue; + + public: + basic_Router(boost::asio::io_service& io_service) + : boost::asio::basic_io_object(io_service) {} + + ~basic_Router() { + boost::system::error_code ec; + close(ec); + } + + boost::system::error_code add_network(prefix_type prefix, + next_endpoint_type next_endpoint, + boost::system::error_code& ec) { + return this->get_service().add_network( + this->implementation, std::move(prefix), std::move(next_endpoint), ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(const boost::system::error_code&)) + async_add_network(const prefix_type& prefix, + const next_endpoint_type& next_endpoint, + Handler handler) { + return this->get_service().async_add_network(this->implementation, prefix, + next_endpoint, handler); + } + + boost::system::error_code remove_network(const prefix_type& prefix, + boost::system::error_code& ec) { + return this->get_service().remove_network(this->implementation, prefix, ec); + } + + ReceiveQueue* get_network_receive_queue(prefix_type prefix, + boost::system::error_code& ec) { + return this->get_service().get_network_receive_queue(this->implementation, + prefix, ec); + } + + SendQueue* get_router_send_queue(boost::system::error_code& ec) { + return this->get_service().get_router_send_queue(this->implementation, ec); + } + + boost::system::error_code add_route( + prefix_type prefix, network_address_type next_endpoint_context, + boost::system::error_code& ec) { + return this->get_service().add_route(this->implementation, + std::move(prefix), + std::move(next_endpoint_context), ec); + } + + boost::system::error_code remove_route(prefix_type prefix, + boost::system::error_code& ec) { + return this->get_service().remove_route(this->implementation, prefix, ec); + } + + network_address_type resolve(const prefix_type& prefix, + boost::system::error_code& ec) const { + return this->get_service().resolve(this->implementation, prefix, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(const boost::system::error_code&, + const network_address_type&)) + async_resolve(const prefix_type& prefix, Handler&& handler) { + return this->get_service().async_resolve(this->implementation, prefix, + std::forward(handler)); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(const boost::system::error_code&, + std::size_t)) + async_send(const SendDatagram& datagram, Handler handler) { + return this->get_service().async_send(this->implementation, datagram, + std::forward(handler)); + } + + boost::system::error_code flush(boost::system::error_code& ec) { + return this->get_service().flush(this->implementation, ec); + } + + boost::system::error_code close(boost::system::error_code& ec) { + return this->get_service().close(this->implementation, ec); + } +}; + +} // routing +} // layer +} // ssf + +#endif // SSF_LAYER_ROUTING_BASIC_ROUTER_H_ diff --git a/src/framework/ssf/layer/routing/basic_router_service.h b/src/framework/ssf/layer/routing/basic_router_service.h new file mode 100644 index 00000000..701d8c5c --- /dev/null +++ b/src/framework/ssf/layer/routing/basic_router_service.h @@ -0,0 +1,482 @@ +#ifndef SSF_LAYER_ROUTING_BASIC_ROUTER_SERVICE_H_ +#define SSF_LAYER_ROUTING_BASIC_ROUTER_SERVICE_H_ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "ssf/error/error.h" +#include "ssf/io/composed_op.h" +#include "ssf/io/handler_helpers.h" + +#include "ssf/layer/protocol_attributes.h" + +#include "ssf/layer/queue/async_queue.h" +#include "ssf/layer/queue/send_queued_datagram_socket.h" +#include "ssf/layer/queue/commutator.h" + +#include "ssf/layer/routing/basic_routing_selector.h" + +#include "ssf/log/log.h" + +namespace ssf { +namespace layer { +namespace routing { + +#include + +template +class basic_Router_service + : public boost::asio::detail::service_base< + basic_Router_service> { + public: + typedef NetworkProtocol next_layer_protocol; + typedef typename next_layer_protocol::SendDatagram SendDatagram; + typedef typename next_layer_protocol::ReceiveDatagram Datagram; + typedef typename next_layer_protocol::endpoint next_endpoint_type; + + typedef + typename next_layer_protocol::endpoint_context_type network_address_type; + typedef network_address_type prefix_type; + + typedef RoutingSelector + routing_selector_type; + typedef queue::Commutator Commutator; + typedef queue::CommutatorPtr CommutatorPtr; + + typedef typename next_layer_protocol::raw_socket next_socket_type; + typedef queue::basic_send_queued_datagram_socket + queued_send_socket_type; + typedef std::shared_ptr p_queued_send_socket_type; + + typedef TRoutingTable RoutingTable; + + using ReceiveQueue = queue::basic_async_queue; + using SendQueue = queue::basic_async_queue; + + public: + struct implementation_type { + std::shared_ptr p_valid; + RoutingTable routing_table; + + boost::recursive_mutex network_sockets_mutex; + std::map network_sockets; + std::map receive_queues; + + std::unique_ptr p_send_queue; + + routing_selector_type selector; + CommutatorPtr p_commutator; + + std::vector local_send_buffer; + }; + + typedef implementation_type& native_handle_type; + typedef native_handle_type native_type; + + public: + explicit basic_Router_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base(io_service) {} + + virtual ~basic_Router_service() {} + + void construct(implementation_type& impl) { + impl.p_valid = std::make_shared(true); + impl.selector.set_routing_table(&impl.routing_table); + + impl.p_send_queue = + std::unique_ptr(new SendQueue(this->get_io_service())); + + impl.p_commutator = + Commutator::Create(this->get_io_service(), impl.selector); + + auto receive_callback = + [this, &impl](typename Commutator::Element datagram, + typename Commutator::OutputHandler handler) { + boost::system::error_code receive_ec; + this->Receive(impl, std::move(datagram), receive_ec); + handler(receive_ec); + }; + + /// Register local output (0) : final destination + /// Populate local receive destination queue + impl.p_commutator->RegisterOutput(0, std::move(receive_callback)); + + impl.local_send_buffer.resize(next_layer_protocol::mtu); + + auto send_callback = + [this, &impl](typename Commutator::InputHandler handler) { + this->AsyncSend(impl, std::move(handler)); + }; + + /// Register local input : local send + /// Get from send local queue + /// Push element to destination output + impl.p_commutator->RegisterInput(0, std::move(send_callback)); + } + + void destroy(implementation_type& impl) { + *(impl.p_valid) = false; + + boost::system::error_code queue_ec; + impl.p_send_queue->close(queue_ec); + impl.p_send_queue.reset(); + + { + boost::recursive_mutex::scoped_lock lock(impl.network_sockets_mutex); + boost::system::error_code close_ec; + + for (auto& p_sockets : impl.network_sockets) { + p_sockets.second->close(close_ec); + } + impl.network_sockets.clear(); + + for (auto& queue : impl.receive_queues) { + queue.second.close(close_ec); + } + impl.receive_queues.clear(); + } + + boost::system::error_code ec; + impl.routing_table.Flush(ec); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + /// Pull the network socket bound to the next endpoint + /// and dispatch the received packet : + /// * locally if the destination id is local + /// * remotely by sending it through the correct network socket + /// (routing table) + /// Declare the network id output to send packet through the network socket + boost::system::error_code add_network(implementation_type& impl, + prefix_type prefix, + next_endpoint_type next_endpoint, + boost::system::error_code& ec) { + auto p_queued_send_socket = + std::make_shared(this->get_io_service()); + p_queued_send_socket->next_layer().bind(next_endpoint, ec); + + boost::recursive_mutex::scoped_lock lock(impl.network_sockets_mutex); + + SSF_LOG(kLogTrace) + << " * Router: add network " << prefix << " bound to interface " + << next_endpoint.next_layer_endpoint().endpoint_context(); + + if (!ec) { + auto inserted = impl.network_sockets.insert(std::make_pair( + next_endpoint.endpoint_context(), p_queued_send_socket)); + + if (!inserted.second) { + ec.assign(ssf::error::address_in_use, ssf::error::get_ssf_category()); + } + } + + if (!ec) { + auto received_queue_it = + impl.receive_queues.find(next_endpoint.endpoint_context()); + if (received_queue_it == impl.receive_queues.end()) { + // The queue does not exists, create it + auto inserted = impl.receive_queues.emplace( + next_endpoint.endpoint_context(), this->get_io_service()); + + if (!inserted.second) { + impl.network_sockets.erase(next_endpoint.endpoint_context()); + ec.assign(ssf::error::address_in_use, ssf::error::get_ssf_category()); + } + } + } + + if (!ec) { + /// Register network as local in routing table + impl.routing_table.AddRoute(next_endpoint.endpoint_context(), + network_address_type(), ec); + + if (ec) { + impl.network_sockets.erase(next_endpoint.endpoint_context()); + impl.receive_queues.erase(next_endpoint.endpoint_context()); + boost::system::error_code close_ec; + p_queued_send_socket->close(close_ec); + } + } + + if (!ec) { + auto output_callback = [p_queued_send_socket]( + typename Commutator::Element datagram, + typename Commutator::OutputHandler handler) { + auto p_datagram = std::make_shared(std::move(datagram)); + + auto complete_handler = [p_datagram, handler, p_queued_send_socket]( + const boost::system::error_code& ec, std::size_t) { handler(ec); }; + + AsyncSendDatagram(*p_queued_send_socket, *p_datagram, + std::move(complete_handler)); + }; + + auto input_callback = + [p_queued_send_socket](typename Commutator::InputHandler handler) { + auto p_datagram = std::make_shared(); + + auto complete_handler = [p_datagram, handler, p_queued_send_socket]( + const boost::system::error_code& ec, std::size_t) { + handler(ec, std::move(*p_datagram)); + }; + + AsyncReceiveDatagram(*p_queued_send_socket, p_datagram.get(), + std::move(complete_handler)); + }; + + /// Register network socket as output channel + impl.p_commutator->RegisterOutput(next_endpoint.endpoint_context(), + std::move(output_callback)); + /// Pull network socket + impl.p_commutator->RegisterInput(prefix, std::move(input_callback)); + } + + return ec; + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(boost::system::error_code)) + async_add_network(implementation_type& impl, const prefix_type& prefix, + const next_endpoint_type& next_endpoint, + Handler&& handler) { + boost::asio::detail::async_result_init + init(std::forward(handler)); + + auto complete_lambda = [this, &impl, prefix, next_endpoint, init]() { + boost::system::error_code ec; + + this->add_network(impl, prefix, next_endpoint, ec); + + init.handler(ec); + }; + + this->get_io_service().post(std::move(complete_lambda)); + + return init.result.get(); + } + + boost::system::error_code remove_network(implementation_type& impl, + const prefix_type& prefix, + boost::system::error_code& ec) { + impl.p_commutator->UnregisterInput(prefix); + impl.p_commutator->UnregisterOutput(prefix); + + auto network_address = impl.routing_table.Resolve(prefix, ec); + + if (ec) { + return ec; + } + + impl.routing_table.RemoveRoute(network_address, ec); + + boost::recursive_mutex::scoped_lock lock(impl.network_sockets_mutex); + + auto p_socket_it = impl.network_sockets.find(network_address); + + if (p_socket_it != std::end(impl.network_sockets)) { + boost::system::error_code close_ec; + p_socket_it->second->close(close_ec); + impl.network_sockets.erase(network_address); + } + + auto queue_it = impl.receive_queues.find(network_address); + + if (queue_it != std::end(impl.receive_queues)) { + boost::system::error_code close_ec; + queue_it->second.close(close_ec); + impl.receive_queues.erase(network_address); + } + + return ec; + } + + /// Get local network receive queue + // TODO : care about lifetime + ReceiveQueue* get_network_receive_queue(implementation_type& impl, + prefix_type prefix, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(impl.network_sockets_mutex); + + auto queue_it = impl.receive_queues.find(prefix); + + // The queue does not exists (interface not up yet ?) + if (queue_it == std::end(impl.receive_queues)) { + // Create the receive queue + auto inserted = + impl.receive_queues.emplace(prefix, this->get_io_service()); + if (inserted.second) { + return &(inserted.first->second); + } + return nullptr; + } + + return &queue_it->second; + } + + SendQueue* get_router_send_queue(implementation_type& impl, + boost::system::error_code& ec) { + return impl.p_send_queue.get(); + } + + boost::system::error_code add_route( + implementation_type& impl, prefix_type prefix, + network_address_type next_endpoint_context, + boost::system::error_code& ec) { + return impl.routing_table.AddRoute(std::move(prefix), + std::move(next_endpoint_context), ec); + } + + boost::system::error_code remove_route(implementation_type& impl, + prefix_type prefix, + boost::system::error_code& ec) { + return impl.routing_table.RemoveRoute(prefix, ec); + } + + network_address_type resolve(const implementation_type& impl, + const prefix_type& prefix, + boost::system::error_code& ec) const { + return impl.routing_table.Resolve(prefix, ec); + } + + template + BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(const boost::system::error_code&, + const network_address_type&)) + async_resolve(const implementation_type& impl, const prefix_type& prefix, + Handler&& handler) { + boost::asio::detail::async_result_init< + Handler, + void(const boost::system::error_code&, const network_address_type&)> + init(std::forward(handler)); + + auto complete_lambda = [this, &impl, prefix, init]() { + boost::system::error_code ec; + + auto resolved = this->resolve(impl, prefix, ec); + + init.handler(ec, resolved); + }; + + this->get_io_service().post(std::move(complete_lambda)); + + return init.result.get(); + } + + /// Async send packet + /// Push packet in the local snd queue + template + BOOST_ASIO_INITFN_RESULT_TYPE(Handler, void(const boost::system::error_code&, + std::size_t)) + async_send(implementation_type& impl, const SendDatagram& datagram, + Handler&& handler) { + boost::asio::detail::async_result_init< + Handler, void(const boost::system::error_code&, std::size_t)> + init(std::forward(handler)); + + Datagram copied_datagram; + + auto& payload_length = copied_datagram.header().payload_length(); + payload_length = datagram.header().payload_length(); + + copied_datagram.payload().SetSize(payload_length); + auto copied = boost::asio::buffer_copy(copied_datagram.GetMutableBuffers(), + datagram.GetConstBuffers()); + + auto push_handler = + [this, copied, init](const boost::system::error_code ec) { + if (ec) { + io::PostHandler(this->get_io_service(), init.handler, ec, 0); + } else { + io::PostHandler(this->get_io_service(), init.handler, ec, copied); + } + }; + + impl.p_send_queue->async_push(std::move(copied_datagram), push_handler); + + return init.result.get(); + } + + boost::system::error_code flush(implementation_type& impl, + boost::system::error_code& ec) { + return impl.routing_table.Flush(ec); + } + + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + impl.p_send_queue->close(ec); + + impl.p_commutator->close(ec); + impl.routing_table.Flush(ec); + + boost::recursive_mutex::scoped_lock lock(impl.network_sockets_mutex); + boost::system::error_code close_ec; + + for (auto& p_sockets : impl.network_sockets) { + p_sockets.second->close(close_ec); + } + impl.network_sockets.clear(); + + for (auto& queue : impl.receive_queues) { + queue.second.close(close_ec); + } + impl.receive_queues.clear(); + + return ec; + } + + private: + void shutdown_service() {} + + void AsyncSend(implementation_type& impl, + typename Commutator::InputHandler handler) { + impl.p_send_queue->async_get(std::move(handler)); + } + + /// A packet is received locally + /// Find the receive queue bound to the destination and push the datagram in + boost::system::error_code Receive(implementation_type& impl, + Datagram datagram, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(impl.network_sockets_mutex); + + auto& destination = datagram.header().id().right_id(); + + auto queue_it = impl.receive_queues.find(destination); + + if (queue_it == std::end(impl.receive_queues)) { + ec.assign(ssf::error::address_not_available, + ssf::error::get_ssf_category()); + return ec; + } + + queue_it->second.push(std::move(datagram), ec); + + return ec; + } +}; + +#include + +} // routing +} // layer +} // ssf + +#endif // SSF_LAYER_ROUTING_BASIC_ROUTER_SERVICE_H_ diff --git a/src/framework/ssf/layer/routing/basic_routing_selector.h b/src/framework/ssf/layer/routing/basic_routing_selector.h new file mode 100644 index 00000000..308c76d1 --- /dev/null +++ b/src/framework/ssf/layer/routing/basic_routing_selector.h @@ -0,0 +1,50 @@ +#ifndef SSF_LAYER_ROUTING_BASIC_ROUTING_SELECTOR_H_ +#define SSF_LAYER_ROUTING_BASIC_ROUTING_SELECTOR_H_ + +#include + +#include + +#include +#include + +#include "ssf/error/error.h" + +namespace ssf { +namespace layer { +namespace routing { + +template +class RoutingSelector { + public: + typedef TRoutingTable RoutingTable; + + public: + RoutingSelector() : p_routing_table_(nullptr) {} + + RoutingSelector(RoutingSelector&& other) + : p_routing_table_(std::move(other.p_routing_selector)) {} + + void set_routing_table(RoutingTable* p_routing_table) { + p_routing_table_ = p_routing_table; + } + + /// Update p_id by resolving the destination id of the element + bool operator()(Identifier* p_id, Element* p_element) const { + boost::system::error_code ec; + + *p_id = p_routing_table_->Resolve( + p_element->header().id().GetSecondHalfId(), ec); + + return !ec; + } + + private: + RoutingTable* p_routing_table_; +}; + +} // routing +} // layer +} // ssf + +#endif // SSF_LAYER_ROUTING_BASIC_ROUTING_SELECTOR_H_ diff --git a/src/framework/ssf/layer/routing/basic_routing_table.h b/src/framework/ssf/layer/routing/basic_routing_table.h new file mode 100644 index 00000000..6ebc3b7a --- /dev/null +++ b/src/framework/ssf/layer/routing/basic_routing_table.h @@ -0,0 +1,95 @@ +#ifndef SSF_LAYER_ROUTING_BASIC_ROUTING_TABLE_H_ +#define SSF_LAYER_ROUTING_BASIC_ROUTING_TABLE_H_ + +#include + +#include + +#include +#include + +#include "ssf/error/error.h" + +#include "ssf/log/log.h" + +namespace ssf { +namespace layer { +namespace routing { + +template +class basic_RoutingTable { + private: + typedef typename NetworkProtocol::endpoint_context_type network_address_type; + typedef network_address_type prefix_type; + + public: + boost::system::error_code AddRoute( + prefix_type prefix, network_address_type network_endpoint_context, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + SSF_LOG(kLogTrace) << " * Routing table : add route from " << prefix + << " to " << network_endpoint_context; + + auto inserted = table_.insert( + std::make_pair(std::move(prefix), std::move(network_endpoint_context))); + + if (!inserted.second) { + ec.assign(ssf::error::address_in_use, ssf::error::get_ssf_category()); + } + + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return ec; + } + + boost::system::error_code RemoveRoute(const prefix_type& prefix, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + auto erased = table_.erase(prefix); + + if (!erased) { + ec.assign(ssf::error::not_connected, ssf::error::get_ssf_category()); + } else { + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + } + + return ec; + } + + /// Resolve a network id and return the associated endpoint + network_address_type Resolve(const prefix_type& prefix, + boost::system::error_code& ec) const { + boost::recursive_mutex::scoped_lock lock(mutex_); + + auto network_endpoint_context_it = table_.find(prefix); + + if (network_endpoint_context_it == std::end(table_)) { + ec.assign(ssf::error::not_connected, ssf::error::get_ssf_category()); + return network_address_type(); + } + + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return network_endpoint_context_it->second; + } + + /// Clear the routing table + boost::system::error_code Flush(boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + table_.clear(); + + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + return ec; + } + + private: + mutable boost::recursive_mutex mutex_; + std::map table_; +}; + +} // routing +} // layer +} // ssf + +#endif // SSF_LAYER_ROUTING_BASIC_ROUTING_TABLE_H_ \ No newline at end of file diff --git a/src/framework/ssf/log/log.cpp b/src/framework/ssf/log/log.cpp new file mode 100644 index 00000000..3c27d722 --- /dev/null +++ b/src/framework/ssf/log/log.cpp @@ -0,0 +1,58 @@ +#include +#include + +#include "ssf/log/log.h" + +namespace ssf { +namespace log { + +Log::Log() : os_() {} + +Log::~Log() { + os_ << std::endl; + fprintf(stderr, "%s", os_.str().c_str()); + fflush(stderr); +} + +std::ostream& Log::GetLog(LogLevel level) { + // generate timestamp string + char time_str[32]; + time_t current_time; + struct tm* timeinfo; + time(¤t_time); + timeinfo = localtime(¤t_time); + strftime(time_str, sizeof(time_str), "[%Y-%m-%d %H:%M:%S]", timeinfo); + + os_ << time_str; + os_ << "[" << LevelToString(level) << "] "; + return os_; +} + +LogLevel& Log::SeverityLevel() { + static LogLevel level = kLogInfo; + return level; +} + +void Log::SetSeverityLevel(LogLevel level) { Log::SeverityLevel() = level; } + +std::string Log::LevelToString(LogLevel level) { + switch (level) { + case kLogCritical: + return "critical"; + case kLogError: + return "error"; + case kLogWarning: + return "warning"; + case kLogInfo: + return "info"; + case kLogDebug: + return "debug"; + case kLogTrace: + return "trace"; + default: + return "unknown"; + } +} + +} // log +} // ssf diff --git a/src/framework/ssf/log/log.h b/src/framework/ssf/log/log.h new file mode 100644 index 00000000..000a5173 --- /dev/null +++ b/src/framework/ssf/log/log.h @@ -0,0 +1,63 @@ +#ifndef SSF_LOG_LOG_H_ +#define SSF_LOG_LOG_H_ + +#include +#include + +namespace ssf { +namespace log { + +enum LogLevel { + kLogNone, + kLogCritical, + kLogError, + kLogWarning, + kLogInfo, + kLogDebug, + kLogTrace +}; + +// Application log +// Example: +// ssf::log::Log::SetSeverityLevel(ssf::log::LogLevel::kLogTrace); +// SSF_LOG(kLogCritical) << "critical"; +// SSF_LOG(kLogError) << "error"; +// SSF_LOG(kLogWarning) << "warning"; +// SSF_LOG(kLogInfo) << "info"; +// SSF_LOG(kLogDebug) << "debug"; +// SSF_LOG(kLogTrace) << "trace"; +class Log { + public: + Log(); + virtual ~Log(); + + std::ostream& GetLog(LogLevel level); + + public: + // Global severity level + static LogLevel& SeverityLevel(); + + // Set global severity level + static void SetSeverityLevel(LogLevel level); + + // Convert log level to string representation + static std::string LevelToString(LogLevel level); + + protected: + std::ostringstream os_; + + private: + Log(const Log&) = delete; + Log& operator=(const Log&) = delete; +}; + +} // log +} // ssf + +#define SSF_LOG(level) \ + if (ssf::log::Log::SeverityLevel() < ssf::log::level) \ + ; \ + else \ + ssf::log::Log().GetLog(ssf::log::level) + +#endif // SSF_LOG_LOG_H_ diff --git a/src/common/network/base_session.h b/src/framework/ssf/network/base_session.h similarity index 80% rename from src/common/network/base_session.h rename to src/framework/ssf/network/base_session.h index 2fe839fb..58102871 100644 --- a/src/common/network/base_session.h +++ b/src/framework/ssf/network/base_session.h @@ -1,11 +1,12 @@ -#ifndef SSF_COMMON_NETWORK_BASE_SESSION_H_ -#define SSF_COMMON_NETWORK_BASE_SESSION_H_ +#ifndef SSF_NETWORK_BASE_SESSION_H_ +#define SSF_NETWORK_BASE_SESSION_H_ #include #include namespace ssf { + /// Base class for ActionableItem types class BaseSession : public std::enable_shared_from_this { public: @@ -21,6 +22,7 @@ class BaseSession : public std::enable_shared_from_this { }; typedef std::shared_ptr BaseSessionPtr; + } // ssf -#endif // SSF_COMMON_NETWORK_BASE_SESSION_H_ +#endif // SSF_NETWORK_BASE_SESSION_H_ diff --git a/src/common/network/manager.h b/src/framework/ssf/network/manager.h similarity index 92% rename from src/common/network/manager.h rename to src/framework/ssf/network/manager.h index 676debe2..37871ea9 100644 --- a/src/common/network/manager.h +++ b/src/framework/ssf/network/manager.h @@ -1,5 +1,5 @@ -#ifndef SSF_COMMON_NETWORK_MANAGER_H -#define SSF_COMMON_NETWORK_MANAGER_H +#ifndef SSF_NETWORK_MANAGER_H +#define SSF_NETWORK_MANAGER_H #include @@ -9,11 +9,11 @@ #include #include -#include -#include "common/error/error.h" +#include "ssf/error/error.h" namespace ssf { + /// Manage actionable items /** Items must support start() and stop() interfaces and * implement operator== @@ -28,7 +28,7 @@ class ItemManager : private boost::noncopyable { public: ItemManager() : id_map_mutex_(), id_map_() {} - ~ItemManager() {} + ~ItemManager() { do_stop_all(); } /// Activate an Item and return a unique ID instance_id_type start(ActionableItem item, boost::system::error_code& ec) { @@ -46,9 +46,7 @@ class ItemManager : private boost::noncopyable { } /// Stop all items - void stop_all() { - do_stop_all(); - } + void stop_all() { do_stop_all(); } private: /// Find the unique ID of the given item @@ -132,6 +130,7 @@ class ItemManager : private boost::noncopyable { boost::recursive_mutex id_map_mutex_; std::map id_map_; }; + } // ssf -#endif // SSF_COMMON_NETWORK_MANAGER_H +#endif // SSF_NETWORK_MANAGER_H diff --git a/src/framework/ssf/network/object_io_helpers.h b/src/framework/ssf/network/object_io_helpers.h new file mode 100644 index 00000000..d7640ed7 --- /dev/null +++ b/src/framework/ssf/network/object_io_helpers.h @@ -0,0 +1,98 @@ +#ifndef SSF_NETWORK_OBJECT_IO_HELPERS_H_ +#define SSF_NETWORK_OBJECT_IO_HELPERS_H_ + +#include + +#include + +#include +#include +#include +#include + +namespace ssf { + +template +void SendBase(Socket& socket, Base to_send, Handler handler) { + auto p_to_send = std::make_shared(std::move(to_send)); + + auto sent_lambda = [p_to_send, handler]( + const boost::system::error_code& ec, std::size_t length) { handler(ec); }; + + boost::asio::async_write( + socket, boost::asio::buffer(p_to_send.get(), sizeof(*p_to_send)), + sent_lambda); +} + +template +void ReceiveBase(Socket& socket, Base* p_to_receive, + Handler handler) { + auto received_lambda = [handler]( + const boost::system::error_code& ec, std::size_t length) mutable { handler(ec); }; + + boost::asio::async_read( + socket, boost::asio::buffer(p_to_receive, sizeof(*p_to_receive)), + received_lambda); +} + +template +void SendString(Socket& socket, std::string to_send, + Handler handler) { + auto p_size = std::make_shared((uint32_t)to_send.size()); + auto p_string = std::make_shared(std::move(to_send)); + + auto string_sent_lambda = [p_string, handler]( + const boost::system::error_code& ec, std::size_t length) mutable { handler(ec); }; + + auto size_sent_lambda = [&socket, p_size, p_string, string_sent_lambda, + handler](const boost::system::error_code& ec, + std::size_t length) mutable { + if (!ec) { + boost::asio::async_write(socket, boost::asio::buffer(*p_string), + string_sent_lambda); + } else { + handler(ec); + } + }; + + boost::asio::async_write(socket, + boost::asio::buffer(p_size.get(), sizeof(*p_size)), + size_sent_lambda); +} + +template +void ReceiveString(Socket& socket, std::string* p_to_receive, + Handler handler) { + auto p_size = std::make_shared(0); + + auto string_received_lambda = [handler]( + const boost::system::error_code& ec, std::size_t length) mutable { handler(ec); }; + + auto size_received_lambda = [&socket, p_size, p_to_receive, + string_received_lambda, handler]( + const boost::system::error_code& ec, std::size_t length) mutable { + if (!ec) { + if (*p_size > 1 * 1024 * 1024) { + handler(boost::system::errc::make_error_code( + boost::system::errc::argument_list_too_long)); + return; + } + + p_to_receive->reserve(*p_size); + p_to_receive->resize(*p_size); + boost::asio::async_read(socket, + boost::asio::buffer(&(*p_to_receive)[0], *p_size), + string_received_lambda); + } else { + handler(ec); + } + }; + + boost::asio::async_read(socket, + boost::asio::buffer(p_size.get(), sizeof(*p_size)), + size_received_lambda); +} + +} // ssf + +#endif // SSF_NETWORK_OBJECT_IO_HELPERS_H_ diff --git a/src/framework/ssf/network/session_forwarder.h b/src/framework/ssf/network/session_forwarder.h new file mode 100644 index 00000000..4e06eb14 --- /dev/null +++ b/src/framework/ssf/network/session_forwarder.h @@ -0,0 +1,135 @@ +#ifndef SSF_NETWORK_SESSION_FORWARDER_H +#define SSF_NETWORK_SESSION_FORWARDER_H + +#include +#include + +#include + +#include + +#include + +#include "ssf/network/base_session.h" // NOLINT +#include "ssf/network/socket_link.h" +#include "ssf/network/manager.h" + +namespace ssf { + +/// Create a Full Duplex Forwarding Link +template +class SessionForwarder : public ssf::BaseSession { + private: + /// Buffer type for the transiting data + typedef std::array buffer_type; + + /// Type for the class managing the different forwarding links + typedef ItemManager SessionManager; + + public: + typedef std::shared_ptr p_SessionForwarder; + + public: + /// Return a shared pointer to a new SessionForwarder object + template + static p_SessionForwarder create(Args&&... args) { + return std::shared_ptr( + new SessionForwarder(std::forward(args)...)); + } + + virtual ~SessionForwarder() {} + + /// Start forwarding + void start(boost::system::error_code&) override { + SSF_LOG(kLogInfo) << "session[forwarder]: start"; + DoForward(); + } + + /// Stop forwarding + void stop(boost::system::error_code&) override { + SSF_LOG(kLogInfo) << "session[forwarder]: stop"; + boost::system::error_code ec; + if (inbound_.lowest_layer().is_open()) { + inbound_.lowest_layer().shutdown(boost::asio::socket_base::shutdown_both, + ec); + inbound_.lowest_layer().close(ec); + } + + if (outbound_.lowest_layer().is_open()) { + outbound_.lowest_layer().shutdown(boost::asio::socket_base::shutdown_both, + ec); + outbound_.lowest_layer().close(ec); + } + } + + private: + /// Function taking a member function and returning a handler + /** + * Function taking a memeber function and returning a handler including + * reference counting functionality. + * + * @param handler A member function with one argument. + */ + + // template + // auto Then(Handler handler, This me) + // -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { + // return boost::bind(handler, me->SelfFromThis(), _1); + //} + + template + auto Then(Handler handler, + This me) -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { + return boost::bind(handler, me->SelfFromThis(), _1); + } + + /// It is needed to cast the shared pointer from shared_from_this because it + /// is the base class which inherit from enable_shared_from_this + std::shared_ptr SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); + } + + private: + /// The constructor is made private to ensure users only use create() + SessionForwarder(SessionManager* manager, InwardStream inbound, + ForwardStream outbound) + : inbound_(std::move(inbound)), + outbound_(std::move(outbound)), + manager_(manager) {} + + /// Start forwarding + void DoForward() { + // Make two Half Duplex links to have a Full Duplex Link + AsyncEstablishHDLink( + ReadFrom(inbound_), WriteTo(outbound_), + boost::asio::buffer(inwardBuffer_), + Then(&SessionForwarder::StopHandler, this->SelfFromThis())); + + AsyncEstablishHDLink( + ReadFrom(outbound_), WriteTo(inbound_), + boost::asio::buffer(forwardBuffer_), + Then(&SessionForwarder::StopHandler, this->SelfFromThis())); + } + + /// Stop forwarding + void StopHandler(const boost::system::error_code& ec) { + boost::system::error_code e; + manager_->stop(this->SelfFromThis(), e); + } + + private: + // The streams to forward to each other + InwardStream inbound_; + ForwardStream outbound_; + + /// The manager handling multiple SessionForwarder + SessionManager* manager_; + + // One buffer for each Half Duplex Link + buffer_type inwardBuffer_; + buffer_type forwardBuffer_; +}; + +} // ssf + +#endif // SSF_NETWORK_SESSION_FORWARDER_H diff --git a/src/common/network/socket_link.h b/src/framework/ssf/network/socket_link.h similarity index 91% rename from src/common/network/socket_link.h rename to src/framework/ssf/network/socket_link.h index 200a0d6c..e8d179f8 100644 --- a/src/common/network/socket_link.h +++ b/src/framework/ssf/network/socket_link.h @@ -1,13 +1,15 @@ -#ifndef SSF_COMMON_NETWORK_SOCKET_LINK_H_ -#define SSF_COMMON_NETWORK_SOCKET_LINK_H_ +#ifndef SSF_NETWORK_SOCKET_LINK_H_ +#define SSF_NETWORK_SOCKET_LINK_H_ -#include -#include -#include -#include +#include // NOLINT + +#include // NOLINT +#include // NOLINT +#include // NOLINT + +#include // NOLINT namespace ssf { -//----------------------------------------------------------------------------- /// Async Half Duplex Stream Socket Forwarder /** @@ -126,6 +128,7 @@ void AsyncEstablishHDLink(ReadFrom rf, WriteTo wt, AsyncTransfer(boost::system::error_code(), 0); } + } // ssf -#endif // SSF_COMMON_NETWORK_SOCKET_LINK_H_ +#endif // SSF_NETWORK_SOCKET_LINK_H_ diff --git a/src/framework/ssf/system/basic_interfaces_collection.h b/src/framework/ssf/system/basic_interfaces_collection.h new file mode 100644 index 00000000..a6d40027 --- /dev/null +++ b/src/framework/ssf/system/basic_interfaces_collection.h @@ -0,0 +1,37 @@ +#ifndef SSF_SYSTEM_BASIC_INTERFACES_COLLECTION_H_ +#define SSF_SYSTEM_BASIC_INTERFACES_COLLECTION_H_ + +#include + +#include +#include + +namespace ssf { +namespace system { + +class BasicInterfacesCollection { + public: + using PropertyTree = boost::property_tree::ptree; + using MountCallback = + std::function; + + public: + virtual ~BasicInterfacesCollection() {} + + virtual std::string GetName() = 0; + + virtual void AsyncMount(boost::asio::io_service& io_service, + const PropertyTree& property_tree, + MountCallback mount_handler) = 0; + + virtual void RemountDownInterfaces() = 0; + + virtual void Umount(const std::string& interface_name) = 0; + + virtual void UmountAll() = 0; +}; + +} // system +} // ssf + +#endif // SSF_SYSTEM_BASIC_INTERFACES_COLLECTION_H_ diff --git a/src/framework/ssf/system/specific_interfaces_collection.h b/src/framework/ssf/system/specific_interfaces_collection.h new file mode 100644 index 00000000..326947cd --- /dev/null +++ b/src/framework/ssf/system/specific_interfaces_collection.h @@ -0,0 +1,351 @@ +#ifndef SSF_SYSTEM_SPECIFIC_INTERFACES_COLLECTION_H_ +#define SSF_SYSTEM_SPECIFIC_INTERFACES_COLLECTION_H_ + +#include +#include +#include + +#include + +#include +#include + +#include "ssf/error/error.h" +#include "ssf/layer/interface_layer/basic_interface_protocol.h" +#include "ssf/layer/interface_layer/basic_interface.h" + +#include "ssf/log/log.h" + +#include "ssf/system/basic_interfaces_collection.h" + +namespace ssf { +namespace system { + +template +class SpecificInterfacesCollection : public BasicInterfacesCollection { + public: + using PropertyTree = boost::property_tree::ptree; + + using InterfaceProtocol = + ssf::layer::interface_layer::basic_InterfaceProtocol; + + template + using Interface = + ssf::layer::interface_layer::basic_Interface; + + using MountCallback = BasicInterfacesCollection::MountCallback; + + typedef typename LayerStack::endpoint Endpoint; + typedef typename LayerStack::resolver Resolver; + + private: + using TimerPtr = std::shared_ptr; + + struct InterfaceConfig { + bool connect; + Endpoint endpoint; + int ttl; + int delay; + MountCallback mount_callback; + }; + + public: + enum { DEFAULT_TTL = 1, DEFAULT_DELAY = 0 }; + + public: + explicit SpecificInterfacesCollection() : interfaces_() {} + + virtual ~SpecificInterfacesCollection() { UmountAll(); } + + virtual std::string GetName() { return LayerStack::get_name(); } + + virtual void AsyncMount(boost::asio::io_service& io_service, + const PropertyTree& property_tree, + MountCallback mount_handler) { + auto given_interface_name = property_tree.get_child_optional("interface"); + if (!given_interface_name) { + io_service.post( + boost::asio::detail::binder2( + mount_handler, + boost::system::error_code(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()), + "")); + return; + } + + std::string interface_name = given_interface_name.get().data(); + + boost::system::error_code ec; + InterfaceConfig config; + InitConfig(io_service, &config, property_tree, mount_handler, ec); + + if (ec) { + io_service.post( + boost::asio::detail::binder2( + mount_handler, + boost::system::error_code(ssf::error::cannot_resolve_endpoint, + ssf::error::get_ssf_category()), + interface_name)); + return; + } + + { + boost::recursive_mutex::scoped_lock lock_interfaces(interfaces_mutex_); + if (interfaces_.find(interface_name) != interfaces_.end()) { + io_service.post(boost::asio::detail::binder2< + MountCallback, boost::system::error_code, std::string>( + mount_handler, + boost::system::error_code(ssf::error::address_not_available, + ssf::error::get_ssf_category()), + interface_name)); + return; + } + interfaces_.emplace(interface_name, io_service); + interfaces_config_.emplace(interface_name, config); + + InitializeInterface(interface_name, config.ttl); + } + } + + virtual void RemountDownInterfaces() { + boost::recursive_mutex::scoped_lock lock_interfaces(interfaces_mutex_); + auto interface_up_it = interfaces_up_.begin(); + + while (interface_up_it != interfaces_up_.end()) { + auto interface_it = interfaces_.find(*interface_up_it); + // interface no longer managed by this collection + if (interface_it == interfaces_.end()) { + interface_up_it = interfaces_up_.erase(interface_up_it); + continue; + } else { + if (!interface_it->second.is_open()) { + auto& interface_name = *interface_up_it; + SSF_LOG(kLogTrace) << " * Interface " << interface_name << " down"; + + const auto& config = interfaces_config_[interface_name]; + InitializeInterface(*interface_up_it, config.ttl); + } + ++interface_up_it; + } + } + } + + virtual void Umount(const std::string& interface_name) { + boost::recursive_mutex::scoped_lock lock_interfaces(interfaces_mutex_); + auto interface_it = interfaces_.find(interface_name); + if (interface_it != interfaces_.end()) { + interfaces_.erase(interface_it); + interfaces_up_.erase(interface_name); + interfaces_config_.erase(interface_name); + } + } + + virtual void UmountAll() { + boost::recursive_mutex::scoped_lock lock_interfaces(interfaces_mutex_); + auto interface_it = interfaces_.begin(); + while (interface_it != interfaces_.end()) { + interfaces_config_.erase(interface_it->first); + interfaces_up_.erase(interface_it->first); + interface_it = interfaces_.erase(interface_it); + } + } + + private: + void InitConfig(boost::asio::io_service& io_service, + InterfaceConfig* p_config, const PropertyTree& property_tree, + MountCallback mount_handler, boost::system::error_code& ec) { + p_config->connect = true; + p_config->ttl = DEFAULT_TTL; + p_config->delay = DEFAULT_DELAY; + p_config->mount_callback = mount_handler; + + auto given_type = property_tree.get_child_optional("type"); + if (given_type && given_type.get().data() == "ACCEPT") { + p_config->connect = false; + } + auto given_ttl = property_tree.get_child_optional("ttl"); + if (given_ttl) { + p_config->ttl = given_ttl->get_value(); + } + auto given_delay = property_tree.get_child_optional("delay"); + if (given_delay) { + p_config->delay = given_delay->get_value(); + } + + typename Resolver::query query; + InitQuery(&query, property_tree, p_config->connect, ec); + + if (ec) { + return; + } + + Resolver resolver(io_service); + auto endpoint_it = resolver.resolve(query, ec); + + if (ec) { + return; + } + + p_config->endpoint = *endpoint_it; + } + + void InitQuery(typename Resolver::query* p_query, + const PropertyTree& property_tree, bool connect, + boost::system::error_code& ec) { + auto layer_stack = property_tree.get_child_optional("layer_stack"); + if (!layer_stack) { + ec.assign(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()); + return; + } + + LayerStack::add_params_from_property_tree(p_query, *layer_stack, connect, + ec); + } + + void InitializeInterface(const std::string& interface_name, + int ttl = DEFAULT_TTL, TimerPtr p_timer = nullptr) { + boost::recursive_mutex::scoped_lock lock_interfaces(interfaces_mutex_); + auto interface_it = interfaces_.find(interface_name); + if (interface_it == interfaces_.end()) { + return; + } + const auto& config = interfaces_config_[interface_name]; + + if (p_timer == nullptr) { + p_timer = std::make_shared( + interface_it->second.get_io_service()); + } + + if (config.connect) { + interface_it->second.async_connect( + interface_name, config.endpoint, + boost::bind(&SpecificInterfacesCollection::MountConnectHandler, this, + _1, interface_name, p_timer, ttl - 1)); + } else { + int timeout = config.ttl * config.delay; + if (timeout > 0) { + p_timer->expires_from_now(std::chrono::milliseconds(timeout)); + p_timer->async_wait( + boost::bind(&SpecificInterfacesCollection::MountAcceptTimeOut, this, + _1, p_timer, interface_name)); + } + interface_it->second.async_accept( + interface_name, config.endpoint, + boost::bind(&SpecificInterfacesCollection::MountAcceptHandler, this, + _1, interface_name, p_timer)); + } + } + + void MountConnectHandler(const boost::system::error_code& ec, + std::string interface_name, TimerPtr p_timer, + int ttl) { + boost::recursive_mutex::scoped_lock lock_interfaces(interfaces_mutex_); + auto interface_it = interfaces_.find(interface_name); + if (interface_it == interfaces_.end()) { + return; + } + + auto& config = interfaces_config_[interface_name]; + auto handler = config.mount_callback; + + if (ec) { + if (ttl <= 0) { + // the interface was not connected previously, dead end + if (interfaces_up_.find(interface_name) == interfaces_up_.end()) { + interface_it->second.get_io_service().post(boost::asio::detail::binder2< + MountCallback, boost::system::error_code, std::string>( + handler, boost::system::error_code(ssf::error::connection_aborted, + ssf::error::get_ssf_category()), + interface_name)); + interfaces_.erase(interface_name); + interfaces_config_.erase(interface_name); + } + return; + } + + p_timer->expires_from_now(std::chrono::milliseconds(config.delay)); + p_timer->async_wait([this, interface_name, p_timer, ttl]( + const boost::system::error_code& ec) { + this->InitializeInterface(interface_name, ttl, p_timer); + }); + + return; + } + + SSF_LOG(kLogTrace) << " * Interface " << interface_name << " up"; + interfaces_up_.insert(interface_name); + + interface_it->second.get_io_service().post( + boost::asio::detail::binder2( + handler, boost::system::error_code(ssf::error::success, + ssf::error::get_ssf_category()), + interface_name)); + } + + void MountAcceptHandler(const boost::system::error_code& ec, + std::string interface_name, TimerPtr p_timer) { + boost::recursive_mutex::scoped_lock lock_interfaces(interfaces_mutex_); + + p_timer->cancel(); + + auto interface_it = interfaces_.find(interface_name); + if (interface_it == interfaces_.end()) { + return; + } + + auto& config = interfaces_config_[interface_name]; + auto handler = config.mount_callback; + + if (ec) { + // the interface was not accepted previously, dead end + if (interfaces_up_.find(interface_name) == interfaces_up_.end()) { + interface_it->second.get_io_service().post(boost::asio::detail::binder2< + MountCallback, boost::system::error_code, std::string>( + handler, boost::system::error_code(ssf::error::connection_aborted, + ssf::error::get_ssf_category()), + interface_name)); + interfaces_.erase(interface_name); + interfaces_config_.erase(interface_name); + } + return; + } + + interfaces_up_.insert(interface_name); + + interface_it->second.get_io_service().post( + boost::asio::detail::binder2( + handler, boost::system::error_code(ssf::error::success, + ssf::error::get_ssf_category()), + interface_name)); + } + + void MountAcceptTimeOut(const boost::system::error_code& ec, TimerPtr p_timer, + std::string interface_name) { + if (!ec) { + boost::recursive_mutex::scoped_lock lock_interfaces(interfaces_mutex_); + auto interface_it = interfaces_.find(interface_name); + if (interface_it == interfaces_.end()) { + return; + } + boost::system::error_code close_ec; + interface_it->second.close(close_ec); + } + } + + private: + boost::recursive_mutex interfaces_mutex_; + std::map> interfaces_; + std::map interfaces_config_; + std::unordered_set interfaces_up_; +}; + +} // system +} // ssf + +#endif // SSF_SYSTEM_SPECIFIC_INTERFACES_COLLECTION_H_ diff --git a/src/framework/ssf/system/system_interfaces.cpp b/src/framework/ssf/system/system_interfaces.cpp new file mode 100644 index 00000000..ecfc81a6 --- /dev/null +++ b/src/framework/ssf/system/system_interfaces.cpp @@ -0,0 +1,140 @@ +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "ssf/error/error.h" + +#include "ssf/system/system_interfaces.h" +#include "ssf/system/specific_interfaces_collection.h" + +namespace ssf { +namespace system { + +SystemInterfaces::SystemInterfaces(boost::asio::io_service& io_service) + : io_service_(io_service), + p_worker_(nullptr), + interfaces_collections_mutex_(), + interfaces_collection_map_(), + remount_timer_(io_service_) {} + +SystemInterfaces::~SystemInterfaces() { Stop(); } + +void SystemInterfaces::UnregisterAllInterfacesCollection() { + interfaces_collection_map_.clear(); +} + +uint32_t SystemInterfaces::AsyncMount(const std::string& config_filepath, + InterfaceUpHandler interface_up_handler) { + uint32_t nb_interfaces_async_mount = 0; + SystemInterfaces::PropertyTree pt; + + try { + boost::property_tree::read_json(config_filepath, pt); + } catch (const std::exception&) { + return 0; + } + + for (auto& config_interface : pt) { + auto layer_stack = + config_interface.second.get_child_optional("layer_stack"); + if (!layer_stack) { + continue; + } + + auto optional_name = GetCollectionNameFromLayerStack(*layer_stack); + + if (!optional_name) { + continue; + } + + { + boost::recursive_mutex::scoped_lock lock_interfaces_collections( + interfaces_collections_mutex_); + auto interfaces_collection_it = + interfaces_collection_map_.find(*optional_name); + if (interfaces_collection_it == interfaces_collection_map_.end()) { + continue; + } + + interfaces_collection_it->second->AsyncMount( + io_service_, config_interface.second, interface_up_handler); + ++nb_interfaces_async_mount; + } + } + + return nb_interfaces_async_mount; +} + +void SystemInterfaces::UmountAll() { + boost::recursive_mutex::scoped_lock lock_interfaces_collections( + interfaces_collections_mutex_); + for (auto& interfaces_collection_pair : interfaces_collection_map_) { + interfaces_collection_pair.second->UmountAll(); + } +} + +void SystemInterfaces::Start(int remount_delay) { + if (!p_worker_) { + p_worker_ = std::unique_ptr( + new boost::asio::io_service::work(io_service_)); + LaunchRemountTimer(remount_delay); + } +} + +void SystemInterfaces::Stop() { + boost::system::error_code ec; + UmountAll(); + remount_timer_.cancel(ec); + if (p_worker_) { + p_worker_.reset(); + } +} + +boost::asio::io_service& SystemInterfaces::get_io_service() { + return io_service_; +} + +boost::optional SystemInterfaces::GetCollectionNameFromLayerStack( + const SystemInterfaces::PropertyTree& property_tree) { + try { + std::stringstream ss; + ss << property_tree.get_child("layer").data(); + auto sublayer = property_tree.get_child_optional("sublayer"); + while (sublayer) { + ss << "_" << sublayer->get_child("layer").data(); + sublayer = sublayer->get_child_optional("sublayer"); + } + + return ss.str(); + } catch (const std::exception&) { + return boost::optional(); + } +} + +void SystemInterfaces::LaunchRemountTimer(int remount_delay) { + remount_timer_.expires_from_now(std::chrono::seconds(remount_delay)); + remount_timer_.async_wait(boost::bind(&SystemInterfaces::RemountTimerHandler, + this, _1, remount_delay)); +} + +void SystemInterfaces::RemountTimerHandler(const boost::system::error_code& ec, + int remount_delay) { + if (!ec) { + for (auto& interfaces_collection_pair : interfaces_collection_map_) { + interfaces_collection_pair.second->RemountDownInterfaces(); + } + LaunchRemountTimer(remount_delay); + } +} + +} // system +} // ssf diff --git a/src/framework/ssf/system/system_interfaces.h b/src/framework/ssf/system/system_interfaces.h new file mode 100644 index 00000000..7361df95 --- /dev/null +++ b/src/framework/ssf/system/system_interfaces.h @@ -0,0 +1,95 @@ +#ifndef SSF_SYSTEM_SYSTEM_INTERFACES_H_ +#define SSF_SYSTEM_SYSTEM_INTERFACES_H_ + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "ssf/system/basic_interfaces_collection.h" +#include "ssf/system/specific_interfaces_collection.h" + +namespace ssf { +namespace system { + +class SystemInterfaces { + public: + using PropertyTree = boost::property_tree::ptree; + using InterfacesCollectionPtr = std::unique_ptr; + using InterfaceUpHandler = BasicInterfacesCollection::MountCallback; + + enum { DEFAULT_REMOUNT_DELAY_SEC = 60 }; + + public: + explicit SystemInterfaces(boost::asio::io_service& io_service); + + SystemInterfaces(const SystemInterfaces&) = delete; + + SystemInterfaces& operator=(const SystemInterfaces&) = delete; + + ~SystemInterfaces(); + + template + bool RegisterInterfacesCollection() { + std::string stack_id(LayerStack::get_name()); + + if (interfaces_collection_map_.find(stack_id) != + interfaces_collection_map_.end()) { + return false; + } + + interfaces_collection_map_.emplace( + stack_id, std::unique_ptr>( + new SpecificInterfacesCollection())); + + return true; + } + + template + void UnregisterInterfacesCollection() { + interfaces_collection_map_.erase(LayerStack::get_name()); + } + + void UnregisterAllInterfacesCollection(); + + uint32_t AsyncMount(const std::string& config_filepath, + InterfaceUpHandler interface_up_handler); + + void UmountAll(); + + void Start(int remount_delay = DEFAULT_REMOUNT_DELAY_SEC); + + void Stop(); + + boost::asio::io_service& get_io_service(); + + private: + boost::optional GetCollectionNameFromLayerStack( + const PropertyTree& property_tree); + + void LaunchRemountTimer(int remount_delay); + + void RemountTimerHandler(const boost::system::error_code& ec, + int remount_delay); + + private: + boost::asio::io_service& io_service_; + std::unique_ptr p_worker_; + boost::recursive_mutex interfaces_collections_mutex_; + std::map interfaces_collection_map_; + boost::asio::steady_timer remount_timer_; +}; + +} // system +} // ssf + +#endif // SSF_CORE_SYSTEM_SYSTEM_INTERFACES_H_ diff --git a/src/framework/ssf/system/system_routers.cpp b/src/framework/ssf/system/system_routers.cpp new file mode 100644 index 00000000..6599ca63 --- /dev/null +++ b/src/framework/ssf/system/system_routers.cpp @@ -0,0 +1,278 @@ +#include "ssf/system/system_routers.h" + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "ssf/error/error.h" +#include "ssf/layer/parameters.h" + +#include "ssf/layer/data_link/basic_circuit_protocol.h" +#include "ssf/layer/data_link/simple_circuit_policy.h" +#include "ssf/layer/data_link/circuit_helpers.h" +#include "ssf/layer/interface_layer/basic_interface_protocol.h" +#include "ssf/layer/physical/tcp.h" +#include "ssf/layer/physical/tlsotcp.h" + +#include "ssf/log/log.h" + +namespace ssf { +namespace system { + +SystemRouters::SystemRouters(boost::asio::io_service& io_service) + : io_service_(io_service), + system_interfaces_(io_service), + p_routers_(), + mount_infos_mutex_(), + mount_infos_() {} + +uint32_t SystemRouters::AsyncConfig( + const std::string& interfaces_config_filepath, + const std::string& routers_config_filepath, boost::system::error_code& ec, + SystemRouters::AllRoutersUpHandler all_up_handler) { + PropertyTree pt; + + try { + boost::property_tree::read_json(routers_config_filepath, pt); + } catch (const std::exception&) { + ec.assign(ssf::error::bad_file_descriptor, ssf::error::get_ssf_category()); + io_service_.post(boost::asio::detail::binder1( + all_up_handler, ec)); + return 0; + } + + std::unordered_set added_routers; + + for (auto& config_router : pt) { + auto given_router_name = config_router.second.get_child_optional("router"); + if (!given_router_name) { + ec.assign(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()); + break; + } + + std::string router_name = given_router_name->data().c_str(); + auto p_router = RoutedProtocol::get_router(router_name); + + // A router already exists with this name, stop + if (p_router) { + ec.assign(ssf::error::device_or_resource_busy, + ssf::error::get_ssf_category()); + break; + } + + RoutedProtocol::StartRouter(router_name, io_service_); + added_routers.insert(router_name); + p_router = RoutedProtocol::get_router(router_name); + p_routers_[router_name] = p_router; + + const auto& given_networks = config_router.second.get_child_optional("networks"); + const auto& given_routes = config_router.second.get_child_optional("routes"); + + if (!given_networks && !given_routes) { + ec.assign(ssf::error::missing_config_parameters, + ssf::error::get_ssf_category()); + break; + } + + if (given_networks) { + AddNetworksFromPropertyTree(router_name, *given_networks, ec); + } + + if (!ec) { + if (given_routes) { + AddRoutesFromPropertyTree(p_router.get(), *given_routes, ec); + } + } + + if (ec) { + break; + } + } + + // If an error occured, stop all previous routers configured + if (ec) { + SSF_LOG(kLogError) << "Error when configuring router"; + for (auto& router_name : added_routers) { + SSF_LOG(kLogError) << " * Remove router " << router_name; + StopRouter(router_name); + } + added_routers.clear(); + io_service_.post(boost::asio::detail::binder1( + all_up_handler, ec)); + } + + system_interfaces_.AsyncMount(interfaces_config_filepath, + boost::bind(&SystemRouters::InterfaceUpHandler, + this, _1, _2, all_up_handler)); + + return static_cast(added_routers.size()); +} + +void SystemRouters::Start(int remount_delay) { + using TCPProtocol = ssf::layer::physical::TCPPhysicalLayer; + using TLSoTCPProtocol = + ssf::layer::physical::TLSboTCPPhysicalLayer; + using CircuitTCPProtocol = + ssf::layer::data_link::basic_CircuitProtocol< + TCPProtocol, ssf::layer::data_link::CircuitPolicy>; + using CircuitTLSoTCPProtocol = + ssf::layer::data_link::basic_CircuitProtocol< + TLSoTCPProtocol, ssf::layer::data_link::CircuitPolicy>; + + system_interfaces_.RegisterInterfacesCollection(); + system_interfaces_.RegisterInterfacesCollection(); + system_interfaces_.RegisterInterfacesCollection(); + system_interfaces_.RegisterInterfacesCollection(); + + system_interfaces_.Start(remount_delay); +} + +void SystemRouters::Stop() { + StopAllRouters(); + system_interfaces_.Stop(); + system_interfaces_.UnregisterAllInterfacesCollection(); +} + +void SystemRouters::StopAllRouters() { + auto p_router_it = p_routers_.begin(); + auto end_router_it = p_routers_.end(); + while (p_router_it != end_router_it) { + boost::system::error_code ec; + RoutedProtocol::StopRouter(p_router_it->first, ec); + p_router_it = p_routers_.erase(p_router_it); + } +} + +void SystemRouters::StopRouter(const std::string& router_name) { + auto p_router_it = p_routers_.find(router_name); + if (p_router_it != p_routers_.end()) { + boost::system::error_code ec; + RoutedProtocol::StopRouter(router_name, ec); + p_routers_.erase(router_name); + } +} + +void SystemRouters::AddNetworksFromPropertyTree(const std::string& router_name, + const PropertyTree& networks_pt, + boost::system::error_code& ec) { + for (auto& network_pt : networks_pt) { + network_address_type network_id = + RoutedProtocol::ResolveToNetworkAddress(network_pt.first.c_str(), ec); + if (ec) { + return; + } + std::string interface_id = network_pt.second.data().c_str(); + ssf::layer::LayerParameters network_parameters; + network_parameters["network_id"] = network_pt.first.c_str(); + ssf::layer::LayerParameters interface_parameters; + interface_parameters["interface_id"] = interface_id; + + ssf::layer::ParameterStack network_query; + network_query.push_back(network_parameters); + network_query.push_back(interface_parameters); + + MountNetworkInfo mount_info; + mount_info.network_id = network_id; + mount_info.network_query = network_query; + mount_info.router_name = router_name; + + { + boost::recursive_mutex::scoped_lock lock_mount_infos(mount_infos_mutex_); + mount_infos_.emplace(interface_id, mount_info); + } + } +} + +void SystemRouters::AddRoutesFromPropertyTree(SystemRouters::Router* p_router, + const PropertyTree& routes_pt, + boost::system::error_code& ec) { + for (auto& route_pt : routes_pt) { + network_address_type from_network_id = + RoutedProtocol::ResolveToNetworkAddress(route_pt.first.c_str(), ec); + + network_address_type to_network_id = + RoutedProtocol::ResolveToNetworkAddress(route_pt.second.data().c_str(), + ec); + if (ec) { + return; + } + p_router->add_route(from_network_id, to_network_id, ec); + if (ec) { + return; + } + } +} + +void SystemRouters::InterfaceUpHandler( + const boost::system::error_code& ec, + const SystemRouters::interface_id& interface_name, + SystemRouters::AllRoutersUpHandler all_up_handler) { + boost::recursive_mutex::scoped_lock lock_mount_infos(mount_infos_mutex_); + auto mount_info_it = mount_infos_.find(interface_name); + if (mount_info_it == mount_infos_.end()) { + return; + } + + auto& mount_info = mount_info_it->second; + + if (ec) { + SSF_LOG(kLogError) << " * Interface " << interface_name + << " error : " << ec.message(); + mount_infos_.erase(interface_name); + PostAllUpHandler(ec, all_up_handler); + return; + } + + auto p_router = RoutedProtocol::get_router(mount_info_it->second.router_name); + if (!p_router) { + mount_infos_.erase(mount_info_it); + PostAllUpHandler(boost::system::error_code(ssf::error::identifier_removed, + ssf::error::get_ssf_category()), + all_up_handler); + return; + } + + NetworkProtocol::resolver network_resolver(io_service_); + boost::system::error_code add_network_ec; + auto network_ep_it = + network_resolver.resolve(mount_info.network_query, add_network_ec); + if (ec) { + mount_infos_.erase(mount_info_it); + PostAllUpHandler( + boost::system::error_code(ssf::error::cannot_resolve_endpoint, + ssf::error::get_ssf_category()), + all_up_handler); + return; + } + + p_router->add_network(mount_info.network_id, *network_ep_it, add_network_ec); + mount_infos_.erase(mount_info_it); + + PostAllUpHandler(boost::system::error_code(ssf::error::success, + ssf::error::get_ssf_category()), + all_up_handler); +} + +void SystemRouters::PostAllUpHandler(const boost::system::error_code& all_up_ec, + AllRoutersUpHandler all_up_handler) { + boost::recursive_mutex::scoped_lock lock_mount_infos(mount_infos_mutex_); + if (mount_infos_.empty()) { + io_service_.post(boost::asio::detail::binder1( + all_up_handler, all_up_ec)); + } +} + +} // system +} // ssf diff --git a/src/framework/ssf/system/system_routers.h b/src/framework/ssf/system/system_routers.h new file mode 100644 index 00000000..9641bb7e --- /dev/null +++ b/src/framework/ssf/system/system_routers.h @@ -0,0 +1,100 @@ +#ifndef SSF_CORE_SYSTEM_SYSTEM_ROUTERS_H_ +#define SSF_CORE_SYSTEM_SYSTEM_ROUTERS_H_ + +#include + +#include +#include +#include + +#include + +#include +#include +#include + +#include "ssf/layer/network/basic_network_protocol.h" +#include "ssf/layer/routing/basic_routed_protocol.h" + +#include "ssf/system/system_interfaces.h" + +namespace ssf { +namespace system { + +class SystemRouters { + private: + using InterfaceProtocol = + ssf::layer::interface_layer::basic_InterfaceProtocol; + using NetworkProtocol = + ssf::layer::network::basic_NetworkProtocol; + + public: + using RoutedProtocol = + ssf::layer::routing::basic_RoutedProtocol; + using Router = RoutedProtocol::router_type; + + private: + using interface_id = InterfaceProtocol::endpoint_context_type; + using network_address_type = RoutedProtocol::network_address_type; + using router_prefix_type = RoutedProtocol::prefix_type; + using PropertyTree = boost::property_tree::ptree; + using AllRoutersUpHandler = + std::function; + + private: + struct MountNetworkInfo { + std::string router_name; + network_address_type network_id; + NetworkProtocol::resolver::query network_query; + }; + + public: + explicit SystemRouters(boost::asio::io_service& io_service); + + SystemRouters(const SystemRouters&) = delete; + + SystemRouters& operator=(const SystemRouters&) = delete; + + uint32_t AsyncConfig(const std::string& interfaces_config_filepath, + const std::string& routers_config_filepath, + boost::system::error_code& ec, + AllRoutersUpHandler all_up_handler = + [](const boost::system::error_code&) {}); + + void Start(int remount_delay = SystemInterfaces::DEFAULT_REMOUNT_DELAY_SEC); + + void Stop(); + + void StopAllRouters(); + + void StopRouter(const std::string& router_name); + + private: + void AddNetworksFromPropertyTree(const std::string& router_name, + const PropertyTree& networks_ptree, + boost::system::error_code& ec); + + void AddRoutesFromPropertyTree(Router* p_router, + const PropertyTree& routes_ptree, + boost::system::error_code& ec); + + void InterfaceUpHandler(const boost::system::error_code& ec, + const interface_id& interface_name, + AllRoutersUpHandler all_up_handler = + [](const boost::system::error_code&) {}); + + void PostAllUpHandler(const boost::system::error_code& ec, + AllRoutersUpHandler all_up_handler); + + private: + boost::asio::io_service& io_service_; + ssf::system::SystemInterfaces system_interfaces_; + std::map> p_routers_; + boost::recursive_mutex mount_infos_mutex_; + std::map mount_infos_; +}; + +} // system +} // ssf + +#endif // SSF_CORE_SYSTEM_SYSTEM_ROUTERS_H_ diff --git a/src/common/utils/cleaner.h b/src/framework/ssf/utils/cleaner.h similarity index 73% rename from src/common/utils/cleaner.h rename to src/framework/ssf/utils/cleaner.h index 6f666934..b4a65d8f 100644 --- a/src/common/utils/cleaner.h +++ b/src/framework/ssf/utils/cleaner.h @@ -1,5 +1,5 @@ -#ifndef SSF_COMMON_UTILS_CLEANER_H_ -#define SSF_COMMON_UTILS_CLEANER_H_ +#ifndef SSF_UTILS_CLEANER_H_ +#define SSF_UTILS_CLEANER_H_ #include @@ -21,4 +21,4 @@ class ScopeCleaner { Handler handler_; }; -#endif // SSF_COMMON_UTILS_CLEANER_H_ +#endif // SSF_UTILS_CLEANER_H_ diff --git a/src/framework/ssf/utils/map_helpers.h b/src/framework/ssf/utils/map_helpers.h new file mode 100644 index 00000000..6144f7b4 --- /dev/null +++ b/src/framework/ssf/utils/map_helpers.h @@ -0,0 +1,23 @@ +#ifndef SSF_UTILS_MAP_HELPERS_H_ +#define SSF_UTILS_MAP_HELPERS_H_ + +#include + +namespace ssf { +namespace helpers { + +template +Value GetField(const Key field, const std::map& parameters) { + auto it = parameters.find(field); + + if (it == std::end(parameters)) { + return Value(); + } + + return it->second; + } + +} // helpers +} // ssf + +#endif // SSF_UTILS_MAP_HELPERS_H_ diff --git a/src/framework/tests/CMakeLists.txt b/src/framework/tests/CMakeLists.txt new file mode 100644 index 00000000..9b4d28c3 --- /dev/null +++ b/src/framework/tests/CMakeLists.txt @@ -0,0 +1,228 @@ +cmake_minimum_required(VERSION 2.8) + +enable_testing() + +set(FRAMEWORK_SRC_TESTS_DIR "${project_SRC_DIR}/framework/tests") +set(FRAMEWORK_BINARY_TESTS_DIR "${project_BINARY_DIR}/src/framework/tests") + +# --- Copy test certs for tests +set(FRAMEWORK_BINARY_CERT_TESTS_DIR ${FRAMEWORK_BINARY_TESTS_DIR}/certs) + +file(MAKE_DIRECTORY ${FRAMEWORK_BINARY_CERT_TESTS_DIR}) +file(MAKE_DIRECTORY ${FRAMEWORK_BINARY_CERT_TESTS_DIR}/trusted) + +file(COPY ${SSF_CERT_TEST_ROOT_FILES} + DESTINATION ${FRAMEWORK_BINARY_CERT_TESTS_DIR}) +file(COPY ${SSF_CERT_TEST_TRUSTED_FILES} + DESTINATION ${FRAMEWORK_BINARY_CERT_TESTS_DIR}/trusted) + +# Copy test configuration files +file(GLOB_RECURSE SYSTEM_CONFIG_TEST_FILES + "${FRAMEWORK_SRC_TESTS_DIR}/system/*.json") +set(PROXY_CONFIG_TEST_FILES + "${FRAMEWORK_SRC_TESTS_DIR}/proxy/proxy.json") + +file(MAKE_DIRECTORY ${FRAMEWORK_SRC_TESTS_DIR}/proxy) +file(MAKE_DIRECTORY ${FRAMEWORK_SRC_TESTS_DIR}/system) + +file(COPY ${PROXY_CONFIG_TEST_FILES} DESTINATION ${FRAMEWORK_BINARY_TESTS_DIR}/proxy) +file(COPY ${SYSTEM_CONFIG_TEST_FILES} DESTINATION ${FRAMEWORK_BINARY_TESTS_DIR}/system) + +include_directories( + ${OpenSSL_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS}) + +include_directories( + ${SSF_FRAMEWORK_GTEST_ROOT_DIR}/include +) + +set(SSF_FRAMEWORK_LAYER_TEST_FIXTURES_FILES + "circuit_test_fixture.h" + "datagram_protocol_helpers.h" + "interface_protocol_helpers.h" + "interface_test_fixture.h" + "proxy_test_fixture.h" + "proxy_test_fixture.cpp" + "routing_test_fixture.h" + "stream_protocol_helpers.h" + "tools.h" + "transport_test_fixture.h" + "virtual_network_helpers.h") + +# --- Log tests +add_target("log_tests" + TYPE executable ${EXEC_FLAG} TEST + LINKS + lib_ssf_network PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + "log_tests.cpp" +) +project_group("Unit Tests/Framework" log_tests) + +# --- Queue tests +add_target("queue_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + "queue_tests.cpp" +) +project_group("Unit Tests/Framework" queue_tests) + +# --- Physical layer tests +add_target("physical_layer_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + "physical_layer_tests.cpp" + ${SSF_FRAMEWORK_LAYER_TEST_FIXTURES_FILES} +) +project_group("Unit Tests/Framework" physical_layer_tests) + +# --- Proxy layer tests +add_target("proxy_layer_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + "proxy_layer_tests.cpp" + ${SSF_FRAMEWORK_LAYER_TEST_FIXTURES_FILES} +) +project_group("Unit Tests/Framework" proxy_layer_tests) + +add_target("proxy_auth_strategies_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + "proxy_auth_strategies_tests.cpp" +) +project_group("Unit Tests/Framework" proxy_auth_strategies_tests) + +# --- Link layer tests +add_target("link_layer_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + "link_layer_tests.cpp" + ${SSF_FRAMEWORK_LAYER_TEST_FIXTURES_FILES} +) +project_group("Unit Tests/Framework" link_layer_tests) + +# --- Interface layer tests +add_target("interface_layer_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + "interface_layer_tests.cpp" + ${SSF_FRAMEWORK_LAYER_TEST_FIXTURES_FILES} +) +project_group("Unit Tests/Framework" interface_layer_tests) + +# --- Routing layer tests +#add_target("routing_layer_tests" +# TYPE +# executable ${EXEC_FLAG} TEST +# LINKS +# ${OpenSSL_LIBRARIES} +# ${Boost_LIBRARIES} +# ${PLATFORM_SPECIFIC_LIB_DEP} +# lib_ssf_network +# PREFIX_SKIP .*/src +# HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" +# FILES +# "routing_layer_tests.cpp" +# ${SSF_FRAMEWORK_LAYER_TEST_FIXTURES_FILES} +#) +#project_group("Unit Tests/Framework" routing_layer_tests) + +# --- Transport layer tests +#add_target("transport_layer_tests" +# TYPE +# executable ${EXEC_FLAG} TEST +# LINKS +# ${OpenSSL_LIBRARIES} +# ${Boost_LIBRARIES} +# ${PLATFORM_SPECIFIC_LIB_DEP} +# lib_ssf_network +# PREFIX_SKIP .*/src +# HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" +# FILES +# "transport_layer_tests.cpp" +# ${SSF_FRAMEWORK_LAYER_TEST_FIXTURES_FILES} +#) +#project_group("Unit Tests/Framework" transport_layer_tests) + +# --- Interface System tests +#add_target("interfaces_system_tests" +# TYPE +# executable ${EXEC_FLAG} TEST +# LINKS +# ${OpenSSL_LIBRARIES} +# ${Boost_LIBRARIES} +# ${PLATFORM_SPECIFIC_LIB_DEP} +# lib_ssf_network +# PREFIX_SKIP .*/src +# HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" +# FILES +# "interfaces_system_tests.cpp" +# ${SSF_FRAMEWORK_LAYER_TEST_FIXTURES_FILES} +#) +#project_group("Unit Tests/Framework" interfaces_system_tests) + +# --- Router System tests +#add_target("router_system_tests" +# TYPE +# executable ${EXEC_FLAG} TEST +# LINKS +# ${OpenSSL_LIBRARIES} +# ${Boost_LIBRARIES} +# ${PLATFORM_SPECIFIC_LIB_DEP} +# lib_ssf_network +# PREFIX_SKIP .*/src +# HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" +# FILES +# "router_system_tests.cpp" +# ${SSF_FRAMEWORK_LAYER_TEST_FIXTURES_FILES} +#) +#project_group("Unit Tests/Framework" router_system_tests) diff --git a/src/framework/tests/circuit_test_fixture.h b/src/framework/tests/circuit_test_fixture.h new file mode 100644 index 00000000..ab4fe5e1 --- /dev/null +++ b/src/framework/tests/circuit_test_fixture.h @@ -0,0 +1,246 @@ +#ifndef SSF_TESTS_CIRCUIT_TEST_FIXTURE_H_ +#define SSF_TESTS_CIRCUIT_TEST_FIXTURE_H_ + +#include + +#include +#include +#include + +#include + +#include + +#include "tests/virtual_network_helpers.h" + +#include "ssf/layer/parameters.h" + +#include "ssf/layer/data_link/circuit_helpers.h" +#include "ssf/layer/data_link/basic_circuit_protocol.h" +#include "ssf/layer/data_link/simple_circuit_policy.h" +#include "ssf/layer/physical/tcp.h" +#include "ssf/layer/physical/tlsotcp.h" + +#include "tests/virtual_network_helpers.h" + +class CircuitTestFixture : public ::testing::Test { + public: + using TLSPhysicalProtocol = ssf::layer::physical::TLSboTCPPhysicalLayer; + using TLSCircuitProtocol = ssf::layer::data_link::basic_CircuitProtocol< + TLSPhysicalProtocol, ssf::layer::data_link::CircuitPolicy>; + + using PhysicalProtocol = ssf::layer::physical::TCPPhysicalLayer; + using CircuitProtocol = ssf::layer::data_link::basic_CircuitProtocol< + PhysicalProtocol, ssf::layer::data_link::CircuitPolicy>; + + protected: + using TLSAcceptorsMap = std::map; + using TLSResolver = TLSCircuitProtocol::resolver; + + using AcceptorsMap = std::map; + using Resolver = CircuitProtocol::resolver; + + protected: + CircuitTestFixture() + : io_service_(), + ssl_circuit_acceptors_(), + circuit_acceptors_(), + ssl_resolver_(io_service_), + resolver_(io_service_), + threads_(), + p_work_(new boost::asio::io_service::work((io_service_))) {} + + virtual void SetUp() { + InitTLSCircuitNodes(); + InitCircuitNodes(); + auto lambda = [this]() { this->io_service_.run(); }; + + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads_.create_thread(lambda); + } + } + + virtual void TearDown() { + boost::system::error_code ec; + for (auto& ssl_circuit_pair : ssl_circuit_acceptors_) { + ssl_circuit_pair.second.close(ec); + } + ssl_circuit_acceptors_.clear(); + for (auto& circuit_pair : circuit_acceptors_) { + circuit_pair.second.close(ec); + } + circuit_acceptors_.clear(); + p_work_.reset(); + threads_.join_all(); + } + + ssf::layer::data_link::NodeParameterList GetClientTLSNodes() { + ssf::layer::data_link::NodeParameterList nodes; + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode({{"addr", "127.0.0.1"}, {"port", "8000"}}); + nodes.AddTopLayerToBackNode( + tests::virtual_network_helpers::tls_client_parameters); + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode({{"addr", "127.0.0.1"}, {"port", "8001"}}); + nodes.AddTopLayerToBackNode( + tests::virtual_network_helpers::tls_client_parameters); + + return nodes; + } + + ssf::layer::data_link::NodeParameterList GetClientDefaultTLSNodes() { + ssf::layer::data_link::NodeParameterList nodes; + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode({{"addr", "127.0.0.1"}, {"port", "8000"}}); + nodes.AddTopLayerToBackNode( + tests::virtual_network_helpers::tls_client_parameters); + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode({{"addr", "127.0.0.1"}, {"port", "8001"}}); + nodes.AddTopLayerToBackNode( + tests::virtual_network_helpers::tls_default_server_parameters); + + return nodes; + } + + ssf::layer::data_link::NodeParameterList GetClientNodes() { + ssf::layer::data_link::NodeParameterList nodes; + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode({{"addr", "127.0.0.1"}, {"port", "7000"}}); + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode({{"addr", "127.0.0.1"}, {"port", "7001"}}); + + return nodes; + } + + private: + void InitTLSCircuitNodes() { + boost::system::error_code ec1; + // TLS Circuit node listening on 8000 + ssf::layer::LayerParameters hop1_physical_parameters; + hop1_physical_parameters["port"] = "8000"; + + ssf::layer::ParameterStack hop1_next_layers_parameters; + hop1_next_layers_parameters.push_front(hop1_physical_parameters); + hop1_next_layers_parameters.push_front( + tests::virtual_network_helpers::tls_server_parameters); + + ssf::layer::ParameterStack default_parameters = { + {}, tests::virtual_network_helpers::tls_server_parameters, {}}; + + ssf::layer::ParameterStack hop1_parameters( + ssf::layer::data_link::make_forwarding_acceptor_parameter_stack( + "ssl_hop1", default_parameters, hop1_next_layers_parameters)); + + auto hop1_endpoint_it = ssl_resolver_.resolve(hop1_parameters, ec1); + if (ec1) { + SSF_LOG(kLogError) << "Fail resolving SSL hop1_endpoint"; + } + + // TLS Circuit node listening on 8001 + boost::system::error_code ec2; + ssf::layer::LayerParameters hop2_physical_parameters; + hop2_physical_parameters["port"] = "8001"; + + ssf::layer::ParameterStack hop2_next_layers_parameters; + hop2_next_layers_parameters.push_front(hop2_physical_parameters); + hop2_next_layers_parameters.push_front( + tests::virtual_network_helpers::tls_server_parameters); + + ssf::layer::ParameterStack hop2_parameters( + ssf::layer::data_link::make_forwarding_acceptor_parameter_stack( + "ssl_hop2", default_parameters, hop2_next_layers_parameters)); + + auto hop2_endpoint_it = ssl_resolver_.resolve(hop2_parameters, ec2); + if (ec2) { + SSF_LOG(kLogError) << "Fail resolving SSL hop2_endpoint"; + } + + if (!ec1) { + TLSCircuitProtocol::endpoint hop1_endpoint(*hop1_endpoint_it); + auto hop1_it = ssl_circuit_acceptors_.emplace( + 1, TLSCircuitProtocol::acceptor(io_service_)); + boost::system::error_code hop1_ec; + hop1_it.first->second.open(); + hop1_it.first->second.set_option(boost::asio::socket_base::reuse_address(true), + ec1); + hop1_it.first->second.bind(hop1_endpoint, hop1_ec); + if (!hop1_ec) { + hop1_it.first->second.listen(100, hop1_ec); + } + } + + if (!ec2) { + TLSCircuitProtocol::endpoint hop2_endpoint(*hop2_endpoint_it); + auto hop2_it = ssl_circuit_acceptors_.emplace( + 2, TLSCircuitProtocol::acceptor(io_service_)); + boost::system::error_code hop2_ec; + hop2_it.first->second.open(); + hop2_it.first->second.set_option(boost::asio::socket_base::reuse_address(true), + ec2); + if (!hop2_ec) { + hop2_it.first->second.bind(hop2_endpoint, hop2_ec); + hop2_it.first->second.listen(100, hop2_ec); + } + } + } + + void InitCircuitNodes() { + boost::system::error_code ec; + // Circuit node listening on 7000 + ssf::layer::LayerParameters hop1_physical_parameters; + hop1_physical_parameters["port"] = "7000"; + + ssf::layer::ParameterStack hop1_next_layers_parameters; + hop1_next_layers_parameters.push_front(hop1_physical_parameters); + + ssf::layer::ParameterStack hop1_parameters( + ssf::layer::data_link::make_forwarding_acceptor_parameter_stack( + "hop1", {}, hop1_next_layers_parameters)); + + auto hop1_endpoint_it = resolver_.resolve(hop1_parameters, ec); + CircuitProtocol::endpoint hop1_endpoint(*hop1_endpoint_it); + + // Circuit node listening on 7001 + ssf::layer::LayerParameters hop2_physical_parameters; + hop2_physical_parameters["port"] = "7001"; + + ssf::layer::ParameterStack hop2_next_layers_parameters; + hop2_next_layers_parameters.push_front(hop2_physical_parameters); + + ssf::layer::ParameterStack hop2_parameters( + ssf::layer::data_link::make_forwarding_acceptor_parameter_stack( + "hop2", {}, hop2_next_layers_parameters)); + + auto hop2_endpoint_it = resolver_.resolve(hop2_parameters, ec); + CircuitProtocol::endpoint hop2_endpoint(*hop2_endpoint_it); + + auto hop1_it = + circuit_acceptors_.emplace(1, CircuitProtocol::acceptor(io_service_)); + boost::system::error_code hop1_ec; + hop1_it.first->second.open(); + hop1_it.first->second.set_option(boost::asio::socket_base::reuse_address(true), + ec); + hop1_it.first->second.bind(hop1_endpoint, hop1_ec); + hop1_it.first->second.listen(100, hop1_ec); + + auto hop2_it = + circuit_acceptors_.emplace(2, CircuitProtocol::acceptor(io_service_)); + boost::system::error_code hop2_ec; + hop2_it.first->second.open(); + hop2_it.first->second.set_option(boost::asio::socket_base::reuse_address(true), + ec); + hop2_it.first->second.bind(hop2_endpoint, hop2_ec); + hop2_it.first->second.listen(100, hop2_ec); + } + + protected: + boost::asio::io_service io_service_; + TLSAcceptorsMap ssl_circuit_acceptors_; + AcceptorsMap circuit_acceptors_; + TLSResolver ssl_resolver_; + Resolver resolver_; + boost::thread_group threads_; + std::unique_ptr p_work_; +}; + +#endif // SSF_TESTS_CIRCUIT_TEST_FIXTURE_H_ diff --git a/src/framework/tests/datagram_protocol_helpers.h b/src/framework/tests/datagram_protocol_helpers.h new file mode 100644 index 00000000..d9b37109 --- /dev/null +++ b/src/framework/tests/datagram_protocol_helpers.h @@ -0,0 +1,561 @@ +#ifndef SSF_TESTS_DATAGRAM_PROTOCOL_HELPERS_H_ +#define SSF_TESTS_DATAGRAM_PROTOCOL_HELPERS_H_ + +#include + +#include +#include + +#include + +#include + +#include "ssf/layer/parameters.h" + +#include "ssf/log/log.h" + +#include "tests/virtual_network_helpers.h" +#include "tests/tools.h" + +template +void TestDatagramProtocolPerfHalfDuplex( + ssf::layer::ParameterStack socket1_parameters, + ssf::layer::ParameterStack socket2_parameters, + uint64_t size_in_mtu_multiples) { + using Buffer = std::vector; + + SSF_LOG(kLogTrace) << " * TestDatagramProtocolPerfHalfDuplex"; + SSF_LOG(kLogTrace) << " MTU : " << DatagramProtocol::mtu; + + std::promise s_finished1; + std::promise finished1; + + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + + uint64_t data_size_to_send = DatagramProtocol::mtu * size_in_mtu_multiples; + + auto p_bandwidth1 = + std::make_shared(data_size_to_send * 8ULL); + + uint64_t s_count1 = 0; + uint64_t count1 = 0; + + Buffer buffer1(DatagramProtocol::mtu); + Buffer r_buffer2(DatagramProtocol::mtu); + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + typename DatagramProtocol::socket socket1(io_service); + typename DatagramProtocol::socket socket2(io_service); + typename DatagramProtocol::resolver resolver(io_service); + + auto socket1_endpoint_it = resolver.resolve(socket1_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving should not be in error: " << resolve_ec.message(); + typename DatagramProtocol::endpoint socket1_endpoint(*socket1_endpoint_it); + + auto socket2_endpoint_it = resolver.resolve(socket2_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving should not be in error: " << resolve_ec.message(); + typename DatagramProtocol::endpoint socket2_endpoint(*socket2_endpoint_it); + + typename DatagramProtocol::endpoint socket2_r_endpoint; + + tests::virtual_network_helpers::ReceiveHandler received_handler2; + tests::virtual_network_helpers::SendHandler sent_handler1; + + received_handler2 = [&, p_bandwidth1](const boost::system::error_code& ec, + std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers(buffer1, r_buffer2, + length)); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0, length); + + count1 += length; + if (count1 < data_size_to_send) { + socket2.async_receive_from(boost::asio::buffer(r_buffer2), + socket2_r_endpoint, received_handler2); + } else { + ASSERT_EQ(data_size_to_send, count1); + finished1.set_value(true); + } + }; + + sent_handler1 = [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + + s_count1 += length; + + if (count1 < data_size_to_send) { + socket1.async_send_to(boost::asio::buffer(buffer1), socket2_endpoint, + sent_handler1); + } else { + SSF_LOG(kLogTrace) << " Packet loss1: " + << static_cast(s_count1 - count1) / + DatagramProtocol::mtu; + s_finished1.set_value(true); + } + }; + + boost::system::error_code bind_ec; + socket1.open(); + socket1.bind(socket1_endpoint, bind_ec); + ASSERT_EQ(0, bind_ec.value()) + << "Bind socket1 should not be in error: " << bind_ec.message(); + socket2.open(); + socket2.bind(socket2_endpoint, bind_ec); + ASSERT_EQ(0, bind_ec.value()) + << "Bind socket2 should not be in error: " << bind_ec.message(); + + boost::system::error_code ec; + + socket2.async_receive_from(boost::asio::buffer(r_buffer2), socket2_r_endpoint, + received_handler2); + + p_bandwidth1->ResetTime(); + socket1.async_send_to(boost::asio::buffer(buffer1), socket2_endpoint, + sent_handler1); + p_bandwidth1.reset(); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + s_finished1.get_future().wait(); + finished1.get_future().wait(); + + socket1.close(ec); + socket2.close(ec); + + threads.join_all(); +} + +/// Bind two sockets to the endpoints resolved by given parameters +/// Send data in ping pong mode +template +void TestDatagramProtocolPerfFullDuplex( + ssf::layer::ParameterStack socket1_parameters, + ssf::layer::ParameterStack socket2_parameters, + uint64_t size_in_mtu_multiples) { + using Buffer = std::vector; + + SSF_LOG(kLogTrace) << " * TestDatagramProtocolPerfFullDuplex"; + SSF_LOG(kLogTrace) << " MTU : " << DatagramProtocol::mtu; + + std::promise s_finished1; + std::promise finished1; + std::promise s_finished2; + std::promise finished2; + + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + + uint64_t data_size_to_send = DatagramProtocol::mtu * size_in_mtu_multiples; + + auto p_bandwidth1 = + std::make_shared(data_size_to_send * 8ULL); + auto p_bandwidth2 = + std::make_shared(data_size_to_send * 8ULL); + + uint64_t s_count1 = 0; + uint64_t count1 = 0; + uint64_t s_count2 = 0; + uint64_t count2 = 0; + + Buffer buffer1(DatagramProtocol::mtu); + Buffer buffer2(DatagramProtocol::mtu); + Buffer r_buffer1(DatagramProtocol::mtu); + Buffer r_buffer2(DatagramProtocol::mtu); + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1); + tests::virtual_network_helpers::ResetBuffer(&buffer2, 1); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + typename DatagramProtocol::socket socket1(io_service); + typename DatagramProtocol::socket socket2(io_service); + typename DatagramProtocol::resolver resolver(io_service); + + auto socket1_endpoint_it = resolver.resolve(socket1_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving should not be in error: " << resolve_ec.message(); + typename DatagramProtocol::endpoint socket1_endpoint(*socket1_endpoint_it); + + auto socket2_endpoint_it = resolver.resolve(socket2_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving should not be in error: " << resolve_ec.message(); + typename DatagramProtocol::endpoint socket2_endpoint(*socket2_endpoint_it); + + typename DatagramProtocol::endpoint socket1_r_endpoint; + typename DatagramProtocol::endpoint socket2_r_endpoint; + + tests::virtual_network_helpers::ReceiveHandler received_handler1; + tests::virtual_network_helpers::ReceiveHandler received_handler2; + tests::virtual_network_helpers::SendHandler sent_handler1; + tests::virtual_network_helpers::SendHandler sent_handler2; + + received_handler1 = [&, p_bandwidth2](const boost::system::error_code& ec, + std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers(buffer2, r_buffer1, + length)); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + + count2 += length; + + if (count2 < data_size_to_send) { + socket1.async_receive_from(boost::asio::buffer(r_buffer1), + socket1_r_endpoint, received_handler1); + } else { + SSF_LOG(kLogTrace) << " Received2 finished"; + ASSERT_EQ(data_size_to_send, count2); + finished2.set_value(true); + } + }; + + received_handler2 = [&, p_bandwidth1](const boost::system::error_code& ec, + std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers(buffer1, r_buffer2, + length)); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0, length); + + count1 += length; + + if (count1 < data_size_to_send) { + socket2.async_receive_from(boost::asio::buffer(r_buffer2), + socket2_r_endpoint, received_handler2); + } else { + SSF_LOG(kLogTrace) << " Received1 finished"; + ASSERT_EQ(data_size_to_send, count1); + finished1.set_value(true); + } + }; + + sent_handler1 = [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + + s_count1 += length; + + if (count1 < data_size_to_send) { + socket1.async_send_to(boost::asio::buffer(buffer1), socket2_endpoint, + sent_handler1); + } else { + SSF_LOG(kLogTrace) << " Packet loss1: " + << static_cast(s_count1 - count1) / + DatagramProtocol::mtu; + s_finished1.set_value(true); + } + }; + + sent_handler2 = [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + + s_count2 += length; + + if (count2 < data_size_to_send) { + socket2.async_send_to(boost::asio::buffer(buffer2), socket1_endpoint, + sent_handler2); + } else { + SSF_LOG(kLogTrace) << " Packet loss2: " + << static_cast(s_count2 - count2) / + DatagramProtocol::mtu; + s_finished2.set_value(true); + } + }; + + boost::system::error_code bind_ec; + socket1.open(); + socket1.bind(socket1_endpoint, bind_ec); + ASSERT_EQ(0, bind_ec.value()) + << "Bind socket1 should not be in error: " << bind_ec.message(); + socket2.open(); + socket2.bind(socket2_endpoint, bind_ec); + ASSERT_EQ(0, bind_ec.value()) + << "Bind socket2 should not be in error: " << bind_ec.message(); + + boost::system::error_code ec; + + socket1.async_receive_from(boost::asio::buffer(r_buffer1), socket1_r_endpoint, + received_handler1); + socket2.async_receive_from(boost::asio::buffer(r_buffer2), socket2_r_endpoint, + received_handler2); + + p_bandwidth1->ResetTime(); + socket1.async_send_to(boost::asio::buffer(buffer1), socket2_endpoint, + sent_handler1); + p_bandwidth1.reset(); + + p_bandwidth2->ResetTime(); + socket2.async_send_to(boost::asio::buffer(buffer2), socket1_endpoint, + sent_handler2); + p_bandwidth2.reset(); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + s_finished1.get_future().wait(); + finished1.get_future().wait(); + s_finished2.get_future().wait(); + finished2.get_future().wait(); + + socket1.close(ec); + socket2.close(ec); + + threads.join_all(); +} + +/// Connect socket1 to the endpoint defined by socket1_d_parameters +/// Bind socket2 to the endpoint defined by socket2_parameters +/// async_send, async_receive on socket1 +/// async_send_to, async_receive_from on socket2 +template +void TestConnectionDatagramProtocol( + ssf::layer::ParameterStack socket1_d_parameters, + ssf::layer::ParameterStack socket2_parameters, uint64_t max_packets) { + typedef std::array Buffer; + boost::asio::io_service io_service; + + uint64_t count1 = 0; + uint64_t count2 = 0; + + Buffer buffer1; + Buffer buffer2; + Buffer r_buffer1; + Buffer r_buffer2; + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1); + tests::virtual_network_helpers::ResetBuffer(&buffer2, 2); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + boost::system::error_code resolve_ec; + typename DatagramProtocol::resolver resolver(io_service); + + auto socket1_d_endpoint_it = + resolver.resolve(socket1_d_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving should not be in error: " << resolve_ec.message(); + typename DatagramProtocol::endpoint socket1_d_endpoint( + *socket1_d_endpoint_it); + + auto socket2_endpoint_it = resolver.resolve(socket2_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving should not be in error: " << resolve_ec.message(); + typename DatagramProtocol::endpoint socket2_endpoint(*socket2_endpoint_it); + + typename DatagramProtocol::endpoint socket1_endpoint; + typename DatagramProtocol::endpoint socket2_r_endpoint; + + typename DatagramProtocol::socket socket1(io_service); + typename DatagramProtocol::socket socket2(io_service); + + tests::virtual_network_helpers::ConnectHandler connected1; + tests::virtual_network_helpers::ReceiveHandler received_handler1; + tests::virtual_network_helpers::ReceiveHandler received_handler2; + tests::virtual_network_helpers::SendHandler sent_handler1; + tests::virtual_network_helpers::SendHandler sent_handler2; + + connected1 = [&](const boost::system::error_code& ec) { + ASSERT_EQ(0, ec.value()) + << "Connect handler should not be in error: " << ec.message(); + + boost::system::error_code local_endpoint_ec; + socket1_endpoint = socket1.local_endpoint(local_endpoint_ec); + ASSERT_EQ(0, local_endpoint_ec.value()) + << "Local endpoint should not be in error: " + << local_endpoint_ec.message(); + + socket1.async_send(boost::asio::buffer(buffer1), sent_handler1); + socket2.async_send_to(boost::asio::buffer(buffer2), socket1_endpoint, + sent_handler2); + }; + + received_handler1 = + [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer2, r_buffer1, length)); + + ++count1; + if (count1 < max_packets) { + socket1.async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + ASSERT_EQ(max_packets, count1); + socket1.close(); + } + }; + + received_handler2 = + [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer1, r_buffer2, length)); + ASSERT_TRUE(socket1_endpoint == socket2_r_endpoint) + << "Received endpoint from socket2 is not socket1 endpoint"; + ++count2; + if (count2 < max_packets) { + socket2.async_send_to(boost::asio::buffer(buffer2), + socket2_r_endpoint, sent_handler2); + } else { + ASSERT_EQ(max_packets, count2); + socket2.close(); + } + }; + + sent_handler1 = [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Send handler should not be in error: " << ec.message(); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + socket1.async_receive(boost::asio::buffer(r_buffer1), received_handler1); + }; + + sent_handler2 = [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Send handler should not be in error: " << ec.message(); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + socket2.async_receive_from(boost::asio::buffer(r_buffer2), + socket2_r_endpoint, received_handler2); + }; + + boost::system::error_code bind_ec; + socket2.open(); + socket2.bind(socket2_endpoint, bind_ec); + ASSERT_EQ(0, bind_ec.value()) + << "Bind socket2 should not be in error: " << bind_ec.message(); + + std::cout << "Transfering " << DatagramProtocol::next_layer_protocol::mtu + << " * " << max_packets << " = " + << DatagramProtocol::next_layer_protocol::mtu* max_packets + << " bytes in both direction" << std::endl; + + socket1.async_connect(socket1_d_endpoint, connected1); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +template +void TestBindSendLocalDatagramProtocol( + ssf::layer::ParameterStack socket1_parameters, + ssf::layer::ParameterStack socket2_parameters, uint64_t max_packets) { + typedef std::array Buffer; + boost::asio::io_service io_service; + + uint64_t count1 = 0; + uint64_t count2 = 0; + + Buffer buffer1; + Buffer buffer2; + Buffer r_buffer1; + Buffer r_buffer2; + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1); + tests::virtual_network_helpers::ResetBuffer(&buffer2, 2); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + boost::system::error_code resolve_ec; + typename DatagramProtocol::resolver resolver(io_service); + + auto socket1_endpoint_it = resolver.resolve(socket1_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving should not be in error: " << resolve_ec.message(); + typename DatagramProtocol::endpoint socket1_endpoint(*socket1_endpoint_it); + + auto socket2_endpoint_it = resolver.resolve(socket2_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving should not be in error: " << resolve_ec.message(); + typename DatagramProtocol::endpoint socket2_endpoint(*socket2_endpoint_it); + + typename DatagramProtocol::endpoint socket1_r_endpoint; + typename DatagramProtocol::endpoint socket2_r_endpoint; + + typename DatagramProtocol::socket socket1(io_service); + typename DatagramProtocol::socket socket2(io_service); + + tests::virtual_network_helpers::ReceiveHandler received_handler1; + tests::virtual_network_helpers::ReceiveHandler received_handler2; + tests::virtual_network_helpers::SendHandler sent_handler1; + tests::virtual_network_helpers::SendHandler sent_handler2; + + received_handler1 = + [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer2, r_buffer1, length)); + ++count1; + if (count1 < max_packets) { + socket1.async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + ASSERT_EQ(max_packets, count1); + socket1.close(); + } + }; + + received_handler2 = + [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer1, r_buffer2, length)); + ++count2; + if (count2 < max_packets) { + socket2.async_send(boost::asio::buffer(buffer2), sent_handler2); + } else { + ASSERT_EQ(max_packets, count2); + socket2.close(); + } + }; + + sent_handler1 = [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Sent handler should not be in error: " << ec.message(); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + if (!ec) { + socket1.async_receive(boost::asio::buffer(r_buffer1), received_handler1); + } + }; + + sent_handler2 = [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Sent handler should not be in error: " << ec.message(); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + if (!ec) { + socket2.async_receive(boost::asio::buffer(r_buffer2), received_handler2); + } + }; + + boost::system::error_code bind_ec; + socket1.open(); + socket1.bind(socket1_endpoint, bind_ec); + ASSERT_EQ(0, bind_ec.value()) + << "Bind socket1 should not be in error: " << bind_ec.message(); + socket2.open(); + socket2.bind(socket2_endpoint, bind_ec); + ASSERT_EQ(0, bind_ec.value()) + << "Bind socket2 should not be in error: " << bind_ec.message(); + + std::cout << "Transfering " << DatagramProtocol::next_layer_protocol::mtu + << " * " << max_packets << " = " + << DatagramProtocol::next_layer_protocol::mtu* max_packets + << " bytes in both direction" << std::endl; + + socket1.async_send(boost::asio::buffer(buffer1), sent_handler1); + socket2.async_send(boost::asio::buffer(buffer2), sent_handler2); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +#endif // SSF_TESTS_DATAGRAM_PROTOCOL_HELPERS_H_ diff --git a/src/framework/tests/interface_layer_tests.cpp b/src/framework/tests/interface_layer_tests.cpp new file mode 100644 index 00000000..23442ea4 --- /dev/null +++ b/src/framework/tests/interface_layer_tests.cpp @@ -0,0 +1,99 @@ +#include + +#include "tests/interface_protocol_helpers.h" +#include "tests/interface_test_fixture.h" + +#include "ssf/layer/parameters.h" + +ssf::layer::LayerParameters tcp_acceptor_parameters = {{"port", "9000"}}; + +ssf::layer::LayerParameters tcp_client_parameters = {{"addr", "127.0.0.1"}, + {"port", "9000"}}; + +TEST(InterfaceLayerTest, InterfaceSetup) { + using SimpleLinkProtocol = ssf::layer::physical::TCPPhysicalLayer; + + boost::asio::io_service io_service; + std::unique_ptr p_work( + new boost::asio::io_service::work(io_service)); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + ssf::layer::ParameterStack simple2_parameters; + simple2_parameters.push_back(tcp_acceptor_parameters); + + ssf::layer::ParameterStack simple1_parameters; + simple1_parameters.push_back(tcp_client_parameters); + + tests::interface_protocol_helpers::InterfaceSetup( + io_service, {tcp_client_parameters}, "int1", {tcp_acceptor_parameters}, + "int2"); + + p_work.reset(); + threads.join_all(); +} + +TEST_F(InterfaceTestFixture, Empty) { + auto& interface_manager = tests::interface_protocol_helpers:: + InterfaceProtocol::get_interface_manager(); + + ASSERT_EQ(interface_manager.Count("simple_lo1"), 1) + << "simple_lo1 not found in interface manager"; + ASSERT_EQ(interface_manager.Count("simple_lo2"), 1) + << "simple_lo2 not found in interface manager"; + ASSERT_EQ(interface_manager.Count("simple_tls_lo1"), 1) + << "simple_tls_lo1 not found in interface manager"; + ASSERT_EQ(interface_manager.Count("simple_tls_lo2"), 1) + << "simple_tls_lo2 not found in interface manager"; + ASSERT_EQ(interface_manager.Count("circuit_lo1"), 1) + << "circuit_lo1 not found in interface manager"; + ASSERT_EQ(interface_manager.Count("circuit_lo2"), 1) + << "circuit_lo2 not found in interface manager"; + ASSERT_EQ(interface_manager.Count("circuit_tls_lo1"), 1) + << "circuit_tls_lo1 not found in interface manager"; + ASSERT_EQ(interface_manager.Count("circuit_tls_lo2"), 1) + << "circuit_tls_lo2 not found in interface manager"; +} + +TEST_F(InterfaceTestFixture, SimpleLocalInterfaceTest) { + tests::interface_protocol_helpers::TestInterfaceHalfDuplex< + tests::interface_protocol_helpers::InterfaceProtocol>("simple_lo1", + "simple_lo2", 200); + tests::interface_protocol_helpers::TestInterfaceFullDuplex< + tests::interface_protocol_helpers::InterfaceProtocol>("simple_lo1", + "simple_lo2", 200); +} + +TEST_F(InterfaceTestFixture, SimpleTLSLocalInterfaceTest) { + tests::interface_protocol_helpers::TestInterfaceHalfDuplex< + tests::interface_protocol_helpers::InterfaceProtocol>( + "simple_tls_lo1", "simple_tls_lo2", 200); + tests::interface_protocol_helpers::TestInterfaceFullDuplex< + tests::interface_protocol_helpers::InterfaceProtocol>( + "simple_tls_lo1", "simple_tls_lo2", 200); +} + +TEST_F(InterfaceTestFixture, DISABLED_CircuitLocalInterfaceTest) { + // TODO: closing circuit acceptor after first interface connection => close + // session forwarders: hang issue + tests::interface_protocol_helpers::TestInterfaceHalfDuplex< + tests::interface_protocol_helpers::InterfaceProtocol>("circuit_lo1", + "circuit_lo2", 200); + tests::interface_protocol_helpers::TestInterfaceFullDuplex< + tests::interface_protocol_helpers::InterfaceProtocol>("circuit_lo1", + "circuit_lo2", 200); +} + +TEST_F(InterfaceTestFixture, DISABLED_CircuitTLSLocalInterfaceTest) { + // TODO: closing circuit acceptor after first interface connection => close + // session forwarders: hang issue + tests::interface_protocol_helpers::TestInterfaceHalfDuplex< + tests::interface_protocol_helpers::InterfaceProtocol>( + "circuit_tls_lo1", "circuit_tls_lo2", 200); + tests::interface_protocol_helpers::TestInterfaceFullDuplex< + tests::interface_protocol_helpers::InterfaceProtocol>( + "circuit_tls_lo1", "circuit_tls_lo2", 200); +} diff --git a/src/framework/tests/interface_protocol_helpers.h b/src/framework/tests/interface_protocol_helpers.h new file mode 100644 index 00000000..8e930852 --- /dev/null +++ b/src/framework/tests/interface_protocol_helpers.h @@ -0,0 +1,308 @@ +#ifndef SSF_TESTS_INTERFACE_PROTOCOL_HELPERS_H_ +#define SSF_TESTS_INTERFACE_PROTOCOL_HELPERS_H_ + +#include + +#include +#include + +#include + +#include + +#include "ssf/layer/parameters.h" + +#include "ssf/layer/interface_layer/basic_interface_protocol.h" +#include "ssf/layer/interface_layer/basic_interface.h" + +#include "tests/tools.h" +#include "tests/virtual_network_helpers.h" + +namespace tests { +namespace interface_protocol_helpers { + +using InterfaceProtocol = + ssf::layer::interface_layer::basic_InterfaceProtocol; + +template +using Interface = + ssf::layer::interface_layer::basic_Interface; + +template +void TestInterfaceHalfDuplex(const std::string& interface_1, + const std::string& interface_2, + uint64_t size_in_mtu_multiples) { + std::cout << " * TestInterfaceHalfDuplex" << std::endl; + using Buffer = std::vector; + + std::promise finished1; + + uint64_t data_size_to_send = InterfaceProtocol::mtu * size_in_mtu_multiples; + + auto p_bandwidth1 = + std::make_shared(data_size_to_send * 8ULL); + + uint64_t s_count1 = 0; + uint64_t count1 = 0; + + auto p_socket1 = + *(InterfaceProtocol::get_interface_manager().Find(interface_1)); + auto p_socket2 = + *(InterfaceProtocol::get_interface_manager().Find(interface_2)); + + Buffer buffer1(InterfaceProtocol::mtu); + Buffer r_buffer2(InterfaceProtocol::mtu); + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + tests::virtual_network_helpers::SendHandler sent_handler1; + tests::virtual_network_helpers::ReceiveHandler received_handler2; + + received_handler2 = + [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer1, r_buffer2, length)); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0, length); + + count1 += length; + + if (count1 < data_size_to_send) { + p_socket2->async_receive(boost::asio::buffer(r_buffer2), + received_handler2); + } else { + ASSERT_EQ(data_size_to_send, count1); + finished1.set_value(true); + } + }; + + sent_handler1 = [&, p_bandwidth1](const boost::system::error_code& ec, + std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + + s_count1 += length; + + if (s_count1 < data_size_to_send) { + if (data_size_to_send - s_count1 >= buffer1.size()) { + p_socket1->async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + p_socket1->async_send( + boost::asio::buffer(&buffer1[0], static_cast( + data_size_to_send - s_count1)), + sent_handler1); + } + } + }; + + boost::system::error_code ec; + + p_socket2->async_receive(boost::asio::buffer(r_buffer2), received_handler2); + + p_bandwidth1->ResetTime(); + if (data_size_to_send >= buffer1.size()) { + p_socket1->async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + p_socket1->async_send( + boost::asio::buffer(&buffer1[0], + static_cast(data_size_to_send)), + sent_handler1); + } + p_bandwidth1.reset(); + + finished1.get_future().wait(); +} + +template +void TestInterfaceFullDuplex(const std::string& interface_1, + const std::string& interface_2, + uint64_t size_in_mtu_multiples) { + std::cout << " * TestInterfaceFullDuplex" << std::endl; + using Buffer = std::vector; + + std::promise finished1; + std::promise finished2; + + uint64_t data_size_to_send = InterfaceProtocol::mtu * size_in_mtu_multiples; + + auto p_bandwidth1 = + std::make_shared(data_size_to_send * 8ULL); + auto p_bandwidth2 = + std::make_shared(data_size_to_send * 8ULL); + + uint64_t s_count1 = 0; + uint64_t count1 = 0; + uint64_t s_count2 = 0; + uint64_t count2 = 0; + + auto p_socket1 = + *(InterfaceProtocol::get_interface_manager().Find(interface_1)); + auto p_socket2 = + *(InterfaceProtocol::get_interface_manager().Find(interface_2)); + + Buffer buffer1(InterfaceProtocol::mtu); + Buffer buffer2(InterfaceProtocol::mtu); + Buffer r_buffer1(InterfaceProtocol::mtu); + Buffer r_buffer2(InterfaceProtocol::mtu); + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1); + tests::virtual_network_helpers::ResetBuffer(&buffer2, 1); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + tests::virtual_network_helpers::SendHandler sent_handler1; + tests::virtual_network_helpers::SendHandler sent_handler2; + tests::virtual_network_helpers::ReceiveHandler received_handler1; + tests::virtual_network_helpers::ReceiveHandler received_handler2; + + received_handler1 = + [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer2, r_buffer1, length)); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + + count2 += length; + + if (count2 < data_size_to_send) { + p_socket1->async_receive(boost::asio::buffer(r_buffer1), + received_handler1); + } else { + ASSERT_EQ(data_size_to_send, count2); + finished2.set_value(true); + } + }; + + received_handler2 = + [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer1, r_buffer2, length)); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0, length); + + count1 += length; + + if (count1 < data_size_to_send) { + p_socket2->async_receive(boost::asio::buffer(r_buffer2), + received_handler2); + } else { + ASSERT_EQ(data_size_to_send, count1); + finished1.set_value(true); + } + }; + + sent_handler1 = [&, p_bandwidth1](const boost::system::error_code& ec, + std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + + s_count1 += length; + + if (s_count1 < data_size_to_send) { + if (data_size_to_send - s_count1 >= buffer1.size()) { + p_socket1->async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + p_socket1->async_send( + boost::asio::buffer(&buffer1[0], static_cast( + data_size_to_send - s_count1)), + sent_handler1); + } + } + }; + + sent_handler2 = [&, p_bandwidth2](const boost::system::error_code& ec, + std::size_t length) { + ASSERT_EQ(0, ec.value()) << "Error: " << ec.message(); + + s_count2 += length; + + if (s_count2 < data_size_to_send) { + if (data_size_to_send - s_count2 >= buffer2.size()) { + p_socket2->async_send(boost::asio::buffer(buffer2), sent_handler2); + } else { + p_socket2->async_send( + boost::asio::buffer(&buffer2[0], static_cast( + data_size_to_send - s_count2)), + sent_handler2); + } + } + }; + + boost::system::error_code ec; + + p_socket1->async_receive(boost::asio::buffer(r_buffer1), received_handler1); + p_socket2->async_receive(boost::asio::buffer(r_buffer2), received_handler2); + + p_bandwidth1->ResetTime(); + if (data_size_to_send >= buffer1.size()) { + p_socket1->async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + p_socket1->async_send( + boost::asio::buffer(&buffer1[0], + static_cast(data_size_to_send)), + sent_handler1); + } + p_bandwidth1.reset(); + + p_bandwidth2->ResetTime(); + if (data_size_to_send >= buffer2.size()) { + p_socket2->async_send(boost::asio::buffer(buffer2), sent_handler2); + } else { + p_socket2->async_send( + boost::asio::buffer(&buffer2[0], + static_cast(data_size_to_send)), + sent_handler2); + } + p_bandwidth2.reset(); + + finished1.get_future().wait(); + finished2.get_future().wait(); +} + +template +std::pair, Interface> InterfaceSetup( + boost::asio::io_service& io_service, + const ssf::layer::ParameterStack& parameters1, + const std::string& interface1_name, + const ssf::layer::ParameterStack& parameters2, + const std::string& interface2_name) { + using SimpleInterface = Interface; + + typename NextLayer::resolver simple_link_resolver(io_service); + boost::system::error_code ec; + + auto simple2_local_endpoint_it = + simple_link_resolver.resolve(parameters2, ec); + typename NextLayer::endpoint simple2_local_endpoint( + *simple2_local_endpoint_it); + + auto simple1_local_endpoint_it = + simple_link_resolver.resolve(parameters1, ec); + typename NextLayer::endpoint simple1_local_endpoint( + *simple1_local_endpoint_it); + + SimpleInterface interface1(io_service); + SimpleInterface interface2(io_service); + + std::promise connect_done; + std::promise accept_done; + + auto connected = + [&](const boost::system::error_code& ec) { connect_done.set_value(ec); }; + + auto accepted = + [&](const boost::system::error_code& ec) { accept_done.set_value(ec); }; + + interface1.async_accept("int1", simple2_local_endpoint, accepted); + interface2.async_connect("int2", simple1_local_endpoint, connected); + + auto connect_ec = connect_done.get_future().get(); + auto accept_ec = accept_done.get_future().get(); + + EXPECT_EQ(true, !connect_ec && !accept_ec); + + return std::make_pair(std::move(interface1), std::move(interface2)); +} + +} // interface_protocol_helpers +} // tests + +#endif // SSF_TESTS_INTERFACE_PROTOCOL_HELPERS_H_ diff --git a/src/framework/tests/interface_test_fixture.h b/src/framework/tests/interface_test_fixture.h new file mode 100644 index 00000000..536d3f3b --- /dev/null +++ b/src/framework/tests/interface_test_fixture.h @@ -0,0 +1,343 @@ +#ifndef SSF_TESTS_INTERFACE_TEST_FIXTURE_H_ +#define SSF_TESTS_INTERFACE_TEST_FIXTURE_H_ + +#include + +#include + +#include + +#include "ssf/layer/data_link/basic_circuit_protocol.h" +#include "ssf/layer/data_link/simple_circuit_policy.h" +#include "ssf/layer/data_link/circuit_helpers.h" + +#include "ssf/layer/interface_layer/basic_interface_protocol.h" +#include "ssf/layer/interface_layer/basic_interface.h" + +#include "ssf/layer/parameters.h" + +#include "ssf/layer/physical/tcp.h" +#include "ssf/layer/physical/tlsotcp.h" + +#include "ssf/log/log.h" + +#include "tests/virtual_network_helpers.h" +#include "tests/circuit_test_fixture.h" + +class InterfaceTestFixture : public CircuitTestFixture { + protected: + using SimpleLinkProtocol = ssf::layer::physical::TCPPhysicalLayer; + using SimpleTLSLinkProtocol = + ssf::layer::physical::TLSboTCPPhysicalLayer; + // virtual_network::physical_layer::TLSoTCPPhysicalLayer; + + using CircuitLinkProtocol = + ssf::layer::data_link::basic_CircuitProtocol< + PhysicalProtocol, ssf::layer::data_link::CircuitPolicy>; + using CircuitTLSLinkProtocol = + ssf::layer::data_link::basic_CircuitProtocol< + TLSPhysicalProtocol, ssf::layer::data_link::CircuitPolicy>; + + using InterfaceProtocol = + ssf::layer::interface_layer::basic_InterfaceProtocol; + + template + using Interface = + ssf::layer::interface_layer::basic_Interface; + + using SimpleInterface = Interface; + using SimpleTLSInterface = Interface; + using CircuitInterface = Interface; + using CircuitTLSInterface = Interface; + + protected: + InterfaceTestFixture() + : CircuitTestFixture(), + simple_interfaces_(), + simple_tls_interfaces_(), + circuit_interfaces_(), + circuit_tls_interfaces_(), + promises_() {} + + virtual ~InterfaceTestFixture() {} + + virtual void SetUp() { + CircuitTestFixture::SetUp(); + + promises_.insert(std::make_pair("simple_lo1", std::promise())); + promises_.insert(std::make_pair("simple_lo2", std::promise())); + promises_.insert(std::make_pair("simple_tls_lo1", std::promise())); + promises_.insert(std::make_pair("simple_tls_lo2", std::promise())); + promises_.insert(std::make_pair("circuit_lo1", std::promise())); + promises_.insert(std::make_pair("circuit_lo2", std::promise())); + promises_.insert(std::make_pair("circuit_tls_lo1", std::promise())); + promises_.insert(std::make_pair("circuit_tls_lo2", std::promise())); + + boost::system::error_code ec; + + //----------------------- simple interfaces -----------------------------// + SimpleLinkProtocol::resolver simple_resolver(this->io_service_); + + ssf::layer::LayerParameters simple1_acceptor_physical_parameters; + simple1_acceptor_physical_parameters["port"] = "6000"; + ssf::layer::ParameterStack simple1_parameters; + simple1_parameters.push_back(simple1_acceptor_physical_parameters); + auto simple1_local_endpoint_it = + simple_resolver.resolve(simple1_parameters, ec); + SimpleLinkProtocol::endpoint simple1_local_endpoint( + *simple1_local_endpoint_it); + auto it_simple1 = + simple_interfaces_.emplace("simple_lo1", this->io_service_); + + it_simple1.first->second.async_accept( + "simple_lo1", simple1_local_endpoint, + boost::bind(&InterfaceTestFixture::InterfaceOnline, this, "simple_lo1", + _1)); + + ssf::layer::LayerParameters simple2_connect_physical_parameters; + simple2_connect_physical_parameters["addr"] = "127.0.0.1"; + simple2_connect_physical_parameters["port"] = "6000"; + ssf::layer::ParameterStack simple2_parameters; + simple2_parameters.push_back(simple2_connect_physical_parameters); + auto simple2_remote_endpoint_it = + simple_resolver.resolve(simple2_parameters, ec); + SimpleLinkProtocol::endpoint simple2_remote_endpoint( + *simple2_remote_endpoint_it); + + auto it_simple2 = + simple_interfaces_.emplace("simple_lo2", this->io_service_); + it_simple2.first->second.async_connect( + "simple_lo2", simple2_remote_endpoint, + boost::bind(&InterfaceTestFixture::InterfaceOnline, this, "simple_lo2", + _1)); + + //---------------------- simple TLS interfaces --------------------------// + SimpleTLSLinkProtocol::resolver simple_tls_resolver(this->io_service_); + + boost::system::error_code tls1_ec; + ssf::layer::LayerParameters simple_tls1_acceptor_physical_parameters; + simple_tls1_acceptor_physical_parameters["port"] = "5000"; + ssf::layer::ParameterStack simple_tls1_parameters; + simple_tls1_parameters.push_back( + tests::virtual_network_helpers::tls_server_parameters); + simple_tls1_parameters.push_back(simple_tls1_acceptor_physical_parameters); + auto simple_tls1_local_endpoint_it = + simple_tls_resolver.resolve(simple_tls1_parameters, tls1_ec); + if (tls1_ec) { + SSF_LOG(kLogError) << "Fail resolving interface tls1 endpoint : " + << tls1_ec.message(); + promises_["simple_tls_lo1"].set_value(false); + } else { + SimpleTLSLinkProtocol::endpoint simple_tls1_local_endpoint( + *simple_tls1_local_endpoint_it); + auto it_simple_tls1 = + simple_tls_interfaces_.emplace("simple_tls_lo1", this->io_service_); + + it_simple_tls1.first->second.async_accept( + "simple_tls_lo1", simple_tls1_local_endpoint, + boost::bind(&InterfaceTestFixture::InterfaceOnline, this, + "simple_tls_lo1", _1)); + } + + boost::system::error_code tls2_ec; + ssf::layer::LayerParameters simple_tls2_client_physical_parameters; + simple_tls2_client_physical_parameters["addr"] = "127.0.0.1"; + simple_tls2_client_physical_parameters["port"] = "5000"; + ssf::layer::ParameterStack simple_tls2_parameters; + simple_tls2_parameters.push_back( + tests::virtual_network_helpers::tls_client_parameters); + simple_tls2_parameters.push_back(simple_tls2_client_physical_parameters); + auto simple_tls2_remote_endpoint_it = + simple_tls_resolver.resolve(simple_tls2_parameters, tls2_ec); + if (tls2_ec) { + SSF_LOG(kLogError) << "Fail resolving interface tls2 endpoint : " + << tls2_ec.message(); + promises_["simple_tls_lo2"].set_value(false); + } else { + SimpleTLSLinkProtocol::endpoint simple_tls2_remote_endpoint( + *simple_tls2_remote_endpoint_it); + auto it_simple_tls2 = + simple_tls_interfaces_.emplace("simple_tls_lo2", this->io_service_); + + it_simple_tls2.first->second.async_connect( + "simple_tls_lo2", simple_tls2_remote_endpoint, + boost::bind(&InterfaceTestFixture::InterfaceOnline, this, + "simple_tls_lo2", _1)); + } + + //------------------------- circuit interfaces --------------------------// + CircuitLinkProtocol::resolver circuit_resolver(this->io_service_); + + ssf::layer::LayerParameters circuit1_acceptor_physical_parameters; + circuit1_acceptor_physical_parameters["port"] = "7002"; + ssf::layer::ParameterStack pre_circuit1_parameters; + ssf::layer::ParameterStack circuit1_acceptor_default_parameters = {{}, {}}; + + pre_circuit1_parameters.push_back(circuit1_acceptor_physical_parameters); + ssf::layer::ParameterStack circuit1_parameters( + ssf::layer::data_link::make_acceptor_parameter_stack( + "server", circuit1_acceptor_default_parameters, + pre_circuit1_parameters)); + auto circuit1_local_endpoint_it = + circuit_resolver.resolve(circuit1_parameters, ec); + CircuitLinkProtocol::endpoint circuit1_local_endpoint( + *circuit1_local_endpoint_it); + auto it_circuit1 = + circuit_interfaces_.emplace("circuit_lo1", this->io_service_); + + it_circuit1.first->second.async_accept( + "circuit_lo1", circuit1_local_endpoint, + boost::bind(&InterfaceTestFixture::InterfaceOnline, this, "circuit_lo1", + _1)); + + ssf::layer::LayerParameters circuit2_client_physical_parameters; + circuit2_client_physical_parameters["addr"] = "127.0.0.1"; + circuit2_client_physical_parameters["port"] = "7002"; + ssf::layer::data_link::NodeParameterList nodes( + this->GetClientNodes()); + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode(circuit2_client_physical_parameters); + + ssf::layer::ParameterStack circuit2_parameters( + ssf::layer::data_link:: + make_client_full_circuit_parameter_stack("server", nodes)); + auto circuit2_local_endpoint_it = + circuit_resolver.resolve(circuit2_parameters, ec); + CircuitLinkProtocol::endpoint circuit2_local_endpoint( + *circuit2_local_endpoint_it); + auto it_circuit2 = + circuit_interfaces_.emplace("circuit_lo2", this->io_service_); + + it_circuit2.first->second.async_connect( + "circuit_lo2", circuit2_local_endpoint, + boost::bind(&InterfaceTestFixture::InterfaceOnline, this, "circuit_lo2", + _1)); + + //----------------------- circuit TLS interfaces ------------------------// + CircuitTLSLinkProtocol::resolver circuit_tls_resolver(this->io_service_); + + boost::system::error_code circuit_tls1_ec; + ssf::layer::LayerParameters circuit_tls1_acceptor_physical_parameters; + circuit_tls1_acceptor_physical_parameters["port"] = "8002"; + ssf::layer::ParameterStack pre_circuit_tls1_parameters; + pre_circuit_tls1_parameters.push_back( + tests::virtual_network_helpers::tls_server_parameters); + pre_circuit_tls1_parameters.push_back( + circuit_tls1_acceptor_physical_parameters); + ssf::layer::ParameterStack circuit_tls1_acceptor_default_parameters = { + {}, {}, {}}; + ssf::layer::ParameterStack circuit_tls1_parameters( + ssf::layer::data_link::make_acceptor_parameter_stack( + "server_tls", circuit_tls1_acceptor_default_parameters, + pre_circuit_tls1_parameters)); + auto circuit_tls1_local_endpoint_it = + circuit_tls_resolver.resolve(circuit_tls1_parameters, circuit_tls1_ec); + + if (circuit_tls1_ec) { + SSF_LOG(kLogError) << "Fail resolving interface circuit tls1 endpoint : " + << circuit_tls1_ec.message(); + promises_["circuit_tls_lo1"].set_value(false); + } else { + CircuitTLSLinkProtocol::endpoint circuit_tls1_local_endpoint( + *circuit_tls1_local_endpoint_it); + auto it_circuit_tls1 = + circuit_tls_interfaces_.emplace("circuit_tls_lo1", this->io_service_); + + it_circuit_tls1.first->second.async_accept( + "circuit_tls_lo1", circuit_tls1_local_endpoint, + boost::bind(&InterfaceTestFixture::InterfaceOnline, this, + "circuit_tls_lo1", _1)); + } + + boost::system::error_code circuit_tls2_ec; + ssf::layer::LayerParameters circuit_tls2_client_physical_parameters; + circuit_tls2_client_physical_parameters["addr"] = "127.0.0.1"; + circuit_tls2_client_physical_parameters["port"] = "8002"; + ssf::layer::data_link::NodeParameterList nodes_tls( + this->GetClientTLSNodes()); + nodes_tls.PushBackNode(); + nodes_tls.AddTopLayerToBackNode(circuit_tls2_client_physical_parameters); + nodes_tls.AddTopLayerToBackNode( + tests::virtual_network_helpers::tls_client_parameters); + + ssf::layer::ParameterStack circuit_tls2_parameters( + ssf::layer::data_link:: + make_client_full_circuit_parameter_stack("server_tls", nodes_tls)); + auto circuit_tls2_local_endpoint_it = + circuit_tls_resolver.resolve(circuit_tls2_parameters, circuit_tls2_ec); + + if (circuit_tls2_ec) { + SSF_LOG(kLogError) << "Fail resolving interface circuit tls2 endpoint : " + << circuit_tls2_ec.message(); + promises_["circuit_tls_lo2"].set_value(false); + } else { + CircuitTLSLinkProtocol::endpoint circuit_tls2_local_endpoint( + *circuit_tls2_local_endpoint_it); + auto it_circuit_tls2 = + circuit_tls_interfaces_.emplace("circuit_tls_lo2", this->io_service_); + + it_circuit_tls2.first->second.async_connect( + "circuit_tls_lo2", circuit_tls2_local_endpoint, + boost::bind(&InterfaceTestFixture::InterfaceOnline, this, + "circuit_tls_lo2", _1)); + } + + Wait(); + } + + virtual void TearDown() { + boost::system::error_code ec; + + for (auto& interface_pair : simple_interfaces_) { + interface_pair.second.close(ec); + } + simple_interfaces_.clear(); + + for (auto& interface_pair : simple_tls_interfaces_) { + interface_pair.second.close(ec); + } + simple_tls_interfaces_.clear(); + + for (auto& interface_pair : circuit_interfaces_) { + interface_pair.second.close(ec); + } + circuit_interfaces_.clear(); + + for (auto& interface_pair : circuit_tls_interfaces_) { + interface_pair.second.close(ec); + } + circuit_tls_interfaces_.clear(); + + CircuitTestFixture::TearDown(); + } + + void InterfaceOnline(const std::string& interface_id, + const boost::system::error_code& ec) { + if (!ec) { + SSF_LOG(kLogInfo) << "Interface up : " << interface_id; + } + promises_[interface_id].set_value(!ec); + } + + private: + bool Wait() { + bool result = true; + + for (auto& promise : promises_) { + result &= promise.second.get_future().get(); + } + + return result; + } + + protected: + std::map simple_interfaces_; + std::map simple_tls_interfaces_; + std::map circuit_interfaces_; + std::map circuit_tls_interfaces_; + + std::map> promises_; +}; + +#endif // SSF_TESTS_INTERFACE_TEST_FIXTURE_H_ diff --git a/src/framework/tests/interfaces_system_tests.cpp b/src/framework/tests/interfaces_system_tests.cpp new file mode 100644 index 00000000..27d35da5 --- /dev/null +++ b/src/framework/tests/interfaces_system_tests.cpp @@ -0,0 +1,351 @@ +#include + +#include + +#include +#include + +#include + +#include +#include + +#include "ssf/system/specific_interfaces_collection.h" +#include "ssf/system/system_interfaces.h" + +#include "ssf/layer/physical/tcp.h" +#include "ssf/layer/physical/tlsotcp.h" +#include "ssf/layer/data_link/basic_circuit_protocol.h" +#include "ssf/layer/data_link/simple_circuit_policy.h" + +#include "ssf/log/log.h" + +class SystemTestFixture : public ::testing::Test { + protected: + SystemTestFixture() + : ::testing::Test(), + tcp_accept_filename_("./system/tcp_accept_config.json"), + tcp_connect_filename_("./system/tcp_connect_config.json"), + fail_tcp_accept_filename_("./system/fail_tcp_accept_config.json"), + fail_tcp_connect_filename_("./system/fail_tcp_connect_config.json"), + tlsotcp_accept_filename_("./system/tlsotcp_accept_config.json"), + tlsotcp_connect_filename_("./system/tlsotcp_connect_config.json"), + link_tcp_accept_filename_("./system/link_tcp_accept_config.json"), + link_tcp_connect_filename_("./system/link_tcp_connect_config.json"), + link_tlsotcp_accept_filename_( + "./system/link_tlsotcp_accept_config.json"), + link_tlsotcp_connect_filename_( + "./system/link_tlsotcp_connect_config.json"), + circuit_tlsotcp_accept1_filename_( + "./system/circuit_tlsotcp_accept1_config.json"), + circuit_tlsotcp_accept2_filename_( + "./system/circuit_tlsotcp_accept2_config.json"), + circuit_tlsotcp_accept3_filename_( + "./system/circuit_tlsotcp_accept3_config.json"), + circuit_tlsotcp_connect_filename_( + "./system/circuit_tlsotcp_connect_config.json"), + system_multiple_config_filename_( + "./system/system_multiple_config.json"), + system_reconnect_config_filename_( + "./system/system_reconnect_config.json") {} + + virtual ~SystemTestFixture() {} + + virtual void SetUp() {} + + virtual void TearDown() {} + + std::string tcp_accept_filename_; + std::string tcp_connect_filename_; + std::string fail_tcp_accept_filename_; + std::string fail_tcp_connect_filename_; + std::string tlsotcp_accept_filename_; + std::string tlsotcp_connect_filename_; + std::string link_tcp_accept_filename_; + std::string link_tcp_connect_filename_; + std::string link_tlsotcp_accept_filename_; + std::string link_tlsotcp_connect_filename_; + std::string circuit_tlsotcp_accept1_filename_; + std::string circuit_tlsotcp_accept2_filename_; + std::string circuit_tlsotcp_accept3_filename_; + std::string circuit_tlsotcp_connect_filename_; + std::string system_multiple_config_filename_; + std::string system_reconnect_config_filename_; +}; + +template +void TestInterface(bool should_fail, const std::string& connect_filename, + const std::string& accept_filename, + const std::vector& other_accepts_filenames) { + boost::asio::io_service io_service; + + ssf::system::SpecificInterfacesCollection interfaces_collection; + + boost::property_tree::ptree accept_pt; + boost::property_tree::ptree connect_pt; + + try { + boost::property_tree::read_json(accept_filename, accept_pt); + boost::property_tree::read_json(connect_filename, connect_pt); + } catch (const std::exception&) { + ASSERT_TRUE(false) << "Config file parsing failed"; + return; + } + + for (auto& other_accept_filename : other_accepts_filenames) { + boost::property_tree::ptree other_accept_pt; + boost::property_tree::read_json(other_accept_filename, other_accept_pt); + interfaces_collection.AsyncMount(io_service, other_accept_pt, + [](const boost::system::error_code&, + const std::string& interface_name) {}); + } + + std::promise accept_mounted; + std::promise connect_mounted; + + std::string connect_interface_name = connect_pt.get_child("interface").data(); + std::string accept_interface_name = accept_pt.get_child("interface").data(); + + interfaces_collection.AsyncMount( + io_service, accept_pt, + [&accept_mounted](const boost::system::error_code& ec, + const std::string& interface_name) { + SSF_LOG(kLogTrace) << " * Accept interface name : " << interface_name + << " " << ec.message(); + accept_mounted.set_value(ec.value() == 0); + }); + + interfaces_collection.AsyncMount( + io_service, connect_pt, + [&connect_mounted](const boost::system::error_code& ec, + const std::string& interface_name) { + SSF_LOG(kLogTrace) << " * Connect interface name : " << interface_name + << " " << ec.message(); + connect_mounted.set_value(ec.value() == 0); + }); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + bool connect_mounted_val = connect_mounted.get_future().get(); + bool accept_mounted_val = accept_mounted.get_future().get(); + if (should_fail) { + EXPECT_FALSE(connect_mounted_val) << "Mount connect interface not failed"; + EXPECT_FALSE(accept_mounted_val) << "Mount accept interface not failed"; + } else { + EXPECT_TRUE(connect_mounted_val) << "Fail mount connect interface"; + EXPECT_TRUE(accept_mounted_val) << "Fail mount accept interface"; + } + + interfaces_collection.UmountAll(); + + threads.join_all(); +} + +TEST_F(SystemTestFixture, SimpleTCPSpecificInterfaces) { + using TCPProtocol = ssf::layer::physical::TCPPhysicalLayer; + + TestInterface(false, tcp_connect_filename_, tcp_accept_filename_, + std::vector()); +} + +TEST_F(SystemTestFixture, FailSimpleTCPSpecificInterfaces) { + using TCPProtocol = ssf::layer::physical::TCPPhysicalLayer; + + TestInterface(true, fail_tcp_connect_filename_, + fail_tcp_accept_filename_, + std::vector()); +} + +TEST_F(SystemTestFixture, TLSoTCPSpecificInterfaces) { + using TLSoTCPProtocol = + ssf::layer::physical::TLSboTCPPhysicalLayer; + + TestInterface(false, tlsotcp_connect_filename_, + tlsotcp_accept_filename_, + std::vector()); +} + +TEST_F(SystemTestFixture, LinkTCPSpecificInterfaces) { + using TCPProtocol = ssf::layer::physical::TCPPhysicalLayer; + using CircuitLinkProtocol = + ssf::layer::data_link::basic_CircuitProtocol< + TCPProtocol, ssf::layer::data_link::CircuitPolicy>; + + TestInterface(false, link_tcp_connect_filename_, + link_tcp_accept_filename_, + std::vector()); +} + +TEST_F(SystemTestFixture, LinkTLSoTCPSpecificInterfaces) { + using TLSoTCPProtocol = + ssf::layer::physical::TLSboTCPPhysicalLayer; + using CircuitLinkProtocol = + ssf::layer::data_link::basic_CircuitProtocol< + TLSoTCPProtocol, ssf::layer::data_link::CircuitPolicy>; + + TestInterface(false, link_tlsotcp_connect_filename_, + link_tlsotcp_accept_filename_, + std::vector()); +} + +TEST_F(SystemTestFixture, CircuitTLSoTCPSpecificInterfaces) { + using SimpleTLSLinkProtocol = + ssf::layer::physical::TLSboTCPPhysicalLayer; + using CircuitLinkProtocol = + ssf::layer::data_link::basic_CircuitProtocol< + SimpleTLSLinkProtocol, + ssf::layer::data_link::CircuitPolicy>; + std::vector circuit; + circuit.push_back(circuit_tlsotcp_accept1_filename_); + circuit.push_back(circuit_tlsotcp_accept2_filename_); + TestInterface(false, circuit_tlsotcp_connect_filename_, + circuit_tlsotcp_accept3_filename_, + circuit); +} + +TEST_F(SystemTestFixture, ImportHeterogeneousInterfaces) { + using TCPProtocol = ssf::layer::physical::TCPPhysicalLayer; + using TLSoTCPProtocol = + ssf::layer::physical::TLSboTCPPhysicalLayer; + using CircuitTCPProtocol = + ssf::layer::data_link::basic_CircuitProtocol< + TCPProtocol, ssf::layer::data_link::CircuitPolicy>; + using CircuitTLSoTCPProtocol = + ssf::layer::data_link::basic_CircuitProtocol< + TLSoTCPProtocol, ssf::layer::data_link::CircuitPolicy>; + + boost::asio::io_service io_service; + ssf::system::SystemInterfaces system_interfaces(io_service); + + system_interfaces.Start(); + + ASSERT_TRUE(system_interfaces.RegisterInterfacesCollection()); + ASSERT_TRUE( + system_interfaces.RegisterInterfacesCollection()); + ASSERT_TRUE( + system_interfaces.RegisterInterfacesCollection()); + ASSERT_TRUE( + system_interfaces.RegisterInterfacesCollection()); + + ASSERT_FALSE(system_interfaces.RegisterInterfacesCollection()); + ASSERT_FALSE( + system_interfaces.RegisterInterfacesCollection()); + ASSERT_FALSE( + system_interfaces.RegisterInterfacesCollection()); + ASSERT_FALSE( + system_interfaces.RegisterInterfacesCollection()); + + std::promise finished; + std::atomic nb_interface_ups(0); + bool ok = true; + + auto interface_up_handler = [&nb_interface_ups, &finished, &ok]( + const boost::system::error_code& ec, const std::string& interface_name) { + SSF_LOG(kLogTrace) << "interface " << interface_name + << " up : " << ec.message(); + ok = ok && !ec; + ++nb_interface_ups; + if (nb_interface_ups.load() == 8) { + finished.set_value(ok); + } + }; + + system_interfaces.AsyncMount(system_multiple_config_filename_, + interface_up_handler); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + EXPECT_TRUE(finished.get_future().get()) << "All interfaces not mounted"; + system_interfaces.Stop(); + threads.join_all(); + + system_interfaces.UnregisterAllInterfacesCollection(); +} + +/*TEST_F(SystemTestFixture, ReconnectHeterogeneousInterfaces) { + using TCPProtocol = ssf::layer::physical::TCPPhysicalLayer; + using InterfaceProtocol = + ssf::layer::interface_layer::basic_InterfaceProtocol; + + boost::asio::io_service io_service; + ssf::system::SystemInterfaces system_interfaces(io_service); + + system_interfaces.Start(1); + + ASSERT_TRUE(system_interfaces.RegisterInterfacesCollection()); + + std::promise finished; + std::atomic nb_interface_ups(0); + + boost::system::error_code ec; + boost::asio::ip::tcp::acceptor acceptor(io_service); + boost::asio::ip::tcp::socket socket(io_service); + + boost::asio::ip::tcp::endpoint ep(boost::asio::ip::tcp::v4(), 8000); + boost::asio::ip::tcp::endpoint remote_ep; + acceptor.open(ep.protocol(), ec); + acceptor.bind(ep, ec); + + auto interface_up_handler = [](const boost::system::error_code& ec, + const std::string& interface_name) { + SSF_LOG(kLogTrace) << "interface " << interface_name + << " up : " << ec.message(); + if (!ec) { + auto socket_optional = + InterfaceProtocol::get_interface_manager().Find(interface_name); + + if (socket_optional) { + int dummy_buf; + (*socket_optional) + ->async_receive( + boost::asio::buffer(&dummy_buf, sizeof(dummy_buf)), + [](const boost::system::error_code& ec, std::size_t length) { + ASSERT_NE(ec.value(), 0) << "Socket should be closed" + << ec.message(); + }); + } + } + }; + + std::function accept_handler; + + accept_handler = + [&socket, &remote_ep, &nb_interface_ups, &acceptor, &finished, + &accept_handler](const boost::system::error_code& accept_ec) { + if (!accept_ec) { + ++nb_interface_ups; + if (nb_interface_ups.load() < 5) { + socket.close(); + + acceptor.async_accept(socket, remote_ep, accept_handler); + } + } + if (nb_interface_ups.load() == 5) { + finished.set_value(true); + } + }; + + acceptor.listen(); + + acceptor.async_accept(socket, remote_ep, accept_handler); + system_interfaces.AsyncMount(system_reconnect_config_filename_, + interface_up_handler); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + ASSERT_TRUE(finished.get_future().get()) << "All interfaces not mounted"; + + system_interfaces.Stop(); + threads.join_all(); + + system_interfaces.UnregisterAllInterfacesCollection(); +}*/ \ No newline at end of file diff --git a/src/framework/tests/link_layer_tests.cpp b/src/framework/tests/link_layer_tests.cpp new file mode 100644 index 00000000..acef8105 --- /dev/null +++ b/src/framework/tests/link_layer_tests.cpp @@ -0,0 +1,263 @@ +#include + +#include "ssf/layer/basic_empty_stream.h" +#include "ssf/layer/cryptography/tls/OpenSSL/impl.h" +#include "ssf/layer/cryptography/basic_crypto_stream.h" + +#include "ssf/layer/data_link/basic_circuit_protocol.h" +#include "ssf/layer/data_link/simple_circuit_policy.h" +#include "ssf/layer/data_link/circuit_helpers.h" + +#include "ssf/layer/parameters.h" + +#include "ssf/layer/physical/tcp.h" + +#include "tests/circuit_test_fixture.h" +#include "tests/stream_protocol_helpers.h" +#include "tests/virtual_network_helpers.h" + +ssf::layer::LayerParameters tcp_server_parameters = {{"port", "9000"}}; + +ssf::layer::LayerParameters tcp_client_parameters = {{"addr", "127.0.0.1"}, + {"port", "9000"}}; + +TEST(LinkLayerTest, TCPBaseLine) { + ssf::layer::LayerParameters acceptor_tcp_parameters; + acceptor_tcp_parameters["port"] = "9000"; + ssf::layer::ParameterStack acceptor_parameters; + acceptor_parameters.push_back(acceptor_tcp_parameters); + + ssf::layer::LayerParameters client_tcp_parameters; + client_tcp_parameters["addr"] = "127.0.0.1"; + client_tcp_parameters["port"] = "9000"; + ssf::layer::ParameterStack client_parameters; + client_parameters.push_back(client_tcp_parameters); + + TCPPerfTestHalfDuplex(client_parameters, acceptor_parameters, 200); +} + +TEST(LinkLayerTest, SimpleCircuitProtocolTest) { + using PhysicalProtocol = ssf::layer::physical::TCPPhysicalLayer; + using DataLinkProtocol = ssf::layer::data_link::basic_CircuitProtocol< + PhysicalProtocol, ssf::layer::data_link::CircuitPolicy>; + + /// Acceptor endpoint parameters + ssf::layer::ParameterStack acceptor_default_parameters = {{}, {}}; + ssf::layer::ParameterStack acceptor_next_layers_parameters; + acceptor_next_layers_parameters.push_back(tcp_server_parameters); + ssf::layer::ParameterStack acceptor_parameters( + ssf::layer::data_link::make_acceptor_parameter_stack( + "server", acceptor_default_parameters, + acceptor_next_layers_parameters)); + + /// Client endpoint parameters + ssf::layer::data_link::NodeParameterList nodes; + nodes.PushFrontNode(); + nodes.AddTopLayerToFrontNode(tcp_client_parameters); + + ssf::layer::ParameterStack client_parameters( + ssf::layer::data_link:: + make_client_full_circuit_parameter_stack("server", nodes)); + + TestStreamProtocol(client_parameters, acceptor_parameters, + 100 * 10); + + /* Uncomment after fix on boost build system + TestStreamProtocolSpawn(client_parameters, + acceptor_parameters);*/ + + TestStreamProtocolFuture(client_parameters, + acceptor_parameters); + + /* Not implemented yet + TestStreamProtocolSynchronous(client_parameters, + acceptor_parameters);*/ + + PerfTestStreamProtocolHalfDuplex(client_parameters, + acceptor_parameters, 200); + + PerfTestStreamProtocolFullDuplex(client_parameters, + acceptor_parameters, 200); +} + +TEST(LinkLayerTest, SimpleTLSCircuitProtocolTest) { + using TLSPhysicalProtocol = ssf::layer::physical::TLSboTCPPhysicalLayer; + using DataLinkProtocol = ssf::layer::data_link::basic_CircuitProtocol< + TLSPhysicalProtocol, ssf::layer::data_link::CircuitPolicy>; + + // Acceptor endpoint parameters + ssf::layer::ParameterStack acceptor_default_parameters = {{}, {}, {}}; + ssf::layer::ParameterStack acceptor_next_layers_parameters; + acceptor_next_layers_parameters.push_front(tcp_server_parameters); + acceptor_next_layers_parameters.push_front( + tests::virtual_network_helpers::tls_server_parameters); + ssf::layer::ParameterStack acceptor_parameters( + ssf::layer::data_link::make_acceptor_parameter_stack( + "server", acceptor_default_parameters, + acceptor_next_layers_parameters)); + + // Client endpoint parameters + ssf::layer::data_link::NodeParameterList nodes; + nodes.PushFrontNode(); + nodes.AddTopLayerToFrontNode(tcp_client_parameters); + nodes.AddTopLayerToFrontNode( + tests::virtual_network_helpers::tls_server_parameters); + + ssf::layer::ParameterStack client_parameters( + ssf::layer::data_link:: + make_client_full_circuit_parameter_stack("server", nodes)); + + TestStreamProtocol(client_parameters, acceptor_parameters, + 100 * 10); + + /* Uncomment after fix on boost build system + TestStreamProtocolSpawn(client_parameters, + acceptor_parameters);*/ + + TestStreamProtocolFuture(client_parameters, + acceptor_parameters); + + /* Not implemented yet + TestStreamProtocolSynchronous(client_parameters, + acceptor_parameters);*/ + + PerfTestStreamProtocolHalfDuplex(client_parameters, + acceptor_parameters, 200); + + PerfTestStreamProtocolFullDuplex(client_parameters, + acceptor_parameters, 200); +} + +TEST_F(CircuitTestFixture, CircuitTest) { + using DataLinkProtocol = CircuitProtocol; + + // Acceptor endpoint parameters + ssf::layer::ParameterStack acceptor_default_parameters = {{}, {}}; + ssf::layer::ParameterStack acceptor_next_layers_parameters; + acceptor_next_layers_parameters.push_back(tcp_server_parameters); + ssf::layer::ParameterStack acceptor_parameters( + ssf::layer::data_link::make_acceptor_parameter_stack( + "server", acceptor_default_parameters, + acceptor_next_layers_parameters)); + + // Client endpoint parameters + ssf::layer::data_link::NodeParameterList nodes( + this->GetClientNodes()); + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode(tcp_client_parameters); + + ssf::layer::ParameterStack client_parameters( + ssf::layer::data_link:: + make_client_full_circuit_parameter_stack("server", nodes)); + + TestStreamProtocol(client_parameters, acceptor_parameters, + 100 * 10); + + /* Uncomment after fix on boost build system + TestStreamProtocolSpawn(client_parameters, + acceptor_parameters);*/ + + TestStreamProtocolFuture(client_parameters, + acceptor_parameters); + + PerfTestStreamProtocolHalfDuplex(client_parameters, + acceptor_parameters, 200); + + PerfTestStreamProtocolFullDuplex(client_parameters, + acceptor_parameters, 200); +} + +TEST_F(CircuitTestFixture, CircuitTLSTest) { + using DataLinkProtocol = TLSCircuitProtocol; + + // Acceptor endpoint parameters + ssf::layer::ParameterStack acceptor_default_parameters = {{}, {}, {}}; + ssf::layer::ParameterStack acceptor_next_layers_parameters; + acceptor_next_layers_parameters.push_front(tcp_server_parameters); + acceptor_next_layers_parameters.push_front( + tests::virtual_network_helpers::tls_server_parameters); + ssf::layer::ParameterStack acceptor_parameters( + ssf::layer::data_link::make_acceptor_parameter_stack( + "server", acceptor_default_parameters, + acceptor_next_layers_parameters)); + + // Client endpoint parameters + ssf::layer::data_link::NodeParameterList nodes( + this->GetClientTLSNodes()); + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode(tcp_client_parameters); + nodes.AddTopLayerToBackNode( + tests::virtual_network_helpers::tls_server_parameters); + + ssf::layer::ParameterStack client_parameters( + ssf::layer::data_link:: + make_client_full_circuit_parameter_stack("server", nodes)); + + TestStreamProtocol(client_parameters, acceptor_parameters, + 100 * 10); + + /* Uncomment after fix on boost build system + TestStreamProtocolSpawn(client_parameters, + acceptor_parameters);*/ + + TestStreamProtocolFuture(client_parameters, + acceptor_parameters); + + /* Not implemented yet + TestStreamProtocolSynchronous(client_parameters, + acceptor_parameters);*/ + + PerfTestStreamProtocolHalfDuplex(client_parameters, + acceptor_parameters, 200); + + PerfTestStreamProtocolFullDuplex(client_parameters, + acceptor_parameters, 200); +} + +TEST_F(CircuitTestFixture, CircuitDefaultTLSTest) { + using DataLinkProtocol = TLSCircuitProtocol; + + // Acceptor endpoint parameters + ssf::layer::ParameterStack acceptor_default_parameters = {{}, {}, {}}; + ssf::layer::ParameterStack acceptor_next_layers_parameters; + acceptor_next_layers_parameters.push_front(tcp_server_parameters); + acceptor_next_layers_parameters.push_front( + tests::virtual_network_helpers::tls_server_parameters); + + ssf::layer::ParameterStack acceptor_parameters( + ssf::layer::data_link::make_acceptor_parameter_stack( + "server", acceptor_default_parameters, + acceptor_next_layers_parameters)); + + // Client endpoint parameters + ssf::layer::data_link::NodeParameterList nodes( + this->GetClientDefaultTLSNodes()); + nodes.PushBackNode(); + nodes.AddTopLayerToBackNode(tcp_client_parameters); + nodes.AddTopLayerToBackNode( + tests::virtual_network_helpers::tls_default_server_parameters); + + ssf::layer::ParameterStack client_parameters( + ssf::layer::data_link:: + make_client_full_circuit_parameter_stack("server", nodes)); + + TestStreamProtocol(client_parameters, acceptor_parameters, + 100 * 10); + + /* Uncomment after fix on boost build system + TestStreamProtocolSpawn(client_parameters, + acceptor_parameters);*/ + + TestStreamProtocolFuture(client_parameters, + acceptor_parameters); + + /* Not implemented yet + TestStreamProtocolSynchronous(client_parameters, + acceptor_parameters);*/ + + PerfTestStreamProtocolHalfDuplex(client_parameters, + acceptor_parameters, 200); + + PerfTestStreamProtocolFullDuplex(client_parameters, + acceptor_parameters, 200); +} diff --git a/src/framework/tests/log_tests.cpp b/src/framework/tests/log_tests.cpp new file mode 100644 index 00000000..ee87dd64 --- /dev/null +++ b/src/framework/tests/log_tests.cpp @@ -0,0 +1,42 @@ +#include + +#include "ssf/log/log.h" + +TEST(LogTests, DefaultLog) { + SSF_LOG(kLogCritical) << "critical"; + SSF_LOG(kLogError) << "error"; + SSF_LOG(kLogWarning) << "warning"; + SSF_LOG(kLogInfo) << "info"; + SSF_LOG(kLogDebug) << "debug"; + SSF_LOG(kLogTrace) << "trace"; +} + +TEST(LogTests, TraceLog) { + ssf::log::Log::SetSeverityLevel(ssf::log::LogLevel::kLogTrace); + SSF_LOG(kLogCritical) << "critical"; + SSF_LOG(kLogError) << "error"; + SSF_LOG(kLogWarning) << "warning"; + SSF_LOG(kLogInfo) << "info"; + SSF_LOG(kLogDebug) << "debug"; + SSF_LOG(kLogTrace) << "trace"; +} + +TEST(LogTests, CriticalLog) { + ssf::log::Log::SetSeverityLevel(ssf::log::LogLevel::kLogCritical); + SSF_LOG(kLogCritical) << "critical"; + SSF_LOG(kLogError) << "error"; + SSF_LOG(kLogWarning) << "warning"; + SSF_LOG(kLogInfo) << "info"; + SSF_LOG(kLogDebug) << "debug"; + SSF_LOG(kLogTrace) << "trace"; +} + +TEST(LogTests, NoLog) { + ssf::log::Log::SetSeverityLevel(ssf::log::LogLevel::kLogNone); + SSF_LOG(kLogCritical) << "critical"; + SSF_LOG(kLogError) << "error"; + SSF_LOG(kLogWarning) << "warning"; + SSF_LOG(kLogInfo) << "info"; + SSF_LOG(kLogDebug) << "debug"; + SSF_LOG(kLogTrace) << "trace"; +} \ No newline at end of file diff --git a/src/framework/tests/physical_layer_tests.cpp b/src/framework/tests/physical_layer_tests.cpp new file mode 100644 index 00000000..0f1e1e1b --- /dev/null +++ b/src/framework/tests/physical_layer_tests.cpp @@ -0,0 +1,145 @@ +#include + +#include "tests/datagram_protocol_helpers.h" +#include "tests/stream_protocol_helpers.h" +#include "tests/virtual_network_helpers.h" + +#include "ssf/layer/parameters.h" + +#include "ssf/layer/physical/tcp.h" +#include "ssf/layer/physical/tlsotcp.h" +#include "ssf/layer/physical/udp.h" + +ssf::layer::LayerParameters tcp_server_parameters = {{"port", "9000"}}; + +ssf::layer::LayerParameters tcp_client_parameters = {{"addr", "127.0.0.1"}, + {"port", "9000"}}; + +TEST(PhysicalLayerTest, TCPBaseLine) { + ssf::layer::ParameterStack acceptor_parameters; + acceptor_parameters.push_back(tcp_server_parameters); + + ssf::layer::ParameterStack client_parameters; + client_parameters.push_back(tcp_client_parameters); + + TCPPerfTestHalfDuplex(client_parameters, acceptor_parameters, 200); +} + + TEST(PhysicalLayerTest, EmptyStreamProtocolStackOverTCPTest) { + typedef ssf::layer::physical::TCPPhysicalLayer + StreamStackProtocol; + + ssf::layer::ParameterStack acceptor_parameters; + acceptor_parameters.push_back(tcp_server_parameters); + + ssf::layer::ParameterStack client_parameters; + client_parameters.push_back(tcp_client_parameters); + + ssf::layer::LayerParameters client_error_tcp_parameters; + client_error_tcp_parameters["addr"] = "127.0.0.1"; + client_error_tcp_parameters["port"] = "9001"; + ssf::layer::ParameterStack client_error_connection_parameters; + client_error_connection_parameters.push_back(client_error_tcp_parameters); + + ssf::layer::ParameterStack client_wrong_number_parameters; + + TestStreamProtocol(client_parameters, + acceptor_parameters, 1024); + + TestStreamProtocolFuture(client_parameters, + acceptor_parameters); + + /* Uncomment after fix on boost build system + TestStreamProtocolSpawn(client_parameters, + acceptor_parameters);*/ + + TestStreamProtocolSynchronous(client_parameters, + acceptor_parameters); + + TestStreamErrorConnectionProtocol( + client_error_connection_parameters); + + TestEndpointResolverError( + client_wrong_number_parameters); + + PerfTestStreamProtocolHalfDuplex( + client_parameters, acceptor_parameters, 200); + + PerfTestStreamProtocolFullDuplex( + client_parameters, acceptor_parameters, 200); +} + +TEST(PhysicalLayerTest, TLSLayerProtocolStackOverTCPTest) { + typedef ssf::layer::physical::TLSboTCPPhysicalLayer + TLSStackProtocol; + + ssf::layer::ParameterStack acceptor_parameters; + acceptor_parameters.push_back( + tests::virtual_network_helpers::tls_server_parameters); + acceptor_parameters.push_back(tcp_server_parameters); + + ssf::layer::ParameterStack client_parameters; + client_parameters.push_back( + tests::virtual_network_helpers::tls_client_parameters); + client_parameters.push_back(tcp_client_parameters); + + ssf::layer::LayerParameters client_error_tcp_parameters; + client_error_tcp_parameters["addr"] = "127.0.0.1"; + client_error_tcp_parameters["port"] = "9001"; + ssf::layer::ParameterStack client_error_parameters; + client_error_parameters.push_back( + tests::virtual_network_helpers::tls_client_parameters); + client_error_parameters.push_back(client_error_tcp_parameters); + + ssf::layer::ParameterStack client_wrong_number_parameters; + client_wrong_number_parameters.push_back(tcp_client_parameters); + + TestStreamProtocol(client_parameters, acceptor_parameters, + 1024); + + TestStreamProtocolFuture(client_parameters, + acceptor_parameters); + + /* Uncomment after fix on boost build system + TestStreamProtocolSpawn(client_parameters, + acceptor_parameters);*/ + + TestStreamProtocolSynchronous(client_parameters, + acceptor_parameters); + + TestStreamErrorConnectionProtocol(client_error_parameters); + + TestEndpointResolverError(client_wrong_number_parameters); + + PerfTestStreamProtocolHalfDuplex(client_parameters, + acceptor_parameters, 200); + + PerfTestStreamProtocolFullDuplex(client_parameters, + acceptor_parameters, 200); +} + +TEST(PhysicalLayerTest, EmptyDatagramProtocolStackOverUDPTest) { + typedef ssf::layer::physical::UDPPhysicalLayer + DatagramStackProtocol; + + ssf::layer::LayerParameters socket1_udp_parameters; + socket1_udp_parameters["addr"] = "127.0.0.1"; + socket1_udp_parameters["port"] = "8000"; + ssf::layer::ParameterStack socket1_parameters; + socket1_parameters.push_back(socket1_udp_parameters); + + ssf::layer::LayerParameters socket2_udp_parameters; + socket2_udp_parameters["addr"] = "127.0.0.1"; + socket2_udp_parameters["port"] = "9000"; + ssf::layer::ParameterStack socket2_parameters; + socket2_parameters.push_back(socket2_udp_parameters); + + TestDatagramProtocolPerfHalfDuplex( + socket1_parameters, socket2_parameters, 100000); + + TestDatagramProtocolPerfFullDuplex( + socket1_parameters, socket2_parameters, 100000); + + TestConnectionDatagramProtocol( + socket1_parameters, socket1_parameters, 100); +} \ No newline at end of file diff --git a/src/framework/tests/proxy/README.md b/src/framework/tests/proxy/README.md new file mode 100644 index 00000000..0265bc75 --- /dev/null +++ b/src/framework/tests/proxy/README.md @@ -0,0 +1,15 @@ +# Proxy test configuration + +* Copy file 'proxy.json.dist' into 'proxy.json' +* Fill options with correct values: + - target_host: address of your machine reachable from proxy + - target_port: port of test server (9000 by default) + - proxy_host: address of http proxy + - proxy_port: port of http proxy + - username: username for proxy authentication + - password: password for proxy authentication + - domain: user domain (NTLM and Negotiate auth on Windows only) + - reuse_ntlm: reuse current computer user credentials to authenticate with proxy NTLM auth (SSO) + "reuse_kerb": reuse current computer user credentials (Kerberos ticket) to authenticate with proxy Negotiate auth (SSO) +* Run `cmake ..` at project build directory +* Build and run `proxy_layer_tests` diff --git a/src/framework/tests/proxy/proxy.json.dist b/src/framework/tests/proxy/proxy.json.dist new file mode 100644 index 00000000..1a7f210c --- /dev/null +++ b/src/framework/tests/proxy/proxy.json.dist @@ -0,0 +1,11 @@ +{ + "target_host": "public.localhost.name", + "target_port": "9000", + "proxy_host": "proxy.example.com", + "proxy_port": "3128", + "username": "", + "password": "", + "domain": "", + "reuse_ntlm": "true", + "reuse_kerb": "true" +} diff --git a/src/framework/tests/proxy_auth_strategies_tests.cpp b/src/framework/tests/proxy_auth_strategies_tests.cpp new file mode 100644 index 00000000..4a852041 --- /dev/null +++ b/src/framework/tests/proxy_auth_strategies_tests.cpp @@ -0,0 +1,160 @@ +#include + +#include "ssf/layer/proxy/base64.h" +#include "ssf/layer/proxy/basic_auth_strategy.h" +#include "ssf/layer/proxy/digest_auth_strategy.h" +#include "ssf/layer/proxy/negotiate_auth_strategy.h" + +#include "ssf/layer/proxy/proxy_endpoint_context.h" + +TEST(Base64Test, Test) { + using Base64 = ssf::layer::proxy::Base64; + std::string str("This is a test string"); + Base64::Buffer buffer({'T', 'h', 'i', 's', ' ', 'i', 's', ' ', 'a', ' ', 't', + 'e', 's', 't', ' ', 's', 't', 'r', 'i', 'n', 'g', '\0', + 'C', 'u', 't'}); + auto empty_encoded = Base64::Encode(""); + auto encoded_str = Base64::Encode(str); + auto encoded_buf = Base64::Encode(buffer); + + ASSERT_EQ("VGhpcyBpcyBhIHRlc3Qgc3RyaW5n", encoded_str); + ASSERT_EQ("VGhpcyBpcyBhIHRlc3Qgc3RyaW5nAEN1dA==", encoded_buf); + + auto empty_decoded = Base64::Decode(empty_encoded); + auto decoded_str_buffer = Base64::Decode(encoded_str); + auto decoded_buffer = Base64::Decode(encoded_buf); + + std::string decoded_str(decoded_str_buffer.begin(), decoded_str_buffer.end()); + + ASSERT_EQ(0, empty_decoded.size()); + ASSERT_EQ(str, decoded_str); + ASSERT_EQ(buffer, decoded_buffer); +} + +TEST(ProxyAuthStrategiesTest, BasicAuthTest) { + using BasicAuthStrategy = ssf::layer::proxy::BasicAuthStrategy; + using HttpRequest = ssf::layer::proxy::HttpRequest; + using HttpResponse = ssf::layer::proxy::HttpResponse; + using Proxy = ssf::layer::proxy::Proxy; + + Proxy proxy_ctx; + proxy_ctx.username = "Aladdin"; + proxy_ctx.password = "open sesame"; + + BasicAuthStrategy basic_auth(proxy_ctx); + + HttpResponse response; + HttpRequest request; + request.Reset("GET", "/dir/index.html"); + + response.set_status_code(HttpResponse::kUnauthorized); + response.AddHeader("WWW-Authenticate", "Basic realm=\"WallyWorld\""); + + ASSERT_NE(basic_auth.status(), BasicAuthStrategy::kAuthenticationFailure); + ASSERT_TRUE(basic_auth.Support(response)); + + basic_auth.ProcessResponse(response); + ASSERT_NE(basic_auth.status(), BasicAuthStrategy::kAuthenticationFailure); + + basic_auth.PopulateRequest(&request); + ASSERT_NE(basic_auth.status(), BasicAuthStrategy::kAuthenticationFailure); + + ASSERT_FALSE(basic_auth.Support(response)); + + auto authorization_hdr = request.Header("Authorization"); + ASSERT_FALSE(authorization_hdr.empty()); + + ASSERT_NE(authorization_hdr.find("QWxhZGRpbjpvcGVuIHNlc2FtZQ=="), + std::string::npos) + << "credentials not found"; +} + +TEST(ProxyAuthStrategiesTest, DigestAuthTest) { + using DigestAuthStrategy = ssf::layer::proxy::DigestAuthStrategy; + using HttpRequest = ssf::layer::proxy::HttpRequest; + using HttpResponse = ssf::layer::proxy::HttpResponse; + using Proxy = ssf::layer::proxy::Proxy; + + Proxy proxy_ctx; + proxy_ctx.username = "Mufasa"; + proxy_ctx.password = "Circle Of Life"; + + DigestAuthStrategy digest_auth(proxy_ctx); + digest_auth.set_cnonce("0a4f113b"); + + HttpResponse response; + HttpRequest request; + request.Reset("GET", "/dir/index.html"); + + response.set_status_code(HttpResponse::kUnauthorized); + response.AddHeader("WWW-Authenticate", + "Digest realm =\"testrealm@host.com\", qop" + "=\"auth,auth-int\", nonce " + "=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\", opaque " + "=\"5ccc069c403ebaf9f0171e9517f40e41\""); + + ASSERT_NE(digest_auth.status(), DigestAuthStrategy::kAuthenticationFailure); + ASSERT_TRUE(digest_auth.Support(response)); + + digest_auth.ProcessResponse(response); + ASSERT_NE(digest_auth.status(), DigestAuthStrategy::kAuthenticationFailure); + + digest_auth.PopulateRequest(&request); + ASSERT_NE(digest_auth.status(), DigestAuthStrategy::kAuthenticationFailure); + + ASSERT_FALSE(digest_auth.Support(response)); + + auto authorization_hdr = request.Header("Authorization"); + ASSERT_FALSE(authorization_hdr.empty()); + + ASSERT_NE(authorization_hdr.find("uri=\"/dir/index.html\""), + std::string::npos) + << "uri not found"; + ASSERT_NE(authorization_hdr.find("qop=auth"), std::string::npos) + << "qop not found"; + ASSERT_NE( + authorization_hdr.find("response=\"6629fae49393a05397450978507c4ef1\""), + std::string::npos) + << "response digest not found"; +} + +TEST(ProxyAuthStrategiesTest, DISABLED_NtlmAuthTest) {} + +TEST(ProxyAuthStrategiesTest, NegotiateAuthTest) { + using NegotiateAuthStrategy = ssf::layer::proxy::NegotiateAuthStrategy; + using HttpRequest = ssf::layer::proxy::HttpRequest; + using HttpResponse = ssf::layer::proxy::HttpResponse; + using Proxy = ssf::layer::proxy::Proxy; + + Proxy proxy_ctx; + proxy_ctx.host = "proxy.example.com"; + proxy_ctx.port = "3128"; + proxy_ctx.username = "Mufasa"; + proxy_ctx.password = "Circle Of Life"; + + NegotiateAuthStrategy negotiate_auth(proxy_ctx); + + HttpResponse response; + HttpRequest request; + request.Reset("GET", "/dir/index.html"); + + response.set_status_code(HttpResponse::kUnauthorized); + response.AddHeader("WWW-Authenticate", "Negotiate"); + + ASSERT_NE(negotiate_auth.status(), + NegotiateAuthStrategy::kAuthenticationFailure); + ASSERT_TRUE(negotiate_auth.Support(response)); + + negotiate_auth.ProcessResponse(response); + + ASSERT_NE(negotiate_auth.status(), + NegotiateAuthStrategy::kAuthenticationFailure); + + negotiate_auth.PopulateRequest(&request); + + ASSERT_NE(negotiate_auth.status(), + NegotiateAuthStrategy::kAuthenticationFailure); + + auto authorization_hdr = request.Header("Authorization"); + ASSERT_FALSE(authorization_hdr.empty()); +} \ No newline at end of file diff --git a/src/framework/tests/proxy_layer_tests.cpp b/src/framework/tests/proxy_layer_tests.cpp new file mode 100644 index 00000000..e85ed7e2 --- /dev/null +++ b/src/framework/tests/proxy_layer_tests.cpp @@ -0,0 +1,195 @@ +#include +#include +#include + +#include + +#include "tests/stream_protocol_helpers.h" +#include "tests/virtual_network_helpers.h" +#include "tests/proxy_test_fixture.h" + +#include "ssf/layer/parameters.h" + +#include "ssf/layer/physical/tcp.h" +#include "ssf/layer/cryptography/basic_crypto_stream.h" +#include "ssf/layer/cryptography/tls/OpenSSL/impl.h" +#include "ssf/layer/proxy/basic_proxy_protocol.h" + +ssf::layer::LayerParameters tcp_server_parameters = {{"port", "9000"}}; + +ssf::layer::LayerParameters empty_layer = {}; + +TEST_F(ProxyTestFixture, ProxySetTest) { + typedef ssf::layer::proxy::basic_ProxyProtocol + StreamStackProtocol; + + ASSERT_TRUE(Initialized()) << "Proxy test fixture was not initialized. " + << "Please check that the config file '" + << config_file_ + << "' exists (cf. proxy/README.md)"; + + ssf::tests::Address client_error_tcp_addr("127.0.0.1", "9001"); + ssf::tests::Address client_error_proxy_addr("192.168.0.0.10", "9000"); + + ssf::layer::ParameterStack acceptor_parameters; + acceptor_parameters.push_back(empty_layer); + acceptor_parameters.push_back(tcp_server_parameters); + + ssf::layer::ParameterStack client_parameters; + client_parameters.push_back(GetProxyParam()); + client_parameters.push_back(GetTcpParam()); + + ssf::layer::ParameterStack client_error_connection_parameters; + client_error_connection_parameters.push_back(GetTcpParam()); + client_error_connection_parameters.push_back( + client_error_tcp_addr.ToTCPParam()); + + ssf::layer::ParameterStack client_wrong_number_parameters; + ssf::layer::ParameterStack client_invalid_proxy_parameters; + client_invalid_proxy_parameters.push_back( + client_error_proxy_addr.ToProxyParam()); + client_invalid_proxy_parameters.push_back(GetTcpParam()); + + TestStreamErrorConnectionProtocol( + client_error_connection_parameters); + + TestEndpointResolverError( + client_wrong_number_parameters); + + TestEndpointResolverError( + client_invalid_proxy_parameters); + + TestStreamProtocol(client_parameters, + acceptor_parameters, 1024); + + TestStreamProtocolFuture(client_parameters, + acceptor_parameters); + + /* Uncomment after fix on boost build system + TestStreamProtocolSpawn(client_parameters, + acceptor_parameters);*/ + + TestStreamProtocolSynchronous(client_parameters, + acceptor_parameters); + + PerfTestStreamProtocolHalfDuplex( + client_parameters, acceptor_parameters, 200); + + PerfTestStreamProtocolFullDuplex( + client_parameters, acceptor_parameters, 200); +} + +TEST_F(ProxyTestFixture, TLSOverProxyTCPTest) { + using ProxyTCPProtocol = + ssf::layer::proxy::basic_ProxyProtocol; + // using TLSStackProtocol = + // ssf::layer::cryptography::basic_CryptoStreamProtocol< + // ProxyTCPProtocol, ssf::layer::cryptography::tls>; + using TLSStackProtocol = ssf::layer::cryptography::basic_CryptoStreamProtocol< + ProxyTCPProtocol, ssf::layer::cryptography::buffered_tls>; + + ASSERT_TRUE(Initialized()) << "Proxy test fixture was not initialized. " + << "Please check that the config file '" + << config_file_ + << "' exists (cf. proxy/README.md)"; + + ssf::tests::Address client_error_tcp_addr("127.0.0.1", "9001"); + + ssf::layer::ParameterStack acceptor_parameters; + acceptor_parameters.push_back( + tests::virtual_network_helpers::tls_server_parameters); + acceptor_parameters.push_back(empty_layer); + acceptor_parameters.push_back(tcp_server_parameters); + + ssf::layer::ParameterStack client_parameters; + client_parameters.push_back( + tests::virtual_network_helpers::tls_client_parameters); + client_parameters.push_back(GetProxyParam()); + client_parameters.push_back(GetTcpParam()); + + ssf::layer::ParameterStack client_error_parameters; + client_error_parameters.push_back( + tests::virtual_network_helpers::tls_client_parameters); + client_error_parameters.push_back(GetTcpParam()); + client_error_parameters.push_back(client_error_tcp_addr.ToTCPParam()); + + ssf::layer::ParameterStack client_wrong_number_parameters; + client_wrong_number_parameters.push_back(empty_layer); + client_wrong_number_parameters.push_back(GetTcpParam()); + + TestEndpointResolverError(client_wrong_number_parameters); + + TestStreamProtocol(client_parameters, acceptor_parameters, + 1024); + + TestStreamProtocolFuture(client_parameters, + acceptor_parameters); + + /* Uncomment after fix on boost build system + TestStreamProtocolSpawn(client_parameters, + acceptor_parameters);*/ + + TestStreamProtocolSynchronous(client_parameters, + acceptor_parameters); + + TestStreamErrorConnectionProtocol(client_error_parameters); + + PerfTestStreamProtocolHalfDuplex(client_parameters, + acceptor_parameters, 200); + + PerfTestStreamProtocolFullDuplex(client_parameters, + acceptor_parameters, 200); +} + +TEST_F(ProxyTestFixture, ProxyNotSetTest) { + typedef ssf::layer::proxy::basic_ProxyProtocol< + ssf::layer::physical::TCPPhysicalLayer> StreamStackProtocol; + + ASSERT_TRUE(Initialized()) << "Proxy test fixture was not initialized. " + << "Please check that the config file '" + << config_file_ + << "' exists (cf. proxy/README.md)"; + + ssf::tests::Address client_error_tcp_addr("127.0.0.1", "9001"); + + ssf::layer::LayerParameters empty_layer = {}; + ssf::layer::ParameterStack acceptor_parameters; + acceptor_parameters.push_back(empty_layer); + acceptor_parameters.push_back(tcp_server_parameters); + + ssf::layer::ParameterStack client_parameters; + client_parameters.push_back(empty_layer); + client_parameters.push_back(GetTcpParam()); + + ssf::layer::ParameterStack client_error_connection_parameters; + client_error_connection_parameters.push_back(empty_layer); + client_error_connection_parameters.push_back( + client_error_tcp_addr.ToTCPParam()); + + ssf::layer::ParameterStack client_wrong_number_parameters; + + TestEndpointResolverError( + client_wrong_number_parameters); + + TestStreamProtocol(client_parameters, + acceptor_parameters, 1024); + + TestStreamProtocolFuture(client_parameters, + acceptor_parameters); + + /* Uncomment after fix on boost build system + TestStreamProtocolSpawn(client_parameters, + acceptor_parameters);*/ + + TestStreamProtocolSynchronous(client_parameters, + acceptor_parameters); + + TestStreamErrorConnectionProtocol( + client_error_connection_parameters); + + PerfTestStreamProtocolHalfDuplex( + client_parameters, acceptor_parameters, 200); + + PerfTestStreamProtocolFullDuplex( + client_parameters, acceptor_parameters, 200); +} diff --git a/src/framework/tests/proxy_test_fixture.cpp b/src/framework/tests/proxy_test_fixture.cpp new file mode 100644 index 00000000..54f903e0 --- /dev/null +++ b/src/framework/tests/proxy_test_fixture.cpp @@ -0,0 +1,102 @@ +#include + +#include + +#include "tests/proxy_test_fixture.h" + +namespace ssf { +namespace tests { + +Address::Address() : addr_(""), port_("") {} + +Address::Address(const std::string& addr, const std::string& port) + : addr_(addr), port_(port) {} + +Address::Address(const Address& address) + : addr_(address.addr_), port_(address.port_) {} + +Address& Address::operator=(const Address& address) { + addr_ = address.addr_; + port_ = address.port_; + + return *this; +} + +bool Address::IsSet() { return !addr_.empty() && !port_.empty(); } + +ssf::layer::LayerParameters Address::ToProxyParam() { + return {{"http_host", addr_}, {"http_port", port_}}; +} + +ssf::layer::LayerParameters Address::ToTCPParam() { + return {{"host", addr_}, {"port", port_}}; +} + +} // tests +} // ssf + +ProxyTestFixture::ProxyTestFixture() + : config_file_("./proxy/proxy.json"), config_options_() {} + +ProxyTestFixture::~ProxyTestFixture() {} + +void ProxyTestFixture::SetUp() { ParseConfigFile(config_file_); } + +void ProxyTestFixture::TearDown() {} + +bool ProxyTestFixture::Initialized() { + return config_options_.count("target_host") > 0 && + config_options_.count("target_port") > 0 && + config_options_.count("proxy_host") > 0 && + config_options_.count("proxy_port") > 0; +} + +std::string ProxyTestFixture::GetOption(const std::string& name) const { + auto opt_it = config_options_.find(name); + + return opt_it != config_options_.end() ? opt_it->second : ""; +} + +ssf::layer::LayerParameters ProxyTestFixture::GetTcpParam() const { + ssf::layer::LayerParameters tcp_params; + tcp_params["addr"] = GetOption("target_host"); + tcp_params["port"] = GetOption("target_port"); + + return tcp_params; +} + +ssf::layer::LayerParameters ProxyTestFixture::GetProxyParam() const { + ssf::layer::LayerParameters proxy_params; + proxy_params["http_host"] = GetOption("proxy_host"); + proxy_params["http_port"] = GetOption("proxy_port"); + proxy_params["http_username"] = GetOption("username"); + proxy_params["http_domain"] = GetOption("domain"); + proxy_params["http_password"] = GetOption("password"); + proxy_params["http_reuse_ntlm"] = GetOption("reuse_ntlm"); + proxy_params["http_reuse_kerb"] = GetOption("reuse_kerb"); + + return proxy_params; +} + +bool ProxyTestFixture::ParseConfigFile(const std::string& filepath) { + if (filepath.empty()) { + return false; + } + + std::ifstream file(filepath); + if (!file.is_open()) { + return false; + } + file.close(); + + try { + boost::property_tree::ptree pt; + boost::property_tree::read_json(filepath, pt); + for (const auto& child : pt) { + config_options_[child.first] = child.second.data(); + } + return true; + } catch (const std::exception&) { + return false; + } +} diff --git a/src/framework/tests/proxy_test_fixture.h b/src/framework/tests/proxy_test_fixture.h new file mode 100644 index 00000000..4cf7a909 --- /dev/null +++ b/src/framework/tests/proxy_test_fixture.h @@ -0,0 +1,59 @@ +#ifndef SSF_TESTS_PROXY_TEST_FIXTURE_H_ +#define SSF_TESTS_PROXY_TEST_FIXTURE_H_ + +#include + +#include + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace tests { + +class Address { + public: + Address(); + Address(const std::string& addr, const std::string& port); + Address(const Address& address); + Address& operator=(const Address& address); + + ssf::layer::LayerParameters ToProxyParam(); + + ssf::layer::LayerParameters ToTCPParam(); + + bool IsSet(); + + private: + std::string addr_; + std::string port_; +}; + +} // tests +} // ssf + +class ProxyTestFixture : public ::testing::Test { + protected: + ProxyTestFixture(); + + virtual ~ProxyTestFixture(); + + virtual void SetUp(); + + virtual void TearDown(); + + bool Initialized(); + + ssf::layer::LayerParameters GetTcpParam() const; + + ssf::layer::LayerParameters GetProxyParam() const; + + private: + bool ParseConfigFile(const std::string& filepath); + std::string GetOption(const std::string& name) const; + + protected: + std::string config_file_; + std::map config_options_; +}; + +#endif // SSF_TESTS_PROXY_TEST_FIXTURE_H_ diff --git a/src/framework/tests/queue_tests.cpp b/src/framework/tests/queue_tests.cpp new file mode 100644 index 00000000..7b485717 --- /dev/null +++ b/src/framework/tests/queue_tests.cpp @@ -0,0 +1,389 @@ +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "ssf/layer/queue/async_queue.h" +#include "ssf/layer/queue/send_queued_datagram_socket.h" + +#include "ssf/log/log.h" + +TEST(QueueTest, async_queue_limit_test) { + boost::asio::io_service io_service; + + typedef ssf::layer::queue::basic_async_queue< + uint8_t, std::queue, 1, 1> LimitQueueTest; + + LimitQueueTest queue(io_service); + + boost::system::error_code main_ec; + + auto pushed = [&](const boost::system::error_code& ec) { + EXPECT_EQ(0, ec.value()); + }; + auto failed_pushed = [&](const boost::system::error_code& ec) { + EXPECT_NE(0, ec.value()); + }; + + auto failed_got = [&](const boost::system::error_code& ec, + uint8_t element) { EXPECT_EQ(true, !!ec); }; + auto got3 = [&](const boost::system::error_code& ec, uint8_t element) { + EXPECT_EQ(0, ec.value()); + EXPECT_EQ(0, queue.size()); + queue.push(4, main_ec); + EXPECT_EQ(0, main_ec.value()); + EXPECT_EQ(1, queue.size()); + queue.clear(); + EXPECT_EQ(0, queue.size()); + queue.close(main_ec); + EXPECT_EQ(0, main_ec.value()); + }; + auto got2 = [&](const boost::system::error_code& ec, uint8_t element) { + EXPECT_EQ(0, ec.value()); + EXPECT_EQ(0, queue.size()); + queue.async_get(got3); // op queued because get_op limit not reached + queue.async_get(failed_got); // op not queued because get_op limit reached + queue.push(4, main_ec); + }; + auto got = [&](const boost::system::error_code& ec, uint8_t element) { + EXPECT_EQ(0, ec.value()); + queue.async_get(got2); // op queued because get_op limit not reached + }; + + EXPECT_EQ(0, queue.size()); + queue.get(main_ec); // fails because the queue is empty + EXPECT_NE(0, main_ec.value()); + EXPECT_EQ(0, queue.size()); + queue.push(1, main_ec); // succeed because the queue limit was not reached + EXPECT_EQ(0, main_ec.value()); + EXPECT_EQ(1, queue.size()); + queue.push(1, main_ec); // fails because the queue limit was reached + EXPECT_NE(0, main_ec.value()); + EXPECT_EQ(1, queue.size()); + queue.get(main_ec); // succeed because the queue is not empty + EXPECT_EQ(0, main_ec.value()); + EXPECT_EQ(0, queue.size()); + queue.push(2, main_ec); // succeed because the queue limit was not reached + EXPECT_EQ(0, main_ec.value()); + EXPECT_EQ(1, queue.size()); + uint8_t push_element3 = 3; + queue.async_push(push_element3, + pushed); // op queued because push_op limit not reached + EXPECT_EQ(1, queue.size()); + queue.async_push( + push_element3, + failed_pushed); // op not queued because push_op limit reached + EXPECT_EQ(1, queue.size()); + queue.async_get(got); // op queued because get_op limit not reached + EXPECT_EQ(1, queue.size()); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +TEST(QueueTest, async_queue_clear_test) { + boost::asio::io_service io_service; + + typedef ssf::layer::queue::basic_async_queue< + uint8_t, std::queue, 3, 3> LimitQueueTest; + + LimitQueueTest queue(io_service); + + boost::system::error_code main_ec; + + auto pushed = [&](const boost::system::error_code& ec) { + EXPECT_EQ(0, ec.value()); + }; + + queue.push(1, main_ec); + queue.push(1, main_ec); + queue.push(1, main_ec); + queue.async_push(1, pushed); + queue.async_push(2, pushed); + queue.async_push(3, pushed); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + queue.clear(); + + threads.join_all(); + + EXPECT_EQ(3, queue.size()); + queue.clear(); +} + +TEST(QueueTest, async_queue_close_test) { + boost::asio::io_service io_service; + + typedef ssf::layer::queue::basic_async_queue< + uint8_t, std::queue, 3, 3> LimitQueueTest; + + LimitQueueTest queue(io_service); + + boost::system::error_code main_ec; + + auto pushed = [&](const boost::system::error_code& ec) { + EXPECT_EQ(true, !!ec); + }; + + queue.push(1, main_ec); + queue.push(1, main_ec); + queue.push(1, main_ec); + queue.async_push(1, pushed); + queue.async_push(2, pushed); + queue.async_push(3, pushed); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + queue.close(main_ec); + EXPECT_EQ(0, main_ec.value()); + + threads.join_all(); + + EXPECT_EQ(0, queue.size()); + queue.clear(); +} + +struct EmptyLog { + EmptyLog() {} + + EmptyLog(uint8_t i) {} + + ~EmptyLog() {} +}; + +uint8_t nb_of_elements = 3; +uint8_t nb_of_pushing_threads = 2; + +TEST(QueueTest, async_queue_future_test) { + boost::asio::io_service io_service; + auto p_work = std::unique_ptr( + new boost::asio::io_service::work(io_service)); + + typedef ssf::layer::queue::basic_async_queue< + EmptyLog, std::queue, 3, 3> LimitQueueTest; + + LimitQueueTest queue(io_service); + + boost::system::error_code main_ec; + + auto pushing_lambda = [&]() { + try { + for (uint8_t i = 0; i < nb_of_elements; ++i) { + auto pushed = queue.async_push(i, boost::asio::use_future); + pushed.get(); + } + } + catch (const std::exception& e) { + std::cout << "exception: " << e.what() << std::endl; + } + }; + auto getting_lambda = [&]() { + try { + for (uint8_t i = 0; i < nb_of_elements * nb_of_pushing_threads; ++i) { + auto got = queue.async_get(boost::asio::use_future); + auto element = got.get(); + } + p_work.reset(); + } + catch (const std::exception& e) { + std::cout << "exception: " << e.what() << std::endl; + p_work.reset(); + } + }; + + boost::thread_group threads; + threads.create_thread(getting_lambda); + + for (uint8_t i = 0; i < nb_of_pushing_threads; ++i) { + threads.create_thread(pushing_lambda); + } + + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + threads.join_all(); + + queue.close(main_ec); + EXPECT_EQ(0, main_ec.value()); + + EXPECT_EQ(0, queue.size()); + queue.clear(); +} + +TEST(QueueTest, async_queue_spawn_test) { + boost::asio::io_service io_service; + auto p_work = std::unique_ptr( + new boost::asio::io_service::work(io_service)); + + typedef ssf::layer::queue::basic_async_queue< + EmptyLog, std::queue, 3, 3> LimitQueueTest; + + LimitQueueTest queue(io_service); + + boost::system::error_code main_ec; + + auto pushing_lambda = [&](boost::asio::yield_context context) { + try { + for (uint8_t i = 0; i < nb_of_elements; ++i) { + queue.async_push(i, context); + } + } + catch (const std::exception& e) { + std::cout << "exception: " << e.what() << std::endl; + } + }; + auto getting_lambda = [&](boost::asio::yield_context context) { + try { + for (uint8_t i = 0; i < nb_of_elements * nb_of_pushing_threads; ++i) { + auto got = queue.async_get(context); + } + p_work.reset(); + } + catch (const std::exception& e) { + std::cout << "exception: " << e.what() << std::endl; + p_work.reset(); + } + }; + + boost::asio::spawn(io_service, getting_lambda); + + for (uint8_t i = 0; i < nb_of_pushing_threads; ++i) { + boost::asio::spawn(io_service, pushing_lambda); + } + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + threads.join_all(); + + queue.close(main_ec); + EXPECT_EQ(0, main_ec.value()); + + EXPECT_EQ(0, queue.size()); + queue.clear(); +} + + TEST(QueueTest, send_queued_datagram_socket) { + static const uint32_t number_of_senders = 100; + static const uint32_t number_of_sends = 1000; + + std::array send_buffer; + std::array read_buffer; + + typedef boost::asio::ip::udp::socket UdpSocket; + typedef ssf::layer::queue::basic_send_queued_datagram_socket< + UdpSocket, number_of_sends * number_of_senders> BufferedUdpSocket; + + std::atomic received(0); + std::atomic fail_sent(0); + std::promise done; + std::atomic timeout(false); + uint32_t max_sending = number_of_sends * number_of_senders; + + boost::asio::io_service io_service; + boost::asio::steady_timer timeout_timer(io_service); + std::unique_ptr p_worker( + new boost::asio::io_service::work(io_service)); + boost::system::error_code ec; + boost::asio::ip::udp::resolver resolver(io_service); + + boost::asio::ip::udp::resolver::query query1("127.0.0.1", "10000"); + boost::asio::ip::udp::resolver::iterator iterator1( + resolver.resolve(query1, ec)); + boost::asio::ip::udp::endpoint endpoint1(*iterator1); + + boost::asio::ip::udp::resolver::query query2("127.0.0.1", "10001"); + boost::asio::ip::udp::resolver::iterator iterator2( + resolver.resolve(query2, ec)); + boost::asio::ip::udp::endpoint endpoint2(*iterator2); + + UdpSocket receive_socket(io_service, endpoint1); + receive_socket.connect(endpoint2, ec); + ASSERT_EQ(0, ec.value()) << "Connect received socket failed"; + BufferedUdpSocket send_socket(io_service, endpoint2); + send_socket.next_layer().connect(endpoint1, ec); + ASSERT_EQ(0, ec.value()) << "Connect sent socket failed"; + + auto timeout_handler = [&] (const boost::system::error_code& ec) { + if (!ec && !timeout) { + timeout = true; + SSF_LOG(kLogInfo) << "Timeout : all packets not received " + << received.load() << " / " << max_sending; + done.set_value(true); + } + }; + + auto sender_lambda = [&]() { + for (std::size_t i = 0; i < number_of_sends; ++i) { + send_socket.async_send( + boost::asio::buffer(send_buffer), + [&](const boost::system::error_code& ec, std::size_t length) { + if (ec) { + ++fail_sent; + } + }); + } + }; + + std::function + receiver_lambda; + + receiver_lambda = [&](const boost::system::error_code& ec, std::size_t length) { + if (!ec) { + boost::system::error_code timer_ec; + timeout_timer.cancel(timer_ec); + ++received; + if (received.load() <= max_sending) { + receive_socket.async_receive(boost::asio::buffer(read_buffer), + receiver_lambda); + timeout_timer.expires_from_now(std::chrono::seconds(2)); + timeout_timer.async_wait(timeout_handler); + } else { + done.set_value(true); + } + } + }; + + boost::thread_group threads; + + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + threads.create_thread(boost::bind(receiver_lambda, ec, 0)); + + for (uint16_t i = 0; i < number_of_senders; ++i) { + threads.create_thread(sender_lambda); + } + + done.get_future().wait(); + + send_socket.close(); + receive_socket.close(); + p_worker.reset(); + threads.join_all(); +} diff --git a/src/framework/tests/router_system_tests.cpp b/src/framework/tests/router_system_tests.cpp new file mode 100644 index 00000000..882991f7 --- /dev/null +++ b/src/framework/tests/router_system_tests.cpp @@ -0,0 +1,99 @@ +#include + +#include + +#include +#include + +#include + +#include +#include +#include + +#include "ssf/system/system_interfaces.h" +#include "ssf/system/system_routers.h" + +class RouterSystemTestFixture : public ::testing::Test { + protected: + RouterSystemTestFixture() + : system_multiple_interfaces_config_filename_( + "./system/system_multiple_config.json"), + system_router_config_filename_("./system/router_config.json"), + fail_system_router_config_filename_( + "./system/fail_router_config.json") {} + + virtual ~RouterSystemTestFixture() {} + + virtual void SetUp() {} + + virtual void TearDown() {} + + protected: + std::string system_multiple_interfaces_config_filename_; + std::string system_router_config_filename_; + std::string fail_system_router_config_filename_; +}; + +TEST_F(RouterSystemTestFixture, FailImportRouters) { + boost::asio::io_service io_service; + boost::system::error_code ec; + + ssf::system::SystemRouters system_routers(io_service); + + system_routers.Start(); + + std::promise all_up; + auto all_routers_up = + [&all_up](const boost::system::error_code& ec) { all_up.set_value(!ec); }; + + ASSERT_EQ(0, system_routers.AsyncConfig( + system_multiple_interfaces_config_filename_, + fail_system_router_config_filename_, ec, all_routers_up)); + + ASSERT_NE(0, ec.value()) << "Error when configuring routers : " + << ec.message(); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + ASSERT_FALSE(all_up.get_future().get()) << "All routers up"; + + system_routers.Stop(); + + threads.join_all(); +} + +TEST_F(RouterSystemTestFixture, ImportRouters) { + boost::asio::io_service io_service; + + ssf::system::SystemRouters system_routers(io_service); + + boost::system::error_code ec; + system_routers.Start(); + + std::promise all_up; + + auto all_routers_up = + [&all_up](const boost::system::error_code& ec) { all_up.set_value(!ec); }; + + ASSERT_EQ(2, system_routers.AsyncConfig( + system_multiple_interfaces_config_filename_, + system_router_config_filename_, ec, all_routers_up)); + + ASSERT_EQ(0, ec.value()) << "Error when configuring routers : " + << ec.message(); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + ASSERT_TRUE(all_up.get_future().get()) << "All routers not up"; + + system_routers.Stop(); + + threads.join_all(); +} diff --git a/src/framework/tests/routing_layer_tests.cpp b/src/framework/tests/routing_layer_tests.cpp new file mode 100644 index 00000000..fbe2b93c --- /dev/null +++ b/src/framework/tests/routing_layer_tests.cpp @@ -0,0 +1,72 @@ +#include + +#include "ssf/layer/parameters.h" + +#include "tests/datagram_protocol_helpers.h" +#include "tests/routing_test_fixture.h" + +TEST_F(RoutingTestFixture, NetworkResolvingTest) { + boost::asio::io_service io_service; + boost::system::error_code ec; + RoutedProtocol::resolver resolver(io_service); + + { + ssf::layer::LayerParameters routed_socket1_parameters; + routed_socket1_parameters["network_address"] = "11"; + routed_socket1_parameters["router"] = "router1"; + ssf::layer::ParameterStack parameters_socket1; + parameters_socket1.push_back(routed_socket1_parameters); + resolver.resolve(parameters_socket1, ec); + ASSERT_NE(0, ec.value()); + } + + { + ssf::layer::LayerParameters routed_socket1_parameters; + routed_socket1_parameters["network_address"] = "1"; + routed_socket1_parameters["router"] = "router1"; + ssf::layer::ParameterStack parameters_socket1; + parameters_socket1.push_back(routed_socket1_parameters); + resolver.resolve(parameters_socket1, ec); + ASSERT_EQ(0, ec.value()); + } +} + +TEST_F(RoutingTestFixture, RoutingPacketLocallyTest) { + ssf::layer::LayerParameters routed_socket1_parameters; + routed_socket1_parameters["network_address"] = "7"; + routed_socket1_parameters["router"] = "router1"; + ssf::layer::ParameterStack parameters_socket1; + parameters_socket1.push_back(routed_socket1_parameters); + + ssf::layer::LayerParameters routed_socket2_parameters; + routed_socket2_parameters["network_address"] = "5"; + routed_socket2_parameters["router"] = "router1"; + ssf::layer::ParameterStack parameters_socket2; + parameters_socket2.push_back(routed_socket2_parameters); + + TestDatagramProtocolPerfHalfDuplex(parameters_socket1, + parameters_socket2, 1000); + + TestDatagramProtocolPerfFullDuplex(parameters_socket1, + parameters_socket2, 1000); +} + +TEST_F(RoutingTestFixture, RoutingPacketRemotelyTest) { + ssf::layer::LayerParameters routed_socket1_parameters; + routed_socket1_parameters["network_address"] = "7"; + routed_socket1_parameters["router"] = "router1"; + ssf::layer::ParameterStack parameters_socket1; + parameters_socket1.push_back(routed_socket1_parameters); + + ssf::layer::LayerParameters routed_socket2_parameters; + routed_socket2_parameters["network_address"] = "2"; + routed_socket2_parameters["router"] = "router2"; + ssf::layer::ParameterStack parameters_socket2; + parameters_socket2.push_back(routed_socket2_parameters); + + TestDatagramProtocolPerfHalfDuplex(parameters_socket1, + parameters_socket2, 1000); + + TestDatagramProtocolPerfFullDuplex(parameters_socket1, + parameters_socket2, 1000); +} diff --git a/src/framework/tests/routing_test_fixture.h b/src/framework/tests/routing_test_fixture.h new file mode 100644 index 00000000..01882b28 --- /dev/null +++ b/src/framework/tests/routing_test_fixture.h @@ -0,0 +1,224 @@ +#ifndef SSF_TESTS_ROUTING_TEST_FIXTURE_H_ +#define SSF_TESTS_ROUTING_TEST_FIXTURE_H_ + +#include + +#include + +#include + +#include + +#include "ssf/layer/parameters.h" + +#include "ssf/layer/network/basic_network_protocol.h" +#include "ssf/layer/routing/basic_router.h" +#include "ssf/layer/routing/basic_routed_protocol.h" + +#include "tests/interface_test_fixture.h" + +class RoutingTestFixture : public InterfaceTestFixture { + protected: + using NetworkProtocol = + ssf::layer::network::basic_NetworkProtocol; + using RoutedProtocol = + ssf::layer::routing::basic_RoutedProtocol; + + protected: + RoutingTestFixture() + : InterfaceTestFixture(), + io_service_(), + resolver_(io_service_), + threads_(), + p_work_(new boost::asio::io_service::work(io_service_)), + p_router1_(nullptr), + p_router2_(nullptr) {} + + virtual ~RoutingTestFixture() { + boost::system::error_code ec; + RoutedProtocol::StopAllRouters(); + } + + virtual void SetUp() { + RoutedProtocol::StartRouter("router1", io_service_); + RoutedProtocol::StartRouter("router2", io_service_); + + p_router1_ = RoutedProtocol::get_router("router1"); + p_router2_ = RoutedProtocol::get_router("router2"); + + InterfaceTestFixture::SetUp(); + + boost::system::error_code ec; + + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "1"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "simple_lo1"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver_.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + uint32_t prefix1 = 1; + p_router1_->add_network(prefix1, socket1_local_endpoint, ec); + } + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "2"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "simple_lo2"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver_.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + uint32_t prefix1 = 2; + p_router2_->add_network(prefix1, socket1_local_endpoint, ec); + } + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "3"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "simple_tls_lo1"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver_.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + uint32_t prefix1 = 3; + p_router1_->add_network(prefix1, socket1_local_endpoint, ec); + } + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "4"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "simple_tls_lo2"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver_.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + uint32_t prefix1 = 4; + p_router2_->add_network(prefix1, socket1_local_endpoint, ec); + } + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "5"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "circuit_lo1"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver_.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + uint32_t prefix1 = 5; + p_router1_->add_network(prefix1, socket1_local_endpoint, ec); + } + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "6"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "circuit_lo2"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver_.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + uint32_t prefix1 = 6; + p_router2_->add_network(prefix1, socket1_local_endpoint, ec); + } + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "7"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "circuit_tls_lo1"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver_.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + uint32_t prefix1 = 7; + p_router1_->add_network(prefix1, socket1_local_endpoint, ec); + } + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "8"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "circuit_tls_lo2"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver_.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + uint32_t prefix1 = 8; + p_router2_->add_network(prefix1, socket1_local_endpoint, ec); + } + + //------------------------- Set routing table --------------------------// + { + boost::system::error_code ec; + p_router1_->add_route(2, 7, ec); + p_router1_->add_route(4, 1, ec); + p_router1_->add_route(6, 3, ec); + p_router1_->add_route(8, 5, ec); + + p_router2_->add_route(1, 4, ec); + p_router2_->add_route(3, 6, ec); + p_router2_->add_route(5, 8, ec); + p_router2_->add_route(7, 2, ec); + } + + //------------------------- run io_service --------------------------// + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads_.create_thread([this]() { this->io_service_.run(); }); + } + + Wait(); + } + + virtual void TearDown() { + boost::system::error_code ec; + p_router1_->remove_network(1, ec); + p_router1_->remove_network(3, ec); + p_router1_->remove_network(5, ec); + p_router1_->remove_network(7, ec); + p_router2_->remove_network(2, ec); + p_router2_->remove_network(4, ec); + p_router2_->remove_network(6, ec); + p_router2_->remove_network(8, ec); + + p_router1_->remove_route(2, ec); + p_router1_->remove_route(4, ec); + p_router1_->remove_route(6, ec); + p_router1_->remove_route(8, ec); + p_router2_->remove_route(1, ec); + p_router2_->remove_route(3, ec); + p_router2_->remove_route(5, ec); + p_router2_->remove_route(7, ec); + + p_router1_->close(ec); + p_router2_->close(ec); + + p_work_.reset(); + threads_.join_all(); + + InterfaceTestFixture::TearDown(); + } + + private: + bool Wait() { return true; } + + protected: + boost::asio::io_service io_service_; + + NetworkProtocol::resolver resolver_; + + boost::thread_group threads_; + std::unique_ptr p_work_; + + std::shared_ptr p_router1_; + std::shared_ptr p_router2_; +}; + +#endif // SSF_TESTS_ROUTING_TEST_FIXTURE_H_ diff --git a/src/framework/tests/stream_protocol_helpers.h b/src/framework/tests/stream_protocol_helpers.h new file mode 100644 index 00000000..b8531f14 --- /dev/null +++ b/src/framework/tests/stream_protocol_helpers.h @@ -0,0 +1,1336 @@ +#ifndef SSF_TESTS_STREAM_PROTOCOL_HELPERS_H_ +#define SSF_TESTS_STREAM_PROTOCOL_HELPERS_H_ + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "ssf/layer/physical/tcp_helpers.h" + +#include "ssf/layer/parameters.h" + +#include "tests/tools.h" +#include "tests/virtual_network_helpers.h" + +const std::size_t buffer_size = 1024; + +void TCPPerfTestHalfDuplex(ssf::layer::ParameterStack client_parameters, + ssf::layer::ParameterStack acceptor_parameters, + uint64_t mbytes) { + using Buffer = std::vector; + + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + + uint64_t data_size_to_send = mbytes * 1024ULL * 1024ULL; + + auto p_bandwidth = + std::make_shared(data_size_to_send * 8ULL); + + uint64_t count1 = 0; + uint64_t count2 = 0; + + Buffer buffer1(1024 * 1024); + Buffer r_buffer2(1024 * 1024); + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + boost::asio::ip::tcp::socket socket1(io_service); + boost::asio::ip::tcp::socket socket2(io_service); + boost::asio::ip::tcp::acceptor acceptor(io_service); + + boost::asio::ip::tcp::endpoint acceptor_endpoint( + ssf::layer::physical::detail::make_tcp_endpoint( + io_service, acceptor_parameters.front(), resolve_ec)); + + boost::asio::ip::tcp::endpoint remote_endpoint( + ssf::layer::physical::detail::make_tcp_endpoint( + io_service, client_parameters.front(), resolve_ec)); + + tests::virtual_network_helpers::AcceptHandler accepted; + tests::virtual_network_helpers::ConnectHandler connected; + tests::virtual_network_helpers::SendHandler sent_handler1; + tests::virtual_network_helpers::ReceiveHandler received_handler2; + + connected = [&, p_bandwidth](const boost::system::error_code& ec) { + ASSERT_EQ(0, ec.value()) + << "Connect handler should not be in error: " << ec.message(); + + p_bandwidth->ResetTime(); + + if (data_size_to_send - count1 >= buffer1.size()) { + socket1.async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + socket1.async_send( + boost::asio::buffer(&buffer1[0], static_cast( + data_size_to_send - count1)), + sent_handler1); + } + }; + + sent_handler1 = [&, p_bandwidth](const boost::system::error_code& ec, + std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Send handler should not be in error: " << ec.message(); + + count1 += length; + if (count1 < data_size_to_send) { + if (data_size_to_send - count1 >= buffer1.size()) { + socket1.async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + socket1.async_send( + boost::asio::buffer(&buffer1[0], static_cast( + data_size_to_send - count1)), + sent_handler1); + } + } + }; + + accepted = [&](const boost::system::error_code& ec) { + ASSERT_EQ(0, ec.value()) + << "Accept handler should not be in error: " << ec.message(); + + socket2.async_receive(boost::asio::buffer(r_buffer2), received_handler2); + }; + + received_handler2 = [&](const boost::system::error_code& ec, + std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers(buffer1, r_buffer2, + length)); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0, length); + + count2 += length; + if (count2 < data_size_to_send) { + socket2.async_receive(boost::asio::buffer(r_buffer2), received_handler2); + } else { + ASSERT_EQ(data_size_to_send, count2); + boost::system::error_code close_ec; + socket1.close(close_ec); + socket2.close(close_ec); + acceptor.close(close_ec); + } + }; + + boost::system::error_code ec; + + acceptor.open(boost::asio::ip::tcp::v4()); + acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + acceptor.bind(acceptor_endpoint, ec); + ASSERT_EQ(0, ec.value()) << "Bind acceptor should not be in error: " + << ec.message(); + acceptor.listen(100, ec); + ASSERT_EQ(0, ec.value()) << "Listen acceptor should not be in error: " + << ec.message(); + + acceptor.async_accept(socket2, accepted); + socket1.async_connect(remote_endpoint, connected); + + p_bandwidth.reset(); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +template +void PerfTestStreamProtocolHalfDuplex( + typename StreamProtocol::resolver::query client_parameters, + typename StreamProtocol::resolver::query acceptor_parameters, + uint64_t mbytes) { + using Buffer = std::vector; + + std::cout << " * PerfTestStreamProtocolHalfDuplex" << std::endl; + + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + + uint64_t data_size_to_send = mbytes * 1024ULL * 1024ULL; + + auto p_bandwidth1 = + std::make_shared(data_size_to_send * 8ULL); + + uint64_t s_count1 = 0; + uint64_t count1 = 0; + + Buffer buffer1(1024 * 1024); + Buffer r_buffer2(1024 * 1024); + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + typename StreamProtocol::socket socket1(io_service); + typename StreamProtocol::socket socket2(io_service); + typename StreamProtocol::acceptor acceptor(io_service); + typename StreamProtocol::resolver resolver(io_service); + + auto acceptor_endpoint_it = resolver.resolve(acceptor_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving acceptor endpoint should not be in error: " + << resolve_ec.message(); + + typename StreamProtocol::endpoint acceptor_endpoint(*acceptor_endpoint_it); + + auto remote_endpoint_it = resolver.resolve(client_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving remote endpoint should not be in error: " + << resolve_ec.message(); + typename StreamProtocol::endpoint remote_endpoint(*remote_endpoint_it); + + tests::virtual_network_helpers::AcceptHandler accepted; + tests::virtual_network_helpers::ConnectHandler connected; + tests::virtual_network_helpers::SendHandler sent_handler1; + tests::virtual_network_helpers::ReceiveHandler received_handler2; + + auto close_all = [&]() { + boost::system::error_code close_ec; + acceptor.close(close_ec); + EXPECT_FALSE(acceptor.is_open()); + socket1.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket1.close(close_ec); + EXPECT_FALSE(socket1.is_open()); + socket2.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket2.close(close_ec); + EXPECT_FALSE(socket2.is_open()); + }; + + connected = [&, p_bandwidth1](const boost::system::error_code& ec) { + EXPECT_EQ(0, ec.value()) + << "Connect handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + p_bandwidth1->ResetTime(); + + if (data_size_to_send - s_count1 >= buffer1.size()) { + socket1.async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + socket1.async_send( + boost::asio::buffer(&buffer1[0], static_cast( + data_size_to_send - s_count1)), + sent_handler1); + } + }; + + sent_handler1 = [&, p_bandwidth1](const boost::system::error_code& ec, + std::size_t length) { + EXPECT_EQ(0, ec.value()) + << "Send handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + s_count1 += length; + if (s_count1 < data_size_to_send) { + if (data_size_to_send - s_count1 >= buffer1.size()) { + socket1.async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + socket1.async_send( + boost::asio::buffer(&buffer1[0], static_cast( + data_size_to_send - s_count1)), + sent_handler1); + } + } + }; + + accepted = [&](const boost::system::error_code& ec) { + EXPECT_EQ(0, ec.value()) + << "Accept handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + socket2.async_receive(boost::asio::buffer(r_buffer2), received_handler2); + }; + + received_handler2 = [&](const boost::system::error_code& ec, + std::size_t length) { + EXPECT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + EXPECT_TRUE(tests::virtual_network_helpers::CheckBuffers(buffer1, r_buffer2, + length)); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0, length); + + count1 += length; + if (count1 < data_size_to_send) { + socket2.async_receive(boost::asio::buffer(r_buffer2), received_handler2); + } else { + EXPECT_EQ(data_size_to_send, count1); + close_all(); + } + }; + + boost::system::error_code ec; + + acceptor.open(); + acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + acceptor.bind(acceptor_endpoint, ec); + ASSERT_EQ(0, ec.value()) << "Bind acceptor should not be in error: " + << ec.message(); + acceptor.listen(100, ec); + ASSERT_EQ(0, ec.value()) << "Listen acceptor should not be in error: " + << ec.message(); + + acceptor.async_accept(socket2, accepted); + socket1.async_connect(remote_endpoint, connected); + + p_bandwidth1.reset(); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +template +void PerfTestStreamProtocolFullDuplex( + typename StreamProtocol::resolver::query client_parameters, + typename StreamProtocol::resolver::query acceptor_parameters, + uint64_t mbytes) { + using Buffer = std::vector; + + std::cout << " * PerfTestStreamProtocolFullDuplex" << std::endl; + + std::mutex cv_mutex; + std::condition_variable cv; + bool finished1 = false; + bool finished2 = false; + + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + + uint64_t data_size_to_send = mbytes * 1024ULL * 1024ULL; + + auto p_bandwidth1 = + std::make_shared(data_size_to_send * 8ULL); + auto p_bandwidth2 = + std::make_shared(data_size_to_send * 8ULL); + + uint64_t s_count1 = 0; + uint64_t count1 = 0; + uint64_t s_count2 = 0; + uint64_t count2 = 0; + + Buffer buffer1(1024 * 1024); + Buffer buffer2(1024 * 1024); + Buffer r_buffer1(1024 * 1024); + Buffer r_buffer2(1024 * 1024); + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1); + tests::virtual_network_helpers::ResetBuffer(&buffer2, 1); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + typename StreamProtocol::socket socket1(io_service); + typename StreamProtocol::socket socket2(io_service); + typename StreamProtocol::acceptor acceptor(io_service); + typename StreamProtocol::resolver resolver(io_service); + + auto acceptor_endpoint_it = resolver.resolve(acceptor_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving acceptor endpoint should not be in error: " + << resolve_ec.message(); + + typename StreamProtocol::endpoint acceptor_endpoint(*acceptor_endpoint_it); + + auto remote_endpoint_it = resolver.resolve(client_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving remote endpoint should not be in error: " + << resolve_ec.message(); + typename StreamProtocol::endpoint remote_endpoint(*remote_endpoint_it); + + tests::virtual_network_helpers::AcceptHandler accepted; + tests::virtual_network_helpers::ConnectHandler connected; + tests::virtual_network_helpers::SendHandler sent_handler1; + tests::virtual_network_helpers::SendHandler sent_handler2; + tests::virtual_network_helpers::ReceiveHandler received_handler1; + tests::virtual_network_helpers::ReceiveHandler received_handler2; + + auto close_all = [&]() { + boost::system::error_code close_ec; + acceptor.close(close_ec); + EXPECT_FALSE(acceptor.is_open()); + socket1.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket1.close(close_ec); + EXPECT_FALSE(socket1.is_open()); + socket2.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket2.close(close_ec); + EXPECT_FALSE(socket2.is_open()); + + { + std::lock_guard lk(cv_mutex); + finished1 = true; + finished2 = true; + } + cv.notify_one(); + }; + + connected = [&, p_bandwidth1](const boost::system::error_code& ec) { + EXPECT_EQ(0, ec.value()) + << "Connect handler should not be in error: " << ec.message(); + + if (ec) { + close_all(); + return; + } + + socket1.async_receive(boost::asio::buffer(r_buffer1), received_handler1); + + p_bandwidth1->ResetTime(); + + if (data_size_to_send >= buffer1.size()) { + socket1.async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + socket1.async_send( + boost::asio::buffer(&buffer1[0], + static_cast(data_size_to_send)), + sent_handler1); + } + }; + + accepted = [&, p_bandwidth2](const boost::system::error_code& ec) { + EXPECT_EQ(0, ec.value()) + << "Accept handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + socket2.async_receive(boost::asio::buffer(r_buffer2), received_handler2); + + p_bandwidth2->ResetTime(); + + if (data_size_to_send >= buffer2.size()) { + socket2.async_send(boost::asio::buffer(buffer2), sent_handler2); + } else { + socket2.async_send( + boost::asio::buffer(&buffer2[0], + static_cast(data_size_to_send)), + sent_handler2); + } + }; + + sent_handler1 = [&, p_bandwidth1](const boost::system::error_code& ec, + std::size_t length) { + EXPECT_EQ(0, ec.value()) + << "Send handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + s_count1 += length; + + if (s_count1 < data_size_to_send) { + if (data_size_to_send - s_count1 >= buffer1.size()) { + socket1.async_send(boost::asio::buffer(buffer1), sent_handler1); + } else { + socket1.async_send( + boost::asio::buffer(&buffer1[0], static_cast( + data_size_to_send - s_count1)), + sent_handler1); + } + } + }; + + sent_handler2 = [&, p_bandwidth2](const boost::system::error_code& ec, + std::size_t length) { + EXPECT_EQ(0, ec.value()) + << "Send handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + s_count2 += length; + + if (s_count2 < data_size_to_send) { + if (data_size_to_send - s_count2 >= buffer2.size()) { + socket2.async_send(boost::asio::buffer(buffer2), sent_handler2); + } else { + socket2.async_send( + boost::asio::buffer(&buffer2[0], static_cast( + data_size_to_send - s_count2)), + sent_handler2); + } + } + }; + + received_handler1 = [&](const boost::system::error_code& ec, + std::size_t length) { + EXPECT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + EXPECT_TRUE(tests::virtual_network_helpers::CheckBuffers(buffer2, r_buffer1, + length)); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0, length); + + count2 += length; + + if (count2 < data_size_to_send) { + socket1.async_receive(boost::asio::buffer(r_buffer1), received_handler1); + } else { + EXPECT_EQ(data_size_to_send, count2); + { + std::lock_guard lk(cv_mutex); + finished2 = true; + } + cv.notify_one(); + } + }; + + received_handler2 = [&](const boost::system::error_code& ec, + std::size_t length) { + EXPECT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + EXPECT_TRUE(tests::virtual_network_helpers::CheckBuffers(buffer1, r_buffer2, + length)); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0, length); + + count1 += length; + + if (count1 < data_size_to_send) { + socket2.async_receive(boost::asio::buffer(r_buffer2), received_handler2); + } else { + EXPECT_EQ(data_size_to_send, count1); + { + std::lock_guard lk(cv_mutex); + finished1 = true; + } + cv.notify_one(); + } + }; + + boost::system::error_code ec; + + acceptor.open(); + acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + acceptor.bind(acceptor_endpoint, ec); + ASSERT_EQ(0, ec.value()) << "Bind acceptor should not be in error: " + << ec.message(); + acceptor.listen(100, ec); + ASSERT_EQ(0, ec.value()) << "Listen acceptor should not be in error: " + << ec.message(); + + p_bandwidth1.reset(); + p_bandwidth2.reset(); + + acceptor.async_accept(socket2, accepted); + socket1.async_connect(remote_endpoint, connected); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + + std::unique_lock lk(cv_mutex); + cv.wait(lk, [&finished1, &finished2] { return finished1 && finished2; }); + lk.unlock(); + + close_all(); + + threads.join_all(); +} + +/// Test a stream protocol +/// Bind an acceptor to the endpoint defined by +/// acceptor_parameters +/// Connect a client socket to an endpoint defined +/// by client_parameters +/// Send data in ping pong mode +template +void TestStreamProtocol( + typename StreamProtocol::resolver::query client_parameters, + typename StreamProtocol::resolver::query acceptor_parameters, + uint64_t max_packets) { + std::cout << " * TestStreamProtocol" << std::endl; + typedef std::array Buffer; + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + + uint64_t count1 = 0; + uint64_t count2 = 0; + + Buffer buffer1; + Buffer buffer2; + Buffer r_buffer1; + Buffer r_buffer2; + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1, false); + tests::virtual_network_helpers::ResetBuffer(&buffer2, 2, false); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + typename StreamProtocol::socket socket1(io_service); + typename StreamProtocol::socket socket2(io_service); + typename StreamProtocol::acceptor acceptor(io_service); + typename StreamProtocol::resolver resolver(io_service); + + auto acceptor_endpoint_it = resolver.resolve(acceptor_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving acceptor endpoint should not be in error: " + << resolve_ec.message(); + + typename StreamProtocol::endpoint acceptor_endpoint(*acceptor_endpoint_it); + + auto remote_endpoint_it = resolver.resolve(client_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving remote endpoint should not be in error: " + << resolve_ec.message(); + typename StreamProtocol::endpoint remote_endpoint(*remote_endpoint_it); + + tests::virtual_network_helpers::AcceptHandler accepted; + tests::virtual_network_helpers::ConnectHandler connected; + tests::virtual_network_helpers::SendHandler sent_handler1; + tests::virtual_network_helpers::SendHandler sent_handler2; + tests::virtual_network_helpers::ReceiveHandler received_handler1; + tests::virtual_network_helpers::ReceiveHandler received_handler2; + + auto close_all = [&]() { + boost::system::error_code close_ec; + acceptor.close(close_ec); + EXPECT_FALSE(acceptor.is_open()); + socket1.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket1.close(close_ec); + EXPECT_FALSE(socket1.is_open()); + socket2.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket2.close(close_ec); + EXPECT_FALSE(socket2.is_open()); + }; + + accepted = [&](const boost::system::error_code& ec) { + EXPECT_EQ(0, ec.value()) + << "Accept handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + boost::system::error_code endpoint_ec; + socket2.local_endpoint(endpoint_ec); + EXPECT_EQ(0, endpoint_ec.value()) + << "Local endpoint should be set: " << endpoint_ec.message(); + + socket2.remote_endpoint(endpoint_ec); + EXPECT_EQ(0, endpoint_ec.value()) + << "Remote endpoint should be set: " << endpoint_ec.message(); + + boost::asio::async_read(socket2, boost::asio::buffer(r_buffer2), + received_handler2); + }; + + connected = [&](const boost::system::error_code& ec) { + EXPECT_EQ(0, ec.value()) + << "Connect handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + boost::system::error_code endpoint_ec; + socket1.local_endpoint(endpoint_ec); + EXPECT_EQ(0, endpoint_ec.value()) + << "Local endpoint should be set: " << endpoint_ec.message(); + + auto remote_ep = socket1.remote_endpoint(endpoint_ec); + EXPECT_EQ(0, endpoint_ec.value()) + << "Remote endpoint should be set: " << endpoint_ec.message(); + EXPECT_EQ(remote_endpoint, remote_ep) << "Remote endpoint invalid"; + + boost::asio::async_write(socket1, boost::asio::buffer(buffer1), + sent_handler1); + }; + + received_handler1 = + [&](const boost::system::error_code& ec, std::size_t length) { + EXPECT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + EXPECT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer2, r_buffer1, length)); + + { + /*boost::system::error_code endpoint_ec; + auto local_ep = socket1.local_endpoint(endpoint_ec); + auto remote_ep = socket2.remote_endpoint(endpoint_ec);*/ + // TODO: endpoint contexts can really be equal? + /*ASSERT_EQ(local_ep.endpoint_context(), remote_ep.endpoint_context()) + << "Endpoints should be equal";*/ + } + + { + /*boost::system::error_code endpoint_ec; + auto local_ep = socket2.local_endpoint(endpoint_ec); + auto remote_ep = socket1.remote_endpoint(endpoint_ec);*/ + // TODO: endpoint contexts can really be equal? + /*ASSERT_EQ(local_ep.endpoint_context(), remote_ep.endpoint_context()) + << "Endpoints should be equal";*/ + } + + ++count1; + if (count1 < max_packets) { + boost::asio::async_write(socket1, boost::asio::buffer(buffer1), + sent_handler1); + } else { + EXPECT_EQ(max_packets, count1); + close_all(); + } + }; + + received_handler2 = + [&](const boost::system::error_code& ec, std::size_t length) { + EXPECT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + EXPECT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer1, r_buffer2, length)); + ++count2; + if (count2 < max_packets) { + boost::asio::async_write(socket2, boost::asio::buffer(buffer2), + sent_handler2); + } else { + boost::asio::async_write( + socket2, boost::asio::buffer(buffer2), + [](const boost::system::error_code&, std::size_t) {}); + EXPECT_EQ(max_packets, count2); + } + }; + + sent_handler1 = [&](const boost::system::error_code& ec, std::size_t length) { + EXPECT_EQ(0, ec.value()) + << "Send handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + boost::asio::async_read(socket1, boost::asio::buffer(r_buffer1), + received_handler1); + }; + + sent_handler2 = [&](const boost::system::error_code& ec, std::size_t length) { + EXPECT_EQ(0, ec.value()) + << "Send handler should not be in error: " << ec.message(); + if (ec) { + close_all(); + return; + } + + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + boost::asio::async_read(socket2, boost::asio::buffer(r_buffer2), + received_handler2); + }; + + boost::system::error_code ec; + + acceptor.open(); + acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + ec.clear(); + acceptor.bind(acceptor_endpoint, ec); + ASSERT_EQ(0, ec.value()) << "Bind acceptor should not be in error: " + << ec.message(); + acceptor.listen(100, ec); + ASSERT_EQ(0, ec.value()) << "Listen acceptor should not be in error: " + << ec.message(); + acceptor.async_accept(socket2, accepted); + + socket1.async_connect(remote_endpoint, connected); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +/// Test a stream protocol future interface +template +void TestStreamProtocolFuture( + typename StreamProtocol::resolver::query client_parameters, + typename StreamProtocol::resolver::query acceptor_parameters) { + std::cout << " * TestStreamProtocolFuture" << std::endl; + typedef std::array Buffer; + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + auto p_worker = std::unique_ptr( + new boost::asio::io_service::work(io_service)); + + Buffer buffer1; + Buffer buffer2; + Buffer r_buffer1; + Buffer r_buffer2; + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1, false); + tests::virtual_network_helpers::ResetBuffer(&buffer2, 2, false); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + typename StreamProtocol::socket socket1(io_service); + typename StreamProtocol::socket socket2(io_service); + typename StreamProtocol::acceptor acceptor(io_service); + typename StreamProtocol::resolver resolver(io_service); + + auto acceptor_endpoint_it = resolver.resolve(acceptor_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving acceptor endpoint should not be in error: " + << resolve_ec.message(); + + typename StreamProtocol::endpoint acceptor_endpoint(*acceptor_endpoint_it); + + auto remote_endpoint_it = resolver.resolve(client_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving remote endpoint should not be in error: " + << resolve_ec.message(); + typename StreamProtocol::endpoint remote_endpoint(*remote_endpoint_it); + + boost::system::error_code ec; + + acceptor.open(); + acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + acceptor.bind(acceptor_endpoint, ec); + ASSERT_EQ(0, ec.value()) << "Bind acceptor should not be in error: " + << ec.message(); + acceptor.listen(100, ec); + ASSERT_EQ(0, ec.value()) << "Listen acceptor should not be in error: " + << ec.message(); + + auto close_all = [&]() { + boost::system::error_code close_ec; + acceptor.close(close_ec); + EXPECT_FALSE(acceptor.is_open()); + socket1.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket1.close(close_ec); + EXPECT_FALSE(socket1.is_open()); + socket2.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket2.close(close_ec); + EXPECT_FALSE(socket2.is_open()); + p_worker.reset(); + }; + + auto lambda2 = [&]() { + try { + auto accepted = acceptor.async_accept(socket2, boost::asio::use_future); + accepted.get(); + auto read = boost::asio::async_read( + socket2, boost::asio::buffer(r_buffer2), boost::asio::use_future); + read.get(); + auto written = boost::asio::async_write( + socket2, boost::asio::buffer(buffer2), boost::asio::use_future); + written.get(); + } catch (const std::exception& e) { + ADD_FAILURE() << e.what(); + close_all(); + } + }; + + auto lambda1 = [&]() { + try { + auto connected = + socket1.async_connect(remote_endpoint, boost::asio::use_future); + connected.get(); + auto written = boost::asio::async_write( + socket1, boost::asio::buffer(buffer1), boost::asio::use_future); + written.get(); + auto read = boost::asio::async_read( + socket1, boost::asio::buffer(r_buffer1), boost::asio::use_future); + read.get(); + + close_all(); + } catch (const std::exception& e) { + ADD_FAILURE() << e.what(); + close_all(); + } + }; + + boost::thread_group threads; + threads.create_thread(lambda2); + threads.create_thread(lambda1); + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +/// Test a stream protocol spawn interface +template +void TestStreamProtocolSpawn( + typename StreamProtocol::resolver::query client_parameters, + typename StreamProtocol::resolver::query acceptor_parameters) { + std::cout << " * TestStreamProtocolSpawn" << std::endl; + typedef std::array Buffer; + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + + Buffer buffer1; + Buffer buffer2; + Buffer r_buffer1; + Buffer r_buffer2; + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1, false); + tests::virtual_network_helpers::ResetBuffer(&buffer2, 2, false); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + typename StreamProtocol::socket socket1(io_service); + typename StreamProtocol::socket socket2(io_service); + typename StreamProtocol::acceptor acceptor(io_service); + typename StreamProtocol::resolver resolver(io_service); + + auto acceptor_endpoint_it = resolver.resolve(acceptor_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving acceptor endpoint should not be in error: " + << resolve_ec.message(); + + typename StreamProtocol::endpoint acceptor_endpoint(*acceptor_endpoint_it); + + auto remote_endpoint_it = resolver.resolve(client_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving remote endpoint should not be in error: " + << resolve_ec.message(); + typename StreamProtocol::endpoint remote_endpoint(*remote_endpoint_it); + + boost::system::error_code ec; + + acceptor.open(); + acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + acceptor.bind(acceptor_endpoint, ec); + ASSERT_EQ(0, ec.value()) << "Bind acceptor should not be in error: " + << ec.message(); + acceptor.listen(100, ec); + ASSERT_EQ(0, ec.value()) << "Listen acceptor should not be in error: " + << ec.message(); + + auto close_all = [&]() { + boost::system::error_code close_ec; + acceptor.close(close_ec); + EXPECT_FALSE(acceptor.is_open()); + socket1.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket1.close(close_ec); + EXPECT_FALSE(socket1.is_open()); + socket2.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket2.close(close_ec); + EXPECT_FALSE(socket2.is_open()); + }; + + auto lambda2 = [&](boost::asio::yield_context yield) { + try { + acceptor.async_accept(socket2, yield); + boost::asio::async_read(socket2, boost::asio::buffer(r_buffer2), yield); + boost::asio::async_write(socket2, boost::asio::buffer(buffer2), yield); + } catch (const std::exception& e) { + ADD_FAILURE() << e.what(); + close_all(); + } + }; + + auto lambda1 = [&](boost::asio::yield_context yield) { + try { + socket1.async_connect(remote_endpoint, yield); + boost::asio::async_write(socket1, boost::asio::buffer(buffer1), yield); + boost::asio::async_read(socket1, boost::asio::buffer(r_buffer1), yield); + + } catch (const std::exception& e) { + ADD_FAILURE() << e.what(); + close_all(); + } + }; + + boost::thread_group threads; + boost::asio::spawn(io_service, lambda2); + boost::asio::spawn(io_service, lambda1); + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +/// Test a stream protocol synchronous interface +template +void TestStreamProtocolSynchronous( + typename StreamProtocol::resolver::query client_parameters, + typename StreamProtocol::resolver::query acceptor_parameters) { + std::cout << " * TestStreamProtocolSynchrounous" << std::endl; + + typedef std::array Buffer; + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + auto p_worker = std::unique_ptr( + new boost::asio::io_service::work(io_service)); + + Buffer buffer1; + Buffer buffer2; + Buffer r_buffer1; + Buffer r_buffer2; + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1, false); + tests::virtual_network_helpers::ResetBuffer(&buffer2, 2, false); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + typename StreamProtocol::socket socket1(io_service); + typename StreamProtocol::socket socket2(io_service); + typename StreamProtocol::acceptor acceptor(io_service); + typename StreamProtocol::resolver resolver(io_service); + + auto acceptor_endpoint_it = resolver.resolve(acceptor_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving acceptor endpoint should not be in error: " + << resolve_ec.message(); + + typename StreamProtocol::endpoint acceptor_endpoint(*acceptor_endpoint_it); + + auto remote_endpoint_it = resolver.resolve(client_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving remote endpoint should not be in error: " + << resolve_ec.message(); + typename StreamProtocol::endpoint remote_endpoint(*remote_endpoint_it); + + boost::system::error_code ec; + + acceptor.open(); + acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + acceptor.bind(acceptor_endpoint, ec); + ASSERT_EQ(0, ec.value()) << "Bind acceptor should not be in error: " + << ec.message(); + acceptor.listen(100, ec); + ASSERT_EQ(0, ec.value()) << "Listen acceptor should not be in error: " + << ec.message(); + + auto close_all = [&]() { + boost::system::error_code close_ec; + acceptor.close(close_ec); + EXPECT_FALSE(acceptor.is_open()); + socket1.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket1.close(close_ec); + EXPECT_FALSE(socket1.is_open()); + socket2.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket2.close(close_ec); + EXPECT_FALSE(socket2.is_open()); + p_worker.reset(); + }; + + auto lambda2 = [&]() { + try { + acceptor.accept(socket2); + boost::asio::read(socket2, boost::asio::buffer(r_buffer2)); + boost::asio::write(socket2, boost::asio::buffer(buffer2)); + } catch (const std::exception& e) { + ADD_FAILURE() << e.what() << std::endl; + close_all(); + } + }; + + auto lambda1 = [&]() { + try { + socket1.connect(remote_endpoint); + boost::asio::write(socket1, boost::asio::buffer(buffer1)); + boost::asio::read(socket1, boost::asio::buffer(r_buffer1)); + + close_all(); + } catch (const std::exception& e) { + ADD_FAILURE() << e.what() << std::endl; + close_all(); + } + }; + + boost::thread_group threads; + threads.create_thread(lambda2); + threads.create_thread(lambda1); + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +/// Check connection failure to an endpoint not listening +template +void TestStreamErrorConnectionProtocol( + typename StreamProtocol::resolver::query client_no_connection_parameters) { + std::cout << " * TestStreamErrorConnectionProtocol" << std::endl; + + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + + typename StreamProtocol::socket socket_client_no_connection(io_service); + typename StreamProtocol::resolver resolver(io_service); + + auto remote_no_connect_endpoint_it = + resolver.resolve(client_no_connection_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving should not be in error: " << resolve_ec.message(); + + typename StreamProtocol::endpoint remote_no_connect_endpoint( + *remote_no_connect_endpoint_it); + + tests::virtual_network_helpers::ConnectHandler connection_error_handler = + [](const boost::system::error_code& ec) { + ASSERT_NE(0, ec.value()) << "Handler should be in error"; + }; + + socket_client_no_connection.async_connect(remote_no_connect_endpoint, + connection_error_handler); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +/// Check endpoint resolving +template +void TestEndpointResolverError( + typename StreamProtocol::resolver::query wrong_parameters) { + std::cout << " * TestEndpointResolverError" << std::endl; + + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + + typename StreamProtocol::resolver resolver(io_service); + + auto wrong_resolving_endpoint_it = + resolver.resolve(wrong_parameters, resolve_ec); + ASSERT_NE(0, resolve_ec.value()) << "Resolving should be in error"; +} + +/// Test a stream protocol +/// Bind an acceptor to the endpoint defined by +/// acceptor_parameters +/// Connect a client socket to an endpoint defined +/// by client_parameters +/// Send data in ping pong mode +template +void TestBindLocalClientStreamProtocol( + typename StreamProtocol::resolver::query local_client_parameters, + typename StreamProtocol::resolver::query remote_client_parameters, + typename StreamProtocol::resolver::query acceptor_parameters, + uint64_t max_packets) { + std::cout << " * TestBindLocalClientStreamProtocol" << std::endl; + typedef std::array Buffer; + boost::asio::io_service io_service; + boost::system::error_code resolve_ec; + + uint64_t count1 = 0; + uint64_t count2 = 0; + + Buffer buffer1; + Buffer buffer2; + Buffer r_buffer1; + Buffer r_buffer2; + tests::virtual_network_helpers::ResetBuffer(&buffer1, 1, false); + tests::virtual_network_helpers::ResetBuffer(&buffer2, 2, false); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + + typename StreamProtocol::socket socket1(io_service); + typename StreamProtocol::socket socket2(io_service); + typename StreamProtocol::acceptor acceptor(io_service); + typename StreamProtocol::resolver resolver(io_service); + + auto acceptor_endpoint_it = resolver.resolve(acceptor_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving acceptor endpoint should not be in error: " + << resolve_ec.message(); + typename StreamProtocol::endpoint acceptor_endpoint(*acceptor_endpoint_it); + + auto local_endpoint_it = + resolver.resolve(local_client_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving remote endpoint should not be in error: " + << resolve_ec.message(); + typename StreamProtocol::endpoint local_endpoint(*local_endpoint_it); + + auto remote_endpoint_it = + resolver.resolve(remote_client_parameters, resolve_ec); + ASSERT_EQ(0, resolve_ec.value()) + << "Resolving remote endpoint should not be in error: " + << resolve_ec.message(); + typename StreamProtocol::endpoint remote_endpoint(*remote_endpoint_it); + + tests::virtual_network_helpers::AcceptHandler accepted; + tests::virtual_network_helpers::ConnectHandler connected; + tests::virtual_network_helpers::SendHandler sent_handler1; + tests::virtual_network_helpers::SendHandler sent_handler2; + tests::virtual_network_helpers::ReceiveHandler received_handler1; + tests::virtual_network_helpers::ReceiveHandler received_handler2; + + accepted = [&](const boost::system::error_code& ec) { + ASSERT_EQ(0, ec.value()) + << "Accept handler should not be in error: " << ec.message(); + + std::cout << " > Accepted" << std::endl; + boost::system::error_code endpoint_ec; + socket2.local_endpoint(endpoint_ec); + ASSERT_EQ(0, endpoint_ec.value()) + << "Local endpoint should be set: " << endpoint_ec.message(); + + socket2.remote_endpoint(endpoint_ec); + ASSERT_EQ(0, endpoint_ec.value()) + << "Remote endpoint should be set: " << endpoint_ec.message(); + + boost::asio::async_read(socket2, boost::asio::buffer(r_buffer2), + received_handler2); + }; + + connected = [&](const boost::system::error_code& ec) { + ASSERT_EQ(0, ec.value()) + << "Connect handler should not be in error: " << ec.message(); + std::cout << " > Connected" << std::endl; + boost::system::error_code endpoint_ec; + socket1.local_endpoint(endpoint_ec); + ASSERT_EQ(0, endpoint_ec.value()) + << "Local endpoint should be set: " << endpoint_ec.message(); + + auto remote_ep = socket1.remote_endpoint(endpoint_ec); + ASSERT_EQ(0, endpoint_ec.value()) + << "Remote endpoint should be set: " << endpoint_ec.message(); + ASSERT_EQ(remote_endpoint, remote_ep) << "Remote endpoint invalid"; + + boost::asio::async_write(socket1, boost::asio::buffer(buffer1), + sent_handler1); + }; + + received_handler1 = + [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer2, r_buffer1, length)); + + { + boost::system::error_code endpoint_ec; + auto local_ep = socket1.local_endpoint(endpoint_ec); + auto remote_ep = socket2.remote_endpoint(endpoint_ec); + ASSERT_EQ(local_ep.endpoint_context(), remote_ep.endpoint_context()) + << "Endpoints should be equal"; + } + + { + boost::system::error_code endpoint_ec; + auto local_ep = socket2.local_endpoint(endpoint_ec); + auto remote_ep = socket1.remote_endpoint(endpoint_ec); + ASSERT_EQ(local_ep.endpoint_context(), remote_ep.endpoint_context()) + << "Endpoints should be equal"; + } + + ++count1; + if (count1 < max_packets) { + boost::asio::async_write(socket1, boost::asio::buffer(buffer1), + sent_handler1); + } else { + ASSERT_EQ(max_packets, count1); + boost::system::error_code close_ec; + + acceptor.close(close_ec); + ASSERT_EQ(false, acceptor.is_open()); + + socket1.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket1.close(close_ec); + ASSERT_EQ(false, socket1.is_open()); + + socket2.shutdown(boost::asio::socket_base::shutdown_both, close_ec); + socket2.close(close_ec); + ASSERT_EQ(false, socket2.is_open()); + } + }; + + received_handler2 = + [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Receive handler should not be in error: " << ec.message(); + + ASSERT_TRUE(tests::virtual_network_helpers::CheckBuffers( + buffer1, r_buffer2, length)); + ++count2; + if (count2 < max_packets) { + boost::asio::async_write(socket2, boost::asio::buffer(buffer2), + sent_handler2); + } else { + boost::asio::async_write( + socket2, boost::asio::buffer(buffer2), + [](const boost::system::error_code&, std::size_t) {}); + ASSERT_EQ(max_packets, count2); + } + }; + + sent_handler1 = [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Send handler should not be in error: " << ec.message(); + tests::virtual_network_helpers::ResetBuffer(&r_buffer1, 0); + boost::asio::async_read(socket1, boost::asio::buffer(r_buffer1), + received_handler1); + }; + + sent_handler2 = [&](const boost::system::error_code& ec, std::size_t length) { + ASSERT_EQ(0, ec.value()) + << "Send handler should not be in error: " << ec.message(); + tests::virtual_network_helpers::ResetBuffer(&r_buffer2, 0); + boost::asio::async_read(socket2, boost::asio::buffer(r_buffer2), + received_handler2); + }; + + boost::system::error_code ec; + + acceptor.open(); + acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec); + ec.clear(); + acceptor.bind(acceptor_endpoint, ec); + ASSERT_EQ(0, ec.value()) << "Bind acceptor should not be in error: " + << ec.message(); + acceptor.listen(100, ec); + ASSERT_EQ(0, ec.value()) << "Listen acceptor should not be in error: " + << ec.message(); + acceptor.async_accept(socket2, accepted); + + socket1.bind(local_endpoint, ec); + ASSERT_EQ(0, ec.value()) << "Bind local_endpoint should not be in error: " + << ec.message(); + socket1.async_connect(remote_endpoint, connected); + + boost::thread_group threads; + for (uint16_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + threads.create_thread([&io_service]() { io_service.run(); }); + } + threads.join_all(); +} + +#endif // SSF_TESTS_STREAM_PROTOCOL_HELPERS_H_ diff --git a/src/framework/tests/system/circuit_tlsotcp_accept1_config.json b/src/framework/tests/system/circuit_tlsotcp_accept1_config.json new file mode 100644 index 00000000..0dbdea83 --- /dev/null +++ b/src/framework/tests/system/circuit_tlsotcp_accept1_config.json @@ -0,0 +1,27 @@ +{ + "interface": "circuit_tlsotcp_accept1", + "type": "ACCEPT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "local_id": "server", + "forward": true, + "nodes": [] + }, + "sublayer": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "6000" + } + } + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/circuit_tlsotcp_accept2_config.json b/src/framework/tests/system/circuit_tlsotcp_accept2_config.json new file mode 100644 index 00000000..12ab1ccc --- /dev/null +++ b/src/framework/tests/system/circuit_tlsotcp_accept2_config.json @@ -0,0 +1,27 @@ +{ + "interface": "circuit_tlsotcp_accept2", + "type": "ACCEPT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "local_id": "server", + "forward": true, + "nodes": [] + }, + "sublayer": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "6001" + } + } + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/circuit_tlsotcp_accept3_config.json b/src/framework/tests/system/circuit_tlsotcp_accept3_config.json new file mode 100644 index 00000000..5a895a4a --- /dev/null +++ b/src/framework/tests/system/circuit_tlsotcp_accept3_config.json @@ -0,0 +1,27 @@ +{ + "interface": "circuit_tlsotcp_accept3", + "type": "ACCEPT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "local_id": "server", + "forward": true, + "nodes": [] + }, + "sublayer": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "6002" + } + } + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/circuit_tlsotcp_connect_config.json b/src/framework/tests/system/circuit_tlsotcp_connect_config.json new file mode 100644 index 00000000..d3d3bf70 --- /dev/null +++ b/src/framework/tests/system/circuit_tlsotcp_connect_config.json @@ -0,0 +1,60 @@ +{ + "interface": "circuit_tlsotcp_connect", + "type": "CONNECT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "remote_id": "server", + "nodes": [ + { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "addr": "127.0.0.1", + "port": "6000" + } + } + }, + { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "addr": "127.0.0.1", + "port": "6001" + } + } + } + ] + }, + "sublayer": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "addr": "127.0.0.1", + "port": "6002" + } + } + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/fail_router_config.json b/src/framework/tests/system/fail_router_config.json new file mode 100644 index 00000000..be5e850a --- /dev/null +++ b/src/framework/tests/system/fail_router_config.json @@ -0,0 +1,24 @@ +[ + { + "router": "router1", + "networks": { + "1" : "simple_lo1", + "3" : "simple_tls_lo1" + }, + "routes": { + "2": "3" + "4": "1" + } + }, + { + "router": "router2", + "networks": { + "2" : "simple_lo3", + "4" : "simple_tls_lo2" + }, + "routes": { + "1": "4", + "4": "2" + } + } +] \ No newline at end of file diff --git a/src/framework/tests/system/fail_tcp_accept_config.json b/src/framework/tests/system/fail_tcp_accept_config.json new file mode 100644 index 00000000..3e481f48 --- /dev/null +++ b/src/framework/tests/system/fail_tcp_accept_config.json @@ -0,0 +1,12 @@ +{ + "interface": "tcp_accept", + "type": "ACCEPT", + "ttl": 5, + "delay": 200, + "layer_stack": { + "layer": "TCP", + "parameters": { + "port": "9000" + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/fail_tcp_connect_config.json b/src/framework/tests/system/fail_tcp_connect_config.json new file mode 100644 index 00000000..d67d5d47 --- /dev/null +++ b/src/framework/tests/system/fail_tcp_connect_config.json @@ -0,0 +1,13 @@ +{ + "interface": "tcp_connect", + "type": "CONNECT", + "ttl": 1, + "delay": 200, + "layer_stack": { + "layer": "TCP", + "parameters": { + "port": "10000", + "addr": "127.0.0.1" + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/link_tcp_accept_config.json b/src/framework/tests/system/link_tcp_accept_config.json new file mode 100644 index 00000000..1d016ca0 --- /dev/null +++ b/src/framework/tests/system/link_tcp_accept_config.json @@ -0,0 +1,17 @@ +{ + "interface": "link_tcp_accept", + "type": "ACCEPT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "forward": false, + "local_id": "server" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "9000" + } + } + } +} diff --git a/src/framework/tests/system/link_tcp_connect_config.json b/src/framework/tests/system/link_tcp_connect_config.json new file mode 100644 index 00000000..f2021af7 --- /dev/null +++ b/src/framework/tests/system/link_tcp_connect_config.json @@ -0,0 +1,18 @@ +{ + "interface": "link_tcp_connect", + "type": "CONNECT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "remote_id": "server", + "nodes": [] + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "addr": "127.0.0.1", + "port": "9000" + } + } + } +} diff --git a/src/framework/tests/system/link_tlsotcp_accept_config.json b/src/framework/tests/system/link_tlsotcp_accept_config.json new file mode 100644 index 00000000..70f38a4d --- /dev/null +++ b/src/framework/tests/system/link_tlsotcp_accept_config.json @@ -0,0 +1,27 @@ +{ + "interface": "link_tlsotcp_accept", + "type": "ACCEPT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "local_id": "server", + "forward": true, + "nodes": [] + }, + "sublayer": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "9000" + } + } + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/link_tlsotcp_connect_config.json b/src/framework/tests/system/link_tlsotcp_connect_config.json new file mode 100644 index 00000000..8ec7af6b --- /dev/null +++ b/src/framework/tests/system/link_tlsotcp_connect_config.json @@ -0,0 +1,27 @@ +{ + "interface": "link_tlsotcp_connect", + "type": "CONNECT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "remote_id": "server", + "nodes": [] + }, + "sublayer": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "addr": "127.0.0.1", + "port": "9000" + } + } + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/router_config.json b/src/framework/tests/system/router_config.json new file mode 100644 index 00000000..d868e7ef --- /dev/null +++ b/src/framework/tests/system/router_config.json @@ -0,0 +1,32 @@ +[ + { + "router": "router1", + "networks": { + "1": "circuit_tlsotcp_connect", + "3": "circuit_tcp_connect", + "5": "tlsotcp_connect", + "7": "tcp_connect" + }, + "routes": { + "2": "7", + "4": "1", + "6": "3", + "8": "5" + } + }, + { + "router": "router2", + "networks": { + "2": "circuit_tlsotcp_accept", + "4": "circuit_tcp_accept", + "6": "tlsotcp_accept", + "8": "tcp_accept" + }, + "routes": { + "1": "4", + "3": "6", + "5": "8", + "7": "2" + } + } +] \ No newline at end of file diff --git a/src/framework/tests/system/system_multiple_config.json b/src/framework/tests/system/system_multiple_config.json new file mode 100644 index 00000000..a9b2fbcd --- /dev/null +++ b/src/framework/tests/system/system_multiple_config.json @@ -0,0 +1,207 @@ +[ + { + "interface": "circuit_tlsotcp_accept", + "type": "ACCEPT", + "ttl": 5, + "delay": 500, + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "local_id": "server", + "forward": true, + "nodes": [] + }, + "sublayer": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "6000" + } + } + } + } + }, + { + "interface": "circuit_tlsotcp_connect", + "type": "CONNECT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "remote_id": "server", + "nodes": [] + }, + "sublayer": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "addr": "127.0.0.1", + "port": "6000" + } + } + } + } + }, + { + "interface": "circuit_tcp_accept", + "type": "ACCEPT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "local_id": "server", + "forward": false, + "nodes": [] + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "7000" + } + } + } + }, + { + "interface": "circuit_tcp_accept1", + "type": "ACCEPT", + "ttl": 0, + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "local_id": "server1", + "forward": true, + "nodes": [] + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "7001" + } + } + } + }, + { + "interface": "circuit_tcp_accept2", + "type": "ACCEPT", + "ttl": 0, + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "local_id": "server2", + "forward": true, + "nodes": [] + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "7002" + } + } + } + }, + { + "interface": "circuit_tcp_connect", + "type": "CONNECT", + "layer_stack": { + "layer": "CIRCUIT", + "parameters": { + "remote_id": "server", + "nodes": [ + { + "layer": "TCP", + "parameters": { + "addr": "127.0.0.1", + "port": "7001" + } + }, + { + "layer": "TCP", + "parameters": { + "addr": "127.0.0.1", + "port": "7002" + } + } + ] + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "addr": "127.0.0.1", + "port": "7000" + } + } + } + }, + { + "interface": "tcp_accept", + "type": "ACCEPT", + "layer_stack": { + "layer": "TCP", + "parameters": { + "port": "8000" + } + } + }, + { + "interface": "tcp_connect", + "type": "CONNECT", + "layer_stack": { + "layer": "TCP", + "parameters": { + "port": "8000", + "addr": "127.0.0.1" + } + } + }, + { + "interface": "tlsotcp_accept", + "type": "ACCEPT", + "layer_stack": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "9000" + } + } + } + }, + { + "interface": "tlsotcp_connect", + "type": "CONNECT", + "layer_stack": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "9000", + "addr": "127.0.0.1" + } + } + } + } +] \ No newline at end of file diff --git a/src/framework/tests/system/system_reconnect_config.json b/src/framework/tests/system/system_reconnect_config.json new file mode 100644 index 00000000..6e3c53cd --- /dev/null +++ b/src/framework/tests/system/system_reconnect_config.json @@ -0,0 +1,13 @@ +[ + { + "interface": "tcp_connect", + "type": "CONNECT", + "layer_stack": { + "layer": "TCP", + "parameters": { + "port": "8000", + "addr": "127.0.0.1" + } + } + } +] \ No newline at end of file diff --git a/src/framework/tests/system/tcp_accept_config.json b/src/framework/tests/system/tcp_accept_config.json new file mode 100644 index 00000000..3e481f48 --- /dev/null +++ b/src/framework/tests/system/tcp_accept_config.json @@ -0,0 +1,12 @@ +{ + "interface": "tcp_accept", + "type": "ACCEPT", + "ttl": 5, + "delay": 200, + "layer_stack": { + "layer": "TCP", + "parameters": { + "port": "9000" + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/tcp_connect_config.json b/src/framework/tests/system/tcp_connect_config.json new file mode 100644 index 00000000..b2b16599 --- /dev/null +++ b/src/framework/tests/system/tcp_connect_config.json @@ -0,0 +1,13 @@ +{ + "interface": "tcp_connect", + "type": "CONNECT", + "ttl": 1, + "delay": 200, + "layer_stack": { + "layer": "TCP", + "parameters": { + "port": "9000", + "addr": "127.0.0.1" + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/tlsotcp_accept_config.json b/src/framework/tests/system/tlsotcp_accept_config.json new file mode 100644 index 00000000..f93e105a --- /dev/null +++ b/src/framework/tests/system/tlsotcp_accept_config.json @@ -0,0 +1,19 @@ +{ + "interface": "tlsotcp_accept", + "type": "ACCEPT", + "layer_stack": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "9000" + } + } + } +} \ No newline at end of file diff --git a/src/framework/tests/system/tlsotcp_connect_config.json b/src/framework/tests/system/tlsotcp_connect_config.json new file mode 100644 index 00000000..b29aeedc --- /dev/null +++ b/src/framework/tests/system/tlsotcp_connect_config.json @@ -0,0 +1,20 @@ +{ + "interface": "tlsotcp_connect", + "type": "CONNECT", + "layer_stack": { + "layer": "TLS", + "parameters": { + "ca_file": "./certs/trusted/ca.crt", + "crt_file": "./certs/certificate.crt", + "key_file": "./certs/private.key", + "dhparam_file": "./certs/dh4096.pem" + }, + "sublayer": { + "layer": "TCP", + "parameters": { + "port": "9000", + "addr": "127.0.0.1" + } + } + } +} \ No newline at end of file diff --git a/src/framework/tests/tools.h b/src/framework/tests/tools.h new file mode 100644 index 00000000..0e6470e6 --- /dev/null +++ b/src/framework/tests/tools.h @@ -0,0 +1,61 @@ +#ifndef SSF_TESTS_TOOLS_H_ +#define SSF_TESTS_TOOLS_H_ + +#include + +#include + +#include "ssf/log/log.h" + +class TimedScope { + public: + TimedScope() : creation_time_(std::chrono::high_resolution_clock::now()) {} + + virtual ~TimedScope() { PrintDuration(); } + + void ResetTime() { + creation_time_ = std::chrono::high_resolution_clock::now(); + } + + std::chrono::nanoseconds Duration() const { + return std::chrono::high_resolution_clock::now() - creation_time_; + } + + double FloatSecondDuration() const { + return this->Duration().count() / static_cast(1000) / + static_cast(1000) / static_cast(1000); + } + + void PrintDuration() { + SSF_LOG(kLogDebug) << "Timed scope: " << Duration().count(); + } + + private: + std::chrono::time_point creation_time_; +}; + +class ScopedBandWidth : public TimedScope { + public: + ScopedBandWidth(uint64_t bits) : TimedScope(), bits_(bits) {} + + virtual ~ScopedBandWidth() { PrintBandWidth(); } + + void ResetBits() { bits_ = 0; } + + void ResetBits(uint64_t bits) { bits_ = bits; } + + void AddBits(uint64_t bits) { bits_ += bits; } + + double BandWidth() const { return bits_ / this->FloatSecondDuration(); } + + void PrintBandWidth() const { + SSF_LOG(kLogInfo) << "Bandwidth: transferred " << bits_ << " bits in " + << this->FloatSecondDuration() << " seconds ==> " + << BandWidth() / 1000 / 1000 << " Mbits/s"; + } + + private: + uint64_t bits_; +}; + +#endif // SSF_TESTS_TOOLS_H_ diff --git a/src/framework/tests/transport_layer_tests.cpp b/src/framework/tests/transport_layer_tests.cpp new file mode 100644 index 00000000..9a1d033e --- /dev/null +++ b/src/framework/tests/transport_layer_tests.cpp @@ -0,0 +1,11 @@ +#include + +#include "tests/datagram_protocol_helpers.h" +#include "tests/stream_protocol_helpers.h" +#include "tests/transport_test_fixture.h" + +#include "ssf/layer/parameters.h" + +TEST_F(TransportTestFixture, DISABLED_DatagramTransportTest) {} + +TEST_F(TransportTestFixture, DISABLED_StreamTransportTest) {} diff --git a/src/framework/tests/transport_test_fixture.h b/src/framework/tests/transport_test_fixture.h new file mode 100644 index 00000000..2c65d5fe --- /dev/null +++ b/src/framework/tests/transport_test_fixture.h @@ -0,0 +1,129 @@ +#ifndef SSF_TESTS_TRANSPORT_TEST_FIXTURE_H_ +#define SSF_TESTS_TRANSPORT_TEST_FIXTURE_H_ + +#include + +#include + +#include + +#include + +#include "tests/routing_test_fixture.h" + +#include "ssf/layer/parameters.h" + +#include "ssf/layer/multiplexing/basic_multiplexer_protocol.h" +#include "ssf/layer/multiplexing/protocol_multiplex_id.h" +#include "ssf/layer/multiplexing/port_multiplex_id.h" + +#include "ssf/layer/congestion/drop_tail_policy.h" + +class TransportTestFixture : public RoutingTestFixture { + protected: + using NetworkProtocol = + ssf::layer::network::basic_NetworkProtocol; + using RouterType = + ssf::layer::routing::basic_Router; + + using StubTransportProtocol = + ssf::layer::multiplexing::basic_MultiplexedProtocol< + NetworkProtocol, ssf::layer::multiplexing::ProtocolID, + ssf::layer::congestion::DropTailPolicy<100>>; + + using DatagramTransportProtocol = + ssf::layer::multiplexing::basic_MultiplexedProtocol< + StubTransportProtocol, ssf::layer::multiplexing::PortID, + ssf::layer::congestion::DropTailPolicy<100>>; + + protected: + TransportTestFixture() + : RoutingTestFixture(), + socket_1_(this->io_service_), + socket_3_(this->io_service_), + socket_5_(this->io_service_), + socket_7_(this->io_service_) {} + + virtual ~TransportTestFixture() {} + + virtual void SetUp() { + RoutingTestFixture::SetUp(); + + boost::system::error_code ec; + NetworkProtocol::resolver resolver(this->io_service_); + + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "11"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "simple_lo2"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + + socket_1_.open(); + socket_1_.bind(socket1_local_endpoint, ec); + } + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "33"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "simple_tls_lo1"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + + socket_3_.open(); + socket_3_.bind(socket1_local_endpoint, ec); + } + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "55"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "circuit_lo1"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + + socket_5_.open(); + socket_5_.bind(socket1_local_endpoint, ec); + } + { + ssf::layer::LayerParameters socket1_network_parameters; + socket1_network_parameters["network_id"] = "77"; + ssf::layer::LayerParameters socket1_interface_parameters; + socket1_interface_parameters["interface_id"] = "circuit_tls_lo1"; + ssf::layer::ParameterStack parameters1; + parameters1.push_back(socket1_network_parameters); + parameters1.push_back(socket1_interface_parameters); + auto socket1_endpoint_it = resolver.resolve(parameters1, ec); + NetworkProtocol::endpoint socket1_local_endpoint(*socket1_endpoint_it); + + socket_7_.open(); + socket_7_.bind(socket1_local_endpoint, ec); + } + + Wait(); + } + + virtual void TearDown() { + RoutingTestFixture::TearDown(); + } + + private: + bool Wait() { return true; } + +protected: + NetworkProtocol::socket socket_1_; + NetworkProtocol::socket socket_3_; + NetworkProtocol::socket socket_5_; + NetworkProtocol::socket socket_7_; +}; + +#endif // SSF_TESTS_TRANSPORT_TEST_FIXTURE_H_ diff --git a/src/framework/tests/virtual_network_helpers.h b/src/framework/tests/virtual_network_helpers.h new file mode 100644 index 00000000..a8e9e244 --- /dev/null +++ b/src/framework/tests/virtual_network_helpers.h @@ -0,0 +1,93 @@ +#ifndef SSF_TESTS_VIRTUAL_NETWORK_HELPERS_H_ +#define SSF_TESTS_VIRTUAL_NETWORK_HELPERS_H_ + +#include + +#include + +#include "ssf/layer/basic_empty_stream.h" +#include "ssf/layer/basic_empty_datagram.h" +#include "ssf/layer/cryptography/tls/OpenSSL/impl.h" +#include "ssf/layer/cryptography/basic_crypto_stream.h" + +#include "ssf/layer/parameters.h" + +namespace tests { +namespace virtual_network_helpers { + +template +using Layer = ssf::layer::VirtualEmptyStreamProtocol; + +template +using TLSLayer = + ssf::layer::cryptography::basic_CryptoStreamProtocol< + NextLayer, ssf::layer::cryptography::buffered_tls>; + +template +using DatagramLayer = ssf::layer::VirtualEmptyDatagramProtocol; + +typedef std::function + SendHandler; +typedef std::function + ReceiveHandler; +typedef std::function ConnectHandler; +typedef std::function AcceptHandler; + +template +void ResetBuffer(Buffer* p_buffer, typename Buffer::value_type value, + std::size_t length, bool identical = true) { + if (p_buffer->size() > length) { + length = p_buffer->size(); + } + for (size_t i = 0; i < length; ++i) { + (*p_buffer)[i] = identical ? value : (value + i) % 256; + } +} + +template +void ResetBuffer(Buffer* p_buffer, typename Buffer::value_type value, + bool identical = true) { + ResetBuffer(p_buffer, value, p_buffer->size(), identical); +} + +template +bool CheckBuffers(const Buffer& buffer1, const Buffer& buffer2, + std::size_t length) { + if (buffer1.size() < length || buffer2.size() < length) { + return false; + } + + for (std::size_t i = 0; i < length; ++i) { + if (buffer1[i] != buffer2[i]) { + return false; + } + } + + return true; +} + +ssf::layer::LayerParameters tls_server_parameters = { + {"ca_src", "file"}, + {"crt_src", "file"}, + {"key_src", "file"}, + {"dhparam_src", "file"}, + {"ca_file", "./certs/trusted/ca.crt"}, + {"crt_file", "./certs/certificate.crt"}, + {"key_file", "./certs/private.key"}, + {"dhparam_file", "./certs/dh4096.pem"}}; + +ssf::layer::LayerParameters tls_client_parameters = { + {"ca_src", "file"}, + {"crt_src", "file"}, + {"key_src", "file"}, + {"ca_file", "./certs/trusted/ca.crt"}, + {"crt_file", "./certs/certificate.crt"}, + {"key_file", "./certs/private.key"}}; + +ssf::layer::LayerParameters tls_default_server_parameters = { + {"default", "true"}}; + +} // virtual_network_helpers +} // tests + +#endif // SSF_TESTS_VIRTUAL_NETWORK_HELPERS_H_ diff --git a/src/linux_compatibility/memcpy.cpp b/src/linux_compatibility/memcpy.cpp new file mode 100644 index 00000000..00ecbac2 --- /dev/null +++ b/src/linux_compatibility/memcpy.cpp @@ -0,0 +1,15 @@ +#if defined( __GNUC__ ) && defined( __LP64__ ) && __LP64__ >= 1 + +#include + +// wrap memcpy and force use 2.2.5 version + +__asm__(".symver memcpy, memcpy@GLIBC_2.2.5"); + +extern "C" { + void *__wrap_memcpy(void *dest, const void *src, size_t n) { + return memcpy(dest, src, n); + } +} + +#endif // 64 bits \ No newline at end of file diff --git a/src/services/admin/admin.h b/src/services/admin/admin.h index 0e251dde..9c003407 100644 --- a/src/services/admin/admin.h +++ b/src/services/admin/admin.h @@ -14,10 +14,11 @@ #include #include // NOLINT +#include + #include "common/boost/fiber/stream_fiber.hpp" #include "common/boost/fiber/basic_fiber_demux.hpp" -#include "common/network/manager.h" #include "services/initialisation.h" #include "services/base_service.h" @@ -30,16 +31,18 @@ #include "services/user_services/base_user_service.h" -namespace ssf { namespace services { namespace admin { +namespace ssf { +namespace services { +namespace admin { template class Admin : public BaseService { -public: - typedef typename ssf::services::BaseUserService::BaseUserServicePtr - BaseUserServicePtr; - typedef std::function AdminCallbackType; + public: + typedef typename ssf::services::BaseUserService::BaseUserServicePtr + BaseUserServicePtr; + typedef std::function AdminCallbackType; private: typedef typename Demux::local_port_type local_port_type; @@ -65,29 +68,26 @@ class Admin : public BaseService { enum { factory_id = 1, - service_port = (1 << 17) + 1, // first of the service range - keep_alive_interval = 120, // seconds + service_port = (1 << 17) + 1, // first of the service range + keep_alive_interval = 120, // seconds service_status_retry_interval = 100, // milliseconds - service_status_retry_number = 500 // retries + service_status_retry_number = 500 // retries }; static void RegisterToServiceFactory( - std::shared_ptr> p_factory) { + std::shared_ptr> p_factory) { p_factory->RegisterServiceCreator(factory_id, &Admin::Create); } void set_server(); - void set_client( - std::vector, - AdminCallbackType callback); + void set_client(std::vector, AdminCallbackType callback); virtual void start(boost::system::error_code& ec); virtual void stop(boost::system::error_code& ec); virtual uint32_t service_type_id(); template - void Command(Request request, - Handler handler) { + void Command(Request request, Handler handler) { std::string parameters_buff_to_send = request.OnSending(); auto serial = GetAvailableSerial(); @@ -111,8 +111,9 @@ class Admin : public BaseService { // execute handler bound to the command serial id if exists void ExecuteAndRemoveCommandHandler(uint32_t serial) { if (command_handlers_.count(command_serial_received_)) { - this->get_io_service().post(boost::bind( - command_handlers_[command_serial_received_], boost::system::error_code())); + this->get_io_service().post( + boost::bind(command_handlers_[command_serial_received_], + boost::system::error_code())); this->EraseHandler(command_serial_received_); } } diff --git a/src/services/admin/admin.ipp b/src/services/admin/admin.ipp index 6ea84692..fb327689 100644 --- a/src/services/admin/admin.ipp +++ b/src/services/admin/admin.ipp @@ -3,17 +3,18 @@ #include -#include #include #include +#include + #include "common/error/error.h" #include "core/factories/command_factory.h" namespace ssf { namespace services { namespace admin { -//---------------------------------------------------------------------------- + template Admin::Admin(boost::asio::io_service& io_service, Demux& fiber_demux) : ssf::BaseService::BaseService(io_service, fiber_demux), @@ -30,7 +31,6 @@ Admin::Admin(boost::asio::io_service& io_service, Demux& fiber_demux) stopped_(false), callback_() {} -//----------------------------------------------------------------------------- template void Admin::set_server() { is_server_ = 1; @@ -39,16 +39,13 @@ void Admin::set_server() { fiber_acceptor_.listen(boost::asio::socket_base::max_connections, init_ec_); } -//----------------------------------------------------------------------------- template -void Admin::set_client( - std::vector user_services, - AdminCallbackType callback) { +void Admin::set_client(std::vector user_services, + AdminCallbackType callback) { user_services_ = std::move(user_services); callback_ = std::move(callback); } -//----------------------------------------------------------------------------- template void Admin::start(boost::system::error_code& ec) { ec = init_ec_; @@ -62,62 +59,55 @@ void Admin::start(boost::system::error_code& ec) { } } -//----------------------------------------------------------------------------- template void Admin::stop(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) << "service admin: stopping"; - ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + SSF_LOG(kLogInfo) << "service[admin]: stopping"; + ec.assign(::error::success, ::error::get_ssf_category()); HandleStop(); } -//----------------------------------------------------------------------------- template uint32_t Admin::service_type_id() { return factory_id; } -//----------------------------------------------------------------------------- template void Admin::StartAccept() { fiber_acceptor_.async_accept( fiber_, Then(&Admin::HandleAccept, this->SelfFromThis())); } -//----------------------------------------------------------------------------- template void Admin::HandleAccept(const boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(trace) << "service admin: handleAccept"; + SSF_LOG(kLogTrace) << "service[admin]: handleAccept"; if (!fiber_acceptor_.is_open()) { return; } if (ec) { - BOOST_LOG_TRIVIAL(error) << "service admin: error accepting new connection: " << ec << " " - << ec.value(); + SSF_LOG(kLogError) << "service[admin]: error accepting new connection: " + << ec << " " << ec.value(); ShutdownServices(); } else { Initialize(); } } -//----------------------------------------------------------------------------- template void Admin::StartConnect() { fiber_.async_connect(endpoint(this->get_demux(), service_port), Then(&Admin::HandleConnect, this->SelfFromThis())); } -//----------------------------------------------------------------------------- template void Admin::HandleConnect(const boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(trace) << "service admin: handle connect"; + SSF_LOG(kLogTrace) << "service[admin]: handle connect"; if (!fiber_.is_open() || ec) { - BOOST_LOG_TRIVIAL(error) - << "service admin: no new connection: " << ec.message() << " " - << ec.value(); + SSF_LOG(kLogError) << "service[admin]: no new connection: " << ec.message() + << " " << ec.value(); // Retry to connect if failed to open the fiber if (retries_ < 50) { this->StartConnect(); @@ -130,7 +120,6 @@ void Admin::HandleConnect(const boost::system::error_code& ec) { } } -//----------------------------------------------------------------------------- template void Admin::Initialize() { // Initialize the command reception processes @@ -140,7 +129,6 @@ void Admin::Initialize() { this->PostKeepAlive(boost::system::error_code(), 0); } -//----------------------------------------------------------------------------- #include // NOLINT /// Initialize micro services (local and remote) asked by the client @@ -151,7 +139,6 @@ void Admin::InitializeRemoteServices( reenter(coroutine_) { // For each user service for (i_ = 0; i_ < user_services_.size(); ++i_) { - // Get the remote micro services to start create_request_vector_ = user_services_[i_]->GetRemoteServiceCreateVector(); @@ -159,10 +146,9 @@ void Admin::InitializeRemoteServices( // For each remote micro service to start for (j_ = 0; j_ < create_request_vector_.size(); ++j_) { // Start remove service and yield until server response comes back - yield StartRemoteService( - create_request_vector_[j_], - boost::bind(&Admin::InitializeRemoteServices, - this->SelfFromThis(), _1)); + yield StartRemoteService(create_request_vector_[j_], + boost::bind(&Admin::InitializeRemoteServices, + this->SelfFromThis(), _1)); } // At this point, all remote services have responded with their statuses @@ -171,13 +157,11 @@ void Admin::InitializeRemoteServices( // If something went wrong remote_all_started_ > 0 if (remote_all_started_) { - BOOST_LOG_TRIVIAL(warning) << "service admin: remote could not start"; + SSF_LOG(kLogWarning) << "service[admin]: remote could not start"; - Notify( - ssf::services::initialisation::SERVICE, - user_services_[i_], - boost::system::error_code(ssf::error::operation_canceled, - ssf::error::get_ssf_category())); + Notify(ssf::services::initialisation::SERVICE, user_services_[i_], + boost::system::error_code(::error::operation_canceled, + ::error::get_ssf_category())); // Get the remote micro services to stop stop_request_vector_ = @@ -199,13 +183,11 @@ void Admin::InitializeRemoteServices( // If something went wrong local_all_started_ == false if (!local_all_started_) { - BOOST_LOG_TRIVIAL(warning) << "service admin: local could not start"; + SSF_LOG(kLogWarning) << "service[admin]: local could not start"; - Notify( - ssf::services::initialisation::SERVICE, - user_services_[i_], - boost::system::error_code(ssf::error::operation_canceled, - ssf::error::get_ssf_category())); + Notify(ssf::services::initialisation::SERVICE, user_services_[i_], + boost::system::error_code(::error::operation_canceled, + ::error::get_ssf_category())); // Get the remote micro services to stop stop_request_vector_ = @@ -219,24 +201,21 @@ void Admin::InitializeRemoteServices( } // Stop local services user_services_[i_]->StopLocalServices(this->get_demux()); - + return; } - Notify( - ssf::services::initialisation::SERVICE, - user_services_[i_], - boost::system::error_code(ssf::error::success, - ssf::error::get_ssf_category())); + Notify(ssf::services::initialisation::SERVICE, user_services_[i_], + boost::system::error_code(::error::success, + ::error::get_ssf_category())); } } } else { - BOOST_LOG_TRIVIAL(debug) << "service admin: ec intializing " << ec.value(); + SSF_LOG(kLogDebug) << "service[admin]: ec intializing " << ec.value(); } } #include // NOLINT -//----------------------------------------------------------------------------- template void Admin::ListenForCommand() { status_ = 101; @@ -244,7 +223,6 @@ void Admin::ListenForCommand() { boost::system::error_code(), 0)); } -//----------------------------------------------------------------------------- template void Admin::DoAdmin(const boost::system::error_code& ec, size_t length) { switch (status_) { @@ -253,7 +231,7 @@ void Admin::DoAdmin(const boost::system::error_code& ec, size_t length) { this->ReceiveInstructionHeader(); } else { this->get_io_service().post( - boost::bind(&Admin::ShutdownServices, SelfFromThis())); + boost::bind(&Admin::ShutdownServices, SelfFromThis())); return; } break; @@ -267,7 +245,7 @@ void Admin::DoAdmin(const boost::system::error_code& ec, size_t length) { } } else { this->get_io_service().post( - boost::bind(&Admin::ShutdownServices, SelfFromThis())); + boost::bind(&Admin::ShutdownServices, SelfFromThis())); return; } break; @@ -277,7 +255,7 @@ void Admin::DoAdmin(const boost::system::error_code& ec, size_t length) { this->TreatInstructionId(); } else { this->get_io_service().post( - boost::bind(&Admin::ShutdownServices, SelfFromThis())); + boost::bind(&Admin::ShutdownServices, SelfFromThis())); return; } break; @@ -291,7 +269,6 @@ void Admin::DoAdmin(const boost::system::error_code& ec, size_t length) { } } -//----------------------------------------------------------------------------- template void Admin::ReceiveInstructionHeader() { status_ = 102; // Receive current instruction parameter size @@ -308,7 +285,6 @@ void Admin::ReceiveInstructionHeader() { boost::bind(&Admin::DoAdmin, SelfFromThis(), _1, _2)); } -//----------------------------------------------------------------------------- template void Admin::ReceiveInstructionParameters() { status_ = 103; // Handler current instruction @@ -325,7 +301,6 @@ void Admin::ReceiveInstructionParameters() { /// Execute the command received and send back the result template void Admin::TreatInstructionId() { - // Get the right function to execute the command using CommandExecuterType = typename ssf::CommandFactory::CommandExecuterType; @@ -364,9 +339,9 @@ void Admin::TreatInstructionId() { command_id_received_); // reply with command serial received (command handler execution) - auto p_command = std::make_shared(command_serial_received_, - *p_reply_command_index, - (uint32_t)reply.size(), reply); + auto p_command = std::make_shared( + command_serial_received_, *p_reply_command_index, + (uint32_t)reply.size(), reply); this->async_SendCommand( *p_command, [p_command](const boost::system::error_code&, size_t) {}); @@ -380,7 +355,6 @@ void Admin::TreatInstructionId() { this->ListenForCommand(); } -//----------------------------------------------------------------------------- template void Admin::StartRemoteService( const admin::CreateServiceRequest& create_request, @@ -388,7 +362,6 @@ void Admin::StartRemoteService( this->Command(create_request, handler); } -//----------------------------------------------------------------------------- template void Admin::StopRemoteService( const admin::StopServiceRequest& stop_request, @@ -396,10 +369,6 @@ void Admin::StopRemoteService( this->Command(stop_request, handler); } -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- template void Admin::SendKeepAlive(const boost::system::error_code& ec) { if (!ec) { @@ -409,35 +378,34 @@ void Admin::SendKeepAlive(const boost::system::error_code& ec) { auto self = this->SelfFromThis(); - auto do_handler = [self, p_command]( - const boost::system::error_code& ec, - size_t length) { self->PostKeepAlive(ec, length); }; + auto do_handler = + [self, p_command](const boost::system::error_code& ec, size_t length) { + self->PostKeepAlive(ec, length); + }; this->async_SendCommand(*p_command, do_handler); } else { this->get_io_service().post( - boost::bind(&Admin::ShutdownServices, SelfFromThis())); + boost::bind(&Admin::ShutdownServices, SelfFromThis())); return; } } -//----------------------------------------------------------------------------- template void Admin::PostKeepAlive(const boost::system::error_code& ec, - size_t length) { + size_t length) { if (!ec) { reserved_keep_alive_timer_.expires_from_now( boost::posix_time::seconds(keep_alive_interval)); // to define reserved_keep_alive_timer_.async_wait( - boost::bind(&Admin::SendKeepAlive, SelfFromThis(), _1)); + boost::bind(&Admin::SendKeepAlive, SelfFromThis(), _1)); } else { this->get_io_service().post( - boost::bind(&Admin::ShutdownServices, SelfFromThis())); + boost::bind(&Admin::ShutdownServices, SelfFromThis())); return; } } -//----------------------------------------------------------------------------- template void Admin::ShutdownServices() { boost::recursive_mutex::scoped_lock lock(stopping_mutex_); @@ -452,7 +420,6 @@ void Admin::ShutdownServices() { } } -//----------------------------------------------------------------------------- template void Admin::HandleStop() { reserved_keep_alive_timer_.cancel(); diff --git a/src/services/admin/admin_command.h b/src/services/admin/admin_command.h index 6b229b29..d03969bd 100644 --- a/src/services/admin/admin_command.h +++ b/src/services/admin/admin_command.h @@ -10,34 +10,32 @@ #include -namespace ssf { namespace services { namespace admin { +namespace ssf { +namespace services { +namespace admin { class AdminCommand { -public: - AdminCommand(uint32_t serial, uint32_t command_id, - uint32_t serialize_arguments_size, std::string serialized_arguments) - : serial_(serial), - command_id_(command_id), - serialize_arguments_size_(serialize_arguments_size), - serialized_arguments_(serialized_arguments) {} + public: + AdminCommand(uint32_t serial, uint32_t command_id, + uint32_t serialize_arguments_size, + std::string serialized_arguments) + : serial_(serial), + command_id_(command_id), + serialize_arguments_size_(serialize_arguments_size), + serialized_arguments_(serialized_arguments) {} std::array const_buffers() const { - std::array buf = - { - { - boost::asio::buffer(&serial_, sizeof(serial_)), - boost::asio::buffer(&command_id_, - sizeof(command_id_)), - boost::asio::buffer(&serialize_arguments_size_, - sizeof(serialize_arguments_size_)), - boost::asio::buffer(serialized_arguments_) - } - }; + std::array buf = { + {boost::asio::buffer(&serial_, sizeof(serial_)), + boost::asio::buffer(&command_id_, sizeof(command_id_)), + boost::asio::buffer(&serialize_arguments_size_, + sizeof(serialize_arguments_size_)), + boost::asio::buffer(serialized_arguments_)}}; return buf; } -private: + private: uint32_t serial_; uint32_t command_id_; uint32_t serialize_arguments_size_; diff --git a/src/services/admin/requests/create_service_request.h b/src/services/admin/requests/create_service_request.h index 4d6f8bb8..993827ac 100644 --- a/src/services/admin/requests/create_service_request.h +++ b/src/services/admin/requests/create_service_request.h @@ -13,45 +13,44 @@ #include #include +#include + #include "core/factories/command_factory.h" #include "core/factories/service_factory.h" #include "core/factory_manager/service_factory_manager.h" - #include "services/admin/requests/service_status.h" -namespace ssf { namespace services { namespace admin { +namespace ssf { +namespace services { +namespace admin { template class CreateServiceRequest { -private: + private: typedef std::map Parameters; -public: + + public: CreateServiceRequest() {} - CreateServiceRequest(uint32_t service_id) - : service_id_(service_id) {} + CreateServiceRequest(uint32_t service_id) : service_id_(service_id) {} CreateServiceRequest(uint32_t service_id, Parameters parameters) - : service_id_(service_id), parameters_(parameters) {} + : service_id_(service_id), parameters_(parameters) {} + + enum { command_id = 1, reply_id = 2 }; - enum { - command_id = 1, - reply_id = 2 - }; - static void RegisterToCommandFactory() { - CommandFactory::RegisterOnReceiveCommand(command_id, - &CreateServiceRequest::OnReceive); - CommandFactory::RegisterOnReplyCommand(command_id, - &CreateServiceRequest::OnReply); + CommandFactory::RegisterOnReceiveCommand( + command_id, &CreateServiceRequest::OnReceive); + CommandFactory::RegisterOnReplyCommand( + command_id, &CreateServiceRequest::OnReply); CommandFactory::RegisterReplyCommandIndex(command_id, reply_id); } - static std::string OnReceive(boost::archive::text_iarchive& ar, - Demux* p_demux, - boost::system::error_code& ec) { + static std::string OnReceive(boost::archive::text_iarchive& ar, + Demux* p_demux, boost::system::error_code& ec) { CreateServiceRequest request; try { @@ -60,15 +59,15 @@ class CreateServiceRequest { return std::string(); } - auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(p_demux); + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(p_demux); auto id = p_service_factory->CreateRunNewService(request.service_id(), request.parameters(), ec); - BOOST_LOG_TRIVIAL(debug) << "service status: create " - << "service unique id " << id - << " - error_code " << ec.value(); + SSF_LOG(kLogDebug) << "service status: create " + << "service unique id " << id << " - error_code " + << ec.value(); std::stringstream ss; std::string result; @@ -79,8 +78,7 @@ class CreateServiceRequest { return result; } - static std::string OnReply(boost::archive::text_iarchive& ar, - Demux* p_demux, + static std::string OnReply(boost::archive::text_iarchive& ar, Demux* p_demux, const boost::system::error_code& ec, std::string serialized_result) { CreateServiceRequest request; @@ -92,8 +90,7 @@ class CreateServiceRequest { } ServiceStatus reply(std::stoul(serialized_result), - request.service_id(), - ec.value(), + request.service_id(), ec.value(), request.parameters()); return reply.OnSending(); @@ -108,28 +105,24 @@ class CreateServiceRequest { return ostrs.str(); } - uint32_t service_id() { - return service_id_; - } + uint32_t service_id() { return service_id_; } - Parameters parameters() { - return parameters_; - } + Parameters parameters() { return parameters_; } void add_parameter(const std::string& key, const std::string& value) { parameters_[key] = value; } -private: + private: friend class boost::serialization::access; template void serialize(Archive& ar, const unsigned int version) { - ar & service_id_; - ar & BOOST_SERIALIZATION_NVP(parameters_); + ar& service_id_; + ar& BOOST_SERIALIZATION_NVP(parameters_); } -private: + private: uint32_t service_id_; Parameters parameters_; }; @@ -138,5 +131,4 @@ class CreateServiceRequest { } // services } // ssf - #endif // SSF_SERVICES_ADMIN_REQUESTS_CREATE_SERVICE_REQUEST_H_ \ No newline at end of file diff --git a/src/services/admin/requests/service_status.h b/src/services/admin/requests/service_status.h index 37d6e986..d723784d 100644 --- a/src/services/admin/requests/service_status.h +++ b/src/services/admin/requests/service_status.h @@ -11,37 +11,35 @@ #include #include #include -#include + +#include #include "core/factories/command_factory.h" #include "core/factories/service_factory.h" #include "core/factory_manager/service_factory_manager.h" - -namespace ssf { namespace services { namespace admin { +namespace ssf { +namespace services { +namespace admin { template class ServiceStatus { -private: + private: typedef std::map Parameters; -public: + + public: ServiceStatus() {} - ServiceStatus(uint32_t id, - uint32_t service_id, - uint32_t error_code_value, + ServiceStatus(uint32_t id, uint32_t service_id, uint32_t error_code_value, Parameters parameters) - : id_(id), - service_id_(service_id), - error_code_value_(error_code_value), - parameters_(parameters) {} - - enum { - command_id = 2, - reply_id = 2 - }; - + : id_(id), + service_id_(service_id), + error_code_value_(error_code_value), + parameters_(parameters) {} + + enum { command_id = 2, reply_id = 2 }; + static void RegisterToCommandFactory() { CommandFactory::RegisterOnReceiveCommand(command_id, &ServiceStatus::OnReceive); @@ -50,45 +48,39 @@ class ServiceStatus { CommandFactory::RegisterReplyCommandIndex(command_id, reply_id); } - static std::string OnReceive(boost::archive::text_iarchive& ar, - Demux* p_demux, - boost::system::error_code& ec) { + static std::string OnReceive(boost::archive::text_iarchive& ar, + Demux* p_demux, boost::system::error_code& ec) { ServiceStatus status; try { ar >> status; - } - catch (const std::exception&) { + } catch (const std::exception&) { return std::string(); } - auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(p_demux); + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(p_demux); if (status.service_id()) { - p_service_factory->UpdateRemoteServiceStatus(status.id(), - status.service_id(), - status.error_code_value(), - status.parameters(), - ec); + p_service_factory->UpdateRemoteServiceStatus( + status.id(), status.service_id(), status.error_code_value(), + status.parameters(), ec); } else { - p_service_factory->UpdateRemoteServiceStatus(status.id(), - status.error_code_value(), - ec); + p_service_factory->UpdateRemoteServiceStatus( + status.id(), status.error_code_value(), ec); } - BOOST_LOG_TRIVIAL(debug) << "service status: received " - << "service unique id " << status.id() - << " service id " << status.service_id() - << " - error_code " << status.error_code_value(); + SSF_LOG(kLogDebug) << "service status: received " + << "service unique id " << status.id() << " service id " + << status.service_id() << " - error_code " + << status.error_code_value(); return std::string(); } - static std::string OnReply(boost::archive::text_iarchive& ar, - Demux* p_demux, - const boost::system::error_code& ec, - std::string serialized_result) { + static std::string OnReply(boost::archive::text_iarchive& ar, Demux* p_demux, + const boost::system::error_code& ec, + std::string serialized_result) { return std::string(); } @@ -101,38 +93,30 @@ class ServiceStatus { return ostrs.str(); } - uint32_t id() { - return id_; - } + uint32_t id() { return id_; } - uint32_t service_id() { - return service_id_; - } + uint32_t service_id() { return service_id_; } - Parameters parameters() { - return parameters_; - } + Parameters parameters() { return parameters_; } - uint32_t error_code_value() { - return error_code_value_; - } + uint32_t error_code_value() { return error_code_value_; } void add_parameter(std::string key, std::string value) { parameters_[key] = value; } -private: + private: friend class boost::serialization::access; template void serialize(Archive& ar, const unsigned int version) { - ar & id_; - ar & service_id_; - ar & error_code_value_; - ar & BOOST_SERIALIZATION_NVP(parameters_); + ar& id_; + ar& service_id_; + ar& error_code_value_; + ar& BOOST_SERIALIZATION_NVP(parameters_); } -private: + private: uint32_t id_; uint32_t service_id_; uint32_t error_code_value_; @@ -143,5 +127,4 @@ class ServiceStatus { } // services } // ssf - #endif // SSF_SERVICES_ADMIN_REQUESTS_SERVICE_STATUS_H_ \ No newline at end of file diff --git a/src/services/admin/requests/stop_service_request.h b/src/services/admin/requests/stop_service_request.h index b376e082..261bd02a 100644 --- a/src/services/admin/requests/stop_service_request.h +++ b/src/services/admin/requests/stop_service_request.h @@ -13,6 +13,8 @@ #include #include +#include + #include "core/factories/command_factory.h" #include "core/factories/service_factory.h" @@ -20,49 +22,46 @@ #include "services/admin/requests/service_status.h" -namespace ssf { namespace services { namespace admin { +namespace ssf { +namespace services { +namespace admin { template class StopServiceRequest { -private: + private: typedef std::map Parameters; -public: + + public: StopServiceRequest() {} - StopServiceRequest(uint32_t unique_id) - : unique_id_(unique_id) {} + StopServiceRequest(uint32_t unique_id) : unique_id_(unique_id) {} + + enum { command_id = 3, reply_id = 2 }; - enum { - command_id = 3, - reply_id = 2 - }; - static void RegisterToCommandFactory() { - CommandFactory::RegisterOnReceiveCommand(command_id, - &StopServiceRequest::OnReceive); + CommandFactory::RegisterOnReceiveCommand( + command_id, &StopServiceRequest::OnReceive); CommandFactory::RegisterOnReplyCommand(command_id, &StopServiceRequest::OnReply); CommandFactory::RegisterReplyCommandIndex(command_id, reply_id); } - static std::string OnReceive(boost::archive::text_iarchive& ar, - Demux* p_demux, - boost::system::error_code& ec) { + static std::string OnReceive(boost::archive::text_iarchive& ar, + Demux* p_demux, boost::system::error_code& ec) { StopServiceRequest request; try { ar >> request; - } - catch (const std::exception&) { + } catch (const std::exception&) { return std::string(); } - auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(p_demux); - + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(p_demux); + p_service_factory->StopService(request.unique_id()); - BOOST_LOG_TRIVIAL(debug) << "service status: stop request"; + SSF_LOG(kLogDebug) << "service status: stop request"; ec.assign(boost::system::errc::interrupted, boost::system::system_category()); @@ -76,8 +75,7 @@ class StopServiceRequest { return result; } - static std::string OnReply(boost::archive::text_iarchive& ar, - Demux* p_demux, + static std::string OnReply(boost::archive::text_iarchive& ar, Demux* p_demux, const boost::system::error_code& ec, std::string serialized_result) { StopServiceRequest request; @@ -88,9 +86,7 @@ class StopServiceRequest { return std::string(); } - ServiceStatus reply(std::stoul(serialized_result), - 0, - ec.value(), + ServiceStatus reply(std::stoul(serialized_result), 0, ec.value(), Parameters()); return reply.OnSending(); @@ -105,19 +101,17 @@ class StopServiceRequest { return ostrs.str(); } - uint32_t unique_id() { - return unique_id_; - } + uint32_t unique_id() { return unique_id_; } -private: + private: friend class boost::serialization::access; template void serialize(Archive& ar, const unsigned int version) { - ar & unique_id_; + ar& unique_id_; } -private: + private: uint32_t unique_id_; }; @@ -125,5 +119,4 @@ class StopServiceRequest { } // services } // ssf - #endif // SSF_SERVICES_ADMIN_REQUESTS_STOP_SERVICE_REQUEST_H_ \ No newline at end of file diff --git a/src/services/copy_file/fiber_to_file/fiber_to_file.h b/src/services/copy_file/fiber_to_file/fiber_to_file.h index 5c9af465..cfe9be3d 100644 --- a/src/services/copy_file/fiber_to_file/fiber_to_file.h +++ b/src/services/copy_file/fiber_to_file/fiber_to_file.h @@ -7,6 +7,8 @@ #include +#include + #include "core/factories/service_factory.h" #include "services/admin/requests/create_service_request.h" #include "services/copy_file/fiber_to_file/fiber_to_ofstream_session.h" @@ -29,7 +31,7 @@ class FiberToFile : public BaseService { using fiber_acceptor = typename ssf::BaseService::fiber_acceptor; public: - enum { factory_id = 7, kServicePort = 40 }; + enum { factory_id = 7, kServicePort = 40 + ((1 << 17) + 1) }; public: // Factory method to create the service @@ -43,8 +45,8 @@ class FiberToFile : public BaseService { // Start service and listen new fiber on demux port kServicePort virtual void start(boost::system::error_code& ec) { endpoint ep(this->get_demux(), kServicePort); - BOOST_LOG_TRIVIAL(info) - << "service fiber to file: start accept on fiber port " << kServicePort; + SSF_LOG(kLogInfo) << "service[fiber to file]: start accept on fiber port " + << kServicePort; fiber_acceptor_.bind(ep, ec); fiber_acceptor_.listen(boost::asio::socket_base::max_connections, ec); if (ec) { @@ -55,7 +57,7 @@ class FiberToFile : public BaseService { // Stop service virtual void stop(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) << "service fiber to file: stopping"; + SSF_LOG(kLogInfo) << "service[fiber to file]: stopping"; manager_.stop_all(); fiber_acceptor_.close(ec); fiber_.close(ec); @@ -91,7 +93,7 @@ class FiberToFile : public BaseService { // Create a session to transmit files for the new connection void StartDataForwarderSessionHandler(const boost::system::error_code& ec) { if (ec) { - BOOST_LOG_TRIVIAL(info) << "service fiber to file: fail accept fiber"; + SSF_LOG(kLogInfo) << "service[fiber to file]: fail accept fiber"; return; } diff --git a/src/services/copy_file/fiber_to_file/fiber_to_ofstream_session.h b/src/services/copy_file/fiber_to_file/fiber_to_ofstream_session.h index 6cb2baf3..36200128 100644 --- a/src/services/copy_file/fiber_to_file/fiber_to_ofstream_session.h +++ b/src/services/copy_file/fiber_to_file/fiber_to_ofstream_session.h @@ -9,10 +9,10 @@ #include -#include "common/network/base_session.h" // NOLINT - -#include "common/network/manager.h" -#include "common/network/base_session.h" +#include +#include // NOLINT +#include +#include #include "services/copy_file/filename_buffer.h" #include "services/copy_file/packet/packet.h" @@ -47,7 +47,7 @@ class FiberToOstreamSession : public ssf::BaseSession { // Stop session virtual void stop(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(debug) << "session fiber to file : stopped"; + SSF_LOG(kLogDebug) << "session[fiber to file]: stopped"; input_socket_stream_.close(ec); // connection interrupted without prior notification (broken pipe) // delete output file @@ -93,16 +93,15 @@ class FiberToOstreamSession : public ssf::BaseSession { boost::bind(&FiberToOstreamSession::ForwardData, this->SelfFromThis(), _1, _2)); - BOOST_LOG_TRIVIAL(info) - << "session fiber to file : start receiving data and writing in file " + SSF_LOG(kLogInfo) + << "session[fiber to file]: start receiving data and writing in file " << request_.GetFilename(); // open output_stream output_stream_.open(request_.GetFilename(), std::ofstream::binary); if (!output_stream_.is_open()) { - BOOST_LOG_TRIVIAL(error) << "session fiber to file : output file " - << request_.GetFilename() - << " could not be opened"; + SSF_LOG(kLogError) << "session[fiber to file]: output file " + << request_.GetFilename() << " could not be opened"; boost::system::error_code stop_ec; p_manager_->stop(this->SelfFromThis(), stop_ec); diff --git a/src/services/copy_file/file_enquirer/file_enquirer.h b/src/services/copy_file/file_enquirer/file_enquirer.h index 812230a4..f8685bab 100644 --- a/src/services/copy_file/file_enquirer/file_enquirer.h +++ b/src/services/copy_file/file_enquirer/file_enquirer.h @@ -3,6 +3,8 @@ #include +#include + #include "core/factories/service_factory.h" #include "services/admin/requests/create_service_request.h" #include "services/copy_file/file_to_fiber/file_to_fiber.h" @@ -84,8 +86,8 @@ class FileEnquirer : public BaseService { } reenter(coroutine_) { - BOOST_LOG_TRIVIAL(debug) - << "service file enquirer: connect to remote fiber acceptor port " + SSF_LOG(kLogDebug) + << "service[file enquirer]: connect to remote fiber acceptor port " << remote_endpoint_.port(); yield fiber_.async_connect( diff --git a/src/services/copy_file/file_to_fiber/file_to_fiber.h b/src/services/copy_file/file_to_fiber/file_to_fiber.h index f90803a3..a7814709 100644 --- a/src/services/copy_file/file_to_fiber/file_to_fiber.h +++ b/src/services/copy_file/file_to_fiber/file_to_fiber.h @@ -19,6 +19,8 @@ #include #include +#include + #include "core/factories/service_factory.h" #include "services/admin/requests/create_service_request.h" #include "services/copy_file/file_to_fiber/istream_to_fiber_session.h" @@ -52,7 +54,7 @@ class FileToFiber : public BaseService { using FilenameBuffer = ssf::services::copy_file::FilenameBuffer; public: - enum { factory_id = 8, kServicePort = 41 }; + enum { factory_id = 8, kServicePort = 41 + ((1 << 17) + 1) }; public: // Factory method to create the service @@ -86,9 +88,8 @@ class FileToFiber : public BaseService { if (accept_) { endpoint ep(this->get_demux(), kServicePort); - BOOST_LOG_TRIVIAL(info) - << "service fiber to file: start accept on fiber port " - << kServicePort; + SSF_LOG(kLogInfo) << "service[file to fiber]: start accept on fiber port " + << kServicePort; fiber_acceptor_.bind(ep, ec); fiber_acceptor_.listen(boost::asio::socket_base::max_connections, ec); if (ec) { @@ -102,7 +103,7 @@ class FileToFiber : public BaseService { // Stop service virtual void stop(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) << "service file to fiber: stopping"; + SSF_LOG(kLogInfo) << "service[file to fiber]: stopping"; manager_.stop_all(); boost::system::error_code close_ec; fiber_acceptor_.close(close_ec); @@ -274,8 +275,8 @@ class FileToFiber : public BaseService { FiberPtr p_fiber = std::make_shared(this->get_demux().get_io_service()); - BOOST_LOG_TRIVIAL(debug) - << "service file to fiber: connect to remote fiber acceptor port " + SSF_LOG(kLogDebug) + << "service[file to fiber]: connect to remote fiber acceptor port " << ssf::services::copy_file::fiber_to_file::FiberToFile< Demux>::kServicePort; @@ -302,19 +303,18 @@ class FileToFiber : public BaseService { } if (from_stdin) { - BOOST_LOG_TRIVIAL(info) - << "service file to fiber: start forward data from stdin to " + SSF_LOG(kLogInfo) + << "service[file to fiber]: start forward data from stdin to " << output_file; } else { - BOOST_LOG_TRIVIAL(info) - << "service file to fiber: start forward data from " << input_file - << " to " << output_file; + SSF_LOG(kLogInfo) << "service[file to fiber]: start forward data from " + << input_file << " to " << output_file; } if (!from_stdin) { auto p_session = IstreamToFiberSession::Create( - &manager_, input_file, std::move(*p_fiber), - output_file, stop_handler); + &manager_, input_file, std::move(*p_fiber), output_file, + stop_handler); boost::system::error_code start_ec; manager_.start(p_session, start_ec); } else { diff --git a/src/services/copy_file/file_to_fiber/istream_to_fiber_session.h b/src/services/copy_file/file_to_fiber/istream_to_fiber_session.h index dbc6f3f1..38e62ab4 100644 --- a/src/services/copy_file/file_to_fiber/istream_to_fiber_session.h +++ b/src/services/copy_file/file_to_fiber/istream_to_fiber_session.h @@ -11,10 +11,11 @@ #include -#include "common/network/base_session.h" +#include -#include "common/network/manager.h" -#include "common/network/base_session.h" +#include +#include +#include #include "services/copy_file/filename_buffer.h" #include "services/copy_file/packet/packet.h" @@ -49,9 +50,9 @@ class IstreamToFiberSession : public ssf::BaseSession { if (!from_stdin_) { input_stream_.open(input_file_, InputStream::binary); if (!input_stream_.is_open() || !input_stream_.good()) { - BOOST_LOG_TRIVIAL(error) << "session istream to fiber : cannot open file " - << input_file_; - ec.assign(ssf::error::bad_file_descriptor, ssf::error::get_ssf_category()); + SSF_LOG(kLogError) << "session[istream to fiber]: cannot open file " + << input_file_; + ec.assign(::error::bad_file_descriptor, ::error::get_ssf_category()); stop_handler_(output_file_); return; @@ -68,7 +69,8 @@ class IstreamToFiberSession : public ssf::BaseSession { } private: - IstreamToFiberSession(SessionManager* p_manager, const std::string& input_file, + IstreamToFiberSession(SessionManager* p_manager, + const std::string& input_file, OutputSocketStream&& output_socket_stream, const std::string& output_file, StopSessionHandler stop_handler) diff --git a/src/services/datagrams_to_fibers/datagrams_to_fibers.h b/src/services/datagrams_to_fibers/datagrams_to_fibers.h index 73f781f8..d61d5246 100644 --- a/src/services/datagrams_to_fibers/datagrams_to_fibers.h +++ b/src/services/datagrams_to_fibers/datagrams_to_fibers.h @@ -1,7 +1,7 @@ #ifndef SSF_SERVICES_DATAGRAMS_TO_FIBERS_DATAGRAMS_TO_FIBERS_H_ #define SSF_SERVICES_DATAGRAMS_TO_FIBERS_DATAGRAMS_TO_FIBERS_H_ -#include +#include #include @@ -19,7 +19,9 @@ #include "services/admin/requests/create_service_request.h" -namespace ssf { namespace services { namespace datagrams_to_fibers { +namespace ssf { +namespace services { +namespace datagrams_to_fibers { template class DatagramsToFibers : public BaseService { @@ -44,26 +46,22 @@ class DatagramsToFibers : public BaseService { public: static DatagramsToFibersPtr Create(boost::asio::io_service& io_service, - Demux& fiber_demux, Parameters parameters) { - if (!parameters.count("local_port") || - !parameters.count("remote_port")) { + Demux& fiber_demux, + Parameters parameters) { + if (!parameters.count("local_port") || !parameters.count("remote_port")) { return DatagramsToFibersPtr(nullptr); } else { return std::shared_ptr( - new DatagramsToFibers( - io_service, - fiber_demux, - (uint16_t)std::stoul(parameters["local_port"]), - std::stoul(parameters["remote_port"]))); + new DatagramsToFibers(io_service, fiber_demux, + (uint16_t)std::stoul(parameters["local_port"]), + std::stoul(parameters["remote_port"]))); } } - enum { - factory_id = 6 - }; + enum { factory_id = 6 }; static void RegisterToServiceFactory( - std::shared_ptr> p_factory) { + std::shared_ptr> p_factory) { p_factory->RegisterServiceCreator(factory_id, &DatagramsToFibers::Create); } @@ -72,7 +70,7 @@ class DatagramsToFibers : public BaseService { virtual uint32_t service_type_id(); static ssf::services::admin::CreateServiceRequest GetCreateRequest( - uint16_t local_port, remote_port_type remote_port) { + uint16_t local_port, remote_port_type remote_port) { ssf::services::admin::CreateServiceRequest create(factory_id); create.add_parameter("local_port", std::to_string(local_port)); create.add_parameter("remote_port", std::to_string(remote_port)); @@ -82,7 +80,7 @@ class DatagramsToFibers : public BaseService { private: DatagramsToFibers(boost::asio::io_service& io_service, Demux& fiber_demux, - uint16_t local, remote_port_type remote_port); + uint16_t local, remote_port_type remote_port); private: void StartReceivingDatagrams(); @@ -95,7 +93,8 @@ class DatagramsToFibers : public BaseService { } std::shared_ptr SelfFromThis() { - return std::static_pointer_cast(this->shared_from_this()); + return std::static_pointer_cast( + this->shared_from_this()); } uint16_t local_port_; diff --git a/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp b/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp index deb94fce..7f2cfb57 100644 --- a/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp +++ b/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp @@ -1,11 +1,13 @@ #ifndef SSF_SERVICES_DATAGRAMS_TO_FIBERS_DATAGRAMS_TO_FIBERS_IPP_ #define SSF_SERVICES_DATAGRAMS_TO_FIBERS_DATAGRAMS_TO_FIBERS_IPP_ -#include +#include -#include "common/network/session_forwarder.h" +#include -namespace ssf { namespace services { namespace datagrams_to_fibers { +namespace ssf { +namespace services { +namespace datagrams_to_fibers { template DatagramsToFibers::DatagramsToFibers(boost::asio::io_service& io_service, Demux& fiber_demux, @@ -15,18 +17,52 @@ DatagramsToFibers::DatagramsToFibers(boost::asio::io_service& io_service, local_port_(local_port), remote_port_(remote_port), socket_(io_service), - endpoint_(boost::asio::ip::udp::v4(), local_port_), + endpoint_(), send_to_(fiber_demux, remote_port), - p_udp_operator_(UdpOperator::Create(socket_)) { - } + p_udp_operator_(UdpOperator::Create(socket_)) {} template void DatagramsToFibers::start(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) << "service datagrams to fibers: starting relay on local port udp " << local_port_; + SSF_LOG(kLogInfo) + << "service[datagrams to fibers]: starting relay on local port udp " + << local_port_; + + boost::asio::ip::udp::resolver resolver(socket_.get_io_service()); + boost::asio::ip::udp::resolver::query query( + boost::asio::ip::udp::v4(), "localhost", std::to_string(local_port_)); + auto ep_it = resolver.resolve(query, ec); - // Listen on all interfaces + if (ec) { + SSF_LOG(kLogError) + << "service[datagrams to fibers]: could not resolve query "; + return; + } + + endpoint_ = *ep_it; + + boost::system::error_code close_ec; socket_.open(boost::asio::ip::udp::v4(), ec); + if (ec) { + SSF_LOG(kLogError) << "service[datagrams to fibers]: could not open socket"; + socket_.close(close_ec); + return; + } + boost::asio::socket_base::reuse_address reuse_address_option(true); + socket_.set_option(reuse_address_option, ec); + if (ec) { + SSF_LOG(kLogError) + << "service[datagrams to fibers]: could not set reuse address option"; + socket_.close(close_ec); + return; + } + socket_.bind(endpoint_, ec); + if (ec) { + SSF_LOG(kLogError) << "service[datagrams to fibers]: could not bind socket"; + socket_.close(close_ec); + return; + } if (!ec) { this->StartReceivingDatagrams(); @@ -35,22 +71,25 @@ void DatagramsToFibers::start(boost::system::error_code& ec) { template void DatagramsToFibers::stop(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) << "service datagrams to fibers: stopping"; + SSF_LOG(kLogInfo) << "service[datagrams to fibers]: stopping"; socket_.close(ec); if (ec) { - BOOST_LOG_TRIVIAL(debug) << "service datagrams to fibers: error on stop " << ec.message() << std::endl; + SSF_LOG(kLogDebug) << "service[datagrams to fibers]: error on stop " + << ec.message(); } p_udp_operator_->StopAll(); } template -uint32_t DatagramsToFibers::service_type_id() { return factory_id; } +uint32_t DatagramsToFibers::service_type_id() { + return factory_id; +} template void DatagramsToFibers::StartReceivingDatagrams() { - BOOST_LOG_TRIVIAL(trace) << "service datagrams to fibers: receiving new datagrams"; + SSF_LOG(kLogTrace) << "service[datagrams to fibers]: receiving new datagrams"; socket_.async_receive_from( boost::asio::buffer(working_buffer_), endpoint_, @@ -68,9 +107,9 @@ void DatagramsToFibers::SocketReceiveHandler( fiber_datagram left(this->get_demux().get_io_service(), datagram_endpoint(this->get_demux(), 0)); p_udp_operator_->AddLink(std::move(left), endpoint_, send_to_, - this->get_io_service()); + this->get_io_service()); p_udp_operator_->Feed(endpoint_, boost::asio::buffer(working_buffer_), - length); + length); } this->StartReceivingDatagrams(); diff --git a/src/services/fibers_to_datagrams/fibers_to_datagrams.h b/src/services/fibers_to_datagrams/fibers_to_datagrams.h index f7364274..ee5db75c 100644 --- a/src/services/fibers_to_datagrams/fibers_to_datagrams.h +++ b/src/services/fibers_to_datagrams/fibers_to_datagrams.h @@ -1,7 +1,7 @@ #ifndef SSF_SERVICES_FIBERS_TO_DATAGRAMS_FIBERS_TO_DATAGRAMS_H_ #define SSF_SERVICES_FIBERS_TO_DATAGRAMS_FIBERS_TO_DATAGRAMS_H_ -#include +#include #include @@ -19,7 +19,9 @@ #include "services/admin/requests/create_service_request.h" -namespace ssf { namespace services { namespace fibers_to_datagrams { +namespace ssf { +namespace services { +namespace fibers_to_datagrams { template class FibersToDatagrams : public BaseService { @@ -32,11 +34,10 @@ class FibersToDatagrams : public BaseService { typedef typename ssf::BaseService::Parameters Parameters; typedef typename ssf::BaseService::demux demux; typedef typename ssf::BaseService::fiber_datagram fiber_datagram; - typedef typename ssf::BaseService::datagram_endpoint - datagram_endpoint; + typedef typename ssf::BaseService::datagram_endpoint datagram_endpoint; typedef DatagramLinkOperator UdpOperator; + remote_udp_endpoint_type, socket> UdpOperator; typedef std::shared_ptr UdpOperatorPtr; typedef std::shared_ptr FibersToDatagramsPtr; @@ -45,25 +46,20 @@ class FibersToDatagrams : public BaseService { public: static FibersToDatagramsPtr create(boost::asio::io_service& io_service, - Demux& fiber_demux, Parameters parameters) { - if (!parameters.count("local_port") || - !parameters.count("remote_ip") || + Demux& fiber_demux, + Parameters parameters) { + if (!parameters.count("local_port") || !parameters.count("remote_ip") || !parameters.count("remote_port")) { return FibersToDatagramsPtr(nullptr); } else { - return std::shared_ptr( - new FibersToDatagrams( - io_service, - fiber_demux, - std::stoul(parameters["local_port"]), - parameters["remote_ip"], - (uint16_t)std::stoul(parameters["remote_port"]))); + return std::shared_ptr(new FibersToDatagrams( + io_service, fiber_demux, std::stoul(parameters["local_port"]), + parameters["remote_ip"], + (uint16_t)std::stoul(parameters["remote_port"]))); } } - enum { - factory_id = 5 - }; + enum { factory_id = 5 }; static void RegisterToServiceFactory( std::shared_ptr> p_factory) { @@ -75,7 +71,8 @@ class FibersToDatagrams : public BaseService { virtual uint32_t service_type_id(); static ssf::services::admin::CreateServiceRequest GetCreateRequest( - local_port_type local_port, std::string remote_addr, uint16_t remote_port) { + local_port_type local_port, std::string remote_addr, + uint16_t remote_port) { ssf::services::admin::CreateServiceRequest create(factory_id); create.add_parameter("local_port", std::to_string(local_port)); create.add_parameter("remote_ip", remote_addr); @@ -86,8 +83,8 @@ class FibersToDatagrams : public BaseService { private: FibersToDatagrams(boost::asio::io_service& io_service, Demux& fiber_demux, - local_port_type local, const std::string& ip, - uint16_t remote_port); + local_port_type local, const std::string& ip, + uint16_t remote_port); private: void StartReceivingDatagrams(); @@ -100,7 +97,8 @@ class FibersToDatagrams : public BaseService { } std::shared_ptr SelfFromThis() { - return std::static_pointer_cast(this->shared_from_this()); + return std::static_pointer_cast( + this->shared_from_this()); } private: @@ -113,7 +111,7 @@ class FibersToDatagrams : public BaseService { datagram_endpoint received_from_; WorkingBufferType working_buffer_; - + UdpOperatorPtr p_udp_operator_; }; diff --git a/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp b/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp index aa305aee..7f6b9cba 100644 --- a/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp +++ b/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp @@ -1,7 +1,7 @@ #ifndef SSF_SERVICES_FIBERS_TO_DATAGRAMS_FIBERS_TO_DATAGRAMS_IPP_ #define SSF_SERVICES_FIBERS_TO_DATAGRAMS_FIBERS_TO_DATAGRAMS_IPP_ -#include +#include #include "common/error/error.h" @@ -20,12 +20,13 @@ FibersToDatagrams::FibersToDatagrams(boost::asio::io_service& io_service, local_port_(local_port), fiber_(io_service), received_from_(fiber_demux, 0), - p_udp_operator_(UdpOperator::Create(fiber_)) { - } + p_udp_operator_(UdpOperator::Create(fiber_)) {} template void FibersToDatagrams::start(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) << "service fibers to datagrams: starting relay on local port udp " << local_port_; + SSF_LOG(kLogInfo) + << "service[fibers to datagrams]: starting relay on local port udp " + << local_port_; // fiber.open() fiber_.bind(datagram_endpoint(this->get_demux(), local_port_), ec); @@ -45,8 +46,8 @@ void FibersToDatagrams::start(boost::system::error_code& ec) { template void FibersToDatagrams::stop(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) << "service fibers to datagrams: stopping"; - ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + SSF_LOG(kLogInfo) << "service[fibers to datagrams]: stopping"; + ec.assign(::error::success, ::error::get_ssf_category()); fiber_.close(); p_udp_operator_->StopAll(); @@ -59,7 +60,7 @@ uint32_t FibersToDatagrams::service_type_id() { template void FibersToDatagrams::StartReceivingDatagrams() { - BOOST_LOG_TRIVIAL(trace) << "service fibers to datagrams: receiving new datagrams"; + SSF_LOG(kLogTrace) << "service[fibers to datagrams]: receiving new datagrams"; fiber_.async_receive_from( boost::asio::buffer(working_buffer_), received_from_, @@ -75,12 +76,12 @@ void FibersToDatagrams::FiberReceiveHandler( if (!already_in) { boost::asio::ip::udp::socket left( - this->get_io_service(), - boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)); + this->get_io_service(), + boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 0)); p_udp_operator_->AddLink(std::move(left), received_from_, endpoint_, - this->get_io_service()); + this->get_io_service()); p_udp_operator_->Feed(received_from_, - boost::asio::buffer(working_buffer_), length); + boost::asio::buffer(working_buffer_), length); } this->StartReceivingDatagrams(); diff --git a/src/services/fibers_to_sockets/fibers_to_sockets.h b/src/services/fibers_to_sockets/fibers_to_sockets.h index f4ffc505..9fb631d8 100644 --- a/src/services/fibers_to_sockets/fibers_to_sockets.h +++ b/src/services/fibers_to_sockets/fibers_to_sockets.h @@ -1,16 +1,16 @@ #ifndef SSF_SERVICES_FIBERS_TO_SOCKETS_FIBERS_TO_SOCKETS_H_ #define SSF_SERVICES_FIBERS_TO_SOCKETS_FIBERS_TO_SOCKETS_H_ -#include +#include #include #include "common/boost/fiber/stream_fiber.hpp" #include "common/boost/fiber/basic_fiber_demux.hpp" -#include "common/network/socket_link.h" -#include "common/network/manager.h" -#include "common/network/base_session.h" +#include +#include +#include #include "services/base_service.h" @@ -18,7 +18,9 @@ #include "services/admin/requests/create_service_request.h" -namespace ssf { namespace services { namespace fibers_to_sockets { +namespace ssf { +namespace services { +namespace fibers_to_sockets { template class FibersToSockets : public BaseService { @@ -38,8 +40,7 @@ class FibersToSockets : public BaseService { public: static FibersToSocketsPtr create(boost::asio::io_service& io_service, demux& fiber_demux, Parameters parameters) { - if (!parameters.count("local_port") || - !parameters.count("remote_ip") || + if (!parameters.count("local_port") || !parameters.count("remote_ip") || !parameters.count("remote_port")) { return FibersToSocketsPtr(nullptr); } else { @@ -50,14 +51,11 @@ class FibersToSockets : public BaseService { } } - enum { - factory_id = 3 - }; + enum { factory_id = 3 }; static void RegisterToServiceFactory( - std::shared_ptr> p_factory) { - p_factory->RegisterServiceCreator(factory_id, - &FibersToSockets::create); + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, &FibersToSockets::create); } virtual void start(boost::system::error_code& ec); @@ -65,7 +63,8 @@ class FibersToSockets : public BaseService { virtual uint32_t service_type_id(); static ssf::services::admin::CreateServiceRequest GetCreateRequest( - local_port_type local_port, std::string remote_addr, uint16_t remote_port) { + local_port_type local_port, std::string remote_addr, + uint16_t remote_port) { ssf::services::admin::CreateServiceRequest create(factory_id); create.add_parameter("local_port", std::to_string(local_port)); create.add_parameter("remote_ip", remote_addr); @@ -80,7 +79,7 @@ class FibersToSockets : public BaseService { uint16_t remote_port); private: - //No check on prioror presence implemented + // No check on prioror presence implemented void StartAcceptFibers(); void FiberAcceptHandler(const boost::system::error_code& ec); @@ -105,7 +104,7 @@ class FibersToSockets : public BaseService { socket socket_; boost::asio::ip::tcp::endpoint endpoint_; - + SessionManager manager_; }; diff --git a/src/services/fibers_to_sockets/fibers_to_sockets.ipp b/src/services/fibers_to_sockets/fibers_to_sockets.ipp index f75a68b6..10c6d527 100644 --- a/src/services/fibers_to_sockets/fibers_to_sockets.ipp +++ b/src/services/fibers_to_sockets/fibers_to_sockets.ipp @@ -1,12 +1,14 @@ #ifndef SSF_SERVICES_FIBERS_TO_SOCKETS_FIBERS_TO_SOCKETS_IPP_ #define SSF_SERVICES_FIBERS_TO_SOCKETS_FIBERS_TO_SOCKETS_IPP_ -#include +#include +#include #include "common/error/error.h" -#include "common/network/session_forwarder.h" -namespace ssf { namespace services { namespace fibers_to_sockets { +namespace ssf { +namespace services { +namespace fibers_to_sockets { template FibersToSockets::FibersToSockets(boost::asio::io_service& io_service, @@ -24,8 +26,8 @@ FibersToSockets::FibersToSockets(boost::asio::io_service& io_service, template void FibersToSockets::start(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) - << "service fibers to sockets: starting relay on local port tcp " + SSF_LOG(kLogInfo) + << "service[fibers to sockets]: starting relay on local port tcp " << local_port_; endpoint ep(this->get_demux(), local_port_); @@ -47,9 +49,8 @@ void FibersToSockets::start(boost::system::error_code& ec) { template void FibersToSockets::stop(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) << "service fibers to sockets: stopping"; - ec.assign(ssf::error::success, - ssf::error::get_ssf_category()); + SSF_LOG(kLogInfo) << "service[fibers to sockets]: stopping"; + ec.assign(::error::success, ::error::get_ssf_category()); fiber_acceptor_.close(); manager_.stop_all(); @@ -62,7 +63,7 @@ uint32_t FibersToSockets::service_type_id() { template void FibersToSockets::StartAcceptFibers() { - BOOST_LOG_TRIVIAL(trace) << "service fibers to sockets: accepting new clients"; + SSF_LOG(kLogTrace) << "service[fibers to sockets]: accepting new clients"; fiber_acceptor_.async_accept( fiber_, Then(&FibersToSockets::FiberAcceptHandler, this->SelfFromThis())); @@ -71,23 +72,23 @@ void FibersToSockets::StartAcceptFibers() { template void FibersToSockets::FiberAcceptHandler( const boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(trace) << "service fibers to sockets: accept handler"; + SSF_LOG(kLogTrace) << "service[fibers to sockets]: accept handler"; if (!fiber_acceptor_.is_open()) { return; } if (!ec) { - socket_.async_connect(endpoint_, - Then(&FibersToSockets::SocketConnectHandler, - this->SelfFromThis())); + socket_.async_connect( + endpoint_, + Then(&FibersToSockets::SocketConnectHandler, this->SelfFromThis())); } } template void FibersToSockets::SocketConnectHandler( const boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(trace) << "service fibers to sockets: connect handler"; + SSF_LOG(kLogTrace) << "service[fibers to sockets]: connect handler"; if (!ec) { auto session = SessionForwarder::create( diff --git a/src/services/initialisation.h b/src/services/initialisation.h index a8a7e80c..ec2859a2 100644 --- a/src/services/initialisation.h +++ b/src/services/initialisation.h @@ -5,13 +5,7 @@ namespace ssf { namespace services { namespace initialisation { - enum type - { - NETWORK = 1, - TRANSPORT = 2, - SERVICE = 3, - CLOSE = 4 - }; +enum type { NETWORK = 1, TRANSPORT = 2, SERVICE = 3, CLOSE = 4 }; } // initialisation } // services diff --git a/src/services/process/posix/session.h b/src/services/process/posix/session.h new file mode 100644 index 00000000..e7057213 --- /dev/null +++ b/src/services/process/posix/session.h @@ -0,0 +1,97 @@ +#ifndef SSF_SERVICES_PROCESS_POSIX_SESSION_H_ +#define SSF_SERVICES_PROCESS_POSIX_SESSION_H_ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "services/socks/v4/request.h" + +#include "common/boost/fiber/stream_fiber.hpp" + +namespace ssf { +namespace services { +namespace process { +namespace posix { + +template +class Session : public ssf::BaseSession { + private: + typedef std::array StreamBuff; + + typedef boost::asio::ip::tcp::socket socket; + typedef typename boost::asio::fiber::stream_fiber< + typename Demux::socket_type>::socket fiber; + + typedef ItemManager SessionManager; + + enum { kInvalidProcessId = -1, kInvalidTtyDescriptor = -1 }; + + public: + Session(SessionManager* sm, fiber client, const std::string& binary_path, + const std::string& binary_args); + + public: + void start(boost::system::error_code&) override; + + void stop(boost::system::error_code&) override; + + private: + std::shared_ptr SelfFromThis(); + + static void ChdirHome(boost::system::error_code& ec); + + static void GenerateArgv(const std::string& binary_name, + const std::list& splitted_argv, + std::vector& argv); + + void InitMasterSlaveTty(int* p_master, int* p_slave, + boost::system::error_code& ec); + + void StartForwarding(boost::system::error_code& ec); + + void StopHandler(const boost::system::error_code& ec); + + void StartSignalWait(); + + void SigchldHandler(const boost::system::error_code& ec, int sig_num); + + private: + boost::asio::io_service& io_service_; + SessionManager* p_session_manager_; + + fiber client_; + boost::asio::signal_set signal_; + + std::string binary_path_; + std::string binary_args_; + + pid_t child_pid_; + + int master_tty_; + + boost::asio::posix::stream_descriptor sd_; + + StreamBuff upstream_; + StreamBuff downstream_; +}; + +} // posix +} // process +} // services +} // ssf + +#include "services/process/posix/session.ipp" + +#endif // SSF_SERVICES_PROCESS_POSIX_SESSION_H_ diff --git a/src/services/process/posix/session.ipp b/src/services/process/posix/session.ipp new file mode 100644 index 00000000..82307a5b --- /dev/null +++ b/src/services/process/posix/session.ipp @@ -0,0 +1,306 @@ +#ifndef SSF_SERVICES_PROCESS_POSIX_SESSION_IPP_ +#define SSF_SERVICES_PROCESS_POSIX_SESSION_IPP_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include "common/error/error.h" + +namespace ssf { +namespace services { +namespace process { +namespace posix { + +template +Session::Session(SessionManager* p_session_manager, fiber client, + const std::string& binary_path, + const std::string& binary_args) + : ssf::BaseSession(), + io_service_(client.get_io_service()), + p_session_manager_(p_session_manager), + client_(std::move(client)), + signal_(io_service_), + binary_path_(binary_path), + binary_args_(binary_args), + child_pid_(kInvalidProcessId), + master_tty_(kInvalidTtyDescriptor), + sd_(io_service_) {} + +template +void Session::start(boost::system::error_code& ec) { + SSF_LOG(kLogInfo) << "session[process]: start"; + int master_tty; + int slave_tty; + + InitMasterSlaveTty(&master_tty, &slave_tty, ec); + + if (ec) { + SSF_LOG(kLogError) << "session[process]: init tty failed"; + stop(ec); + return; + } + + signal_.add(SIGCHLD, ec); + if (ec) { + SSF_LOG(kLogError) + << "session[process]: init signal handler on SIGCHLD failed"; + stop(ec); + return; + } + + child_pid_ = fork(); + if (child_pid_ < 0) { + SSF_LOG(kLogError) << "session[process]: fork failed"; + ec.assign(::error::process_not_created, ::error::get_ssf_category()); + stop(ec); + return; + } + + if (child_pid_ == 0) { + // child + boost::system::error_code ec; + struct termios new_term_settings; + close(master_tty); + + tcgetattr(slave_tty, &new_term_settings); + // IGNCR: ignore carriage return on input + new_term_settings.c_iflag |= ( IGNCR ); + tcsetattr(slave_tty, TCSANOW, &new_term_settings); + + // new process as session leader + setsid(); + + // slave side as controlling terminal + ioctl(slave_tty, TIOCSCTTY, 1); + + close(STDOUT_FILENO); + close(STDERR_FILENO); + close(STDIN_FILENO); + + // set slave_tty as process I/O + while ((dup2(slave_tty, STDOUT_FILENO) == -1) && (errno == EINTR)) { + } + while ((dup2(slave_tty, STDERR_FILENO) == -1) && (errno == EINTR)) { + } + while ((dup2(slave_tty, STDIN_FILENO) == -1) && (errno == EINTR)) { + } + + // dup2 done, close descriptor + close(slave_tty); + + // Change dir to user home + ChdirHome(ec); + + std::size_t slash_pos = binary_path_.rfind('/'); + std::string binary_name = std::string::npos != slash_pos + ? binary_path_.substr(slash_pos + 1) + : binary_path_; + + // Generate argv array + std::vector argv; + std::list split_args; + if (!binary_args_.empty()) { + boost::split(split_args, binary_args_, boost::algorithm::is_any_of(" ")); + } + GenerateArgv(binary_name, split_args, argv); + + execv(binary_path_.c_str(), argv.data()); + + fprintf(stderr, "Exiting: fail to exec <%s>\n", binary_path_.c_str()); + exit(1); + } + + // parent + close(slave_tty); + master_tty_ = master_tty; + + StartSignalWait(); + + StartForwarding(ec); + if (ec) { + stop(ec); + } +} + +template +void Session::stop(boost::system::error_code& ec) { + SSF_LOG(kLogInfo) << "session[process]: stop"; + + client_.close(); + if (ec) { + SSF_LOG(kLogError) << "session[process]: stop error " << ec.message(); + } + + if (child_pid_ > 0) { + kill(child_pid_, SIGTERM); + child_pid_ = kInvalidProcessId; + } + + if (master_tty_ > kInvalidTtyDescriptor) { + close(master_tty_); + master_tty_ = kInvalidTtyDescriptor; + } + + sd_.close(ec); + signal_.cancel(ec); +} + +template +std::shared_ptr> Session::SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); +} + +template +void Session::StopHandler(const boost::system::error_code& ec) { + boost::system::error_code e; + p_session_manager_->stop(this->SelfFromThis(), e); +} + +template +void Session::StartSignalWait() { + signal_.async_wait( + boost::bind(&Session::SigchldHandler, this->SelfFromThis(), _1, _2)); +} + +template +void Session::SigchldHandler(const boost::system::error_code& ec, + int sig_num) { + if (ec) { + return; + } + + int status; + if (waitpid(child_pid_, &status, WNOHANG) <= 0) { + // child state did not change + StartSignalWait(); + return; + } + + if (!WIFEXITED(status) && !WIFSIGNALED(status)) { + // child did not terminate + StartSignalWait(); + return; + } + + // child terminated, close session + child_pid_ = kInvalidProcessId; + StopHandler(ec); +} + +template +void Session::ChdirHome(boost::system::error_code& ec) { + const char* home_dir = getenv("HOME"); + + if (home_dir == NULL) { + struct passwd* p_pw = getpwuid(getuid()); + if (p_pw == NULL) { + fprintf(stderr, "Could not find passwd entry for current user\n"); + ec.assign(::error::file_not_found, ::error::get_ssf_category()); + return; + } + + home_dir = p_pw->pw_dir; + } + + if (chdir(home_dir) < 0) { + fprintf(stderr, "Could not chdir to user home <%s>\n", home_dir); + ec.assign(::error::file_not_found, ::error::get_ssf_category()); + return; + } + + // overwrite PWD env var after chdir + if (setenv("PWD", home_dir, 1) < 0) { + fprintf(stderr, "Could not set PWD env var <%s>\n", home_dir); + } +} + +template +void Session::GenerateArgv(const std::string& binary_name, + const std::list& split_args, + std::vector& argv) { + if (split_args.size() > 0) { + argv.resize(split_args.size() + 2); + std::size_t i = 1; + for (auto& arg : split_args) { + argv[i] = const_cast(arg.c_str()); + ++i; + } + } else { + argv.resize(2); + } + + argv[0] = const_cast(binary_name.c_str()); + argv[argv.size() - 1] = nullptr; +} + +template +void Session::InitMasterSlaveTty(int* p_master_tty, int* p_slave_tty, + boost::system::error_code& ec) { + // open an available pseudo terminal device (master/slave pair) + *p_master_tty = posix_openpt(O_RDWR | O_NOCTTY); + if (*p_master_tty < 0) { + SSF_LOG(kLogError) << "session[process]: could not open master tty"; + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); + return; + } + + // change permissions and owner of the slave side + if (grantpt(*p_master_tty) != 0) { + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); + return; + } + + // unlock master/slave pair + if (unlockpt(*p_master_tty) != 0) { + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); + return; + } + + // open slave side + *p_slave_tty = open(ptsname(*p_master_tty), O_RDWR | O_NOCTTY); + if (*p_slave_tty < 0) { + SSF_LOG(kLogError) << "session[process]: could not open slave tty"; + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); + return; + } +} + +template +void Session::StartForwarding(boost::system::error_code& ec) { + sd_.assign(master_tty_, ec); + if (ec) { + SSF_LOG(kLogError) + << "session[process]: could not initialize stream handle"; + return; + } + + // pipe process stdout/stderr to socket output + AsyncEstablishHDLink( + ReadFrom(sd_), WriteTo(client_), boost::asio::buffer(downstream_), + boost::bind(&Session::StopHandler, this->SelfFromThis(), _1)); + // pipe socket input to process stdin + AsyncEstablishHDLink( + ReadFrom(client_), WriteTo(sd_), boost::asio::buffer(upstream_), + boost::bind(&Session::StopHandler, this->SelfFromThis(), _1)); +} + +} // posix +} // process +} // services +} // ssf + +#endif // SSF_SERVICES_PROCESS_POSIX_SESSION_IPP_ diff --git a/src/services/process/server.h b/src/services/process/server.h new file mode 100644 index 00000000..a2870375 --- /dev/null +++ b/src/services/process/server.h @@ -0,0 +1,136 @@ +#ifndef SSF_SERVICES_PROCESS_PROCESS_SERVER_H_ +#define SSF_SERVICES_PROCESS_PROCESS_SERVER_H_ + +#include + +#include +#include + +#include +#include + +#include "services/base_service.h" + +#include "common/config/config.h" +#include "common/boost/fiber/stream_fiber.hpp" +#include "common/boost/fiber/basic_fiber_demux.hpp" + +#include "core/factories/service_factory.h" + +#include "services/admin/requests/create_service_request.h" + +#if defined(BOOST_ASIO_WINDOWS) +#include "services/process/windows/session.h" +#else +#include "services/process/posix/session.h" +#endif // defined(BOOST_ASIO_WINDOWS) + +namespace ssf { +namespace services { +namespace process { + +template +class Server : public BaseService { + private: +#if defined(BOOST_ASIO_WINDOWS) + using session_impl = windows::Session; +#else + using session_impl = posix::Session; +#endif // defined(BOOST_ASIO_WINDOWS) + + using local_port_type = typename Demux::local_port_type; + + using ServerPtr = std::shared_ptr; + using SessionManager = ItemManager; + using Parameters = typename ssf::BaseService::Parameters; + using demux = typename ssf::BaseService::demux; + using fiber = typename ssf::BaseService::fiber; + using fiber_acceptor = typename ssf::BaseService::fiber_acceptor; + using endpoint = typename ssf::BaseService::endpoint; + + public: + // SSF service ID for identification in the service factory + enum { factory_id = 10 }; + + public: + Server(const Server&) = delete; + Server& operator=(const Server&) = delete; + + public: + // Create a new instance of the service + static ServerPtr Create(boost::asio::io_service& io_service, + demux& fiber_demux, Parameters parameters, + const std::string& binary_path, + const std::string& binary_args) { + if (!parameters.count("local_port") || binary_path.empty()) { + return ServerPtr(nullptr); + } else { + return std::shared_ptr(new Server( + io_service, fiber_demux, std::stoul(parameters["local_port"]), + binary_path, binary_args)); + } + } + + // Function used to register the micro service to the given factory + static void RegisterToServiceFactory( + std::shared_ptr> p_factory, + const ssf::config::ProcessService& config) { + p_factory->RegisterServiceCreator( + factory_id, + boost::bind(&Server::Create, _1, _2, _3, config.path(), config.args())); + } + + // Function used to generate create service request + static ssf::services::admin::CreateServiceRequest GetCreateRequest( + uint16_t local_port) { + ssf::services::admin::CreateServiceRequest create(factory_id); + create.add_parameter("local_port", std::to_string(local_port)); + + return create; + } + + public: + // Start the service instance + void start(boost::system::error_code& ec) override; + + // Stop the service instance + void stop(boost::system::error_code& ec) override; + + // Return the type of the service + uint32_t service_type_id() override; + + private: + Server(boost::asio::io_service& io_service, demux& fiber_demux, + const local_port_type& port, const std::string& binary_path, + const std::string& binary_args); + + private: + void StartAccept(); + void HandleAccept(const boost::system::error_code& e); + void HandleStop(); + + std::shared_ptr SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); + } + + bool CheckBinaryPath(); + + private: + fiber_acceptor fiber_acceptor_; + + private: + SessionManager session_manager_; + fiber new_connection_; + boost::system::error_code init_ec_; + local_port_type local_port_; + std::string binary_path_; + std::string binary_args_; +}; + +} // process +} // services +} // ssf + +#include "services/process/server.ipp" + +#endif // SSF_SERVICES_PROCESS_PROCESS_SERVER_H_ diff --git a/src/services/process/server.ipp b/src/services/process/server.ipp new file mode 100644 index 00000000..341393b2 --- /dev/null +++ b/src/services/process/server.ipp @@ -0,0 +1,121 @@ +#ifndef SSF_SERVICES_PROCESS_PROCESS_SERVER_IPP_ +#define SSF_SERVICES_PROCESS_PROCESS_SERVER_IPP_ + +#include +#include + +#include // NOLINT +#include // NOLINT +#include //NOLINT + +#include + +namespace ssf { +namespace services { +namespace process { + +template +Server::Server(boost::asio::io_service& io_service, demux& fiber_demux, + const local_port_type& port, + const std::string& binary_path, + const std::string& binary_args) + : ssf::BaseService::BaseService(io_service, fiber_demux), + fiber_acceptor_(io_service), + session_manager_(), + new_connection_(io_service, endpoint(fiber_demux, 0)), + local_port_(port), + binary_path_(binary_path), + binary_args_(binary_args) {} + +template +void Server::start(boost::system::error_code& ec) { + endpoint ep(this->get_demux(), local_port_); + fiber_acceptor_.bind(ep, ec); + + if (ec) { + SSF_LOG(kLogError) + << "service[process]: fiber acceptor could not bind on port " + << local_port_; + return; + } + + fiber_acceptor_.listen(boost::asio::socket_base::max_connections, ec); + if (ec) { + SSF_LOG(kLogError) << "service[process]: fiber acceptor could not listen"; + return; + } + + if (!CheckBinaryPath()) { + SSF_LOG(kLogError) << "service[process]: binary not found"; + ec.assign(::error::file_not_found, ::error::get_ssf_category()); + return; + } + + SSF_LOG(kLogInfo) << "service[process]: starting server on port " + << local_port_; + + this->StartAccept(); +} + +template +void Server::stop(boost::system::error_code& ec) { + ec.assign(boost::system::errc::success, boost::system::system_category()); + + SSF_LOG(kLogInfo) << "service[process]: stopping server"; + this->HandleStop(); +} + +template +uint32_t Server::service_type_id() { + return factory_id; +} + +template +void Server::StartAccept() { + SSF_LOG(kLogTrace) << "service[process]: accept new session"; + fiber_acceptor_.async_accept( + new_connection_, + boost::bind(&Server::HandleAccept, this->SelfFromThis(), _1)); +} + +template +void Server::HandleAccept(const boost::system::error_code& ec) { + SSF_LOG(kLogTrace) << "service[process]: HandleAccept"; + + if (!fiber_acceptor_.is_open()) { + return; + } + + if (ec) { + SSF_LOG(kLogError) << "service[process]: error accepting new connection: " + << ec.message() << " " << ec.value(); + this->StartAccept(); + } + + SSF_LOG(kLogInfo) << "service[process]: start session"; + ssf::BaseSessionPtr new_process_session = std::make_shared( + &(this->session_manager_), std::move(this->new_connection_), binary_path_, + binary_args_); + boost::system::error_code e; + this->session_manager_.start(new_process_session, e); + + this->StartAccept(); +} + +template +void Server::HandleStop() { + fiber_acceptor_.close(); + session_manager_.stop_all(); +} + +template +bool Server::CheckBinaryPath() { + std::ifstream binary_file(binary_path_); + return binary_file.good(); +} + +} // process +} // services +} // ssf + +#endif // SSF_SERVICES_PROCESS_PROCESS_SERVER_IPP_ diff --git a/src/services/process/windows/session.h b/src/services/process/windows/session.h new file mode 100644 index 00000000..ce8897e8 --- /dev/null +++ b/src/services/process/windows/session.h @@ -0,0 +1,102 @@ +#ifndef SSF_SERVICES_PROCESS_WINDOWS_SESSION_H_ +#define SSF_SERVICES_PROCESS_WINDOWS_SESSION_H_ + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include "services/socks/v4/request.h" + +#include "common/boost/fiber/stream_fiber.hpp" + +namespace ssf { +namespace services { +namespace process { +namespace windows { + +template +class Session : public ssf::BaseSession { + private: + typedef std::array StreamBuff; + + typedef boost::asio::ip::tcp::socket socket; + typedef typename boost::asio::fiber::stream_fiber< + typename Demux::socket_type>::socket fiber; + typedef boost::asio::windows::stream_handle stream_handle; + typedef ItemManager SessionManager; + + public: + Session(SessionManager* sm, fiber client, const std::string& binary_path, + const std::string& binary_args); + + public: + void start(boost::system::error_code&) override; + + void stop(boost::system::error_code&) override; + + private: + void StartForwarding(boost::system::error_code& ec); + void StartProcess(boost::system::error_code& ec); + void InitPipes(boost::system::error_code& ec); + + static void InitOutNamedPipe(const std::string& pipe_name, + HANDLE* p_read_pipe, HANDLE* p_write_pipe, + SECURITY_ATTRIBUTES* p_pipe_attributes, + DWORD pipe_size, boost::system::error_code& ec); + + static void InitInNamedPipe(const std::string& pipe_name, HANDLE* p_read_pipe, + HANDLE* p_write_pipe, + SECURITY_ATTRIBUTES* p_pipe_attributes, + DWORD pipe_size, boost::system::error_code& ec); + + std::shared_ptr SelfFromThis(); + + void StopHandler(const boost::system::error_code& ec); + + private: + boost::asio::io_service& io_service_; + SessionManager* p_session_manager_; + + fiber client_; + + std::string binary_path_; + std::string binary_args_; + + std::string out_pipe_name_; + std::string err_pipe_name_; + std::string in_pipe_name_; + + PROCESS_INFORMATION process_info_; + HANDLE data_out_; + HANDLE data_err_; + HANDLE data_in_; + HANDLE proc_out_; + HANDLE proc_err_; + HANDLE proc_in_; + + boost::asio::windows::stream_handle h_out_; + boost::asio::windows::stream_handle h_err_; + boost::asio::windows::stream_handle h_in_; + + StreamBuff upstream_; + StreamBuff downstream_out_; + StreamBuff downstream_err_; +}; + +} // windows +} // process +} // services +} // ssf + +#include "services/process/windows/session.ipp" + +#endif // SSF_SERVICES_PROCESS_WINDOWS_SESSION_H_ \ No newline at end of file diff --git a/src/services/process/windows/session.ipp b/src/services/process/windows/session.ipp new file mode 100644 index 00000000..b9d50bc4 --- /dev/null +++ b/src/services/process/windows/session.ipp @@ -0,0 +1,342 @@ +#ifndef SSF_SERVICES_PROCESS_WINDOWS_SESSION_IPP_ +#define SSF_SERVICES_PROCESS_WINDOWS_SESSION_IPP_ + +#include + +#include + +#include +#include +#include + +#include "common/error/error.h" + +namespace ssf { +namespace services { +namespace process { +namespace windows { + +template +Session::Session(SessionManager* p_session_manager, fiber client, + const std::string& binary_path, + const std::string& binary_args) + : ssf::BaseSession(), + io_service_(client.get_io_service()), + p_session_manager_(p_session_manager), + client_(std::move(client)), + binary_path_(binary_path), + binary_args_(binary_args), + out_pipe_name_("\\\\.\\pipe\\ssf_out_pipe_"), + err_pipe_name_("\\\\.\\pipe\\ssf_err_pipe_"), + in_pipe_name_("\\\\.\\pipe\\ssf_in_pipe_"), + data_in_(INVALID_HANDLE_VALUE), + data_out_(INVALID_HANDLE_VALUE), + data_err_(INVALID_HANDLE_VALUE), + proc_in_(INVALID_HANDLE_VALUE), + proc_out_(INVALID_HANDLE_VALUE), + proc_err_(INVALID_HANDLE_VALUE), + h_out_(client_.get_io_service()), + h_err_(client_.get_io_service()), + h_in_(client_.get_io_service()) { + memset(&process_info_, 0, sizeof(PROCESS_INFORMATION)); + process_info_.hProcess = INVALID_HANDLE_VALUE; + process_info_.hThread = INVALID_HANDLE_VALUE; +} + +template +void Session::start(boost::system::error_code& ec) { + SSF_LOG(kLogInfo) << "session[process]: start"; + + InitPipes(ec); + if (ec) { + SSF_LOG(kLogError) << "session[process]: pipes initialization failed"; + stop(ec); + return; + } + + StartProcess(ec); + if (ec) { + SSF_LOG(kLogError) << "session[process]: start process failed"; + stop(ec); + return; + } + + StartForwarding(ec); + if (ec) { + SSF_LOG(kLogError) + << "session[process]: forwarding data from process to client failed"; + stop(ec); + return; + } +} + +template +void Session::stop(boost::system::error_code& ec) { + SSF_LOG(kLogInfo) << "session[process]: stop"; + + if (process_info_.hProcess != INVALID_HANDLE_VALUE) { + ::TerminateProcess(process_info_.hProcess, 0); + } + + if (process_info_.hThread != INVALID_HANDLE_VALUE) { + ::CloseHandle(process_info_.hThread); + } + if (process_info_.hProcess != INVALID_HANDLE_VALUE) { + ::CloseHandle(process_info_.hProcess); + } + + if (proc_in_ == INVALID_HANDLE_VALUE) { + ::CloseHandle(proc_in_); + } + if (proc_out_ == INVALID_HANDLE_VALUE) { + ::CloseHandle(proc_out_); + } + if (proc_err_ == INVALID_HANDLE_VALUE) { + ::CloseHandle(proc_err_); + } + if (data_in_ == INVALID_HANDLE_VALUE) { + ::CloseHandle(data_in_); + } + if (data_out_ == INVALID_HANDLE_VALUE) { + ::CloseHandle(data_out_); + } + if (data_err_ == INVALID_HANDLE_VALUE) { + ::CloseHandle(data_err_); + } + + h_out_.close(ec); + h_err_.close(ec); + h_in_.close(ec); + + client_.close(); + + if (ec) { + SSF_LOG(kLogError) << "session[process]: stop error " << ec.message(); + } +} + +template +void Session::StopHandler(const boost::system::error_code& ec) { + boost::system::error_code e; + p_session_manager_->stop(this->SelfFromThis(), e); +} + +template +std::shared_ptr> Session::SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); +} + +template +void Session::StartForwarding(boost::system::error_code& ec) { + h_out_.assign(data_out_, ec); + if (ec) { + SSF_LOG(kLogError) + << "session[process]: could not initialize out stream handle"; + return; + } + data_out_ = INVALID_HANDLE_VALUE; + h_err_.assign(data_err_, ec); + if (ec) { + SSF_LOG(kLogError) + << "session[process]: could not initialize err stream handle"; + return; + } + data_err_ = INVALID_HANDLE_VALUE; + h_in_.assign(data_in_, ec); + if (ec) { + SSF_LOG(kLogError) + << "session[process]: could not initialize in stream handle"; + return; + } + data_in_ = INVALID_HANDLE_VALUE; + + // pipe process stdout to socket output + AsyncEstablishHDLink( + ReadFrom(h_out_), WriteTo(client_), boost::asio::buffer(downstream_out_), + boost::bind(&Session::StopHandler, this->SelfFromThis(), _1)); + // pipe process stderr to socket output + AsyncEstablishHDLink( + ReadFrom(h_err_), WriteTo(client_), boost::asio::buffer(downstream_err_), + boost::bind(&Session::StopHandler, this->SelfFromThis(), _1)); + // pipe socket input to process stdin + AsyncEstablishHDLink( + ReadFrom(client_), WriteTo(h_in_), boost::asio::buffer(upstream_), + boost::bind(&Session::StopHandler, this->SelfFromThis(), _1)); +} + +template +void Session::StartProcess(boost::system::error_code& ec) { + memset(&process_info_, 0, sizeof(PROCESS_INFORMATION)); + STARTUPINFO startup_info; + + ZeroMemory(&startup_info, sizeof(STARTUPINFO)); + startup_info.cb = sizeof(STARTUPINFO); + startup_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + startup_info.wShowWindow = SW_HIDE; + startup_info.hStdOutput = proc_out_; + startup_info.hStdError = proc_err_; + startup_info.hStdInput = proc_in_; + + // chdir to home dir at process creation + char home_dir[MAX_PATH]; + bool home_dir_set = SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_PROFILE, NULL, + SHGFP_TYPE_CURRENT, home_dir)); + + std::string command_line = binary_path_ + " " + binary_args_; + + if (!::CreateProcessA(NULL, const_cast(command_line.c_str()), NULL, + NULL, TRUE, CREATE_NEW_CONSOLE, NULL, + (home_dir_set ? home_dir : NULL), &startup_info, + &process_info_)) { + SSF_LOG(kLogError) << "session[process]: create process <" << command_line + << "> failed"; + ec.assign(::error::process_not_created, ::error::get_ssf_category()); + } + + ::CloseHandle(proc_out_); + ::CloseHandle(proc_err_); + ::CloseHandle(proc_in_); + + proc_out_ = INVALID_HANDLE_VALUE; + proc_err_ = INVALID_HANDLE_VALUE; + proc_in_ = INVALID_HANDLE_VALUE; +} + +template +void Session::InitPipes(boost::system::error_code& ec) { + auto local_fib_ep = client_.remote_endpoint(ec); + if (ec) { + SSF_LOG(kLogError) + << "session[process]: could not get fiber local endpoint"; + return; + } + + std::string remote_port(std::to_string(local_fib_ep.port())); + out_pipe_name_ += remote_port; + err_pipe_name_ += remote_port; + in_pipe_name_ += remote_port; + + DWORD pipe_size = 4096; + SECURITY_ATTRIBUTES sec_attr; + sec_attr.nLength = sizeof(SECURITY_ATTRIBUTES); + sec_attr.bInheritHandle = TRUE; + sec_attr.lpSecurityDescriptor = NULL; + + InitOutNamedPipe(out_pipe_name_, &data_out_, &proc_out_, &sec_attr, pipe_size, + ec); + if (ec) { + SSF_LOG(kLogError) << "session[process]: init out pipe failed"; + return; + } + + InitOutNamedPipe(err_pipe_name_, &data_err_, &proc_err_, &sec_attr, pipe_size, + ec); + if (ec) { + SSF_LOG(kLogError) << "session[process]: init err pipe failed"; + return; + } + + InitInNamedPipe(in_pipe_name_, &proc_in_, &data_in_, &sec_attr, pipe_size, + ec); + if (ec) { + SSF_LOG(kLogError) << "session[process]: init in pipe failed"; + return; + } +} + +template +void Session::InitOutNamedPipe(const std::string& pipe_name, + HANDLE* p_read_pipe, HANDLE* p_write_pipe, + SECURITY_ATTRIBUTES* p_pipe_attributes, + DWORD pipe_size, + boost::system::error_code& ec) { + HANDLE read_pipe_tmp = ::CreateNamedPipeA( + pipe_name.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, pipe_size, pipe_size, + 0, p_pipe_attributes); + + if (read_pipe_tmp == INVALID_HANDLE_VALUE) { + SSF_LOG(kLogError) << "session[process]: create read side of named pipe <" + << pipe_name << "> failed"; + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); + goto cleanup; + } + + *p_write_pipe = ::CreateFileA( + pipe_name.c_str(), FILE_WRITE_DATA | SYNCHRONIZE, 0, p_pipe_attributes, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); + + if (*p_write_pipe == INVALID_HANDLE_VALUE) { + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); + SSF_LOG(kLogError) << "session[process]: create write side of named pipe <" + << pipe_name << "> failed"; + goto cleanup; + } + + if (!::DuplicateHandle(GetCurrentProcess(), read_pipe_tmp, + GetCurrentProcess(), p_read_pipe, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + SSF_LOG(kLogError) + << "session[process]: duplicate read side of named pipe <" << pipe_name + << "> failed"; + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); + goto cleanup; + } + +cleanup: + if (read_pipe_tmp != INVALID_HANDLE_VALUE) { + ::CloseHandle(read_pipe_tmp); + } +} + +template +void Session::InitInNamedPipe(const std::string& pipe_name, + HANDLE* p_read_pipe, HANDLE* p_write_pipe, + SECURITY_ATTRIBUTES* p_pipe_attributes, + DWORD pipe_size, + boost::system::error_code& ec) { + HANDLE write_pipe_tmp = ::CreateNamedPipeA( + pipe_name.c_str(), PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, pipe_size, pipe_size, + 0, p_pipe_attributes); + + if (write_pipe_tmp == INVALID_HANDLE_VALUE) { + SSF_LOG(kLogError) << "session[process]: create write side of named pipe <" + << pipe_name << "> failed"; + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); + goto cleanup; + } + + *p_read_pipe = ::CreateFileA(pipe_name.c_str(), FILE_READ_DATA | SYNCHRONIZE, + 0, p_pipe_attributes, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); + + if (*p_read_pipe == INVALID_HANDLE_VALUE) { + SSF_LOG(kLogError) << "session[process]: create read side of named pipe <" + << pipe_name << "> failed"; + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); + goto cleanup; + } + + if (!::DuplicateHandle(::GetCurrentProcess(), write_pipe_tmp, + ::GetCurrentProcess(), p_write_pipe, 0, FALSE, + DUPLICATE_SAME_ACCESS)) { + SSF_LOG(kLogError) + << "session[process]: duplicate write side of named pipe <" << pipe_name + << "> failed"; + ec.assign(::error::broken_pipe, ::error::get_ssf_category()); + goto cleanup; + } + +cleanup: + if (write_pipe_tmp != INVALID_HANDLE_VALUE) { + ::CloseHandle(write_pipe_tmp); + } +} + +} // windows +} // process +} // services +} // ssf + +#endif // SSF_SERVICES_PROCESS_WINDOWS_SESSION_IPP_ \ No newline at end of file diff --git a/src/services/sockets_to_fibers/sockets_to_fibers.h b/src/services/sockets_to_fibers/sockets_to_fibers.h index 8907653d..f567c0d7 100644 --- a/src/services/sockets_to_fibers/sockets_to_fibers.h +++ b/src/services/sockets_to_fibers/sockets_to_fibers.h @@ -6,9 +6,9 @@ #include "common/boost/fiber/stream_fiber.hpp" #include "common/boost/fiber/basic_fiber_demux.hpp" -#include "common/network/socket_link.h" -#include "common/network/manager.h" -#include "common/network/base_session.h" +#include +#include +#include #include "services/base_service.h" @@ -16,13 +16,13 @@ #include "services/admin/requests/create_service_request.h" -namespace ssf { -namespace services { +namespace ssf { +namespace services { namespace sockets_to_fibers { template class SocketsToFibers : public BaseService { -public: + public: typedef typename Demux::remote_port_type remote_port_type; typedef std::shared_ptr SocketsToFibersPtr; @@ -35,12 +35,10 @@ class SocketsToFibers : public BaseService { typedef boost::asio::ip::tcp::socket socket; typedef boost::asio::ip::tcp::acceptor socket_acceptor; -public: + public: static SocketsToFibersPtr Create(boost::asio::io_service& io_service, - demux& fiber_demux, - Parameters parameters) { - if (!parameters.count("local_port") || - !parameters.count("remote_port")) { + demux& fiber_demux, Parameters parameters) { + if (!parameters.count("local_port") || !parameters.count("remote_port")) { return SocketsToFibersPtr(nullptr); } else { return std::shared_ptr( @@ -50,14 +48,11 @@ class SocketsToFibers : public BaseService { } } - enum { - factory_id = 4 - }; + enum { factory_id = 4 }; static void RegisterToServiceFactory( - std::shared_ptr> p_factory) { - p_factory->RegisterServiceCreator(factory_id, - &SocketsToFibers::Create); + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, &SocketsToFibers::Create); } virtual void start(boost::system::error_code& ec); @@ -65,7 +60,7 @@ class SocketsToFibers : public BaseService { virtual uint32_t service_type_id(); static ssf::services::admin::CreateServiceRequest GetCreateRequest( - uint16_t local_port, remote_port_type remote_port) { + uint16_t local_port, remote_port_type remote_port) { ssf::services::admin::CreateServiceRequest create(factory_id); create.add_parameter("local_port", std::to_string(local_port)); create.add_parameter("remote_port", std::to_string(remote_port)); @@ -73,11 +68,11 @@ class SocketsToFibers : public BaseService { return create; } -private: - SocketsToFibers(boost::asio::io_service& io_service, demux& fiber_demux, - uint16_t local, remote_port_type remote_port); + private: + SocketsToFibers(boost::asio::io_service& io_service, demux& fiber_demux, + uint16_t local, remote_port_type remote_port); -private: + private: void StartAcceptSockets(); void SocketAcceptHandler(const boost::system::error_code& ec); @@ -91,8 +86,7 @@ class SocketsToFibers : public BaseService { } std::shared_ptr SelfFromThis() { - return std::static_pointer_cast( - this->shared_from_this()); + return std::static_pointer_cast(this->shared_from_this()); } uint16_t local_port_; diff --git a/src/services/sockets_to_fibers/sockets_to_fibers.ipp b/src/services/sockets_to_fibers/sockets_to_fibers.ipp index ba1fff33..a2b9f375 100644 --- a/src/services/sockets_to_fibers/sockets_to_fibers.ipp +++ b/src/services/sockets_to_fibers/sockets_to_fibers.ipp @@ -1,11 +1,12 @@ #ifndef SSF_SERVICES_SOCKETS_TO_FIBERS_SOCKETS_TO_FIBERS_IPP_ #define SSF_SERVICES_SOCKETS_TO_FIBERS_SOCKETS_TO_FIBERS_IPP_ -#include +#include +#include -#include "common/network/session_forwarder.h" - -namespace ssf { namespace services { namespace sockets_to_fibers { +namespace ssf { +namespace services { +namespace sockets_to_fibers { template SocketsToFibers::SocketsToFibers(boost::asio::io_service& io_service, @@ -20,39 +21,52 @@ SocketsToFibers::SocketsToFibers(boost::asio::io_service& io_service, template void SocketsToFibers::start(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) - << "service sockets to fibers: starting relay on local port tcp " + SSF_LOG(kLogInfo) + << "service[sockets to fibers]: starting relay on local port tcp " << local_port_; - boost::system::error_code close_ec; - // Accept on all local interfaces - boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), - local_port_); + boost::asio::ip::tcp::resolver resolver(socket_.get_io_service()); + boost::asio::ip::tcp::resolver::query query( + boost::asio::ip::tcp::v4(), "localhost", std::to_string(local_port_)); + auto ep_it = resolver.resolve(query, ec); - socket_acceptor_.open(endpoint.protocol(), ec); if (ec) { - socket_acceptor_.close(close_ec); - return; + SSF_LOG(kLogError) + << "service[sockets to fibers]: could not resolve query "; + return; } - boost::asio::socket_base::reuse_address option(true); + boost::asio::ip::tcp::endpoint endpoint(*ep_it); - socket_acceptor_.bind(endpoint, ec); + boost::system::error_code close_ec; + socket_acceptor_.open(endpoint.protocol(), ec); if (ec) { - socket_acceptor_.close(close_ec); - return ; + socket_acceptor_.close(close_ec); + return; } + boost::asio::socket_base::reuse_address option(true); socket_acceptor_.set_option(option, ec); if (ec) { - socket_acceptor_.close(close_ec); - return ; + SSF_LOG(kLogError) + << "service[sockets to fibers]: could not set reuse address option"; + socket_acceptor_.close(close_ec); + return; + } + + socket_acceptor_.bind(endpoint, ec); + if (ec) { + SSF_LOG(kLogError) << "service[sockets to fibers]: could not bind acceptor"; + socket_acceptor_.close(close_ec); + return; } socket_acceptor_.listen(boost::asio::socket_base::max_connections, ec); if (ec) { - socket_acceptor_.close(close_ec); - return ; + SSF_LOG(kLogError) << "service[sockets to fibers]: could not listen"; + socket_acceptor_.close(close_ec); + return; } this->StartAcceptSockets(); @@ -60,11 +74,10 @@ void SocketsToFibers::start(boost::system::error_code& ec) { template void SocketsToFibers::stop(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) << "service sockets to fibers: stopping"; + SSF_LOG(kLogInfo) << "service[sockets to fibers]: stopping"; socket_acceptor_.close(ec); if (ec) { - BOOST_LOG_TRIVIAL(debug) << "service sockets to fibers: " << ec.message() - << std::endl; + SSF_LOG(kLogDebug) << "service[sockets to fibers]: " << ec.message(); } manager_.stop_all(); } @@ -76,8 +89,7 @@ uint32_t SocketsToFibers::service_type_id() { template void SocketsToFibers::StartAcceptSockets() { - BOOST_LOG_TRIVIAL(trace) - << "service sockets to fibers: accepting new clients"; + SSF_LOG(kLogTrace) << "service[sockets to fibers]: accepting new clients"; if (!socket_acceptor_.is_open()) { return; @@ -91,7 +103,7 @@ void SocketsToFibers::StartAcceptSockets() { template void SocketsToFibers::SocketAcceptHandler( const boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(trace) << "service sockets to fibers: accept handler"; + SSF_LOG(kLogTrace) << "service[sockets to fibers]: accept handler"; if (!socket_acceptor_.is_open()) { return; @@ -100,15 +112,14 @@ void SocketsToFibers::SocketAcceptHandler( if (!ec) { endpoint ep(this->get_demux(), remote_port_); fiber_.async_connect( - ep, - Then(&SocketsToFibers::FiberConnectHandler, this->SelfFromThis())); + ep, Then(&SocketsToFibers::FiberConnectHandler, this->SelfFromThis())); } } template void SocketsToFibers::FiberConnectHandler( const boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(trace) << "service sockets to fibers: connect handler"; + SSF_LOG(kLogTrace) << "service[sockets to fibers]: connect handler"; if (!ec) { auto session = SessionForwarder::create( diff --git a/src/services/socks/socks_server.h b/src/services/socks/socks_server.h index dbc68708..387d02cb 100644 --- a/src/services/socks/socks_server.h +++ b/src/services/socks/socks_server.h @@ -7,8 +7,8 @@ #include #include // NOLINT -#include "common/network/manager.h" -#include "common/network/base_session.h" +#include +#include #include "services/base_service.h" @@ -22,7 +22,7 @@ namespace ssf { namespace services { namespace socks { -//----------------------------------------------------------------------------- + template class SocksServer : public BaseService { private: @@ -37,8 +37,8 @@ class SocksServer : public BaseService { typedef typename ssf::BaseService::endpoint endpoint; public: - SocksServer(const SocksServer&) = delete; - SocksServer& operator=(const SocksServer&) = delete; + SocksServer(const SocksServer&) = delete; + SocksServer& operator=(const SocksServer&) = delete; /// Create a new instance of the service static SocksServerPtr create(boost::asio::io_service& io_service, @@ -88,8 +88,8 @@ class SocksServer : public BaseService { void HandleStop(); template - auto Then(Handler handler, This me) - -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { + auto Then(Handler handler, + This me) -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { return boost::bind(handler, me->SelfFromThis(), _1); } diff --git a/src/services/socks/socks_server.ipp b/src/services/socks/socks_server.ipp index 2ee02c4c..1ad2588d 100644 --- a/src/services/socks/socks_server.ipp +++ b/src/services/socks/socks_server.ipp @@ -3,17 +3,20 @@ #include -#include // NOLINT -#include // NOLINT +#include // NOLINT +#include // NOLINT #include //NOLINT -#include + +#include #include "services/socks/socks_version.h" // NOLINT -#include "services/socks/v4/session.h" // NOLINT -#include "services/socks/v5/session.h" // NOLINT +#include "services/socks/v4/session.h" // NOLINT +#include "services/socks/v5/session.h" // NOLINT + +namespace ssf { +namespace services { +namespace socks { -namespace ssf { namespace services { namespace socks { -//----------------------------------------------------------------------------- template SocksServer::SocksServer(boost::asio::io_service& io_service, demux& fiber_demux, const local_port_type& port) @@ -22,18 +25,17 @@ SocksServer::SocksServer(boost::asio::io_service& io_service, session_manager_(), new_connection_(io_service, endpoint(fiber_demux, 0)), local_port_(port) { - // The init_ec will be returned when start() is called - //fiber_acceptor_.open(); + // fiber_acceptor_.open(); endpoint ep(this->get_demux(), port); fiber_acceptor_.bind(ep, init_ec_); fiber_acceptor_.listen(boost::asio::socket_base::max_connections, init_ec_); } -//----------------------------------------------------------------------------- template void SocksServer::start(boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(info) << "Starting socks server on port " << local_port_; + SSF_LOG(kLogInfo) << "service[socks]: starting server on port " + << local_port_; ec = init_ec_; if (!init_ec_) { @@ -41,38 +43,36 @@ void SocksServer::start(boost::system::error_code& ec) { } } -//----------------------------------------------------------------------------- template void SocksServer::stop(boost::system::error_code& ec) { ec.assign(boost::system::errc::success, boost::system::system_category()); - BOOST_LOG_TRIVIAL(info) << "#### Stopping server socks ####"; + SSF_LOG(kLogInfo) << "service[socks]: stopping server"; this->HandleStop(); } -//----------------------------------------------------------------------------- template -uint32_t SocksServer::service_type_id() { return factory_id; } +uint32_t SocksServer::service_type_id() { + return factory_id; +} -//----------------------------------------------------------------------------- template void SocksServer::StartAccept() { fiber_acceptor_.async_accept( - new_connection_, Then(&SocksServer::HandleAccept, this->SelfFromThis())); + new_connection_, Then(&SocksServer::HandleAccept, this->SelfFromThis())); } -//----------------------------------------------------------------------------- template void SocksServer::HandleAccept(const boost::system::error_code& ec) { - BOOST_LOG_TRIVIAL(trace) << "HandleAccept"; + SSF_LOG(kLogTrace) << "service[socks]: HandleAccept"; if (!fiber_acceptor_.is_open()) { return; } if (ec) { - fprintf(stderr, "[!] Error accepting new connection: %s %d", - ec.message().c_str(), ec.value()); + SSF_LOG(kLogError) << "service[socks]: error accepting new connection: " + << ec.message() << " " << ec.value(); this->StartAccept(); } @@ -82,20 +82,20 @@ void SocksServer::HandleAccept(const boost::system::error_code& ec) { auto start_handler = [this, self, p_version](boost::system::error_code ec, std::size_t) { if (ec) { - fprintf(stderr, "[!] Error reading protocol version: %s %d", - ec.message().c_str(), ec.value()); + SSF_LOG(kLogError) << "service[socks]: error reading protocol version: " + << ec.message() << " " << ec.value(); fiber fib = std::move(this->new_connection_); this->StartAccept(); } else if (p_version->Number() == 4) { - BOOST_LOG_TRIVIAL(trace) << "Version accepted: v4"; + SSF_LOG(kLogTrace) << "service[socks]: version accepted: v4"; ssf::BaseSessionPtr new_socks_session = - std::make_shared>( + std::make_shared >( &(this->session_manager_), std::move(this->new_connection_)); boost::system::error_code e; this->session_manager_.start(new_socks_session, e); this->StartAccept(); } else if (p_version->Number() == 5) { - BOOST_LOG_TRIVIAL(trace) << "Version accepted: v5"; + SSF_LOG(kLogTrace) << "service[socks]: version accepted: v5"; ssf::BaseSessionPtr new_socks_session = std::make_shared >( &(this->session_manager_), std::move(this->new_connection_)); @@ -103,7 +103,8 @@ void SocksServer::HandleAccept(const boost::system::error_code& ec) { this->session_manager_.start(new_socks_session, e); this->StartAccept(); } else { - fprintf(stderr, "Protocol not supported yet: %X\n", p_version->Number()); + SSF_LOG(kLogError) << "service[socks]: protocol not supported yet: " + << p_version->Number(); this->new_connection_.close(); fiber fib = std::move(this->new_connection_); this->StartAccept(); @@ -114,7 +115,6 @@ void SocksServer::HandleAccept(const boost::system::error_code& ec) { boost::asio::async_read(new_connection_, p_version->Buffer(), start_handler); } -//----------------------------------------------------------------------------- template void SocksServer::HandleStop() { fiber_acceptor_.close(); diff --git a/src/services/socks/socks_version.h b/src/services/socks/socks_version.h index 28c46522..ba6c2e02 100644 --- a/src/services/socks/socks_version.h +++ b/src/services/socks/socks_version.h @@ -1,12 +1,13 @@ -#ifndef SSF_SOCKS_VERSION_H_ -#define SSF_SOCKS_VERSION_H_ +#ifndef SSF_SERVICES_SOCKS_VERSION_H_ +#define SSF_SERVICES_SOCKS_VERSION_H_ #include #include #include -namespace ssf { namespace services { namespace socks { -//----------------------------------------------------------------------------- +namespace ssf { +namespace services { +namespace socks { class Version { public: @@ -14,10 +15,7 @@ class Version { std::array Buffer() { std::array buf = { - { - boost::asio::buffer(&version_number_, 1) - } - }; + {boost::asio::buffer(&version_number_, 1)}}; return buf; } @@ -29,5 +27,4 @@ class Version { } // services } // ssf - -#endif // SSF_SOCKS_VERSION_H_ +#endif // SSF_SERVICES_SOCKS_VERSION_H_ diff --git a/src/services/socks/v4/reply.cpp b/src/services/socks/v4/reply.cpp index 1f35e789..d32687af 100644 --- a/src/services/socks/v4/reply.cpp +++ b/src/services/socks/v4/reply.cpp @@ -2,35 +2,27 @@ #include // NOLINT -namespace ssf { namespace socks { namespace v4 { -//----------------------------------------------------------------------------- +namespace ssf { +namespace socks { +namespace v4 { + Reply::Reply(const boost::system::error_code& err, const boost::asio::ip::tcp::endpoint& ep) - : null_byte_(0), - status_(ErrorCodeToStatus(err)) { + : null_byte_(0), status_(ErrorCodeToStatus(err)) { uint16_t port = ep.port(); port_high_byte_ = (port >> 8) & 0xff; port_low_byte_ = port & 0xff; address_ = ep.address().to_v4().to_bytes(); } - -//----------------------------------------------------------------------------- std::array Reply::Buffer() const { std::array buf = { - { - boost::asio::buffer(&null_byte_, 1), - boost::asio::buffer(&status_, 1), - boost::asio::buffer(&port_high_byte_, 1), - boost::asio::buffer(&port_low_byte_, 1), - boost::asio::buffer(address_) - } - }; + {boost::asio::buffer(&null_byte_, 1), boost::asio::buffer(&status_, 1), + boost::asio::buffer(&port_high_byte_, 1), + boost::asio::buffer(&port_low_byte_, 1), boost::asio::buffer(address_)}}; return buf; } - -//----------------------------------------------------------------------------- Reply::Status Reply::ErrorCodeToStatus(const boost::system::error_code& err) { Status s = kFailed; @@ -42,4 +34,4 @@ Reply::Status Reply::ErrorCodeToStatus(const boost::system::error_code& err) { } } // v4 } // socks -} // ssf \ No newline at end of file +} // ssf diff --git a/src/services/socks/v4/reply.h b/src/services/socks/v4/reply.h index 02e8ea4a..346548c4 100644 --- a/src/services/socks/v4/reply.h +++ b/src/services/socks/v4/reply.h @@ -1,5 +1,5 @@ -#ifndef SSF_V4_REPLY_H_ -#define SSF_V4_REPLY_H_ +#ifndef SSF_SERVICES_SOCKS_V4_REPLY_H_ +#define SSF_SERVICES_SOCKS_V4_REPLY_H_ #include #include @@ -13,12 +13,13 @@ #include -namespace ssf { namespace socks { namespace v4 { -//----------------------------------------------------------------------------- +namespace ssf { +namespace socks { +namespace v4 { class Reply { public: - // Status constants + // Status constants enum Status { kGranted = 0x5a, kFailed = 0x5b, @@ -43,18 +44,13 @@ class Reply { boost::asio::ip::address_v4::bytes_type address_; }; - -//----------------------------------------------------------------------------- -// S E N D R E P L Y -//----------------------------------------------------------------------------- -template +template void AsyncSendReply(StreamSocket& c, const Reply& r, VerifyHandler handler) { boost::asio::async_write(c, r.Buffer(), handler); } -//----------------------------------------------------------------------------- } // v4 } // socks } // ssf -#endif // SSF_V4_REPLY_H_ +#endif // SSF_SERVICES_SOCKS_V4_REPLY_H_ diff --git a/src/services/socks/v4/request.cpp b/src/services/socks/v4/request.cpp index a5ca0049..8beed6d2 100644 --- a/src/services/socks/v4/request.cpp +++ b/src/services/socks/v4/request.cpp @@ -1,25 +1,22 @@ #include "services/socks/v4/request.h" -#include // NOLINT +#include // NOLINT #include // NOLINT -#include // NOLINT +#include // NOLINT #include // NOLINT +namespace ssf { +namespace socks { +namespace v4 { -namespace ssf { namespace socks { namespace v4 { -//----------------------------------------------------------------------------- -uint8_t Request::Command() const { - return command_; -} - +uint8_t Request::Command() const { return command_; } -//----------------------------------------------------------------------------- boost::asio::ip::tcp::endpoint Request::Endpoint() const { uint16_t port = port_high_byte_; port = (port << 8) & 0xff00; port = port | port_low_byte_; - + if (is_4a_version()) { return boost::asio::ip::tcp::endpoint(); } else { @@ -28,29 +25,17 @@ boost::asio::ip::tcp::endpoint Request::Endpoint() const { } } - -//----------------------------------------------------------------------------- std::array Request::Buffer() { std::array buf = { - { - boost::asio::buffer(&command_, 1), - boost::asio::buffer(&port_high_byte_, 1), - boost::asio::buffer(&port_low_byte_, 1), - boost::asio::buffer(address_) - } - }; + {boost::asio::buffer(&command_, 1), + boost::asio::buffer(&port_high_byte_, 1), + boost::asio::buffer(&port_low_byte_, 1), boost::asio::buffer(address_)}}; return buf; } +std::string Request::Name() const { return name_; } -//----------------------------------------------------------------------------- -std::string Request::Name() const { - return name_; -} - -std::string Request::Domain() const { - return domain_; -} +std::string Request::Domain() const { return domain_; } uint16_t Request::Port() const { uint16_t port = port_high_byte_; @@ -60,22 +45,15 @@ uint16_t Request::Port() const { return port; } -//----------------------------------------------------------------------------- -void Request::SetName(const std::string& name) { - name_ = name; -} +void Request::SetName(const std::string& name) { name_ = name; } -void Request::SetDomain(const std::string& domain) { - domain_ = domain; -} +void Request::SetDomain(const std::string& domain) { domain_ = domain; } -//----------------------------------------------------------------------------- bool Request::is_4a_version() const { - return (address_[0] == 0) && (address_[1] == 0) && - (address_[2] == 0) && (address_[3] != 0); + return (address_[0] == 0) && (address_[1] == 0) && (address_[2] == 0) && + (address_[3] != 0); } } // v4 } // socks } // ssf - diff --git a/src/services/socks/v4/request.h b/src/services/socks/v4/request.h index 1bdf9a79..f7e17931 100644 --- a/src/services/socks/v4/request.h +++ b/src/services/socks/v4/request.h @@ -1,30 +1,29 @@ -#ifndef SSF_V4_REQUEST_H_ -#define SSF_V4_REQUEST_H_ +#ifndef SSF_SERVICES_SOCKS_V4_REQUEST_H_ +#define SSF_SERVICES_SOCKS_V4_REQUEST_H_ #include #include -#include // NOLINT +#include // NOLINT #include // NOLINT #include #include #include #include // NOLINT -#include // NOLINT -#include // NOLINT +#include // NOLINT +#include // NOLINT #include #include // NOLINT -#include // NOLINT +#include // NOLINT + +namespace ssf { +namespace socks { +namespace v4 { -namespace ssf { namespace socks { namespace v4 { -//----------------------------------------------------------------------------- class Request { public: - enum CommandId { - kConnect = 0x01, - kBind = 0x02 - }; + enum CommandId { kConnect = 0x01, kBind = 0x02 }; public: uint8_t Command() const; @@ -51,17 +50,15 @@ class Request { std::string domain_; }; - -//----------------------------------------------------------------------------- -// R E A D R E Q U E S T -//----------------------------------------------------------------------------- - -template +template class ReadRequestCoro : public boost::asio::coroutine { -public: + public: ReadRequestCoro(StreamSocket& c, Request* p_r, VerifyHandler h) - : c_(c), r_(*p_r), handler_(h), total_length_(0), - p_stream_(new boost::asio::streambuf){ } + : c_(c), + r_(*p_r), + handler_(h), + total_length_(0), + p_stream_(new boost::asio::streambuf) {} #include // NOLINT void operator()(const boost::system::error_code& ec, std::size_t length) { @@ -70,31 +67,33 @@ class ReadRequestCoro : public boost::asio::coroutine { std::string domain; if (!ec) reenter(this) { - // Read Request fixed size buffer - yield boost::asio::async_read(c_, r_.Buffer(), std::move(*this)); - total_length_ += length; - - // Read Request variable size name (from now, until '\0') - yield boost::asio::async_read_until(c_, - *p_stream_, '\0', std::move(*this)); - total_length_ += length; - - // Set the name to complete the request - r_.SetName(boost::asio::buffer_cast(p_stream_->data())); - p_stream_->consume(length); - - if (r_.is_4a_version()) { - // Read Request variable size domain (from now, until '\0') - yield boost::asio::async_read_until(c_, - *p_stream_, '\0', std::move(*this)); + // Read Request fixed size buffer + yield boost::asio::async_read(c_, r_.Buffer(), std::move(*this)); + total_length_ += length; + + // Read Request variable size name (from now, until '\0') + yield boost::asio::async_read_until(c_, *p_stream_, '\0', + std::move(*this)); total_length_ += length; // Set the name to complete the request - r_.SetDomain(boost::asio::buffer_cast(p_stream_->data())); - } + r_.SetName(boost::asio::buffer_cast(p_stream_->data())); + p_stream_->consume(length); - boost::get<0>(handler_)(ec, total_length_); - } else { + if (r_.is_4a_version()) { + // Read Request variable size domain (from now, until '\0') + yield boost::asio::async_read_until(c_, *p_stream_, '\0', + std::move(*this)); + total_length_ += length; + + // Set the name to complete the request + r_.SetDomain( + boost::asio::buffer_cast(p_stream_->data())); + } + + boost::get<0>(handler_)(ec, total_length_); + } + else { boost::get<0>(handler_)(ec, total_length_); } } @@ -108,21 +107,19 @@ class ReadRequestCoro : public boost::asio::coroutine { std::shared_ptr p_stream_; }; - -template +template void AsyncReadRequest(StreamSocket& c, Request* p_r, VerifyHandler handler) { - ReadRequestCoro, StreamSocket> - RequestReader(c, p_r, boost::make_tuple(handler)); + ReadRequestCoro, StreamSocket> RequestReader( + c, p_r, boost::make_tuple(handler)); RequestReader(boost::system::error_code(), 0); } -//----------------------------------------------------------------------------- } // v4 } // socks void print(const socks::v4::Request&); -} // ssf +} // ssf -#endif // SSF_V4_REQUEST_H_ +#endif // SSF_SERVICES_SOCKS_V4_REQUEST_H_ diff --git a/src/services/socks/v4/session.h b/src/services/socks/v4/session.h index 2ed95900..7e6d1705 100644 --- a/src/services/socks/v4/session.h +++ b/src/services/socks/v4/session.h @@ -1,5 +1,5 @@ -#ifndef SRC_SERVICES_SOCKS_V4_SESSION_H_ -#define SRC_SERVICES_SOCKS_V4_SESSION_H_ +#ifndef SSF_SERVICES_SOCKS_V4_SESSION_H_ +#define SSF_SERVICES_SOCKS_V4_SESSION_H_ #include @@ -7,20 +7,22 @@ #include #include // NOLINT -#include "common/network/base_session.h" // NOLINT -#include "common/network/socket_link.h" // NOLINT -#include "common/network/manager.h" -#include "common/network/base_session.h" +#include // NOLINT +#include // NOLINT +#include +#include #include "services/socks/v4/request.h" // NOLINT #include "common/boost/fiber/stream_fiber.hpp" -namespace ssf { namespace socks { namespace v4 { +namespace ssf { +namespace socks { +namespace v4 { template class Session : public ssf::BaseSession { -private: + private: typedef std::array StreamBuff; typedef boost::asio::ip::tcp::socket socket; @@ -30,13 +32,12 @@ class Session : public ssf::BaseSession { typedef ItemManager SessionManager; public: - Session(SessionManager* sm, fiber client); + Session(SessionManager* sm, fiber client); public: - virtual void start(boost::system::error_code&); - - virtual void stop(boost::system::error_code&); + virtual void start(boost::system::error_code&); + virtual void stop(boost::system::error_code&); private: void HandleRequestDispatch(const boost::system::error_code&, std::size_t); @@ -73,4 +74,4 @@ class Session : public ssf::BaseSession { #include "services/socks/v4/session.ipp" -#endif // SRC_SERVICES_SOCKS_V4_SESSION_H_ +#endif // SSF_SERVICES_SOCKS_V4_SESSION_H_ diff --git a/src/services/socks/v4/session.ipp b/src/services/socks/v4/session.ipp index 247e3484..0d74d102 100644 --- a/src/services/socks/v4/session.ipp +++ b/src/services/socks/v4/session.ipp @@ -1,17 +1,21 @@ -#ifndef SRC_SERVICES_SOCKS_V4_SESSION_IPP_ -#define SRC_SERVICES_SOCKS_V4_SESSION_IPP_ +#ifndef SSF_SERVICES_SOCKS_V4_SESSION_IPP_ +#define SSF_SERVICES_SOCKS_V4_SESSION_IPP_ #include #include #include +#include + #include "services/socks/v4/request.h" #include "services/socks/v4/reply.h" #include -namespace ssf { namespace socks { namespace v4 { -//----------------------------------------------------------------------------- +namespace ssf { +namespace socks { +namespace v4 { + template Session::Session(SessionManager* p_session_manager, fiber client) : ssf::BaseSession(), @@ -20,41 +24,34 @@ Session::Session(SessionManager* p_session_manager, fiber client) client_(std::move(client)), app_server_(client.get_io_service()) {} -//----------------------------------------------------------------------------- template void Session::HandleStop() { boost::system::error_code ec; p_session_manager_->stop(shared_from_this(), ec); } - -//----------------------------------------------------------------------------- template void Session::stop(boost::system::error_code&) { client_.close(); boost::system::error_code ec; app_server_.close(ec); if (ec) { - BOOST_LOG_TRIVIAL(error) << "socks session: stop error " << ec.message() << std::endl; + SSF_LOG(kLogError) << "session[socks]: stop error " << ec.message(); } } - -//----------------------------------------------------------------------------- template void Session::start(boost::system::error_code&) { - AsyncReadRequest(client_, &request_, - boost::bind(&Session::HandleRequestDispatch, - self_shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); + AsyncReadRequest( + client_, &request_, + boost::bind(&Session::HandleRequestDispatch, self_shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); } - -//----------------------------------------------------------------------------- template void Session::HandleRequestDispatch(const boost::system::error_code& ec, - std::size_t) { + std::size_t) { if (ec) { HandleStop(); return; @@ -62,27 +59,25 @@ void Session::HandleRequestDispatch(const boost::system::error_code& ec, // Dispatch request according to it's command id switch (request_.Command()) { - case Request::kConnect: - DoConnectRequest(); - break; + case Request::kConnect: + DoConnectRequest(); + break; - case Request::kBind: - DoBindRequest(); - break; + case Request::kBind: + DoBindRequest(); + break; - default: - fprintf(stderr, "[!][Session][0x%p] Invalid v4 command\n", (void*)this); - break; + default: + SSF_LOG(kLogError) << "session[socks]: Invalid v4 command"; + break; } } - -//----------------------------------------------------------------------------- template void Session::DoConnectRequest() { - auto handler = boost::bind(&Session::HandleApplicationServerConnect, - self_shared_from_this(), - boost::asio::placeholders::error); + auto handler = + boost::bind(&Session::HandleApplicationServerConnect, + self_shared_from_this(), boost::asio::placeholders::error); boost::system::error_code ec; boost::asio::ip::tcp::endpoint endpoint; @@ -109,41 +104,35 @@ void Session::DoConnectRequest() { } } - -//----------------------------------------------------------------------------- template void Session::DoBindRequest() { - fprintf(stderr, "[!][Session][0x%p] Bind Not implemented yet\n", (void*)this); - HandleStop(); + SSF_LOG(kLogError) << "session[socks]: Bind Not implemented yet"; + HandleStop(); } - -//----------------------------------------------------------------------------- template -void Session::HandleApplicationServerConnect(const boost::system::error_code& - err) { +void Session::HandleApplicationServerConnect( + const boost::system::error_code& err) { auto self = self_shared_from_this(); auto p_reply = std::make_shared(err, boost::asio::ip::tcp::endpoint()); if (err) { // Error connecting to the server, informing client and stopping AsyncSendReply(client_, *p_reply, - [this, self, p_reply](boost::system::error_code, std::size_t) { - HandleStop(); - }); + [this, self, p_reply](boost::system::error_code, + std::size_t) { HandleStop(); }); } else { // We successfully connect to application server - AsyncSendReply(client_, *p_reply, - [this, self, p_reply](boost::system::error_code ec, std::size_t) { - if (ec) { - HandleStop(); - return; - } // reply successfully sent, Establishing link - EstablishLink(); - }); + AsyncSendReply( + client_, *p_reply, + [this, self, p_reply](boost::system::error_code ec, std::size_t) { + if (ec) { + HandleStop(); + return; + } // reply successfully sent, Establishing link + EstablishLink(); + }); } } - -//----------------------------------------------------------------------------- template void Session::EstablishLink() { auto self = self_shared_from_this(); @@ -151,7 +140,6 @@ void Session::EstablishLink() { upstream_.reset(new StreamBuff); downstream_.reset(new StreamBuff); - // Two half duplex links AsyncEstablishHDLink(ssf::ReadFrom(client_), ssf::WriteTo(app_server_), boost::asio::buffer(*upstream_), @@ -161,8 +149,9 @@ void Session::EstablishLink() { boost::asio::buffer(*downstream_), boost::bind(&Session::HandleStop, self)); } + } // v4 } // socks } // ssf -#endif // SRC_SERVICES_SOCKS_V4_SESSION_IPP_ +#endif // SSF_SERVICES_SOCKS_V4_SESSION_IPP_ diff --git a/src/services/socks/v5/reply.cpp b/src/services/socks/v5/reply.cpp index bebcb941..fc49e1a9 100644 --- a/src/services/socks/v5/reply.cpp +++ b/src/services/socks/v5/reply.cpp @@ -2,14 +2,13 @@ #include // NOLINT -namespace ssf { namespace socks { namespace v5 { -//----------------------------------------------------------------------------- +namespace ssf { +namespace socks { +namespace v5 { + Reply::Reply(const boost::system::error_code& err) - : version_(0x05), - status_(ErrorCodeToStatus(err)), - reserved_(0x00) { } + : version_(0x05), status_(ErrorCodeToStatus(err)), reserved_(0x00) {} -//----------------------------------------------------------------------------- void Reply::set_ipv4(boost::asio::ip::address_v4::bytes_type ipv4) { addr_type_ = 0x01; ipv4_ = ipv4; @@ -31,7 +30,6 @@ void Reply::set_port(uint16_t port) { port_low_byte_ = port & 0xff; } -//----------------------------------------------------------------------------- std::vector Reply::Buffers() const { std::vector buf; @@ -41,26 +39,24 @@ std::vector Reply::Buffers() const { buf.push_back(boost::asio::buffer(&addr_type_, 1)); switch (addr_type_) { - case kIPv4: - buf.push_back(boost::asio::buffer(ipv4_)); - break; - case kDNS: - buf.push_back(boost::asio::buffer(&domainLength_, 1)); - buf.push_back(boost::asio::buffer(domain_)); - break; - case kIPv6: - buf.push_back(boost::asio::buffer(ipv6_)); - break; + case kIPv4: + buf.push_back(boost::asio::buffer(ipv4_)); + break; + case kDNS: + buf.push_back(boost::asio::buffer(&domainLength_, 1)); + buf.push_back(boost::asio::buffer(domain_)); + break; + case kIPv6: + buf.push_back(boost::asio::buffer(ipv6_)); + break; } buf.push_back(boost::asio::buffer(&port_high_byte_, 1)); buf.push_back(boost::asio::buffer(&port_low_byte_, 1)); - + return buf; } - -//----------------------------------------------------------------------------- Reply::Status Reply::ErrorCodeToStatus(const boost::system::error_code& err) { Status s = kFailed; diff --git a/src/services/socks/v5/reply.h b/src/services/socks/v5/reply.h index 2844626a..2b6bc6de 100644 --- a/src/services/socks/v5/reply.h +++ b/src/services/socks/v5/reply.h @@ -1,5 +1,5 @@ -#ifndef SSF_V5_REPLY_H_ -#define SSF_V5_REPLY_H_ +#ifndef SSF_SERVICES_SOCKS_V5_REPLY_H_ +#define SSF_SERVICES_SOCKS_V5_REPLY_H_ #include #include @@ -13,12 +13,13 @@ #include -namespace ssf { namespace socks { namespace v5 { -//----------------------------------------------------------------------------- +namespace ssf { +namespace socks { +namespace v5 { class Reply { public: - // Status constants + // Status constants enum Status { kGranted = 0x00, kFailed = 0x01, @@ -32,11 +33,7 @@ class Reply { }; // Address type constants - enum AddressTypeId { - kIPv4 = 0x01, - kDNS = 0x03, - kIPv6 = 0x04 - }; + enum AddressTypeId { kIPv4 = 0x01, kDNS = 0x03, kIPv6 = 0x04 }; public: Reply(const boost::system::error_code&); @@ -69,18 +66,13 @@ class Reply { uint8_t port_low_byte_; }; - -//----------------------------------------------------------------------------- -// S E N D R E P L Y -//----------------------------------------------------------------------------- -template +template void AsyncSendReply(StreamSocket& c, const Reply& r, VerifyHandler handler) { boost::asio::async_write(c, r.Buffers(), handler); } -//----------------------------------------------------------------------------- } // v5 } // socks } // ssf -#endif // SSF_V5_REPLY_H_ +#endif // SSF_SERVICES_SOCKS_V5_REPLY_H_ diff --git a/src/services/socks/v5/reply_auth.cpp b/src/services/socks/v5/reply_auth.cpp index 4205ace9..c3a81f6d 100644 --- a/src/services/socks/v5/reply_auth.cpp +++ b/src/services/socks/v5/reply_auth.cpp @@ -2,21 +2,17 @@ #include // NOLINT -namespace ssf { namespace socks { namespace v5 { -//----------------------------------------------------------------------------- - AuthReply::AuthReply(uint8_t authMethod) - : version_(0x05), - authMethod_(authMethod) { } +namespace ssf { +namespace socks { +namespace v5 { +AuthReply::AuthReply(uint8_t authMethod) + : version_(0x05), authMethod_(authMethod) {} -//----------------------------------------------------------------------------- - std::array AuthReply::Buffer() const { - std::array buf = { - { - boost::asio::buffer(&version_, 1), - boost::asio::buffer(&authMethod_, 1), - } - }; +std::array AuthReply::Buffer() const { + std::array buf = {{ + boost::asio::buffer(&version_, 1), boost::asio::buffer(&authMethod_, 1), + }}; return buf; } diff --git a/src/services/socks/v5/reply_auth.h b/src/services/socks/v5/reply_auth.h index 70bb587c..49d1b445 100644 --- a/src/services/socks/v5/reply_auth.h +++ b/src/services/socks/v5/reply_auth.h @@ -1,5 +1,5 @@ -#ifndef SSF_V5_REPLY_AUTH_H_ -#define SSF_V5_REPLY_AUTH_H_ +#ifndef SSF_SERVICES_SOCKS_V5_REPLY_AUTH_H_ +#define SSF_SERVICES_SOCKS_V5_REPLY_AUTH_H_ #include #include @@ -7,8 +7,9 @@ #include #include -namespace ssf { namespace socks { namespace v5 { -//----------------------------------------------------------------------------- +namespace ssf { +namespace socks { +namespace v5 { class AuthReply { public: @@ -21,18 +22,14 @@ class AuthReply { uint8_t authMethod_; }; - -//----------------------------------------------------------------------------- -// S E N D R E P L Y -//----------------------------------------------------------------------------- -template -void AsyncSendAuthReply(StreamSocket& c, const AuthReply& r, VerifyHandler handler) { +template +void AsyncSendAuthReply(StreamSocket& c, const AuthReply& r, + VerifyHandler handler) { boost::asio::async_write(c, r.Buffer(), handler); } -//----------------------------------------------------------------------------- } // v5 } // socks } // ssf -#endif // SSF_V5_REPLY_AUTH_H_ +#endif // SSF_SERVICES_SOCKS_V5_REPLY_AUTH_H_ diff --git a/src/services/socks/v5/request.cpp b/src/services/socks/v5/request.cpp index 25f998c4..a890dad1 100644 --- a/src/services/socks/v5/request.cpp +++ b/src/services/socks/v5/request.cpp @@ -1,14 +1,15 @@ #include "services/socks/v5/request.h" -#include // NOLINT +#include // NOLINT #include // NOLINT -#include // NOLINT +#include // NOLINT #include // NOLINT +namespace ssf { +namespace socks { +namespace v5 { -namespace ssf { namespace socks { namespace v5 { -//----------------------------------------------------------------------------- uint8_t Request::version() const { return version_; } uint8_t Request::command() const { return command_; } @@ -23,23 +24,18 @@ std::vector Request::domain() const { return domain_; } boost::asio::ip::address_v6::bytes_type Request::ipv6() const { return ipv6_; } -uint16_t Request::port() const { +uint16_t Request::port() const { uint16_t port = port_high_byte_; port = (port << 8) & 0xff00; port = port | port_low_byte_; return port; } -//----------------------------------------------------------------------------- std::array Request::First_Part_Buffers() { std::array buf = { - { - boost::asio::buffer(&version_, 1), - boost::asio::buffer(&command_, 1), - boost::asio::buffer(&reserved_, 1), - boost::asio::buffer(&addressType_, 1) - } - }; + {boost::asio::buffer(&version_, 1), boost::asio::buffer(&command_, 1), + boost::asio::buffer(&reserved_, 1), + boost::asio::buffer(&addressType_, 1)}}; return buf; } @@ -51,19 +47,19 @@ std::vector Request::Address_Buffer() { std::vector buf; switch (addressType_) { - case kIPv4: - buf.push_back(boost::asio::buffer(ipv4_)); - break; - case kDNS: - while (domain_.size() < domainLength_) { - domain_.push_back(0); - } - for (size_t i = 0; i < domainLength_; ++i) { - buf.push_back(boost::asio::mutable_buffer(&(domain_[i]), 1)); - } - break; - case kIPv6: - buf.push_back(boost::asio::buffer(ipv6_)); + case kIPv4: + buf.push_back(boost::asio::buffer(ipv4_)); + break; + case kDNS: + while (domain_.size() < domainLength_) { + domain_.push_back(0); + } + for (size_t i = 0; i < domainLength_; ++i) { + buf.push_back(boost::asio::mutable_buffer(&(domain_[i]), 1)); + } + break; + case kIPv6: + buf.push_back(boost::asio::buffer(ipv6_)); } return buf; @@ -71,15 +67,11 @@ std::vector Request::Address_Buffer() { std::array Request::Port_Buffers() { std::array buf = { - { - boost::asio::buffer(&port_high_byte_, 1), - boost::asio::buffer(&port_low_byte_, 1) - } - }; + {boost::asio::buffer(&port_high_byte_, 1), + boost::asio::buffer(&port_low_byte_, 1)}}; return buf; } } // v5 } // socks } // ssf - diff --git a/src/services/socks/v5/request.h b/src/services/socks/v5/request.h index edff9bfb..7cdcadf5 100644 --- a/src/services/socks/v5/request.h +++ b/src/services/socks/v5/request.h @@ -1,40 +1,34 @@ -#ifndef SSF_V5_REQUEST_H_ -#define SSF_V5_REQUEST_H_ +#ifndef SSF_SERVICES_SOCKS_V5_REQUEST_H_ +#define SSF_SERVICES_SOCKS_V5_REQUEST_H_ #include #include -#include // NOLINT +#include // NOLINT #include // NOLINT #include #include #include // NOLINT -#include // NOLINT -#include // NOLINT +#include // NOLINT +#include // NOLINT #include #include // NOLINT -#include // NOLINT +#include // NOLINT #include "common/error/error.h" -namespace ssf { namespace socks { namespace v5 { -//----------------------------------------------------------------------------- +namespace ssf { +namespace socks { +namespace v5 { + class Request { public: - // Command types constants - enum CommandId { - kConnect = 0x01, - kBind = 0x02, - kUDP = 0x03 - }; + // Command types constants + enum CommandId { kConnect = 0x01, kBind = 0x02, kUDP = 0x03 }; // Address type constants - enum AddressTypeId { - kIPv4 = 0x01, - kDNS = 0x03, - kIPv6 = 0x04 - }; + enum AddressTypeId { kIPv4 = 0x01, kDNS = 0x03, kIPv6 = 0x04 }; public: uint8_t version() const; @@ -58,7 +52,7 @@ class Request { uint8_t addressType_; boost::asio::ip::address_v4::bytes_type ipv4_; - + uint8_t domainLength_; std::vector domain_; @@ -68,52 +62,53 @@ class Request { uint8_t port_low_byte_; }; - -//----------------------------------------------------------------------------- -// R E A D R E Q U E S T -//----------------------------------------------------------------------------- - -template +template class ReadRequestCoro : public boost::asio::coroutine { -public: + public: ReadRequestCoro(StreamSocket& c, Request* p_r, VerifyHandler h) - : c_(c), r_(*p_r), handler_(h), total_length_(0) { } + : c_(c), r_(*p_r), handler_(h), total_length_(0) {} #include // NOLINT void operator()(const boost::system::error_code& ec, std::size_t length) { - if (!ec) reenter(this) { - // Read Request fixed size buffer - yield boost::asio::async_read(c_, r_.First_Part_Buffers(), std::move(*this)); - total_length_ += length; - - // Read the address - if (r_.addressType() == Request::kIPv4) { - // Read IPv4 address - yield boost::asio::async_read(c_, r_.Address_Buffer(), std::move(*this)); - total_length_ += length; - } else if (r_.addressType() == Request::kDNS) { - // Read length of the domain name - yield boost::asio::async_read(c_, r_.Domain_Length_Buffer(), std::move(*this)); - total_length_ += length; - // Read domain name - yield boost::asio::async_read(c_, r_.Address_Buffer(), std::move(*this)); + // Read Request fixed size buffer + yield boost::asio::async_read(c_, r_.First_Part_Buffers(), + std::move(*this)); total_length_ += length; - } else if (r_.addressType() == Request::kIPv6) { - // Read IPv6 address - yield boost::asio::async_read(c_, r_.Address_Buffer(), std::move(*this)); - total_length_ += length; - } else { - boost::get<0>(handler_)(boost::system::error_code(ssf::error::protocol_error, - ssf::error::get_ssf_category()), - total_length_); - } - yield boost::asio::async_read(c_, r_.Port_Buffers(), std::move(*this)); - total_length_ += length; + // Read the address + if (r_.addressType() == Request::kIPv4) { + // Read IPv4 address + yield boost::asio::async_read(c_, r_.Address_Buffer(), + std::move(*this)); + total_length_ += length; + } else if (r_.addressType() == Request::kDNS) { + // Read length of the domain name + yield boost::asio::async_read(c_, r_.Domain_Length_Buffer(), + std::move(*this)); + total_length_ += length; + // Read domain name + yield boost::asio::async_read(c_, r_.Address_Buffer(), + std::move(*this)); + total_length_ += length; + } else if (r_.addressType() == Request::kIPv6) { + // Read IPv6 address + yield boost::asio::async_read(c_, r_.Address_Buffer(), + std::move(*this)); + total_length_ += length; + } else { + boost::get<0>(handler_)( + boost::system::error_code(::error::protocol_error, + ::error::get_ssf_category()), + total_length_); + } + + yield boost::asio::async_read(c_, r_.Port_Buffers(), std::move(*this)); + total_length_ += length; - boost::get<0>(handler_)(ec, total_length_); - } else { + boost::get<0>(handler_)(ec, total_length_); + } + else { boost::get<0>(handler_)(ec, total_length_); } } @@ -126,11 +121,10 @@ class ReadRequestCoro : public boost::asio::coroutine { std::size_t total_length_; }; - -template +template void AsyncReadRequest(StreamSocket& c, Request* p_r, VerifyHandler handler) { - ReadRequestCoro, StreamSocket> - RequestReader(c, p_r, boost::make_tuple(handler)); + ReadRequestCoro, StreamSocket> RequestReader( + c, p_r, boost::make_tuple(handler)); RequestReader(boost::system::error_code(), 0); } @@ -139,5 +133,4 @@ void AsyncReadRequest(StreamSocket& c, Request* p_r, VerifyHandler handler) { } // socks } // ssf - -#endif // SSF_V5_REQUEST_H_ +#endif // SSF_SERVICES_SOCKS_V5_REQUEST_H_ diff --git a/src/services/socks/v5/request_auth.cpp b/src/services/socks/v5/request_auth.cpp index dc9057ff..7420c064 100644 --- a/src/services/socks/v5/request_auth.cpp +++ b/src/services/socks/v5/request_auth.cpp @@ -1,34 +1,30 @@ #include "services/socks/v5/request_auth.h" -#include // NOLINT +#include // NOLINT #include // NOLINT -#include // NOLINT +#include // NOLINT #include // NOLINT +namespace ssf { +namespace socks { +namespace v5 { -namespace ssf { namespace socks { namespace v5 { -//----------------------------------------------------------------------------- uint8_t RequestAuth::numberOfAuthSupported() const { return numberOfAuthSupported_; } -//----------------------------------------------------------------------------- void RequestAuth::addAuthMethod(uint8_t authMethod) { authMethods_.push_back(authMethod); } -//----------------------------------------------------------------------------- std::array RequestAuth::Buffers() { - std::array buf = { - { + std::array buf = {{ boost::asio::buffer(&numberOfAuthSupported_, 1), - } - }; + }}; return buf; } -//---------------------------------------------------------------------------- bool RequestAuth::is_no_auth_present() { for (size_t i = 0; i < numberOfAuthSupported_; ++i) { if (authMethods_[i] == kNoAuth) { @@ -42,4 +38,3 @@ bool RequestAuth::is_no_auth_present() { } // v5 } // socks } // ssf - diff --git a/src/services/socks/v5/request_auth.h b/src/services/socks/v5/request_auth.h index d11131a0..a2163cb0 100644 --- a/src/services/socks/v5/request_auth.h +++ b/src/services/socks/v5/request_auth.h @@ -1,5 +1,5 @@ -#ifndef SSF_V5_REQUEST_AUTH_H_ -#define SSF_V5_REQUEST_AUTH_H_ +#ifndef SSF_SERVICES_SOCKS_V5_REQUEST_AUTH_H_ +#define SSF_SERVICES_SOCKS_V5_REQUEST_AUTH_H_ #include #include // NOLINT @@ -11,11 +11,13 @@ #include // NOLINT #include -namespace ssf { namespace socks { namespace v5 { -//----------------------------------------------------------------------------- +namespace ssf { +namespace socks { +namespace v5 { + class RequestAuth { public: - // Authentication method constants + // Authentication method constants enum AuthMethodIds { kNoAuth = 0x00, kGSSAPI = 0x01, @@ -36,16 +38,11 @@ class RequestAuth { std::vector authMethods_; }; - -//----------------------------------------------------------------------------- -// R E A D A U T H E N T I C A T I O N R E Q U E S T -//----------------------------------------------------------------------------- - -template +template class ReadRequestAuthCoro : public boost::asio::coroutine { -public: + public: ReadRequestAuthCoro(StreamSocket& c, RequestAuth* p_r, VerifyHandler h) - : c_(c), r_(*p_r), handler_(h), total_length_(0) { } + : c_(c), r_(*p_r), handler_(h), total_length_(0) {} #include // NOLINT void operator()(const boost::system::error_code& ec, std::size_t length) { @@ -53,21 +50,22 @@ class ReadRequestAuthCoro : public boost::asio::coroutine { uint8_t method = 0; if (!ec) reenter(this) { - // Read Request fixed size number of authentication methods - yield boost::asio::async_read(c_, r_.Buffers(), std::move(*this)); - total_length_ += length; - - // Read each supported method - for (numberOfMethods = 0; numberOfMethods < r_.numberOfAuthSupported(); ++numberOfMethods) { - yield boost::asio::async_read(c_, - boost::asio::mutable_buffers_1(&method, 1), - std::move(*this)); - r_.addAuthMethod(method); + // Read Request fixed size number of authentication methods + yield boost::asio::async_read(c_, r_.Buffers(), std::move(*this)); total_length_ += length; - } - boost::get<0>(handler_)(ec, total_length_); - } else { + // Read each supported method + for (numberOfMethods = 0; numberOfMethods < r_.numberOfAuthSupported(); + ++numberOfMethods) { + yield boost::asio::async_read( + c_, boost::asio::mutable_buffers_1(&method, 1), std::move(*this)); + r_.addAuthMethod(method); + total_length_ += length; + } + + boost::get<0>(handler_)(ec, total_length_); + } + else { boost::get<0>(handler_)(ec, total_length_); } } @@ -80,19 +78,17 @@ class ReadRequestAuthCoro : public boost::asio::coroutine { std::size_t total_length_; }; - -template -void AsyncReadRequestAuth(StreamSocket& c, RequestAuth* p_r, VerifyHandler handler) { +template +void AsyncReadRequestAuth(StreamSocket& c, RequestAuth* p_r, + VerifyHandler handler) { ReadRequestAuthCoro, StreamSocket> - RequestAuthReader(c, p_r, boost::make_tuple(handler)); + RequestAuthReader(c, p_r, boost::make_tuple(handler)); RequestAuthReader(boost::system::error_code(), 0); } -//----------------------------------------------------------------------------- } // v5 } // socks } // ssf - -#endif // SSF_V5_REQUEST_AUTH_H_ +#endif // SSF_SERVICES_SOCKS_V5_REQUEST_AUTH_H_ diff --git a/src/services/socks/v5/session.h b/src/services/socks/v5/session.h index 5fdeb9bb..c280841f 100644 --- a/src/services/socks/v5/session.h +++ b/src/services/socks/v5/session.h @@ -1,5 +1,5 @@ -#ifndef SRC_SERVICES_SOCKS_V5_SESSION_H_ -#define SRC_SERVICES_SOCKS_V5_SESSION_H_ +#ifndef SSF_SERVICES_SOCKS_V5_SESSION_H_ +#define SSF_SERVICES_SOCKS_V5_SESSION_H_ #include @@ -7,22 +7,23 @@ #include #include // NOLINT -#include "common/network/base_session.h" // NOLINT -#include "common/network/socket_link.h" // NOLINT +#include // NOLINT +#include // NOLINT +#include +#include -#include "common/network/manager.h" -#include "common/network/base_session.h" - -#include "services/socks/v5/request.h" // NOLINT +#include "services/socks/v5/request.h" // NOLINT #include "services/socks/v5/request_auth.h" // NOLINT #include "common/boost/fiber/stream_fiber.hpp" -namespace ssf { namespace socks { namespace v5 { +namespace ssf { +namespace socks { +namespace v5 { template class Session : public ssf::BaseSession { -private: + private: typedef std::array StreamBuff; typedef boost::asio::ip::tcp::socket socket; @@ -32,15 +33,16 @@ class Session : public ssf::BaseSession { typedef ItemManager SessionManager; public: - Session(SessionManager* sm, fiber client); + Session(SessionManager* sm, fiber client); public: - virtual void start(boost::system::error_code&); + virtual void start(boost::system::error_code&); - virtual void stop(boost::system::error_code&); + virtual void stop(boost::system::error_code&); private: - void HandleRequestAuthDispatch(const boost::system::error_code& ec, std::size_t); + void HandleRequestAuthDispatch(const boost::system::error_code& ec, + std::size_t); void DoNoAuth(); void DoErrorAuth(); @@ -66,7 +68,6 @@ class Session : public ssf::BaseSession { return std::static_pointer_cast(shared_from_this()); } - private: boost::asio::io_service& io_service_; SessionManager* p_session_manager_; @@ -85,4 +86,4 @@ class Session : public ssf::BaseSession { #include "services/socks/v5/session.ipp" -#endif // SRC_SERVICES_SOCKS_V5_SESSION_H_ +#endif // SSF_SERVICES_SOCKS_V5_SESSION_H_ diff --git a/src/services/socks/v5/session.ipp b/src/services/socks/v5/session.ipp index 1f965984..4c5523c8 100644 --- a/src/services/socks/v5/session.ipp +++ b/src/services/socks/v5/session.ipp @@ -1,5 +1,5 @@ -#ifndef SRC_SERVICES_SOCKS_V5_SESSION_IPP_ -#define SRC_SERVICES_SOCKS_V5_SESSION_IPP_ +#ifndef SSF_SERVICES_SOCKS_V5_SESSION_IPP_ +#define SSF_SERVICES_SOCKS_V5_SESSION_IPP_ #include #include @@ -12,51 +12,46 @@ #include -namespace ssf { namespace socks { namespace v5 { -//----------------------------------------------------------------------------- +namespace ssf { +namespace socks { +namespace v5 { + template Session::Session(SessionManager* p_session_manager, fiber client) : ssf::BaseSession(), io_service_(client.get_io_service()), p_session_manager_(p_session_manager), client_(std::move(client)), - app_server_(client.get_io_service()) { } + app_server_(client.get_io_service()) {} -//----------------------------------------------------------------------------- template void Session::HandleStop() { boost::system::error_code ec; p_session_manager_->stop(shared_from_this(), ec); } - -//----------------------------------------------------------------------------- template void Session::stop(boost::system::error_code&) { client_.close(); boost::system::error_code ec; app_server_.close(ec); if (ec) { - BOOST_LOG_TRIVIAL(error) << "socks session: stop error " << ec.message() << std::endl; + SSF_LOG(kLogError) << "session[socks]: stop error " << ec.message(); } } - -//----------------------------------------------------------------------------- template void Session::start(boost::system::error_code&) { - AsyncReadRequestAuth(client_, &request_auth_, - boost::bind(&Session::HandleRequestAuthDispatch, - self_shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); + AsyncReadRequestAuth( + client_, &request_auth_, + boost::bind(&Session::HandleRequestAuthDispatch, self_shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); } - -//----------------------------------------------------------------------------- template -void Session::HandleRequestAuthDispatch(const boost::system::error_code& ec, - std::size_t) { +void Session::HandleRequestAuthDispatch( + const boost::system::error_code& ec, std::size_t) { if (ec) { HandleStop(); return; @@ -70,7 +65,6 @@ void Session::HandleRequestAuthDispatch(const boost::system::error_code& } } -//----------------------------------------------------------------------------- template void Session::DoNoAuth() { auto self = self_shared_from_this(); @@ -79,12 +73,12 @@ void Session::DoNoAuth() { AsyncSendAuthReply(client_, *p_reply, [this, self, p_reply](boost::system::error_code ec, std::size_t transferred) { - if (ec) { - HandleStop(); - return; - } - HandleReplyAuthSent(); - }); + if (ec) { + HandleStop(); + return; + } + HandleReplyAuthSent(); + }); } template @@ -93,25 +87,22 @@ void Session::DoErrorAuth() { auto p_reply = std::make_shared(0xFF); AsyncSendAuthReply(client_, *p_reply, - [this, self, p_reply](boost::system::error_code, std::size_t) { - HandleStop(); - }); + [this, self, p_reply](boost::system::error_code, + std::size_t) { HandleStop(); }); } -//----------------------------------------------------------------------------- template void Session::HandleReplyAuthSent() { - AsyncReadRequest(client_, &request_, - boost::bind(&Session::HandleRequestDispatch, - self_shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); + AsyncReadRequest( + client_, &request_, + boost::bind(&Session::HandleRequestDispatch, self_shared_from_this(), + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); } -//----------------------------------------------------------------------------- template void Session::HandleRequestDispatch(const boost::system::error_code& ec, - std::size_t) { + std::size_t) { if (ec) { HandleStop(); return; @@ -119,41 +110,41 @@ void Session::HandleRequestDispatch(const boost::system::error_code& ec, // Check command asked switch (request_.command()) { - case Request::kConnect: - DoConnectRequest(); - break; - case Request::kBind: - DoBindRequest(); - break; - case Request::kUDP: - DoUDPRequest(); - break; - default: - fprintf(stderr, "[!][Session][0x%p] Invalid v5 command\n", (void*)this); - HandleStop(); - break; + case Request::kConnect: + DoConnectRequest(); + break; + case Request::kBind: + DoBindRequest(); + break; + case Request::kUDP: + DoUDPRequest(); + break; + default: + SSF_LOG(kLogError) << "session[socks]: invalid v5 command"; + HandleStop(); + break; } } -//----------------------------------------------------------------------------- template void Session::DoConnectRequest() { - auto handler = boost::bind(&Session::HandleApplicationServerConnect, - self_shared_from_this(), - boost::asio::placeholders::error); + auto handler = + boost::bind(&Session::HandleApplicationServerConnect, + self_shared_from_this(), boost::asio::placeholders::error); boost::system::error_code ec; uint16_t port = request_.port(); - + if (request_.addressType() == Request::kIPv4) { boost::asio::ip::address_v4 address(request_.ipv4()); app_server_.async_connect(boost::asio::ip::tcp::endpoint(address, port), handler); } else if (request_.addressType() == Request::kDNS) { boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(std::string(request_.domain().data(), - request_.domain().size()), - std::to_string(port)); - boost::asio::ip::tcp::resolver::iterator iterator(resolver.resolve(query, ec)); + boost::asio::ip::tcp::resolver::query query( + std::string(request_.domain().data(), request_.domain().size()), + std::to_string(port)); + boost::asio::ip::tcp::resolver::iterator iterator( + resolver.resolve(query, ec)); if (ec) { handler(ec); } else { @@ -161,14 +152,15 @@ void Session::DoConnectRequest() { } } else if (request_.addressType() == Request::kIPv6) { boost::asio::ip::address_v6 address(request_.ipv6()); - app_server_.async_connect(boost::asio::ip::tcp::endpoint(address, port), + app_server_.async_connect(boost::asio::ip::tcp::endpoint(address, port), handler); } else { boost::asio::ip::tcp::resolver resolver(io_service_); - boost::asio::ip::tcp::resolver::query query(std::string(request_.domain().data(), - request_.domain().size()), - std::to_string(port)); - boost::asio::ip::tcp::resolver::iterator iterator(resolver.resolve(query, ec)); + boost::asio::ip::tcp::resolver::query query( + std::string(request_.domain().data(), request_.domain().size()), + std::to_string(port)); + boost::asio::ip::tcp::resolver::iterator iterator( + resolver.resolve(query, ec)); if (ec) { handler(ec); } else { @@ -179,59 +171,56 @@ void Session::DoConnectRequest() { template void Session::DoBindRequest() { - fprintf(stderr, "[!][Session][0x%p] Bind Not implemented yet\n", (void*)this); - HandleStop(); + SSF_LOG(kLogError) << "session[socks]: Bind not implemented yet"; + HandleStop(); } template void Session::DoUDPRequest() { - fprintf(stderr, "[!][Session][0x%p] UDP Not implemented yet\n", (void*)this); + SSF_LOG(kLogError) << "session[socks]: UDP not implemented yet"; HandleStop(); } -//----------------------------------------------------------------------------- template -void Session::HandleApplicationServerConnect(const boost::system::error_code& - err) { +void Session::HandleApplicationServerConnect( + const boost::system::error_code& err) { auto self = self_shared_from_this(); auto p_reply = std::make_shared(err); switch (request_.addressType()) { - case Request::kIPv4: - p_reply->set_ipv4(request_.ipv4()); - break; - case Request::kDNS: - p_reply->set_domain(request_.domain()); - break; - case Request::kIPv6: - p_reply->set_ipv6(request_.ipv6()); - break; - default: - HandleStop(); - break; + case Request::kIPv4: + p_reply->set_ipv4(request_.ipv4()); + break; + case Request::kDNS: + p_reply->set_domain(request_.domain()); + break; + case Request::kIPv6: + p_reply->set_ipv6(request_.ipv6()); + break; + default: + HandleStop(); + break; } p_reply->set_port(request_.port()); if (err) { // Error connecting to the server, informing client and stopping AsyncSendReply(client_, *p_reply, - [this, self, p_reply](boost::system::error_code, std::size_t) { - HandleStop(); - }); + [this, self, p_reply](boost::system::error_code, + std::size_t) { HandleStop(); }); } else { // We successfully connect to application server - AsyncSendReply(client_, *p_reply, - [this, self, p_reply](boost::system::error_code ec, std::size_t) { - if (ec) { - HandleStop(); - return; - } // reply successfully sent, Establishing link - EstablishLink(); - }); + AsyncSendReply( + client_, *p_reply, + [this, self, p_reply](boost::system::error_code ec, std::size_t) { + if (ec) { + HandleStop(); + return; + } // reply successfully sent, Establishing link + EstablishLink(); + }); } } - -//----------------------------------------------------------------------------- template void Session::EstablishLink() { auto self = self_shared_from_this(); @@ -251,4 +240,4 @@ void Session::EstablishLink() { } // socks } // ssf -#endif // SRC_SERVICES_SOCKS_V5_SESSION_IPP_ +#endif // SSF_SERVICES_SOCKS_V5_SESSION_IPP_ diff --git a/src/services/user_services/base_user_service.h b/src/services/user_services/base_user_service.h index a15d1c78..c2c425dd 100644 --- a/src/services/user_services/base_user_service.h +++ b/src/services/user_services/base_user_service.h @@ -11,7 +11,6 @@ namespace ssf { namespace services { -//---------------------------------------------------------------------------- template class BaseUserService : public std::enable_shared_from_this> { @@ -19,9 +18,9 @@ class BaseUserService typedef typename std::shared_ptr> BaseUserServicePtr; virtual std::vector> - GetRemoteServiceCreateVector() = 0; + GetRemoteServiceCreateVector() = 0; virtual std::vector> - GetRemoteServiceStopVector(Demux& demux) = 0; + GetRemoteServiceStopVector(Demux& demux) = 0; virtual uint32_t CheckRemoteServiceStatus(Demux& demux) = 0; virtual std::string GetName() = 0; diff --git a/src/services/user_services/copy_file_service.h b/src/services/user_services/copy_file_service.h index dae663d2..f4954dc3 100644 --- a/src/services/user_services/copy_file_service.h +++ b/src/services/user_services/copy_file_service.h @@ -33,7 +33,7 @@ class CopyFileService : public BaseUserService { bool from_stdin, bool from_local_to_remote, const std::string& input_pattern, const std::string& output_pattern, boost::system::error_code& ec) { - ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + ec.assign(::error::success, ::error::get_ssf_category()); return CopyFileServicePtr(new CopyFileService( from_stdin, from_local_to_remote, input_pattern, output_pattern)); } @@ -80,8 +80,14 @@ class CopyFileService : public BaseUserService { output_pattern_); local_service_ids_.insert(p_service_factory->CreateRunNewService( - local_service_request.service_id(), - local_service_request.parameters(), ec)); + local_service_request.service_id(), + local_service_request.parameters(), ec)); + + if (ec) { + SSF_LOG(kLogError) << "user_service[copy file]: " + << "local_service[file to fibers]: start failed: " + << ec.message(); + } return !ec; } @@ -97,10 +103,20 @@ class CopyFileService : public BaseUserService { local_service_ids_.insert(p_service_factory->CreateRunNewService( fiber_to_file_request.service_id(), fiber_to_file_request.parameters(), ec)); + if (ec) { + SSF_LOG(kLogError) << "user_service[copy file]: " + << "local_service[fiber to file]: start failed: " + << ec.message(); + } local_service_ids_.insert(p_service_factory->CreateRunNewService( file_enquirer_request.service_id(), file_enquirer_request.parameters(), ec)); + if (ec) { + SSF_LOG(kLogError) << "user_service[copy file]: " + << "local_service[file enquirer]: start failed: " + << ec.message(); + } return !ec; } diff --git a/src/services/user_services/port_forwarding.h b/src/services/user_services/port_forwarding.h index 0518292e..1f438791 100644 --- a/src/services/user_services/port_forwarding.h +++ b/src/services/user_services/port_forwarding.h @@ -34,38 +34,35 @@ namespace ssf { namespace services { template -class PortForwading : public BaseUserService -{ -private: +class PortForwarding : public BaseUserService { + private: typedef boost::asio::fiber::detail::fiber_id::local_port_type local_port_type; private: - PortForwading(uint16_t local_port, std::string remote_addr, + PortForwarding(uint16_t local_port, std::string remote_addr, uint16_t remote_port) - : local_port_(local_port), - remote_addr_(remote_addr), - remote_port_(remote_port), - remoteServiceId_(0), - localServiceId_(0) { + : local_port_(local_port), + remote_addr_(remote_addr), + remote_port_(remote_port), + remoteServiceId_(0), + localServiceId_(0) { relay_fiber_port_ = local_port_; } public: - static std::string GetFullParseName() { - return "forward,L"; - } + static std::string GetFullParseName() { return "forward,L"; } - static std::string GetParseName() { - return "forward"; - } + static std::string GetParseName() { return "forward"; } + + static std::string GetValueName() { return "loc_port:dest_ip:dest_port"; } static std::string GetParseDesc() { - return "Forward local port on given target"; + return "Forward local port on given target from server"; } public: - static std::shared_ptr CreateServiceOptions( - std::string line, boost::system::error_code& ec) { + static std::shared_ptr CreateServiceOptions( + std::string line, boost::system::error_code& ec) { using boost::spirit::qi::int_; using boost::spirit::qi::alnum; using boost::spirit::qi::char_; @@ -75,60 +72,52 @@ class PortForwading : public BaseUserService rule target_pattern = +char_("0-9a-zA-Z.-"); uint16_t listening_port, target_port; std::string target_addr; - str_it begin = line.begin(), - end = line.end(); - bool parsed = boost::spirit::qi::parse(begin, - end, - int_ >> ":" >> target_pattern - >> ":" >> int_, - listening_port, - target_addr, - target_port); + str_it begin = line.begin(), end = line.end(); + bool parsed = boost::spirit::qi::parse( + begin, end, int_ >> ":" >> target_pattern >> ":" >> int_, + listening_port, target_addr, target_port); if (parsed) { - return std::shared_ptr( - new PortForwading(listening_port, target_addr, target_port)); + return std::shared_ptr( + new PortForwarding(listening_port, target_addr, target_port)); } else { - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); - return std::shared_ptr(nullptr); + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + return std::shared_ptr(nullptr); } } static void RegisterToServiceOptionFactory() { ServiceOptionFactory::RegisterUserServiceParser( - GetParseName(), GetFullParseName(), GetParseDesc(), - &PortForwading::CreateServiceOptions); + GetParseName(), GetFullParseName(), GetValueName(), GetParseDesc(), + &PortForwarding::CreateServiceOptions); } - virtual std::string GetName() { - return "forward"; - } + virtual std::string GetName() { return "forward"; } virtual std::vector> - GetRemoteServiceCreateVector() { - std::vector> result; + GetRemoteServiceCreateVector() { + std::vector> result; - services::admin::CreateServiceRequest r_socks( - services::fibers_to_sockets::FibersToSockets< - Demux>::GetCreateRequest(relay_fiber_port_, remote_addr_, - remote_port_)); + services::admin::CreateServiceRequest r_socks( + services::fibers_to_sockets::FibersToSockets::GetCreateRequest( + relay_fiber_port_, remote_addr_, remote_port_)); - result.push_back(r_socks); + result.push_back(r_socks); - return result; - } + return result; + } virtual std::vector> - GetRemoteServiceStopVector(Demux& demux) { - std::vector> result; + GetRemoteServiceStopVector(Demux& demux) { + std::vector> result; - auto id = GetRemoteServiceId(demux); + auto id = GetRemoteServiceId(demux); - if (id) { - result.push_back(admin::StopServiceRequest(id)); - } + if (id) { + result.push_back(admin::StopServiceRequest(id)); + } - return result; + return result; } virtual bool StartLocalServices(Demux& demux) { @@ -137,29 +126,33 @@ class PortForwading : public BaseUserService local_port_, relay_fiber_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); boost::system::error_code ec; localServiceId_ = p_service_factory->CreateRunNewService( - l_forward.service_id(), l_forward.parameters(), ec); + l_forward.service_id(), l_forward.parameters(), ec); + if (ec) { + SSF_LOG(kLogError) << "user_service[forward]: " + << "local_service[sockets to fibers]: start failed: " + << ec.message(); + } return !ec; } virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { services::admin::CreateServiceRequest r_socks( - services::fibers_to_sockets::FibersToSockets< - Demux>::GetCreateRequest(relay_fiber_port_, remote_addr_, - remote_port_)); + services::fibers_to_sockets::FibersToSockets::GetCreateRequest( + relay_fiber_port_, remote_addr_, remote_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); auto status = p_service_factory->GetStatus( - r_socks.service_id(), r_socks.parameters(), GetRemoteServiceId(demux)); + r_socks.service_id(), r_socks.parameters(), GetRemoteServiceId(demux)); return status; } virtual void StopLocalServices(Demux& demux) { auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); } @@ -169,14 +162,13 @@ class PortForwading : public BaseUserService return remoteServiceId_; } else { services::admin::CreateServiceRequest r_forward( - services::fibers_to_sockets::FibersToSockets< - Demux>::GetCreateRequest(relay_fiber_port_, remote_addr_, - remote_port_)); + services::fibers_to_sockets::FibersToSockets::GetCreateRequest( + relay_fiber_port_, remote_addr_, remote_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); auto id = p_service_factory->GetIdFromParameters(r_forward.service_id(), - r_forward.parameters()); + r_forward.parameters()); remoteServiceId_ = id; return id; } diff --git a/src/services/user_services/process.h b/src/services/user_services/process.h new file mode 100644 index 00000000..d3ec07f7 --- /dev/null +++ b/src/services/user_services/process.h @@ -0,0 +1,150 @@ +#ifndef SSF_SERVICES_USER_SERVICES_PROCESS_H_ +#define SSF_SERVICES_USER_SERVICES_PROCESS_H_ + +#include + +#include +#include +#include + +#include "core/factories/service_option_factory.h" + +#include "services/admin/requests/create_service_request.h" +#include "services/admin/requests/stop_service_request.h" +#include "services/process/server.h" +#include "services/sockets_to_fibers/sockets_to_fibers.h" +#include "services/user_services/base_user_service.h" + +namespace ssf { +namespace services { + +template +class Process : public BaseUserService { + private: + Process(uint16_t local_port) + : local_port_(local_port), remoteServiceId_(0), localServiceId_(0) {} + + public: + static std::string GetFullParseName() { return "process,X"; } + + static std::string GetParseName() { return "process"; } + + static std::string GetValueName() { return "local_port"; } + + static std::string GetParseDesc() { + return "Open a port on the client side, each connection to that port " + "creates a process with I/O forwarded to/from the server side"; + } + + static std::shared_ptr CreateServiceOptions( + std::string line, boost::system::error_code& ec) { + try { + uint16_t port = (uint16_t)std::stoul(line); + return std::shared_ptr(new Process(port)); + } catch (const std::invalid_argument&) { + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + return std::shared_ptr(nullptr); + } catch (const std::out_of_range&) { + ec.assign(::error::out_of_range, ::error::get_ssf_category()); + return std::shared_ptr(nullptr); + } + } + + static void RegisterToServiceOptionFactory() { + ServiceOptionFactory::RegisterUserServiceParser( + GetParseName(), GetFullParseName(), GetValueName(), GetParseDesc(), + &Process::CreateServiceOptions); + } + + public: + std::vector> + GetRemoteServiceCreateVector() { + std::vector> result; + + services::admin::CreateServiceRequest r_process_server( + services::process::Server::GetCreateRequest(local_port_)); + + result.push_back(r_process_server); + + return result; + }; + + std::vector> GetRemoteServiceStopVector( + Demux& demux) { + std::vector> result; + + auto id = GetRemoteServiceId(demux); + + if (id) { + result.push_back(admin::StopServiceRequest(id)); + } + + return result; + }; + + uint32_t CheckRemoteServiceStatus(Demux& demux) { + services::admin::CreateServiceRequest r_process_server( + services::process::Server::GetCreateRequest(local_port_)); + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + return p_service_factory->GetStatus(r_process_server.service_id(), + r_process_server.parameters(), + GetRemoteServiceId(demux)); + }; + + std::string GetName() { return "process"; }; + + bool StartLocalServices(Demux& demux) { + services::admin::CreateServiceRequest l_forward( + services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( + local_port_, local_port_)); + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + boost::system::error_code ec; + localServiceId_ = p_service_factory->CreateRunNewService( + l_forward.service_id(), l_forward.parameters(), ec); + if (ec) { + SSF_LOG(kLogError) << "user_service[process]: " + << "local_service[sockets to fibers]: start failed: " + << ec.message(); + } + return !ec; + }; + + void StopLocalServices(Demux& demux) { + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + p_service_factory->StopService(localServiceId_); + }; + + private: + uint32_t GetRemoteServiceId(Demux& demux) { + if (remoteServiceId_) { + return remoteServiceId_; + } else { + services::admin::CreateServiceRequest l_forward( + services::process::Server::GetCreateRequest(local_port_)); + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + + auto id = p_service_factory->GetIdFromParameters(l_forward.service_id(), + l_forward.parameters()); + + remoteServiceId_ = id; + return id; + } + } + + private: + uint16_t local_port_; + uint32_t remoteServiceId_; + uint32_t localServiceId_; +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_USER_SERVICES_PROCESS_H_ diff --git a/src/services/user_services/remote_port_forwarding.h b/src/services/user_services/remote_port_forwarding.h index 76fc1d6b..2149e046 100644 --- a/src/services/user_services/remote_port_forwarding.h +++ b/src/services/user_services/remote_port_forwarding.h @@ -34,37 +34,34 @@ namespace ssf { namespace services { template -class RemotePortForwading : public BaseUserService -{ -private: +class RemotePortForwarding : public BaseUserService { + private: typedef boost::asio::fiber::detail::fiber_id::local_port_type local_port_type; private: - RemotePortForwading(uint16_t remote_port, std::string local_addr, + RemotePortForwarding(uint16_t remote_port, std::string local_addr, uint16_t local_port) - : local_port_(local_port), - local_addr_(local_addr), - remote_port_(remote_port), - remoteServiceId_(0), - localServiceId_(0) { + : local_port_(local_port), + local_addr_(local_addr), + remote_port_(remote_port), + remoteServiceId_(0), + localServiceId_(0) { relay_fiber_port_ = remote_port_; } public: - static std::string GetFullParseName() { - return "remote,R"; - } + static std::string GetFullParseName() { return "remote,R"; } - static std::string GetParseName() { - return "remote"; - } + static std::string GetParseName() { return "remote"; } + + static std::string GetValueName() { return "rem_port:dest_ip:dest_port"; } static std::string GetParseDesc() { - return "Forward remote port on given target"; + return "Forward remote port on given target from client"; } public: - static std::shared_ptr CreateServiceOptions( + static std::shared_ptr CreateServiceOptions( std::string line, boost::system::error_code& ec) { using boost::spirit::qi::int_; using boost::spirit::qi::alnum; @@ -75,50 +72,43 @@ class RemotePortForwading : public BaseUserService rule target_pattern = +char_("0-9a-zA-Z.-"); uint16_t listening_port, target_port; std::string target_addr; - str_it begin = line.begin(), - end = line.end(); - bool parsed = boost::spirit::qi::parse(begin, - end, - int_ >> ":" >> target_pattern - >> ":" >> int_, - listening_port, - target_addr, - target_port); + str_it begin = line.begin(), end = line.end(); + bool parsed = boost::spirit::qi::parse( + begin, end, int_ >> ":" >> target_pattern >> ":" >> int_, + listening_port, target_addr, target_port); if (parsed) { - return std::shared_ptr( - new RemotePortForwading(listening_port, target_addr, target_port)); + return std::shared_ptr( + new RemotePortForwarding(listening_port, target_addr, target_port)); } else { - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); - return std::shared_ptr(nullptr); + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + return std::shared_ptr(nullptr); } } static void RegisterToServiceOptionFactory() { ServiceOptionFactory::RegisterUserServiceParser( - GetParseName(), GetFullParseName(), GetParseDesc(), - &RemotePortForwading::CreateServiceOptions); + GetParseName(), GetFullParseName(), GetValueName(), GetParseDesc(), + &RemotePortForwarding::CreateServiceOptions); } - virtual std::string GetName() { - return "remote"; - } + virtual std::string GetName() { return "remote"; } virtual std::vector> - GetRemoteServiceCreateVector() { - std::vector> result; + GetRemoteServiceCreateVector() { + std::vector> result; - services::admin::CreateServiceRequest r_forward( - services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - remote_port_, relay_fiber_port_)); + services::admin::CreateServiceRequest r_forward( + services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( + remote_port_, relay_fiber_port_)); - result.push_back(r_forward); + result.push_back(r_forward); - return result; - } + return result; + } - virtual std::vector> GetRemoteServiceStopVector( - Demux& demux) { + virtual std::vector> + GetRemoteServiceStopVector(Demux& demux) { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -132,15 +122,19 @@ class RemotePortForwading : public BaseUserService virtual bool StartLocalServices(Demux& demux) { services::admin::CreateServiceRequest l_forward( - services::fibers_to_sockets::FibersToSockets< - Demux>::GetCreateRequest(relay_fiber_port_, local_addr_, - local_port_)); + services::fibers_to_sockets::FibersToSockets::GetCreateRequest( + relay_fiber_port_, local_addr_, local_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); boost::system::error_code ec; localServiceId_ = p_service_factory->CreateRunNewService( - l_forward.service_id(), l_forward.parameters(), ec); + l_forward.service_id(), l_forward.parameters(), ec); + if (ec) { + SSF_LOG(kLogError) << "user_service[forward]: " + << "local_service[fibers to sockets]: start failed: " + << ec.message(); + } return !ec; } @@ -149,17 +143,17 @@ class RemotePortForwading : public BaseUserService services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( remote_port_, relay_fiber_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); auto status = p_service_factory->GetStatus(r_forward.service_id(), - r_forward.parameters(), - GetRemoteServiceId(demux)); + r_forward.parameters(), + GetRemoteServiceId(demux)); return status; } virtual void StopLocalServices(Demux& demux) { auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); } @@ -173,9 +167,9 @@ class RemotePortForwading : public BaseUserService remote_port_, relay_fiber_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); auto id = p_service_factory->GetIdFromParameters(r_forward.service_id(), - r_forward.parameters()); + r_forward.parameters()); remoteServiceId_ = id; return id; } diff --git a/src/services/user_services/remote_process.h b/src/services/user_services/remote_process.h new file mode 100644 index 00000000..77c00bd9 --- /dev/null +++ b/src/services/user_services/remote_process.h @@ -0,0 +1,152 @@ +#ifndef SSF_SERVICES_USER_SERVICES_REMOTE_PROCESS_H_ +#define SSF_SERVICES_USER_SERVICES_REMOTE_PROCESS_H_ + +#include + +#include +#include +#include + +#include "core/factories/service_option_factory.h" + +#include "services/admin/requests/create_service_request.h" +#include "services/admin/requests/stop_service_request.h" +#include "services/process/server.h" +#include "services/sockets_to_fibers/sockets_to_fibers.h" +#include "services/user_services/base_user_service.h" + +namespace ssf { +namespace services { + +template +class RemoteProcess : public BaseUserService { + private: + RemoteProcess(uint16_t remote_port) + : remote_port_(remote_port), remoteServiceId_(0), localServiceId_(0) {} + + public: + static std::string GetFullParseName() { return "remote_process,Y"; } + + static std::string GetParseName() { return "remote_process"; } + + static std::string GetValueName() { return "remote_port"; } + + static std::string GetParseDesc() { + return "Open a port on the server side, each connection to that port " + "creates a process with I/O forwarded to/from the client side"; + } + + static std::shared_ptr CreateServiceOptions( + std::string line, boost::system::error_code& ec) { + try { + uint16_t port = (uint16_t)std::stoul(line); + return std::shared_ptr(new RemoteProcess(port)); + } catch (const std::invalid_argument&) { + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + return std::shared_ptr(nullptr); + } catch (const std::out_of_range&) { + ec.assign(::error::out_of_range, ::error::get_ssf_category()); + return std::shared_ptr(nullptr); + } + } + + static void RegisterToServiceOptionFactory() { + ServiceOptionFactory::RegisterUserServiceParser( + GetParseName(), GetFullParseName(), GetValueName(), GetParseDesc(), + &RemoteProcess::CreateServiceOptions); + } + + public: + std::vector> + GetRemoteServiceCreateVector() { + std::vector> result; + + services::admin::CreateServiceRequest r_forward( + services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( + remote_port_, remote_port_)); + + result.push_back(r_forward); + + return result; + }; + + std::vector> GetRemoteServiceStopVector( + Demux& demux) { + std::vector> result; + + auto id = GetRemoteServiceId(demux); + + if (id) { + result.push_back(admin::StopServiceRequest(id)); + } + + return result; + }; + + uint32_t CheckRemoteServiceStatus(Demux& demux) { + services::admin::CreateServiceRequest r_forward( + services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( + remote_port_, remote_port_)); + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + return p_service_factory->GetStatus(r_forward.service_id(), + r_forward.parameters(), + GetRemoteServiceId(demux)); + }; + + std::string GetName() { return "remote_process"; }; + + bool StartLocalServices(Demux& demux) { + services::admin::CreateServiceRequest l_process_server( + services::process::Server::GetCreateRequest(remote_port_)); + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + boost::system::error_code ec; + localServiceId_ = p_service_factory->CreateRunNewService( + l_process_server.service_id(), l_process_server.parameters(), ec); + if (ec) { + SSF_LOG(kLogError) << "user_service[remote process]: " + << "local_service[process]: start failed: " + << ec.message(); + } + return !ec; + }; + + void StopLocalServices(Demux& demux) { + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + p_service_factory->StopService(localServiceId_); + }; + + private: + uint32_t GetRemoteServiceId(Demux& demux) { + if (remoteServiceId_) { + return remoteServiceId_; + } else { + services::admin::CreateServiceRequest r_forward( + services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( + remote_port_, remote_port_)); + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + + auto id = p_service_factory->GetIdFromParameters(r_forward.service_id(), + r_forward.parameters()); + + remoteServiceId_ = id; + return id; + } + } + + private: + uint16_t remote_port_; + uint32_t remoteServiceId_; + uint32_t localServiceId_; +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_USER_SERVICES_PROCESS_H_ diff --git a/src/services/user_services/remote_socks.h b/src/services/user_services/remote_socks.h index e0c664d4..6ad759a0 100644 --- a/src/services/user_services/remote_socks.h +++ b/src/services/user_services/remote_socks.h @@ -11,61 +11,61 @@ #include "common/error/error.h" -#include "services/user_services/base_user_service.h" #include "services/admin/requests/create_service_request.h" #include "services/admin/requests/stop_service_request.h" +#include "services/sockets_to_fibers/sockets_to_fibers.h" +#include "services/socks/socks_server.h" +#include "services/user_services/base_user_service.h" #include "core/factories/service_option_factory.h" -namespace ssf { namespace services { +namespace ssf { +namespace services { template class RemoteSocks : public BaseUserService { private: RemoteSocks(uint16_t remote_port) - : remote_port_(remote_port), remote_service_id_(0), local_service_id_(0) {} + : remote_port_(remote_port), + remote_service_id_(0), + local_service_id_(0) {} public: - static std::string GetFullParseName() { - return "remote_socks,F"; - } + static std::string GetFullParseName() { return "remote_socks,F"; } - static std::string GetParseName() { - return "remote_socks"; - } + static std::string GetParseName() { return "remote_socks"; } + + static std::string GetValueName() { return "remote_port"; } static std::string GetParseDesc() { return "Run a proxy socks on local host"; } public: - static std::shared_ptr - CreateServiceOptions(const std::string &line, - boost::system::error_code& ec) { + static std::shared_ptr CreateServiceOptions( + const std::string& line, boost::system::error_code& ec) { try { uint16_t port = (uint16_t)std::stoul(line); return std::shared_ptr(new RemoteSocks(port)); } catch (const std::invalid_argument&) { - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return std::shared_ptr(nullptr); } catch (const std::out_of_range&) { - ec.assign(ssf::error::out_of_range, ssf::error::get_ssf_category()); + ec.assign(::error::out_of_range, ::error::get_ssf_category()); return std::shared_ptr(nullptr); } } static void RegisterToServiceOptionFactory() { ServiceOptionFactory::RegisterUserServiceParser( - GetParseName(), GetFullParseName(), GetParseDesc(), - &RemoteSocks::CreateServiceOptions); + GetParseName(), GetFullParseName(), GetValueName(), GetParseDesc(), + &RemoteSocks::CreateServiceOptions); } - virtual std::string GetName() { - return "remote_socks"; - } + virtual std::string GetName() { return "remote_socks"; } virtual std::vector> - GetRemoteServiceCreateVector() { + GetRemoteServiceCreateVector() { std::vector> result; services::admin::CreateServiceRequest r_forward( @@ -77,8 +77,8 @@ class RemoteSocks : public BaseUserService { return result; } - virtual std::vector> GetRemoteServiceStopVector( - Demux& demux) { + virtual std::vector> + GetRemoteServiceStopVector(Demux& demux) { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -92,13 +92,19 @@ class RemoteSocks : public BaseUserService { virtual bool StartLocalServices(Demux& demux) { services::admin::CreateServiceRequest l_socks( - services::socks::SocksServer::GetCreateRequest(remote_port_)); + services::socks::SocksServer::GetCreateRequest(remote_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); boost::system::error_code ec; local_service_id_ = p_service_factory->CreateRunNewService( - l_socks.service_id(), l_socks.parameters(), ec); + l_socks.service_id(), l_socks.parameters(), ec); + + if (ec) { + SSF_LOG(kLogError) << "user_service[remote socks]: " + << "local_service[socks server]: start failed: " + << ec.message(); + } return !ec; } @@ -107,7 +113,7 @@ class RemoteSocks : public BaseUserService { services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( remote_port_, remote_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); auto status = p_service_factory->GetStatus(r_forward.service_id(), r_forward.parameters(), GetRemoteServiceId(demux)); @@ -117,7 +123,7 @@ class RemoteSocks : public BaseUserService { virtual void StopLocalServices(Demux& demux) { auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(local_service_id_); } @@ -133,7 +139,7 @@ class RemoteSocks : public BaseUserService { auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); auto id = p_service_factory->GetIdFromParameters(r_forward.service_id(), - r_forward.parameters()); + r_forward.parameters()); remote_service_id_ = id; return id; } diff --git a/src/services/user_services/socks.h b/src/services/user_services/socks.h index 72db7b88..7e6216bb 100644 --- a/src/services/user_services/socks.h +++ b/src/services/user_services/socks.h @@ -11,72 +11,71 @@ #include "common/error/error.h" -#include "services/user_services/base_user_service.h" #include "services/admin/requests/create_service_request.h" #include "services/admin/requests/stop_service_request.h" +#include "services/sockets_to_fibers/sockets_to_fibers.h" +#include "services/socks/socks_server.h" +#include "services/user_services/base_user_service.h" #include "core/factories/service_option_factory.h" -namespace ssf { namespace services { +namespace ssf { +namespace services { template class Socks : public BaseUserService { -private: - Socks(uint16_t local_port) - : local_port_(local_port), remoteServiceId_(0), localServiceId_(0) {} + private: + Socks(uint16_t local_port) + : local_port_(local_port), remoteServiceId_(0), localServiceId_(0) {} -public: - static std::string GetFullParseName() { - return "socks,D"; - } + public: + static std::string GetFullParseName() { return "socks,D"; } - static std::string GetParseName() { - return "socks"; - } + static std::string GetParseName() { return "socks"; } + + static std::string GetValueName() { return "local_port"; } static std::string GetParseDesc() { return "Run a proxy socks on remote host"; } -public: + public: static std::shared_ptr CreateServiceOptions( std::string line, boost::system::error_code& ec) { try { uint16_t port = (uint16_t)std::stoul(line); return std::shared_ptr(new Socks(port)); } catch (const std::invalid_argument&) { - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return std::shared_ptr(nullptr); } catch (const std::out_of_range&) { - ec.assign(ssf::error::out_of_range, ssf::error::get_ssf_category()); + ec.assign(::error::out_of_range, ::error::get_ssf_category()); return std::shared_ptr(nullptr); } } static void RegisterToServiceOptionFactory() { ServiceOptionFactory::RegisterUserServiceParser( - GetParseName(), GetFullParseName(), GetParseDesc(), - &Socks::CreateServiceOptions); + GetParseName(), GetFullParseName(), GetValueName(), GetParseDesc(), + &Socks::CreateServiceOptions); } - virtual std::string GetName() { - return "socks"; - } + virtual std::string GetName() { return "socks"; } virtual std::vector> - GetRemoteServiceCreateVector() { + GetRemoteServiceCreateVector() { std::vector> result; services::admin::CreateServiceRequest r_socks( - services::socks::SocksServer::GetCreateRequest(local_port_)); + services::socks::SocksServer::GetCreateRequest(local_port_)); result.push_back(r_socks); return result; } - virtual std::vector> GetRemoteServiceStopVector( - Demux& demux) { + virtual std::vector> + GetRemoteServiceStopVector(Demux& demux) { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -98,6 +97,12 @@ class Socks : public BaseUserService { boost::system::error_code ec; localServiceId_ = p_service_factory->CreateRunNewService( l_forward.service_id(), l_forward.parameters(), ec); + + if (ec) { + SSF_LOG(kLogError) << "user_service[socks]: " + << "local_service[sockets to fibers]: start failed: " + << ec.message(); + } return !ec; } @@ -130,7 +135,7 @@ class Socks : public BaseUserService { ServiceFactoryManager::GetServiceFactory(&demux); auto id = p_service_factory->GetIdFromParameters(l_forward.service_id(), - l_forward.parameters()); + l_forward.parameters()); remoteServiceId_ = id; return id; diff --git a/src/services/user_services/udp_port_forwarding.h b/src/services/user_services/udp_port_forwarding.h index 9557c18e..51a6daa1 100644 --- a/src/services/user_services/udp_port_forwarding.h +++ b/src/services/user_services/udp_port_forwarding.h @@ -34,38 +34,35 @@ namespace ssf { namespace services { template -class UdpPortForwading : public BaseUserService -{ +class UdpPortForwarding : public BaseUserService { private: typedef boost::asio::fiber::detail::fiber_id::local_port_type local_port_type; private: - UdpPortForwading(uint16_t local_port, std::string remote_addr, - uint16_t remote_port) - : local_port_(local_port), - remote_addr_(remote_addr), - remote_port_(remote_port), - remoteServiceId_(0), - localServiceId_(0) { + UdpPortForwarding(uint16_t local_port, std::string remote_addr, + uint16_t remote_port) + : local_port_(local_port), + remote_addr_(remote_addr), + remote_port_(remote_port), + remoteServiceId_(0), + localServiceId_(0) { relay_fiber_port_ = remote_port_ + (1 << 16); } public: - static std::string GetFullParseName() { - return "udpforward,U"; - } + static std::string GetFullParseName() { return "udpforward,U"; } - static std::string GetParseName() { - return "udpforward"; - } + static std::string GetParseName() { return "udpforward"; } + + static std::string GetValueName() { return "local_port:dest_ip:dest_port"; } static std::string GetParseDesc() { - return "Forward UDP local port on given target"; + return "Forward UDP local port on given target from server"; } public: - static std::shared_ptr CreateServiceOptions( - std::string line, boost::system::error_code& ec) { + static std::shared_ptr CreateServiceOptions( + std::string line, boost::system::error_code& ec) { using boost::spirit::qi::int_; using boost::spirit::qi::alnum; using boost::spirit::qi::char_; @@ -75,42 +72,36 @@ class UdpPortForwading : public BaseUserService rule target_pattern = +char_("0-9a-zA-Z.-"); uint16_t listening_port, target_port; std::string target_addr; - str_it begin = line.begin(), - end = line.end(); - bool parsed = boost::spirit::qi::parse(begin, - end, - int_ >> ":" >> target_pattern - >> ":" >> int_, - listening_port, - target_addr, - target_port); + str_it begin = line.begin(), end = line.end(); + bool parsed = boost::spirit::qi::parse( + begin, end, int_ >> ":" >> target_pattern >> ":" >> int_, + listening_port, target_addr, target_port); if (parsed) { - return std::shared_ptr( - new UdpPortForwading(listening_port, target_addr, target_port)); + return std::shared_ptr( + new UdpPortForwarding(listening_port, target_addr, target_port)); } else { - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); - return std::shared_ptr(nullptr); + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + return std::shared_ptr(nullptr); } } static void RegisterToServiceOptionFactory() { ServiceOptionFactory::RegisterUserServiceParser( - GetParseName(), GetFullParseName(), GetParseDesc(), - &UdpPortForwading::CreateServiceOptions); + GetParseName(), GetFullParseName(), GetValueName(), GetParseDesc(), + &UdpPortForwarding::CreateServiceOptions); } - virtual std::string GetName() { - return "udpforward"; - } + virtual std::string GetName() { return "udpforward"; } virtual std::vector> - GetRemoteServiceCreateVector() { + GetRemoteServiceCreateVector() { std::vector> result; services::admin::CreateServiceRequest r_forward( - services::fibers_to_datagrams::FibersToDatagrams::GetCreateRequest( - relay_fiber_port_, remote_addr_, remote_port_)); + services::fibers_to_datagrams::FibersToDatagrams< + Demux>::GetCreateRequest(relay_fiber_port_, remote_addr_, + remote_port_)); result.push_back(r_forward); @@ -118,7 +109,7 @@ class UdpPortForwading : public BaseUserService } virtual std::vector> - GetRemoteServiceStopVector(Demux& demux) { + GetRemoteServiceStopVector(Demux& demux) { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -132,32 +123,40 @@ class UdpPortForwading : public BaseUserService virtual bool StartLocalServices(Demux& demux) { services::admin::CreateServiceRequest l_forward( - services::datagrams_to_fibers::DatagramsToFibers::GetCreateRequest( - local_port_, relay_fiber_port_)); + services::datagrams_to_fibers::DatagramsToFibers< + Demux>::GetCreateRequest(local_port_, relay_fiber_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); boost::system::error_code ec; localServiceId_ = p_service_factory->CreateRunNewService( - l_forward.service_id(), l_forward.parameters(), ec); + l_forward.service_id(), l_forward.parameters(), ec); + + if (ec) { + SSF_LOG(kLogError) << "user_service[udp forward]: " + << "local_service[datagrams to fibers]: start failed: " + << ec.message(); + } + return !ec; } virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { services::admin::CreateServiceRequest r_socks( - services::fibers_to_datagrams::FibersToDatagrams::GetCreateRequest( - relay_fiber_port_, remote_addr_, remote_port_)); + services::fibers_to_datagrams::FibersToDatagrams< + Demux>::GetCreateRequest(relay_fiber_port_, remote_addr_, + remote_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); auto status = p_service_factory->GetStatus( - r_socks.service_id(), r_socks.parameters(), GetRemoteServiceId(demux)); + r_socks.service_id(), r_socks.parameters(), GetRemoteServiceId(demux)); return status; } virtual void StopLocalServices(Demux& demux) { auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); } @@ -167,11 +166,12 @@ class UdpPortForwading : public BaseUserService return remoteServiceId_; } else { services::admin::CreateServiceRequest r_forward( - services::fibers_to_datagrams::FibersToDatagrams::GetCreateRequest( - relay_fiber_port_, remote_addr_, remote_port_)); + services::fibers_to_datagrams::FibersToDatagrams< + Demux>::GetCreateRequest(relay_fiber_port_, remote_addr_, + remote_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); auto id = p_service_factory->GetIdFromParameters(r_forward.service_id(), r_forward.parameters()); remoteServiceId_ = id; diff --git a/src/services/user_services/udp_remote_port_forwarding.h b/src/services/user_services/udp_remote_port_forwarding.h index b9551ef0..4a4bf747 100644 --- a/src/services/user_services/udp_remote_port_forwarding.h +++ b/src/services/user_services/udp_remote_port_forwarding.h @@ -34,38 +34,35 @@ namespace ssf { namespace services { template -class UdpRemotePortForwading : public BaseUserService -{ +class UdpRemotePortForwarding : public BaseUserService { private: typedef boost::asio::fiber::detail::fiber_id::local_port_type local_port_type; private: - UdpRemotePortForwading(uint16_t remote_port, std::string local_addr, - uint16_t local_port) - : local_port_(local_port), - local_addr_(local_addr), - remote_port_(remote_port), - remoteServiceId_(0), - localServiceId_(0) { + UdpRemotePortForwarding(uint16_t remote_port, std::string local_addr, + uint16_t local_port) + : local_port_(local_port), + local_addr_(local_addr), + remote_port_(remote_port), + remoteServiceId_(0), + localServiceId_(0) { relay_fiber_port_ = remote_port_ + (1 << 16); } public: - static std::string GetFullParseName() { - return "udpremote,V"; - } + static std::string GetFullParseName() { return "udpremote,V"; } - static std::string GetParseName() { - return "udpremote"; - } + static std::string GetParseName() { return "udpremote"; } + + static std::string GetValueName() { return "rem_port:dest_ip:dest_port"; } static std::string GetParseDesc() { - return "Forward remote udp port on given target"; + return "Forward remote udp port on given target from client"; } public: - static std::shared_ptr CreateServiceOptions( - std::string line, boost::system::error_code& ec) { + static std::shared_ptr CreateServiceOptions( + std::string line, boost::system::error_code& ec) { using boost::spirit::qi::int_; using boost::spirit::qi::alnum; using boost::spirit::qi::char_; @@ -75,50 +72,43 @@ class UdpRemotePortForwading : public BaseUserService rule target_pattern = +char_("0-9a-zA-Z.-"); uint16_t listening_port, target_port; std::string target_addr; - str_it begin = line.begin(), - end = line.end(); - bool parsed = boost::spirit::qi::parse(begin, - end, - int_ >> ":" >> target_pattern - >> ":" >> int_, - listening_port, - target_addr, - target_port); + str_it begin = line.begin(), end = line.end(); + bool parsed = boost::spirit::qi::parse( + begin, end, int_ >> ":" >> target_pattern >> ":" >> int_, + listening_port, target_addr, target_port); if (parsed) { - return std::shared_ptr( - new UdpRemotePortForwading(listening_port, target_addr, target_port)); + return std::shared_ptr( + new UdpRemotePortForwarding(listening_port, target_addr, target_port)); } else { - ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); - return std::shared_ptr(nullptr); + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + return std::shared_ptr(nullptr); } } static void RegisterToServiceOptionFactory() { ServiceOptionFactory::RegisterUserServiceParser( - GetParseName(), GetFullParseName(), GetParseDesc(), - &UdpRemotePortForwading::CreateServiceOptions); + GetParseName(), GetFullParseName(), GetValueName(), GetParseDesc(), + &UdpRemotePortForwarding::CreateServiceOptions); } - virtual std::string GetName() { - return "udpremote"; - } + virtual std::string GetName() { return "udpremote"; } virtual std::vector> - GetRemoteServiceCreateVector() { - std::vector> result; + GetRemoteServiceCreateVector() { + std::vector> result; - services::admin::CreateServiceRequest r_forward( - services::datagrams_to_fibers::DatagramsToFibers:: - GetCreateRequest(remote_port_, relay_fiber_port_)); + services::admin::CreateServiceRequest r_forward( + services::datagrams_to_fibers::DatagramsToFibers< + Demux>::GetCreateRequest(remote_port_, relay_fiber_port_)); - result.push_back(r_forward); + result.push_back(r_forward); - return result; - } + return result; + } - virtual std::vector> GetRemoteServiceStopVector( - Demux& demux) { + virtual std::vector> + GetRemoteServiceStopVector(Demux& demux) { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -132,33 +122,41 @@ class UdpRemotePortForwading : public BaseUserService virtual bool StartLocalServices(Demux& demux) { services::admin::CreateServiceRequest l_forward( - services::fibers_to_datagrams::FibersToDatagrams::GetCreateRequest( - relay_fiber_port_, local_addr_, local_port_)); + services::fibers_to_datagrams::FibersToDatagrams< + Demux>::GetCreateRequest(relay_fiber_port_, local_addr_, + local_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); boost::system::error_code ec; localServiceId_ = p_service_factory->CreateRunNewService( - l_forward.service_id(), l_forward.parameters(), ec); + l_forward.service_id(), l_forward.parameters(), ec); + + if (ec) { + SSF_LOG(kLogError) << "user_service[remote udp forward]: " + << "local_service[fibers to datagrams]: start failed: " + << ec.message(); + } + return !ec; } virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { services::admin::CreateServiceRequest r_forward( - services::datagrams_to_fibers::DatagramsToFibers::GetCreateRequest( - remote_port_, relay_fiber_port_)); + services::datagrams_to_fibers::DatagramsToFibers< + Demux>::GetCreateRequest(remote_port_, relay_fiber_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); auto status = p_service_factory->GetStatus(r_forward.service_id(), - r_forward.parameters(), - GetRemoteServiceId(demux)); + r_forward.parameters(), + GetRemoteServiceId(demux)); return status; } virtual void StopLocalServices(Demux& demux) { auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); } @@ -168,13 +166,13 @@ class UdpRemotePortForwading : public BaseUserService return remoteServiceId_; } else { services::admin::CreateServiceRequest r_forward( - services::datagrams_to_fibers::DatagramsToFibers::GetCreateRequest( - remote_port_, relay_fiber_port_)); + services::datagrams_to_fibers::DatagramsToFibers< + Demux>::GetCreateRequest(remote_port_, relay_fiber_port_)); auto p_service_factory = - ServiceFactoryManager::GetServiceFactory(&demux); + ServiceFactoryManager::GetServiceFactory(&demux); auto id = p_service_factory->GetIdFromParameters(r_forward.service_id(), - r_forward.parameters()); + r_forward.parameters()); remoteServiceId_ = id; return id; } diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index eca70c2c..ce82efe7 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -1,7 +1,7 @@ -project(RUN_TESTS CXX) +cmake_minimum_required(VERSION 2.8) enable_testing() - + include_directories( ${OpenSSL_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) @@ -10,309 +10,6 @@ include_directories( ${GTEST_ROOT_DIR}/include ) -# --- Load config test -set(load_config_test_source_files - "load_config_tests.cpp" - ${COMMON_CONFIG_FILES} - ${COMMON_ERROR_FILES} -) - -add_target("load_config_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${load_config_test_source_files} -) -target_link_libraries(load_config_tests gtest gtest_main) -add_test( - NAME load_config_tests - COMMAND $) - -# --- Fiber asio tests -set(fiber_asio_test_source_files - "fiber_asio_tests.cpp" - ${COMMON_BOOST_FIBER_FILES} - ${COMMON_ERROR_FILES} -) - -add_target("fiber_asio_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${fiber_asio_test_source_files} -) -target_link_libraries(fiber_asio_tests gtest gtest_main) -add_test( - NAME fiber_asio_tests - COMMAND $) - -# --- SSF Client Server tests -set(ssf_client_server_source_files - "ssf_client_server_tests.cpp" - ${SSF_SOURCES} -) - -add_target("ssf_client_server_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${ssf_client_server_source_files} -) -target_link_libraries(ssf_client_server_tests gtest gtest_main) -add_test( - NAME ssf_client_server_tests - COMMAND $) - -# --- SSF Client Server cipher suites tests -set(ssf_client_server_cipher_suites_source_files - "ssf_client_server_cipher_suites_tests.cpp" - ${SSF_SOURCES} -) - -add_target("ssf_client_server_cipher_suites_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${ssf_client_server_cipher_suites_source_files} -) -target_link_libraries(ssf_client_server_cipher_suites_tests gtest gtest_main) -add_test( - NAME ssf_client_server_cipher_suites_tests - COMMAND $) - -# --- Socks test -set(socks_test_source_files - "socks_tests.cpp" - ${SSF_SOURCES} -) - -add_target("socks_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${socks_test_source_files} -) -target_link_libraries(socks_tests gtest gtest_main) -add_test( - NAME socks_tests - COMMAND $) - -# --- Remote socks test -set(remote_socks_test_source_files - "remote_socks_tests.cpp" - ${SSF_SOURCES} -) - -add_target("remote_socks_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${remote_socks_test_source_files} -) -target_link_libraries(remote_socks_tests gtest gtest_main) -add_test( - NAME remote_socks_tests - COMMAND $) - -# --- Stream forward test -set(stream_forwarding_test_source_files - "stream_forwarding_tests.cpp" - ${SSF_SOURCES} -) - -add_target("stream_forward_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${stream_forwarding_test_source_files} -) -target_link_libraries(stream_forward_tests gtest gtest_main) -add_test( - NAME stream_forward_tests - COMMAND $) - -# --- Remote stream forward test -set(remote_stream_forwarding_test_source_files - "remote_stream_forwarding_tests.cpp" - ${SSF_SOURCES} -) - -add_target("remote_stream_forward_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${remote_stream_forwarding_test_source_files} -) -target_link_libraries(remote_stream_forward_tests gtest gtest_main) -add_test( - NAME remote_stream_forward_tests - COMMAND $) - -# --- UDP forward test -set(udp_test_source_files - "udp_forwarding_tests.cpp" - ${SSF_SOURCES} -) - -add_target("udp_forward_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${udp_test_source_files} -) -target_link_libraries(udp_forward_tests gtest gtest_main) -add_test( - NAME udp_forward_tests - COMMAND $) - -# --- Remote UDP forward test -set(remote_udp_test_source_files - "remote_udp_forwarding_tests.cpp" - ${SSF_SOURCES} -) - -add_target("remote_udp_forward_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${remote_udp_test_source_files} -) -target_link_libraries(remote_udp_forward_tests gtest gtest_main) -add_test( - NAME remote_udp_forward_tests - COMMAND $) - -# --- Bouncing chain test -set(bouncing_test_source_files - "bouncing_tests.cpp" - ${SSF_SOURCES} -) - -add_target("bouncing_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${bouncing_test_source_files} -) -target_link_libraries(bouncing_tests gtest gtest_main) -add_test( - NAME bouncing_tests - COMMAND $) - - -# --- File copy from client test - -file(MAKE_DIRECTORY ${project_BINARY_DIR}/src/tests/files_to_copy) -file(MAKE_DIRECTORY ${project_BINARY_DIR}/src/tests/files_copied) - -FILE(GLOB_RECURSE TEST_FILES_TO_COPY - "${project_SRC_DIR}/tests/files_to_copy/*" -) - -file(COPY ${TEST_FILES_TO_COPY} DESTINATION ${project_BINARY_DIR}/src/tests/files_to_copy) - -set(file_copy_from_client_files - "file_copy_from_client_tests.cpp" - ${SSF_SOURCES} -) - -add_target("file_copy_from_client_tests" - TYPE - executable - LINKS - ${OpenSSL_LIBRARIES} - ${Boost_LIBRARIES} - ${PLATFORM_SPECIFIC_LIB_DEP} - PREFIX_SKIP .*/src - HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" - FILES - ${file_copy_from_client_files} -) -target_link_libraries(file_copy_from_client_tests gtest gtest_main) -add_test( - NAME file_copy_from_client_tests - COMMAND $) - - -# --- Unit tests list -project_group( - "Unit tests" - load_config_tests - fiber_asio_tests - ssf_client_server_tests - ssf_client_server_cipher_suites_tests - socks_tests - remote_socks_tests - stream_forward_tests - remote_stream_forward_tests - udp_forward_tests - remote_udp_forward_tests - bouncing_tests - file_copy_from_client_tests -) \ No newline at end of file +add_subdirectory(config) +add_subdirectory(network) +add_subdirectory(services) \ No newline at end of file diff --git a/src/tests/bouncing_tests.cpp b/src/tests/bouncing_tests.cpp deleted file mode 100644 index 72d3c792..00000000 --- a/src/tests/bouncing_tests.cpp +++ /dev/null @@ -1,169 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "common/config/config.h" - -#include "core/client/client.h" -#include "core/server/server.h" - -#include "core/network_virtual_layer_policies/link_policies/ssl_policy.h" -#include "core/network_virtual_layer_policies/link_policies/tcp_policy.h" -#include "core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h" -#include "core/network_virtual_layer_policies/bounce_protocol_policy.h" -#include "core/parser/bounce_parser.h" -#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" - -#include "services/initialisation.h" -#include "services/user_services/port_forwarding.h" - -//----------------------------------------------------------------------------- -TEST(BouncingTests, BouncingChain) { - using BounceParser = ssf::parser::BounceParser; - using Server = - ssf::SSFServer; - using Client = - ssf::SSFClient; - - typedef boost::asio::ip::tcp::socket socket; - typedef ssf::SSLWrapper<> ssl_socket; - typedef boost::asio::fiber::basic_fiber_demux Demux; - typedef ssf::services::BaseUserService::BaseUserServicePtr - BaseUserServicePtr; - - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - - boost::recursive_mutex mutex; - std::promise network_set; - std::promise transport_set; - std::promise service_set; - - boost::asio::io_service client_io_service; - std::unique_ptr p_client_worker( - new boost::asio::io_service::work(client_io_service)); - boost::thread_group client_threads; - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - client_threads.create_thread([&]() { client_io_service.run(); }); - } - - boost::asio::io_service bouncer_io_service; - std::unique_ptr p_bouncer_worker( - new boost::asio::io_service::work(bouncer_io_service)); - boost::thread_group bouncer_threads; - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - bouncer_threads.create_thread([&]() { bouncer_io_service.run(); }); - } - - boost::asio::io_service server_io_service; - std::unique_ptr p_server_worker( - new boost::asio::io_service::work(server_io_service)); - boost::thread_group server_threads; - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - server_threads.create_thread([&]() { server_io_service.run(); }); - } - - std::list servers; - std::list bouncers; - - uint16_t initial_server_port = 10000; - uint8_t nb_of_servers = 10; - - { - ssf::Config ssf_config; - ++initial_server_port; - servers.emplace_front(server_io_service, ssf_config, initial_server_port); - servers.front().run(); - bouncers.emplace_front(std::string("127.0.0.1:") + - std::to_string(initial_server_port)); - } - - for (uint8_t i = 0; i < nb_of_servers - 1; ++i) { - ssf::Config ssf_config; - ++initial_server_port; - servers.emplace_front(bouncer_io_service, ssf_config, initial_server_port); - servers.front().run(); - bouncers.emplace_front(std::string("127.0.0.1:") + - std::to_string(initial_server_port)); - } - - std::vector client_options; - std::string error_msg; - - std::map params; - - auto first = bouncers.front(); - bouncers.pop_front(); - params["remote_addr"] = BounceParser::GetRemoteAddress(first); - params["remote_port"] = BounceParser::GetRemotePort(first); - - std::ostringstream ostrs; - boost::archive::text_oarchive ar(ostrs); - ar << BOOST_SERIALIZATION_NVP(bouncers); - - params["bouncing_nodes"] = ostrs.str(); - - ssf::Config ssf_config; - - auto callback = [&mutex, &network_set, &service_set, &transport_set]( - ssf::services::initialisation::type type, - BaseUserServicePtr p_user_service, - const boost::system::error_code& ec) { - boost::recursive_mutex::scoped_lock lock(mutex); - if (type == ssf::services::initialisation::NETWORK) { - network_set.set_value(!ec); - if (ec) { - service_set.set_value(false); - transport_set.set_value(false); - } - - return; - } - - if (type == ssf::services::initialisation::TRANSPORT) { - transport_set.set_value(!ec); - - return; - } - }; - - Client client(client_io_service, "127.0.0.1", - std::to_string(initial_server_port - nb_of_servers), - ssf_config, client_options, std::move(callback)); - client.run(params); - - network_set.get_future().wait(); - transport_set.get_future().wait(); - - { boost::recursive_mutex::scoped_lock lock(mutex); } - - client.stop(); - for (auto& server : servers) { - server.stop(); - } - - - p_client_worker.reset(); - client_threads.join_all(); - client_io_service.stop(); - - p_bouncer_worker.reset(); - bouncer_threads.join_all(); - bouncer_io_service.stop(); - - p_server_worker.reset(); - server_threads.join_all(); - server_io_service.stop(); - - servers.clear(); -} \ No newline at end of file diff --git a/src/tests/config/CMakeLists.txt b/src/tests/config/CMakeLists.txt new file mode 100644 index 00000000..ad293c2b --- /dev/null +++ b/src/tests/config/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 2.8) + +set(config_tests_group_name "Unit Tests/Config") + +set(project_BINARY_CONFIG_TESTS_DIR "${project_BINARY_DIR}/src/tests/config") + +# --- copy test config files +set(project_BINARY_CONFIG_TESTS_CONF_DIR ${project_BINARY_CONFIG_TESTS_DIR}/config_files) + +file(GLOB_RECURSE CONFIG_TEST_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/config_files/*.json") + +file(MAKE_DIRECTORY ${project_BINARY_CONFIG_TESTS_CONF_DIR}) +file(COPY ${CONFIG_TEST_FILES} + DESTINATION ${project_BINARY_CONFIG_TESTS_CONF_DIR}) + +# --- Config tests + +# --- Load config test +set(load_config_test_source_files + "load_config_tests.cpp" + ${COMMON_CONFIG_FILES} + ${COMMON_ERROR_FILES} +) + +add_target("load_config_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${load_config_test_source_files} +) +project_group(${config_tests_group_name} load_config_tests) \ No newline at end of file diff --git a/src/tests/config/config_files/complete.json b/src/tests/config/config_files/complete.json new file mode 100644 index 00000000..67c9773a --- /dev/null +++ b/src/tests/config/config_files/complete.json @@ -0,0 +1,29 @@ +{ + "ssf": { + "tls" : { + "ca_cert_path": "test_ca_path", + "cert_path": "test_cert_path", + "key_path": "test_key_path", + "key_password": "test_key_password", + "dh_path": "test_dh_path", + "cipher_alg": "test_cipher_alg" + }, + "http_proxy" : { + "host": "127.0.0.1", + "port": "8080", + "credentials": { + "username": "test_user", + "password": "test_password", + "domain": "test_domain", + "reuse_ntlm": "false", + "reuse_kerb": "false" + } + }, + "services" : { + "shell": { + "path": "/bin/custom_path", + "args": "-custom args" + } + } + } +} diff --git a/src/tests/config/config_files/empty.json b/src/tests/config/config_files/empty.json new file mode 100644 index 00000000..e69de29b diff --git a/src/tests/config/config_files/proxy.json b/src/tests/config/config_files/proxy.json new file mode 100644 index 00000000..18f81e80 --- /dev/null +++ b/src/tests/config/config_files/proxy.json @@ -0,0 +1,15 @@ +{ + "ssf": { + "http_proxy" : { + "host": "127.0.0.1", + "port": "8080", + "credentials": { + "username": "test_user", + "password": "test_password", + "domain": "test_domain", + "reuse_ntlm": "false", + "reuse_kerb": "false" + } + } + } +} diff --git a/src/tests/config/config_files/services.json b/src/tests/config/config_files/services.json new file mode 100644 index 00000000..560c8fd3 --- /dev/null +++ b/src/tests/config/config_files/services.json @@ -0,0 +1,10 @@ +{ + "ssf": { + "services" : { + "shell": { + "path": "/bin/custom_path", + "args": "-custom args" + } + } + } +} diff --git a/src/tests/config/config_files/tls_complete.json b/src/tests/config/config_files/tls_complete.json new file mode 100644 index 00000000..610bf437 --- /dev/null +++ b/src/tests/config/config_files/tls_complete.json @@ -0,0 +1,12 @@ +{ + "ssf": { + "tls" : { + "ca_cert_path": "test_ca_path", + "cert_path": "test_cert_path", + "key_path": "test_key_path", + "key_password": "test_key_password", + "dh_path": "test_dh_path", + "cipher_alg": "test_cipher_alg" + } + } +} diff --git a/src/tests/config/config_files/tls_partial.json b/src/tests/config/config_files/tls_partial.json new file mode 100644 index 00000000..d9ac038a --- /dev/null +++ b/src/tests/config/config_files/tls_partial.json @@ -0,0 +1,9 @@ +{ + "ssf": { + "tls" : { + "ca_cert_path": "test_ca_path", + "key_path": "test_key_path", + "dh_path": "test_dh_path" + } + } +} diff --git a/src/tests/config/config_files/wrong_format.json b/src/tests/config/config_files/wrong_format.json new file mode 100644 index 00000000..f80706b0 --- /dev/null +++ b/src/tests/config/config_files/wrong_format.json @@ -0,0 +1,6 @@ +{ + "ssf": { + "tls" : { + "ca_cert_path": "test" + } + } diff --git a/src/tests/config/load_config_tests.cpp b/src/tests/config/load_config_tests.cpp new file mode 100644 index 00000000..daa05d3c --- /dev/null +++ b/src/tests/config/load_config_tests.cpp @@ -0,0 +1,130 @@ +#include +#include +#include + +#include +#include +#include + +#include "common/config/config.h" + +class LoadConfigTest : public ::testing::Test { + protected: + ssf::config::Config config_; +}; + +TEST_F(LoadConfigTest, LoadNoFileTest) { + boost::system::error_code ec; + + config_.Update("", ec); + + ASSERT_EQ(ec.value(), 0) << "Success if no file given"; +} + +TEST_F(LoadConfigTest, LoadNonExistantFileTest) { + boost::system::error_code ec; + config_.Update("./config_files/unknown.json", ec); + + ASSERT_NE(ec.value(), 0) << "No success if file not existant"; +} + +TEST_F(LoadConfigTest, LoadEmptyFileTest) { + boost::system::error_code ec; + + config_.Update("./config_files/empty.json", ec); + + ASSERT_NE(ec.value(), 0) << "No success if file empty"; +} + +TEST_F(LoadConfigTest, LoadWrongFormatFileTest) { + boost::system::error_code ec; + + config_.Update("./config_files/wrong_format.json", ec); + + ASSERT_NE(ec.value(), 0) << "No success if wrong file format"; +} + +TEST_F(LoadConfigTest, LoadTlsPartialFileTest) { + boost::system::error_code ec; + + config_.Update("./config_files/tls_partial.json", ec); + + ASSERT_EQ(ec.value(), 0) << "Success if partial file format"; + ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); + ASSERT_EQ(config_.tls().cert_path(), "./certs/certificate.crt"); + ASSERT_EQ(config_.tls().key_path(), "test_key_path"); + ASSERT_EQ(config_.tls().key_password(), ""); + ASSERT_EQ(config_.tls().dh_path(), "test_dh_path"); + ASSERT_EQ(config_.tls().cipher_alg(), "DHE-RSA-AES256-GCM-SHA384"); +} + +TEST_F(LoadConfigTest, LoadTlsCompleteFileTest) { + boost::system::error_code ec; + + config_.Update("./config_files/tls_complete.json", ec); + + ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; + ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); + ASSERT_EQ(config_.tls().cert_path(), "test_cert_path"); + ASSERT_EQ(config_.tls().key_path(), "test_key_path"); + ASSERT_EQ(config_.tls().key_password(), "test_key_password"); + ASSERT_EQ(config_.tls().dh_path(), "test_dh_path"); + ASSERT_EQ(config_.tls().cipher_alg(), "test_cipher_alg"); + ASSERT_EQ(config_.http_proxy().host(), ""); + ASSERT_EQ(config_.http_proxy().port(), ""); + ASSERT_EQ(config_.http_proxy().username(), ""); + ASSERT_EQ(config_.http_proxy().domain(), ""); + ASSERT_EQ(config_.http_proxy().password(), ""); + ASSERT_EQ(config_.http_proxy().reuse_kerb(), true); + ASSERT_EQ(config_.http_proxy().reuse_ntlm(), true); + ASSERT_EQ(config_.services().process().path(), + SSF_PROCESS_SERVICE_BINARY_PATH); + ASSERT_EQ(config_.services().process().args(), ""); +} + +TEST_F(LoadConfigTest, LoadProxyFileTest) { + boost::system::error_code ec; + + config_.Update("./config_files/proxy.json", ec); + ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; + ASSERT_EQ(config_.http_proxy().host(), "127.0.0.1"); + ASSERT_EQ(config_.http_proxy().port(), "8080"); + ASSERT_EQ(config_.http_proxy().username(), "test_user"); + ASSERT_EQ(config_.http_proxy().domain(), "test_domain"); + ASSERT_EQ(config_.http_proxy().password(), "test_password"); + ASSERT_EQ(config_.http_proxy().reuse_ntlm(), false); + ASSERT_EQ(config_.http_proxy().reuse_kerb(), false); +} + +TEST_F(LoadConfigTest, LoadServicesFileTest) { + boost::system::error_code ec; + + config_.Update("./config_files/services.json", ec); + + ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; + ASSERT_EQ(config_.services().process().path(), "/bin/custom_path"); + ASSERT_EQ(config_.services().process().args(), "-custom args"); +} + +TEST_F(LoadConfigTest, LoadCompleteFileTest) { + boost::system::error_code ec; + + config_.Update("./config_files/complete.json", ec); + + ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; + ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); + ASSERT_EQ(config_.tls().cert_path(), "test_cert_path"); + ASSERT_EQ(config_.tls().key_path(), "test_key_path"); + ASSERT_EQ(config_.tls().key_password(), "test_key_password"); + ASSERT_EQ(config_.tls().dh_path(), "test_dh_path"); + ASSERT_EQ(config_.tls().cipher_alg(), "test_cipher_alg"); + ASSERT_EQ(config_.http_proxy().host(), "127.0.0.1"); + ASSERT_EQ(config_.http_proxy().port(), "8080"); + ASSERT_EQ(config_.http_proxy().username(), "test_user"); + ASSERT_EQ(config_.http_proxy().password(), "test_password"); + ASSERT_EQ(config_.http_proxy().domain(), "test_domain"); + ASSERT_EQ(config_.http_proxy().reuse_ntlm(), false); + ASSERT_EQ(config_.http_proxy().reuse_kerb(), false); + ASSERT_EQ(config_.services().process().path(), "/bin/custom_path"); + ASSERT_EQ(config_.services().process().args(), "-custom args"); +} diff --git a/src/tests/copy_file_test_fixture.h b/src/tests/copy_file_test_fixture.h deleted file mode 100644 index 7c5b740b..00000000 --- a/src/tests/copy_file_test_fixture.h +++ /dev/null @@ -1,283 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "common/config/config.h" - -#include "core/client/client.h" -#include "core/server/server.h" - -#include "core/network_virtual_layer_policies/link_policies/ssl_policy.h" -#include "core/network_virtual_layer_policies/link_policies/tcp_policy.h" -#include "core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h" -#include "core/network_virtual_layer_policies/bounce_protocol_policy.h" -#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" - -#include "services/initialisation.h" -#include "services/user_services/copy_file_service.h" - -class CopyFileTestFixture : public ::testing::Test { - public: - typedef boost::asio::ip::tcp::socket socket; - typedef ssf::SSLWrapper<> ssl_socket; - typedef boost::asio::fiber::basic_fiber_demux demux; - typedef ssf::services::BaseUserService::BaseUserServicePtr - BaseUserServicePtr; - - protected: - CopyFileTestFixture() - : client_io_service_(), - p_client_worker_(new boost::asio::io_service::work(client_io_service_)), - server_io_service_(), - p_server_worker_(new boost::asio::io_service::work(server_io_service_)), - p_ssf_client_(nullptr), - p_ssf_server_(nullptr) {} - - ~CopyFileTestFixture() {} - - virtual void SetUp() { - StartServer(); - StartClient(); - } - - virtual void TearDown() { - StopClientThreads(); - StopServerThreads(); - - // Clean receiver path - boost::filesystem::path receiver_path(GetOutputPattern()); - if (boost::filesystem::is_directory(receiver_path)) { - for (boost::filesystem::directory_iterator end_it, it(receiver_path); - it != end_it; ++it) { - std::remove(it->path().string().c_str()); - } - return; - } - - if (boost::filesystem::is_regular_file(receiver_path)) { - std::remove(GetOutputPattern().c_str()); - } - } - - void StartServer() { - ssf::Config ssf_config; - - p_ssf_server_.reset( - new ssf::SSFServer(server_io_service_, - ssf_config, 8000)); - - StartServerThreads(); - p_ssf_server_->run(); - } - - virtual void StartClient() { - std::vector client_services; - boost::system::error_code ec; - auto p_service = - ssf::services::CopyFileService::CreateServiceFromParams( - GetFromStdin(), GetFromLocalToRemote(), GetInputPattern(), - GetOutputPattern(), ec); - - client_services.push_back(p_service); - - std::map params( - {{"remote_addr", "127.0.0.1"}, {"remote_port", "8000"}}); - - ssf::Config ssf_config; - - p_ssf_client_.reset(new ssf::SSFClient< - ssf::SSLPolicy, ssf::NullLinkAuthenticationPolicy, - ssf::BounceProtocolPolicy, ssf::TransportProtocolPolicy>( - client_io_service_, "127.0.0.1", "8000", ssf_config, client_services, - boost::bind(&CopyFileTestFixture::SSFClientCallback, this, _1, _2, - _3))); - StartClientThreads(); - p_ssf_client_->run(params); - } - - bool Wait() { - auto network_set_future = network_set_.get_future(); - auto service_set_future = service_set_.get_future(); - auto transport_set_future = transport_set_.get_future(); - - network_set_future.wait(); - service_set_future.wait(); - transport_set_future.wait(); - - return network_set_future.get() && service_set_future.get() && - transport_set_future.get(); - } - - bool WaitClose() { - auto close_future = close_set_.get_future(); - close_future.wait(); - return close_future.get(); - } - - virtual bool GetFromStdin() = 0; - virtual bool GetFromLocalToRemote() = 0; - virtual std::string GetInputPattern() = 0; - - virtual std::string GetOutputPattern() { return "files_copied/"; } - - void StartServerThreads() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - server_threads_.create_thread([&]() { server_io_service_.run(); }); - } - } - - void StartClientThreads() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - client_threads_.create_thread([&]() { client_io_service_.run(); }); - } - } - - void StopServerThreads() { - p_ssf_server_->stop(); - p_server_worker_.reset(); - server_threads_.join_all(); - server_io_service_.stop(); - } - - void StopClientThreads() { - p_client_worker_.reset(); - client_threads_.join_all(); - client_io_service_.stop(); - } - - void SSFClientCallback(ssf::services::initialisation::type type, - BaseUserServicePtr p_user_service, - const boost::system::error_code& ec) { - if (type == ssf::services::initialisation::NETWORK) { - network_set_.set_value(!ec); - if (ec) { - service_set_.set_value(false); - transport_set_.set_value(false); - } - return; - } - - if (type == ssf::services::initialisation::TRANSPORT) { - transport_set_.set_value(!ec); - return; - } - - if (type == ssf::services::initialisation::SERVICE && - p_user_service->GetName() == "copy_file") { - service_set_.set_value(!ec); - return; - } - - if (type == ssf::services::initialisation::CLOSE) { - close_set_.set_value(true); - return; - } - } - - protected: - boost::asio::io_service client_io_service_; - std::unique_ptr p_client_worker_; - boost::thread_group client_threads_; - - boost::asio::io_service server_io_service_; - std::unique_ptr p_server_worker_; - boost::thread_group server_threads_; - std::unique_ptr> p_ssf_client_; - std::unique_ptr> p_ssf_server_; - - std::promise network_set_; - std::promise transport_set_; - std::promise service_set_; - std::promise close_set_; -}; - -class CopyNoFileFromClientToRemoteTest : public CopyFileTestFixture { - public: - CopyNoFileFromClientToRemoteTest() : CopyFileTestFixture() {} - - virtual bool GetFromStdin() { return false; } - - virtual bool GetFromLocalToRemote() { return true; } - - virtual std::string GetInputPattern() { - return "files_to_copy/test_filex.txt"; - } -}; - -class CopyUniqueFileFromClientToRemoteTest : public CopyFileTestFixture { - public: - CopyUniqueFileFromClientToRemoteTest() : CopyFileTestFixture() {} - - virtual bool GetFromStdin() { return false; } - - virtual bool GetFromLocalToRemote() { return true; } - - virtual std::string GetInputPattern() { - return "files_to_copy/test_file1.txt"; - } -}; - -class CopyGlobFileFromClientToRemoteTest : public CopyFileTestFixture { - public: - CopyGlobFileFromClientToRemoteTest() : CopyFileTestFixture() {} - - virtual bool GetFromStdin() { return false; } - - virtual bool GetFromLocalToRemote() { return true; } - - virtual std::string GetInputPattern() { return "files_to_copy/test_*.txt"; } -}; - -class CopyUniqueFileFromRemoteToClientTest : public CopyFileTestFixture { - public: - CopyUniqueFileFromRemoteToClientTest() : CopyFileTestFixture() {} - - virtual bool GetFromStdin() { return false; } - - virtual bool GetFromLocalToRemote() { return false; } - - virtual std::string GetInputPattern() { - return "files_to_copy/test_file1.txt"; - } -}; - -class CopyGlobFileFromRemoteToClientTest : public CopyFileTestFixture { - public: - CopyGlobFileFromRemoteToClientTest() : CopyFileTestFixture() {} - - virtual bool GetFromStdin() { return false; } - - virtual bool GetFromLocalToRemote() { return false; } - - virtual std::string GetInputPattern() { return "files_to_copy/test_*.txt"; } -}; - -class CopyStdinFromClientToRemoteTest : public CopyFileTestFixture { - public: - CopyStdinFromClientToRemoteTest() : CopyFileTestFixture() {} - - virtual bool GetFromStdin() { return true; } - - virtual bool GetFromLocalToRemote() { return true; } - - virtual std::string GetInputPattern() { return "files_to_copy/test_*.txt"; } - - virtual std::string GetOutputPattern() { - return "files_copied/test_file1.txt"; - } -}; diff --git a/src/tests/fiber_basic_tests.cpp b/src/tests/fiber_basic_tests.cpp deleted file mode 100644 index e211ded8..00000000 --- a/src/tests/fiber_basic_tests.cpp +++ /dev/null @@ -1,996 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include "common/boost/basic_fiber.h" -#include "common/boost/basic_fiber_datagram.h" -#include "common/boost/basic_fiber_demux.h" -#include "common/boost/basic_fiber_acceptor.h" - -class FiberTest : public ::testing::Test { -protected: - FiberTest() - : io_service_client(), - p_client_worker(new boost::asio::io_service::work(io_service_client)), - socket_client(io_service_client), - resolver_client(io_service_client), - query(boost::asio::ip::tcp::v4(), "127.0.0.1", "8011"), - iterator_client(resolver_client.resolve(query)), - demux_client(io_service_client), - io_service_server(), - socket_server(io_service_server), - acceptor_server(io_service_server), - resolver(io_service_server), - query_server("127.0.0.1", "8011"), - endpoint(*resolver.resolve(query_server)), - demux_server(io_service_server), - fib_acceptor_serv(demux_server), - fib_server1(demux_server, 0), - fib_client1(demux_client, 1) {} - - ~FiberTest() { - - } - - typedef boost::asio::ip::tcp::socket socket; - typedef boost::asio::basic_fiber_demux fiber_demux; - typedef boost::asio::basic_fiber_acceptor fiber_acceptor; - typedef boost::asio::basic_fiber fiber; - typedef boost::asio::basic_datagram_fiber dgr_fiber; - typedef std::function accept_handler_type; - - void startServer(accept_handler_type accept_h) { - boost::asio::socket_base::reuse_address option(true); - - acceptor_server.open(endpoint.protocol()); - acceptor_server.set_option(option); - acceptor_server.bind(endpoint); - acceptor_server.listen(); - acceptor_server.async_accept(socket_server, accept_h); - - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - auto lambda = [&]() { - boost::system::error_code ec; - try { - io_service_server.run(ec); - } catch (std::exception e) { - std::cout << "Exception: " << e.what() << std::endl; - } - }; - - serverThreads_.create_thread(lambda); - } - } - - void serverStop() { - p_server_worker.reset(); - demux_server.close(); - socket_server.close(); - acceptor_server.close(); - io_service_server.stop(); - } - - void clientStop() { - p_client_worker.reset(); - demux_client.close(); - socket_client.close(); - io_service_client.stop(); - } - - void connectClient() { - boost::asio::connect(socket_client, iterator_client); - } - - void fiberizeServer() { - demux_server.fiberize(std::move(socket_server)); - } - - void fiberizeClient() { - demux_client.fiberize(std::move(socket_client)); - } - - virtual void SetUp() {} - - virtual void TearDown() {} - -public: - -protected: - boost::thread clientThreads_; - boost::asio::io_service io_service_client; - boost::asio::ip::tcp::socket socket_client; - boost::asio::ip::tcp::resolver resolver_client; - boost::asio::ip::tcp::resolver::query query; - boost::asio::ip::tcp::resolver::iterator iterator_client; - fiber_demux demux_client; - std::shared_ptr p_client_worker; - - std::shared_ptr p_server_worker; - boost::thread_group serverThreads_; - boost::asio::io_service io_service_server; - boost::asio::ip::tcp::socket socket_server; - boost::asio::ip::tcp::acceptor acceptor_server; - boost::asio::ip::tcp::resolver resolver; - boost::asio::ip::tcp::resolver::query query_server; - boost::asio::ip::tcp::endpoint endpoint; - fiber_demux demux_server; - fiber_acceptor fib_acceptor_serv; - - fiber fib_server1; - fiber fib_client1; -}; - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, startStopServerBeforeSocket) { - auto handler = [](const boost::system::error_code&) {}; - startServer(handler); - serverStop(); - serverThreads_.join_all(); -} - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, startStopServerBeforeFiberizeFromServer) { - auto handler = [&](const boost::system::error_code&) { - serverStop(); - }; - - startServer(handler); - connectClient(); - serverThreads_.join_all(); -} - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, startStopServerBeforeFiberizeFromClient) { - auto handler = [&](const boost::system::error_code&) {}; - - startServer(handler); - connectClient(); - socket_client.close(); - serverThreads_.join_all(); - serverStop(); -} - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, startStopServerAfterFiberizeFromServer) { - auto handler = [this](const boost::system::error_code&) { - fiberizeServer(); - serverStop(); - }; - - startServer(handler); - connectClient(); - fiberizeClient(); - serverThreads_.join_all(); -} - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, startStopServerAfterFiberizeFromClient) { - auto handler = [&](const boost::system::error_code&) { - fiberizeServer(); - }; - - startServer(handler); - connectClient(); - fiberizeClient(); - clientStop(); - serverStop(); - serverThreads_.join_all(); -} - - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, connectDisconnectFiberFromServer) { - const uint32_t number_of_connections = 1000; - std::atomic number_of_connected = 0; - - boost::log::core::get()->set_filter - ( - //boost::log::trivial::severity >= boost::log::trivial::fatal - boost::log::trivial::severity >= boost::log::trivial::info - ); - - std::function p_fiber, const boost::system::error_code&)> async_accept_h1; - std::function async_accept_h2; - std::function handler; - - async_accept_h2 = [this, &async_accept_h1]() { - auto p_fiber = std::make_shared(demux_server, 0); - fib_acceptor_serv.async_accept(*p_fiber, boost::bind(boost::ref(async_accept_h1), p_fiber, _1)); - }; - - async_accept_h1 = [this, &async_accept_h2](std::shared_ptr p_fiber, const boost::system::error_code&) { - p_fiber->close(); - this->io_service_server.dispatch(async_accept_h2); - }; - - handler = [this, &async_accept_h2](const boost::system::error_code&) { - boost::system::error_code ec_server; - - fiberizeServer(); - fib_acceptor_serv.bind(1, ec_server); - fib_acceptor_serv.listen(1, ec_server); - - async_accept_h2(); - }; - - boost::system::error_code ec_client; - auto async_connect_h1 = [this, &number_of_connected, &number_of_connections]( - std::shared_ptr p_fiber, const boost::system::error_code& ec) { - p_fiber->close(); - ++number_of_connected; - - if (number_of_connected.load() == number_of_connections) { - serverStop(); - clientStop(); - } - }; - - startServer(handler); - connectClient(); - fiberizeClient(); - - boost::thread_group clientThreads; - for (uint8_t i = 0; i < boost::thread::hardware_concurrency(); ++i) { - auto lambda = [&]() { - boost::system::error_code ec; - try { - io_service_client.run(ec); - } catch (std::exception e) { - std::cout << "Exception: " << e.what() << std::endl; - } - }; - clientThreads.create_thread(lambda); - } - - - for (size_t i = 0; i < number_of_connections; ++i) { - auto p_fiber = std::make_shared(demux_client, 1); - p_fiber->async_connect(boost::bind(async_connect_h1, p_fiber, _1)); - } - - serverThreads_.join_all(); - clientThreads.join_all(); -} - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, connectDisconnectFiberFromClient) { - boost::log::core::get()->set_filter - ( - //boost::log::trivial::severity >= boost::log::trivial::fatal - boost::log::trivial::severity >= boost::log::trivial::info - ); - - auto handler = [this](const boost::system::error_code&) { - boost::system::error_code ec_server; - - auto async_accept_h1 = [this](const boost::system::error_code&) {}; - - fiberizeServer(); - fib_acceptor_serv.bind(1, ec_server); - fib_acceptor_serv.listen(1, ec_server); - fib_acceptor_serv.async_accept(fib_server1, async_accept_h1); - }; - - boost::system::error_code ec_client; - auto async_connect_h1 = [this](const boost::system::error_code&) { - fib_client1.close(); - clientStop(); - serverStop(); - }; - - startServer(handler); - connectClient(); - fiberizeClient(); - fib_client1.async_connect(async_connect_h1); - io_service_client.run(); - serverThreads_.join_all(); -} - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, MTU) { - boost::log::core::get()->set_filter - ( - //boost::log::trivial::severity >= boost::log::trivial::fatal - boost::log::trivial::severity >= boost::log::trivial::info - ); - - uint8_t buffer_server[100 * 1024] = { 0 }; - uint8_t buffer_client[100 * 1024] = { 0 }; - bool result_server = true; - boost::system::error_code ec_client; - - std::function async_receive_h1; - - async_receive_h1 = [&, this](const boost::system::error_code& ec, size_t length) { - ASSERT_EQ(!ec, true); - for (size_t i = 0; i < 50 * 1024; ++i) { - result_server &= (buffer_server[i] == (i % 256)); - } - - ASSERT_EQ(result_server, true); - - for (size_t i = 50 * 1024; i < 100 * 1024; ++i) { - result_server &= (buffer_server[i] == (i % 256)); - } - - fib_server1.close([this](const boost::system::error_code&) { - clientStop(); - serverStop(); - }); - }; - - auto async_accept_h = [&, this](const boost::system::error_code& ec) { - ASSERT_EQ(!ec, true); - for (size_t i = 0; i < 1024; ++i) { - buffer_server[i] = (i+11) % 256; - } - - boost::asio::async_read(fib_server1, boost::asio::buffer(buffer_server), - async_receive_h1); - }; - - auto handler_server = [&, this](const boost::system::error_code& ec) { - ASSERT_EQ(!ec, true); - boost::system::error_code ec_server; - - fiberizeServer(); - fib_acceptor_serv.bind(1, ec_server); - fib_acceptor_serv.listen(1, ec_server); - fib_acceptor_serv.async_accept(fib_server1, async_accept_h); - - }; - - std::function async_send_h2; - - async_send_h2 = [&, this](const boost::system::error_code& ec, size_t) { - ASSERT_EQ(!ec, true); - }; - - auto async_connect_h = [&, this](const boost::system::error_code& ec) { - ASSERT_EQ(!ec, true); - for (size_t i = 0; i < 100 * 1024; ++i) { - buffer_client[i] = i % 256; - } - - boost::asio::async_write(fib_client1, boost::asio::buffer(buffer_client), - async_send_h2); - }; - - startServer(handler_server); - connectClient(); - fiberizeClient(); - fib_client1.async_connect(async_connect_h); - boost::system::error_code ec; - io_service_client.run(ec); - BOOST_LOG_TRIVIAL(debug) << ec.message() << " " << ec.value() << std::endl; - serverThreads_.join_all(); - - ASSERT_EQ(true, result_server); -} - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, exchangePackets) { - boost::log::core::get()->set_filter - ( - //boost::log::trivial::severity >= boost::log::trivial::fatal - boost::log::trivial::severity >= boost::log::trivial::info - ); - - const int32_t packet_number = 1000; - - uint8_t buffer_client[5] = { 0 }; - uint8_t buffer_server[5] = { 0 }; - bool result_server = true; - boost::system::error_code ec_client; - int server_received = 0; - - std::function async_receive_h1; - std::function async_send_h1; - - async_receive_h1 = [&, this](const boost::system::error_code& ec, size_t) { - ASSERT_EQ(!ec, true); - result_server &= (buffer_server[0] == 'a'); - result_server &= (buffer_server[1] == 'b'); - result_server &= (buffer_server[2] == 'c'); - result_server &= (buffer_server[3] == 'd'); - result_server &= (buffer_server[4] == 'e'); - - buffer_server[0] = '1'; - buffer_server[1] = '2'; - buffer_server[2] = '3'; - buffer_server[3] = '4'; - buffer_server[4] = '5'; - - if (++server_received < packet_number) { - boost::asio::async_write(fib_server1, boost::asio::buffer(buffer_server), - async_send_h1); - } - else { - fib_server1.close([this](const boost::system::error_code&) { - clientStop(); - serverStop(); - }); - } - }; - - async_send_h1 = [&, this](const boost::system::error_code& ec, size_t) { - ASSERT_EQ(!ec, true); - boost::asio::async_read(fib_server1, boost::asio::buffer(buffer_server), - async_receive_h1); - }; - - auto async_accept_h = [&, this](const boost::system::error_code& ec) { - ASSERT_EQ(!ec, true); - buffer_server[0] = '1'; - buffer_server[1] = '2'; - buffer_server[2] = '3'; - buffer_server[3] = '4'; - buffer_server[4] = '5'; - - boost::asio::async_read(fib_server1, boost::asio::buffer(buffer_server), - async_receive_h1); - }; - - auto handler_server = [&, this](const boost::system::error_code& ec) { - ASSERT_EQ(!ec, true); - boost::system::error_code ec_server; - - fiberizeServer(); - fib_acceptor_serv.bind(1, ec_server); - fib_acceptor_serv.listen(1, ec_server); - fib_acceptor_serv.async_accept(fib_server1, async_accept_h); - - }; - - std::function async_receive_h2; - std::function async_send_h2; - - async_receive_h2 = [&, this](const boost::system::error_code& ec, size_t) { - if (!ec) { - buffer_client[0] = 'a'; - buffer_client[1] = 'b'; - buffer_client[2] = 'c'; - buffer_client[3] = 'd'; - buffer_client[4] = 'e'; - - boost::asio::async_write(fib_client1, boost::asio::buffer(buffer_client), - async_send_h2); - } else { - fib_client1.close(); - } - }; - - async_send_h2 = [&, this](const boost::system::error_code& ec, size_t) { - ASSERT_EQ(!ec, true); - boost::asio::async_read(fib_client1, boost::asio::buffer(buffer_client), - async_receive_h2); - }; - - auto async_connect_h = [&, this](const boost::system::error_code& ec) { - ASSERT_EQ(!ec, true); - buffer_client[0] = 'a'; - buffer_client[1] = 'b'; - buffer_client[2] = 'c'; - buffer_client[3] = 'd'; - buffer_client[4] = 'e'; - - boost::asio::async_write(fib_client1, boost::asio::buffer(buffer_client), - async_send_h2); - }; - - startServer(handler_server); - connectClient(); - fiberizeClient(); - fib_client1.async_connect(async_connect_h); - boost::system::error_code ec; - io_service_client.run(ec); - BOOST_LOG_TRIVIAL(debug) << ec.message() << " " << ec.value() << std::endl; - serverThreads_.join_all(); - - ASSERT_EQ(true, result_server); -} - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, exchangePacketsFiveClients) { - boost::log::core::get()->set_filter - ( - //boost::log::trivial::severity >= boost::log::trivial::fatal - boost::log::trivial::severity >= boost::log::trivial::info - ); - - typedef std::array buffer_type; - - buffer_type buffer_client1; - buffer_type buffer_client2; - buffer_type buffer_client3; - buffer_type buffer_client4; - buffer_type buffer_client5; - - buffer_type buffer_server1; - buffer_type buffer_server2; - buffer_type buffer_server3; - buffer_type buffer_server4; - buffer_type buffer_server5; - - boost::system::error_code ec_client; - boost::recursive_mutex server_received_mutex; - int32_t server_received = 0; - int32_t number_of_packets = 5 * 100; - - fiber fib_server2(demux_server, 0); - fiber fib_server3(demux_server, 0); - fiber fib_server4(demux_server, 0); - fiber fib_server5(demux_server, 0); - - fiber fib_client2(demux_client, 1); - fiber fib_client3(demux_client, 1); - fiber fib_client4(demux_client, 1); - fiber fib_client5(demux_client, 1); - - std::function async_receive_h1; - std::function async_send_h1; - - async_receive_h1 = [&](const boost::system::error_code& ec, size_t, - fiber& fib, buffer_type& buffer_server) { - - - if (ec) { - boost::recursive_mutex::scoped_lock lock(server_received_mutex); - ASSERT_EQ(number_of_packets, server_received); - return; - } - - ASSERT_EQ(buffer_server[0], 'a'); - ASSERT_EQ(buffer_server[1], 'b'); - ASSERT_EQ(buffer_server[2], 'c'); - ASSERT_EQ(buffer_server[3], 'd'); - ASSERT_EQ(buffer_server[4], 'e'); - - buffer_server[0] = '1'; - buffer_server[1] = '2'; - buffer_server[2] = '3'; - buffer_server[3] = '4'; - buffer_server[4] = '5'; - - boost::recursive_mutex::scoped_lock lock(server_received_mutex); - if (++server_received < number_of_packets - 4) { - boost::asio::async_write( - fib, boost::asio::buffer(buffer_server), - boost::bind(async_send_h1, _1, _2, boost::ref(fib), - boost::ref(buffer_server))); - } else if (server_received < number_of_packets) { - fib.close([this](const boost::system::error_code&) {}); - } else { - fib.close([this](const boost::system::error_code&) { - clientStop(); - serverStop(); - }); - } - }; - - async_send_h1 = [&](const boost::system::error_code& ec, size_t, fiber& fib, - buffer_type& buffer_server) { - ASSERT_EQ(!ec, true); - boost::asio::async_read( - fib, boost::asio::buffer(buffer_server), - boost::bind(async_receive_h1, _1, _2, boost::ref(fib), - boost::ref(buffer_server))); - }; - - std::function - async_accept_h = [&](const boost::system::error_code& ec, fiber& fib, - buffer_type& buffer_server) { - EXPECT_EQ(!ec, true); - - buffer_server[0] = '1'; - buffer_server[1] = '2'; - buffer_server[2] = '3'; - buffer_server[3] = '4'; - buffer_server[4] = '5'; - - boost::asio::async_read( - fib, boost::asio::buffer(buffer_server), - boost::bind(async_receive_h1, _1, _2, boost::ref(fib), - boost::ref(buffer_server))); - }; - - auto handler_server = [&](const boost::system::error_code& ec, fiber& fib1, - fiber& fib2, fiber& fib3, fiber& fib4, - fiber& fib5) { - EXPECT_EQ(!ec, true); - boost::system::error_code ec_server; - - fiberizeServer(); - fib_acceptor_serv.bind(1, ec_server); - fib_acceptor_serv.listen(1, ec_server); - fib_acceptor_serv.async_accept( - fib1, boost::bind(async_accept_h, _1, boost::ref(fib1), - boost::ref(buffer_server1))); - fib_acceptor_serv.async_accept( - fib2, boost::bind(async_accept_h, _1, boost::ref(fib2), - boost::ref(buffer_server2))); - fib_acceptor_serv.async_accept( - fib3, boost::bind(async_accept_h, _1, boost::ref(fib3), - boost::ref(buffer_server3))); - fib_acceptor_serv.async_accept( - fib4, boost::bind(async_accept_h, _1, boost::ref(fib4), - boost::ref(buffer_server4))); - fib_acceptor_serv.async_accept( - fib5, boost::bind(async_accept_h, _1, boost::ref(fib5), - boost::ref(buffer_server5))); - }; - - std::function async_receive_h2; - std::function async_send_h2; - - async_receive_h2 = [&](const boost::system::error_code& ec, size_t, - fiber& fib, buffer_type& buffer_client) { - if (!ec) { - buffer_client[0] = 'a'; - buffer_client[1] = 'b'; - buffer_client[2] = 'c'; - buffer_client[3] = 'd'; - buffer_client[4] = 'e'; - - boost::asio::async_write( - fib, boost::asio::buffer(buffer_client), - boost::bind(async_send_h2, _1, _2, boost::ref(fib), - boost::ref(buffer_client))); - } else { - fib.close(); - } - }; - - async_send_h2 = [&](const boost::system::error_code& ec, size_t, fiber& fib, - buffer_type& buffer_client) { - boost::asio::async_read( - fib, boost::asio::buffer(buffer_client), - boost::bind(async_receive_h2, _1, _2, boost::ref(fib), - boost::ref(buffer_client))); - }; - - std::function async_connect_h = [&]( - fiber& fib, buffer_type& buffer_client) { - buffer_client[0] = 'a'; - buffer_client[1] = 'b'; - buffer_client[2] = 'c'; - buffer_client[3] = 'd'; - buffer_client[4] = 'e'; - - boost::asio::async_write(fib, boost::asio::buffer(buffer_client), - boost::bind(async_send_h2, _1, _2, boost::ref(fib), - boost::ref(buffer_client))); - }; - - startServer( - boost::bind(handler_server, _1, boost::ref(fib_server1), - boost::ref(fib_server2), boost::ref(fib_server3), - boost::ref(fib_server4), boost::ref(fib_server5))); - connectClient(); - fiberizeClient(); - fib_client1.async_connect(boost::bind( - async_connect_h, boost::ref(fib_client1), boost::ref(buffer_client1))); - fib_client2.async_connect(boost::bind( - async_connect_h, boost::ref(fib_client2), boost::ref(buffer_client2))); - fib_client3.async_connect(boost::bind( - async_connect_h, boost::ref(fib_client3), boost::ref(buffer_client3))); - fib_client4.async_connect(boost::bind( - async_connect_h, boost::ref(fib_client4), boost::ref(buffer_client4))); - fib_client5.async_connect(boost::bind( - async_connect_h, boost::ref(fib_client5), boost::ref(buffer_client5))); - - boost::thread t1([&]() { io_service_client.run(); }); - boost::thread t2([&]() { io_service_client.run(); }); - boost::thread t3([&]() { io_service_client.run(); }); - boost::thread t4([&]() { io_service_client.run(); }); - boost::thread t5([&]() { io_service_client.run(); }); - t1.join(); - t2.join(); - t3.join(); - t4.join(); - t5.join(); - serverThreads_.join_all(); -} - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, tooSmallReceiveBuffer) { - std::array buffer_client; - std::array buffer_server; - - boost::recursive_mutex received_mutex; - uint8_t count = 0; - size_t received = 0; - size_t sent = 0; - - auto void_handler_receive = [&](const boost::system::error_code& ec, size_t s) { - boost::recursive_mutex::scoped_lock lock(received_mutex); - received += s; - ++count; - - if (count == 4) { - serverStop(); - } - }; - - auto async_accept_h1 = [&, this](const boost::system::error_code& ec) { - ASSERT_EQ(!ec, true); - boost::asio::async_read(fib_server1, boost::asio::buffer(buffer_server), - void_handler_receive); - boost::asio::async_read(fib_server1, boost::asio::buffer(buffer_server), - void_handler_receive); - boost::asio::async_read(fib_server1, boost::asio::buffer(buffer_server), - void_handler_receive); - boost::asio::async_read(fib_server1, boost::asio::buffer(buffer_server), - void_handler_receive); - }; - - auto handler = [&, this](const boost::system::error_code& ec) { - ASSERT_EQ(!ec, true); - boost::system::error_code ec_server; - - //check how to get a reliable synchronization - fiberizeServer(); - fib_acceptor_serv.bind(1, ec_server); - fib_acceptor_serv.listen(1, ec_server); - fib_acceptor_serv.async_accept(fib_server1, async_accept_h1); - }; - - auto close_handler = [&](const boost::system::error_code& ec) { - clientStop(); - }; - - auto void_handler_send = [&](const boost::system::error_code& ec, size_t s) { - ASSERT_EQ(!ec, true); - sent += s; - - fib_client1.close(close_handler); - }; - - auto async_connect_h1 = [&, this](const boost::system::error_code& ec) { - ASSERT_EQ(!ec, true); - - buffer_client[0] = 'a'; - buffer_client[1] = 'b'; - buffer_client[2] = 'c'; - buffer_client[3] = 'd'; - buffer_client[4] = 'e'; - - boost::asio::async_write(fib_client1, boost::asio::buffer(buffer_client), - void_handler_send); - }; - - startServer(handler); - connectClient(); - fiberizeClient(); - fib_client1.async_connect(async_connect_h1); - io_service_client.run(); - serverThreads_.join_all(); - - EXPECT_EQ(received, 5); - EXPECT_EQ(sent, 5); -} - -//----------------------------------------------------------------------------- -TEST_F(FiberTest, UDPfiber) { - auto handler = [this](const boost::system::error_code&) { - fiberizeServer(); - }; - - startServer(handler); - connectClient(); - fiberizeClient(); - - uint32_t rem_p = (1 << 16) + 1; - - dgr_fiber dgr_f_l(demux_server, 0); - dgr_fiber dgr_f(demux_client, rem_p); - std::array buffer_client; - boost::system::error_code ec; - uint32_t from_port; - std::array buffer_server; - - std::function - sent_server = [&](const boost::system::error_code& ec, - size_t length) { - dgr_f_l.close(); - }; - - std::function - received_server = [&](const boost::system::error_code& ec, - size_t length) { - EXPECT_EQ(buffer_client[0], buffer_server[0]); - EXPECT_EQ(buffer_client[1], buffer_server[1]); - EXPECT_EQ(buffer_client[2], buffer_server[2]); - EXPECT_EQ(buffer_client[3], buffer_server[3]); - EXPECT_EQ(buffer_client[4], buffer_server[4]); - - buffer_server[0] = 10; - buffer_server[1] = 11; - buffer_server[2] = 12; - buffer_server[3] = 13; - buffer_server[4] = 14; - - dgr_f_l.async_send_to(boost::asio::buffer(buffer_server), from_port, - sent_server); - }; - - dgr_f_l.bind(rem_p, ec); - dgr_f_l.async_receive_from(boost::asio::buffer(buffer_server), from_port, - received_server); - - buffer_client[0] = 1; - buffer_client[1] = 2; - buffer_client[2] = 3; - buffer_client[3] = 4; - buffer_client[4] = 5; - - uint32_t new_rem_p = 0; - - std::function - received_client = [&](const boost::system::error_code& ec, size_t length) { - EXPECT_EQ(buffer_client[0], buffer_server[0]); - EXPECT_EQ(buffer_client[1], buffer_server[1]); - EXPECT_EQ(buffer_client[2], buffer_server[2]); - EXPECT_EQ(buffer_client[3], buffer_server[3]); - EXPECT_EQ(buffer_client[4], buffer_server[4]); - - - EXPECT_EQ(new_rem_p, rem_p); - - dgr_f.close(); - clientStop(); - serverStop(); - }; - - std::function - sent_client = [&](const boost::system::error_code& ec, size_t length) { - dgr_f.async_receive_from(boost::asio::buffer(buffer_client), new_rem_p, - received_client); - }; - - dgr_f.async_send_to(boost::asio::buffer(buffer_client), rem_p, sent_client); - io_service_client.run(); - - serverThreads_.join_all(); -} - -//----------------------------------------------------------------------------- -//TEST_F(FiberTest, SSLconnectDisconnectFiberFromClient) { -// typedef boost::asio::ssl::stream ssl_fiber_t; -// boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12); -// -// std::function verify_callback = -// [](bool preverified, -// boost::asio::ssl::verify_context& ctx) { -// std::cout << "------------------------------" << std::endl; -// X509_STORE_CTX* cts = ctx.native_handle(); -// X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); -// -// char subject_name[256]; -// auto err = X509_STORE_CTX_get_error(ctx.native_handle()); -// auto depth_err = X509_STORE_CTX_get_error_depth(ctx.native_handle()); -// -// std::cout << "Error " << X509_verify_cert_error_string(err) << std::endl; -// std::cout << "Depth " << depth_err << std::endl; -// -// X509* issuer = cts->current_issuer; -// if (issuer) { -// X509_NAME_oneline(X509_get_subject_name(issuer), subject_name, 256); -// std::cout << "Issuer " << subject_name << "\n"; -// } -// -// X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); -// std::cout << "Verifying " << subject_name << "\n"; -// -// std::cout << "------------------------------" << std::endl; -// -// return preverified; -// }; -// -// ctx.set_verify_depth(100); -// -// // Set the mutual authentication -// ctx.set_verify_mode(boost::asio::ssl::verify_peer | -// boost::asio::ssl::verify_fail_if_no_peer_cert); -// -// // Set the callback to verify the cetificate chains of the peer -// ctx.set_verify_callback( -// boost::bind(verify_callback, _1, _2)); -// -// // Load the file containing the trusted certificate authorities -// SSL_CTX_load_verify_locations(ctx.native_handle(), -// "./certs/allowed/allowed.crt", NULL); -// -// // The certificate used by the local peer -// ctx.use_certificate_chain_file("./certs/cert.pem"); -// -// // The private key used by the local peer -// ctx.use_private_key_file("./certs/key.pem", boost::asio::ssl::context::pem); -// -// // The Diffie-Hellman parameter file -// ctx.use_tmp_dh_file("./certs/dh4096.pem"); -// -// // Force a specific cipher suite -// SSL_CTX_set_cipher_list(ctx.native_handle(), "DHE-RSA-AES256-GCM-SHA384"); -// -// ssl_fiber_t ssl_fiber_client(io_service_client, ctx); -// ssl_fiber_t ssl_fiber_server(io_service_server, ctx); -// -// ssl_fiber_client.next_layer().open(demux_client, 1); -// ssl_fiber_server.next_layer().open(demux_server, 0); -// -// uint32_t buffer_s = 0; -// uint32_t buffer_c = 0; -// -// auto sent = [this, &ssl_fiber_client, &buffer_s]( -// const boost::system::error_code& ec, size_t length) { -// std::cout << "Server sent: " << buffer_s << std::endl; -// }; -// -// auto async_handshaked_s = [this, &ssl_fiber_server, &buffer_s, sent]( -// const boost::system::error_code& ec) { -// buffer_s = 10; -// boost::asio::async_write(ssl_fiber_server, -// boost::asio::buffer(&buffer_s, sizeof(buffer_s)), -// sent); -// }; -// -// auto async_accept_s = [this, &ssl_fiber_server, async_handshaked_s]( -// const boost::system::error_code& ec) { -// ssl_fiber_server.async_handshake(boost::asio::ssl::stream_base::server, -// async_handshaked_s); -// }; -// -// auto handler_s = [this, async_accept_s, &ssl_fiber_server]( -// const boost::system::error_code& ec) { -// boost::system::error_code ec_server; -// fiberizeServer(); -// fib_acceptor_serv.bind(1, ec_server); -// fib_acceptor_serv.listen(1, ec_server); -// fib_acceptor_serv.async_accept(ssl_fiber_server.next_layer(), async_accept_s); -// }; -// -// auto received = [this, &ssl_fiber_client, &buffer_c, &buffer_s]( -// const boost::system::error_code& ec, size_t length) { -// ASSERT_EQ(buffer_s, buffer_c); -// std::cout << "client received: " << buffer_c << std::endl; -// ssl_fiber_client.next_layer().close(); -// demux_client.close(); -// socket_client.close(); -// serverStop(); -// }; -// -// auto async_handshaked_c = [this, &ssl_fiber_client, &buffer_c, received]( -// const boost::system::error_code& ec) { -// boost::asio::async_read(ssl_fiber_client, -// boost::asio::buffer(&buffer_c, sizeof(buffer_c)), -// received); -// }; -// -// auto async_connect_c = [this, &ssl_fiber_client, async_handshaked_c]( -// const boost::system::error_code& ec) { -// ssl_fiber_client.async_handshake(boost::asio::ssl::stream_base::client, -// async_handshaked_c); -// }; -// -// startServer(handler_s); -// -// connectClient(); -// fiberizeClient(); -// ssl_fiber_client.next_layer().async_connect(async_connect_c); -// io_service_client.run(); -// serverThread_.join(); -//} \ No newline at end of file diff --git a/src/tests/load_config_tests.cpp b/src/tests/load_config_tests.cpp index b5bf5ed2..e9faf17d 100644 --- a/src/tests/load_config_tests.cpp +++ b/src/tests/load_config_tests.cpp @@ -10,98 +10,91 @@ class LoadConfigTest : public ::testing::Test { protected: - LoadConfigTest(): filename_("config.json") {} - - ~LoadConfigTest() {} - - virtual void TearDown() { - if (boost::filesystem::exists(filename_)) { - std::remove(filename_.c_str()); - } - } - - std::string filename_; + ssf::config::Config config_; }; -//----------------------------------------------------------------------------- -TEST_F(LoadConfigTest, loadNoFile) { +TEST_F(LoadConfigTest, LoadNoFileTest) { boost::system::error_code ec; - ssf::LoadConfig("", ec); + + config_.Update("", ec); + ASSERT_EQ(ec.value(), 0) << "Success if no file given"; } -//----------------------------------------------------------------------------- -TEST_F(LoadConfigTest, loadNonExistantFile) { +TEST_F(LoadConfigTest, LoadNonExistantFileTest) { boost::system::error_code ec; - ssf::LoadConfig(filename_, ec); + config_.Update("./config_files/unknown.json", ec); + ASSERT_NE(ec.value(), 0) << "No success if file not existant"; } -//----------------------------------------------------------------------------- -TEST_F(LoadConfigTest, loadEmptyFile) { +TEST_F(LoadConfigTest, LoadEmptyFileTest) { boost::system::error_code ec; - std::ofstream file; - file.open(filename_); - file.close(); - ssf::LoadConfig(filename_, ec); + + config_.Update("./config_files/empty.json", ec); + ASSERT_NE(ec.value(), 0) << "No success if file empty"; } -//----------------------------------------------------------------------------- -TEST_F(LoadConfigTest, loadWrongFormatFile) { +TEST_F(LoadConfigTest, LoadWrongFormatFileTest) { boost::system::error_code ec; - std::ofstream file; - file.open(filename_); - file << "{ \"ssf\": { \"tls\" : { \"ca_cert_path\": \"test\" } }"; - file.close(); - ssf::LoadConfig(filename_, ec); - std::remove(filename_.c_str()); + + config_.Update("./config_files/wrong_format.json", ec); + ASSERT_NE(ec.value(), 0) << "No success if wrong file format"; } -//----------------------------------------------------------------------------- -TEST_F(LoadConfigTest, loadPartialFile) { +TEST_F(LoadConfigTest, LoadTlsPartialFileTest) { boost::system::error_code ec; - std::ofstream file; - file.open(filename_); - file << "{ \"ssf\": { \"tls\" : { " << std::endl; - file << "\"ca_cert_path\": \"test_ca_path\"," << std::endl; - file << "\"key_path\": \"test_key_path\"," << std::endl; - file << "\"dh_path\": \"test_dh_path\"" << std::endl; - file << "} } }"; - file.close(); - ssf::Config config; - config = ssf::LoadConfig(filename_, ec); + + config_.Update("./config_files/tls_partial.json", ec); + ASSERT_EQ(ec.value(), 0) << "Success if partial file format"; - ASSERT_EQ(config.tls.ca_cert_path, "test_ca_path"); - ASSERT_EQ(config.tls.cert_path, "./certs/certificate.crt"); - ASSERT_EQ(config.tls.key_path, "test_key_path"); - ASSERT_EQ(config.tls.key_password, ""); - ASSERT_EQ(config.tls.dh_path, "test_dh_path"); - ASSERT_EQ(config.tls.cipher_alg, "DHE-RSA-AES256-GCM-SHA384"); + ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); + ASSERT_EQ(config_.tls().cert_path(), "./certs/certificate.crt"); + ASSERT_EQ(config_.tls().key_path(), "test_key_path"); + ASSERT_EQ(config_.tls().key_password(), ""); + ASSERT_EQ(config_.tls().dh_path(), "test_dh_path"); + ASSERT_EQ(config_.tls().cipher_alg(), "DHE-RSA-AES256-GCM-SHA384"); } -//----------------------------------------------------------------------------- -TEST_F(LoadConfigTest, loadCompleteFile) { +TEST_F(LoadConfigTest, LoadTlsCompleteFileTest) { boost::system::error_code ec; - std::ofstream file; - file.open(filename_); - file << "{ \"ssf\": { \"tls\" : { " << std::endl; - file << "\"ca_cert_path\": \"test_ca_path\"," << std::endl; - file << "\"cert_path\": \"test_cert_path\"," << std::endl; - file << "\"key_path\": \"test_key_path\"," << std::endl; - file << "\"key_password\": \"test_key_password\"," << std::endl; - file << "\"dh_path\": \"test_dh_path\"," << std::endl; - file << "\"cipher_alg\": \"test_cipher_alg\"" << std::endl; - file << "} } }"; - file.close(); - ssf::Config config; - config = ssf::LoadConfig(filename_, ec); + + config_.Update("./config_files/tls_complete.json", ec); + + ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; + ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); + ASSERT_EQ(config_.tls().cert_path(), "test_cert_path"); + ASSERT_EQ(config_.tls().key_path(), "test_key_path"); + ASSERT_EQ(config_.tls().key_password(), "test_key_password"); + ASSERT_EQ(config_.tls().dh_path(), "test_dh_path"); + ASSERT_EQ(config_.tls().cipher_alg(), "test_cipher_alg"); + ASSERT_EQ(config_.proxy().http_addr(), ""); + ASSERT_EQ(config_.proxy().http_port(), ""); +} + +TEST_F(LoadConfigTest, LoadProxyFileTest) { + boost::system::error_code ec; + + config_.Update("./config_files/proxy.json", ec); + ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; + ASSERT_EQ(config_.proxy().http_addr(), "127.0.0.1"); + ASSERT_EQ(config_.proxy().http_port(), "8080"); +} + +TEST_F(LoadConfigTest, LoadCompleteFileTest) { + boost::system::error_code ec; + + config_.Update("./config_files/complete.json", ec); + ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; - ASSERT_EQ(config.tls.ca_cert_path, "test_ca_path"); - ASSERT_EQ(config.tls.cert_path, "test_cert_path"); - ASSERT_EQ(config.tls.key_path, "test_key_path"); - ASSERT_EQ(config.tls.key_password, "test_key_password"); - ASSERT_EQ(config.tls.dh_path, "test_dh_path"); - ASSERT_EQ(config.tls.cipher_alg, "test_cipher_alg"); + ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); + ASSERT_EQ(config_.tls().cert_path(), "test_cert_path"); + ASSERT_EQ(config_.tls().key_path(), "test_key_path"); + ASSERT_EQ(config_.tls().key_password(), "test_key_password"); + ASSERT_EQ(config_.tls().dh_path(), "test_dh_path"); + ASSERT_EQ(config_.tls().cipher_alg(), "test_cipher_alg"); + ASSERT_EQ(config_.proxy().http_addr(), "127.0.0.1"); + ASSERT_EQ(config_.proxy().http_port(), "8080"); } diff --git a/src/tests/network/CMakeLists.txt b/src/tests/network/CMakeLists.txt new file mode 100644 index 00000000..9f550c24 --- /dev/null +++ b/src/tests/network/CMakeLists.txt @@ -0,0 +1,123 @@ +cmake_minimum_required(VERSION 2.8) + +set(network_tests_group_name "Unit Tests/Network") + +set(project_BINARY_NETWORK_TESTS_DIR "${project_BINARY_DIR}/src/tests/network") + +# --- Copy test certs for tests +set(project_BINARY_NETWORK_TESTS_CERT_DIR ${project_BINARY_NETWORK_TESTS_DIR}/certs) + +file(MAKE_DIRECTORY ${project_BINARY_NETWORK_TESTS_CERT_DIR}) +file(MAKE_DIRECTORY ${project_BINARY_NETWORK_TESTS_CERT_DIR}/trusted) + +file(COPY ${SSF_CERT_TEST_ROOT_FILES} + DESTINATION ${project_BINARY_NETWORK_TESTS_CERT_DIR}) +file(COPY ${SSF_CERT_TEST_TRUSTED_FILES} + DESTINATION ${project_BINARY_NETWORK_TESTS_CERT_DIR}/trusted) + +# --- Network tests + +# --- Fiber asio tests +set(fiber_asio_test_source_files + "fiber_asio_tests.cpp" + ${SSF_SOURCES} +) + +add_target("fiber_asio_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${fiber_asio_test_source_files} +) +project_group(${network_tests_group_name} fiber_asio_tests) + +# --- SSF Client Server tests +set(ssf_client_server_source_files + "ssf_client_server_tests.cpp" + ${SSF_SOURCES} +) + +add_target("ssf_client_server_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${ssf_client_server_source_files} +) +project_group(${network_tests_group_name} ssf_client_server_tests) + +# --- SSF Client Server cipher suites tests +set(ssf_client_server_cipher_suites_source_files + "ssf_client_server_cipher_suites_tests.cpp" + ${SSF_SOURCES} +) + +add_target("ssf_client_server_cipher_suites_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${ssf_client_server_cipher_suites_source_files} +) +project_group(${network_tests_group_name} ssf_client_server_cipher_suites_tests) + +# --- SSF Server tests +set(ssf_server_source_files + "ssf_server_tests.cpp" + ${SSF_SOURCES} +) + +add_target("ssf_server_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${ssf_server_source_files} +) +project_group(${network_tests_group_name} ssf_server_tests) + +# --- Bouncing chain test +set(bouncing_test_source_files + "bouncing_tests.cpp" + ${SSF_SOURCES} +) + +add_target("bouncing_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${bouncing_test_source_files} +) +project_group(${network_tests_group_name} bouncing_tests) diff --git a/src/tests/network/bouncing_tests.cpp b/src/tests/network/bouncing_tests.cpp new file mode 100644 index 00000000..ba7eb277 --- /dev/null +++ b/src/tests/network/bouncing_tests.cpp @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include + +#include + +#include + +#include "common/config/config.h" + +#include "core/circuit/config.h" +#include "core/network_protocol.h" + +#include "core/client/client.h" +#include "core/server/server.h" + +#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" + +#include "services/initialisation.h" +#include "services/user_services/port_forwarding.h" + +using NetworkProtocol = ssf::network::NetworkProtocol; + +TEST(BouncingTests, BouncingChain) { + using Client = + ssf::SSFClient; + using Server = + ssf::SSFServer; + + using demux = Client::Demux; + using BaseUserServicePtr = + ssf::services::BaseUserService::BaseUserServicePtr; + + std::promise network_set; + std::promise transport_set; + std::promise service_set; + + std::list servers; + ssf::circuit::NodeList node_list; + + uint16_t initial_server_port = 10000; + uint8_t nb_of_servers = 5; + ssf::config::Config ssf_config; + + boost::system::error_code client_ec; + boost::system::error_code circuit_ec; + boost::system::error_code server_ec; + + ++initial_server_port; + auto server_endpoint_query = NetworkProtocol::GenerateServerQuery( + "", std::to_string(initial_server_port), ssf_config); + servers.emplace_front(ssf_config.services()); + servers.front().Run(server_endpoint_query, server_ec); + ASSERT_EQ(server_ec.value(), 0) << "Server could not run"; + + node_list.emplace_front("127.0.0.1", std::to_string(initial_server_port)); + + for (uint8_t i = 0; i < nb_of_servers - 1; ++i) { + ++initial_server_port; + auto circuit_endpoint_query = NetworkProtocol::GenerateServerQuery( + "", std::to_string(initial_server_port), ssf_config); + + servers.emplace_front(ssf_config.services()); + servers.front().Run(circuit_endpoint_query, circuit_ec); + ASSERT_EQ(server_ec.value(), 0) << "Circuit node " << i << "could not run"; + node_list.emplace_front("127.0.0.1", std::to_string(initial_server_port)); + } + + std::vector client_options; + boost::system::error_code client_option_ec; + auto p_service = ssf::services::PortForwarding::CreateServiceOptions( + "5454:127.0.0.1:5354", client_option_ec); + client_options.push_back(p_service); + + std::string error_msg; + + std::string remote_addr; + std::string remote_port; + + auto first_node = node_list.front(); + node_list.pop_front(); + + auto callback = [&network_set, &transport_set, &service_set]( + ssf::services::initialisation::type type, + BaseUserServicePtr p_user_service, const boost::system::error_code& ec) { + if (type == ssf::services::initialisation::NETWORK) { + network_set.set_value(!ec); + + EXPECT_EQ(ec.value(), 0) << "Error on network initialisation"; + if (ec) { + service_set.set_value(false); + transport_set.set_value(false); + } + + return; + } + + if (type == ssf::services::initialisation::TRANSPORT) { + EXPECT_EQ(ec.value(), 0) << "Error on network initialisation"; + transport_set.set_value(!ec); + if (ec) { + service_set.set_value(false); + } + + return; + } + + if (type == ssf::services::initialisation::SERVICE) { + EXPECT_EQ(ec.value(), 0) << "Error on network initialisation"; + service_set.set_value(!ec); + return; + } + }; + + auto client_endpoint_query = NetworkProtocol::GenerateClientQuery( + first_node.addr(), first_node.port(), ssf_config, node_list); + + Client client(client_options, ssf_config.services(), std::move(callback)); + client.Run(client_endpoint_query, client_ec); + ASSERT_EQ(client_ec.value(), 0) << "Client could not run"; + + auto network_future = network_set.get_future(); + auto transport_future = transport_set.get_future(); + auto service_future = service_set.get_future(); + + network_future.wait(); + transport_future.wait(); + service_future.wait(); + + EXPECT_TRUE(network_future.get()) << "Network should be set"; + EXPECT_TRUE(transport_future.get()) << "Transport should be set"; + EXPECT_TRUE(service_future.get()) << "Service should be set"; + + client.Stop(); + for (auto& server : servers) { + server.Stop(); + } + + servers.clear(); +} \ No newline at end of file diff --git a/src/tests/fiber_asio_tests.cpp b/src/tests/network/fiber_asio_tests.cpp similarity index 66% rename from src/tests/fiber_asio_tests.cpp rename to src/tests/network/fiber_asio_tests.cpp index 315c8a74..b2b326f6 100644 --- a/src/tests/fiber_asio_tests.cpp +++ b/src/tests/network/fiber_asio_tests.cpp @@ -7,12 +7,11 @@ #include #include #include -#include -#include -#include #include +#include + #include "common/boost/fiber/basic_fiber_demux.hpp" #include "common/boost/fiber/stream_fiber.hpp" #include "common/boost/fiber/datagram_fiber.hpp" @@ -27,7 +26,7 @@ class FiberTest : public ::testing::Test { new boost::asio::io_service::work(io_service_client_))), socket_client_(io_service_client_), resolver_client_(io_service_client_), - query_client_(boost::asio::ip::tcp::v4(), "127.0.0.1", "8011"), + query_client_(boost::asio::ip::tcp::v4(), "127.0.0.1", "9011"), iterator_client_(resolver_client_.resolve(query_client_)), demux_client_(io_service_client_), client_ready_(), @@ -38,7 +37,7 @@ class FiberTest : public ::testing::Test { socket_server_(io_service_server_), acceptor_server_(io_service_server_), resolver_server_(io_service_server_), - query_server_(boost::asio::ip::tcp::v4(), "127.0.0.1", "8011"), + query_server_(boost::asio::ip::tcp::v4(), "127.0.0.1", "9011"), endpoint_server_(*resolver_server_.resolve(query_server_)), demux_server_(io_service_server_), server_ready_() {} @@ -81,8 +80,8 @@ class FiberTest : public ::testing::Test { } private: - void startServer(accept_handler_type accept_h = []( - const boost::system::error_code& ec) {}) { + void startServer(accept_handler_type accept_h = + [](const boost::system::error_code& ec) {}) { auto lambda = [this]() { boost::system::error_code ec; this->io_service_server_.run(ec); @@ -92,15 +91,15 @@ class FiberTest : public ::testing::Test { serverThreads_.create_thread(lambda); } - auto accepted_lambda = [this, accept_h]( - const boost::system::error_code& ec) { - if (!ec) { - this->demux_server_.fiberize(std::move(this->socket_server_)); - } + auto accepted_lambda = + [this, accept_h](const boost::system::error_code& ec) { + if (!ec) { + this->demux_server_.fiberize(std::move(this->socket_server_)); + } - this->server_ready_.set_value(!ec); - accept_h(ec); - }; + this->server_ready_.set_value(!ec); + accept_h(ec); + }; boost::system::error_code server_ec; boost::asio::socket_base::reuse_address option(true); @@ -182,17 +181,10 @@ class FiberTest : public ::testing::Test { }; //----------------------------------------------------------------------------- -TEST_F(FiberTest, startStopServer) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - Wait(); -} +TEST_F(FiberTest, startStopServer) { Wait(); } //---------------------------------------------------------------------------- TEST_F(FiberTest, connectDisconnectFiberFromServer) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - Wait(); std::promise accept_closed; @@ -202,34 +194,34 @@ TEST_F(FiberTest, connectDisconnectFiberFromServer) { fiber fib_server(io_service_server_); fiber fib_client(io_service_client_); - auto accepted_lambda = [this, &fib_server, &accept_closed]( - const boost::system::error_code& ec) { - ASSERT_EQ(ec.value(), 0) << "Accept handler should not be in error"; + auto accepted_lambda = + [this, &fib_server, &accept_closed](const boost::system::error_code& ec) { + ASSERT_EQ(ec.value(), 0) << "Accept handler should not be in error"; - boost::system::error_code close_fib_server_ec; - fib_server.close(close_fib_server_ec); + boost::system::error_code close_fib_server_ec; + fib_server.close(close_fib_server_ec); - accept_closed.set_value(!ec); - }; + accept_closed.set_value(!ec); + }; - auto wait_closing_lambda = [&fib_client, &connect_closed]( - const boost::system::error_code& ec) { - auto p_int = std::make_shared(0); - auto& connect_closed_ref = connect_closed; - auto receive_handler = [p_int, &connect_closed_ref](const boost::system::error_code& ec, - size_t) { - connect_closed_ref.set_value(!!ec); - }; - fib_client.async_receive(boost::asio::buffer(p_int.get(), sizeof(int)), - std::move(receive_handler)); - }; + auto wait_closing_lambda = + [&fib_client, &connect_closed](const boost::system::error_code& ec) { + auto p_int = std::make_shared(0); + auto& connect_closed_ref = connect_closed; + auto receive_handler = [p_int, &connect_closed_ref]( + const boost::system::error_code& ec, size_t) { + connect_closed_ref.set_value(!!ec); + }; + fib_client.async_receive(boost::asio::buffer(p_int.get(), sizeof(int)), + std::move(receive_handler)); + }; - auto connected_lambda = [this, &wait_closing_lambda]( - const boost::system::error_code& ec) { - ASSERT_EQ(ec.value(), 0) << "Connect handler should not be in error"; + auto connected_lambda = + [this, &wait_closing_lambda](const boost::system::error_code& ec) { + ASSERT_EQ(ec.value(), 0) << "Connect handler should not be in error"; - io_service_client_.post(boost::bind(wait_closing_lambda, ec)); - }; + io_service_client_.post(boost::bind(wait_closing_lambda, ec)); + }; boost::system::error_code acceptor_ec; fiber_endpoint fib_server_endpoint( @@ -250,13 +242,8 @@ TEST_F(FiberTest, connectDisconnectFiberFromServer) { fib_acceptor.close(close_fib_acceptor_ec); } - - //---------------------------------------------------------------------------- TEST_F(FiberTest, connectDisconnectFiberFromClient) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - Wait(); std::promise accept_closed; @@ -266,24 +253,24 @@ TEST_F(FiberTest, connectDisconnectFiberFromClient) { fiber fib_server(io_service_server_); fiber fib_client(io_service_client_); - auto wait_closing_lambda = [&fib_server, &accept_closed]( - const boost::system::error_code& ec) { - auto p_int = std::make_shared(0); - auto& accept_closed_ref = accept_closed; - auto receive_handler = [p_int, &accept_closed_ref](const boost::system::error_code& ec, - size_t) { + auto wait_closing_lambda = + [&fib_server, &accept_closed](const boost::system::error_code& ec) { + auto p_int = std::make_shared(0); + auto& accept_closed_ref = accept_closed; + auto receive_handler = [p_int, &accept_closed_ref]( + const boost::system::error_code& ec, size_t) { accept_closed_ref.set_value(!!ec); - }; - fib_server.async_receive(boost::asio::buffer(p_int.get(), sizeof(int)), - std::move(receive_handler)); - }; + }; + fib_server.async_receive(boost::asio::buffer(p_int.get(), sizeof(int)), + std::move(receive_handler)); + }; - auto accepted_lambda = [this, &wait_closing_lambda]( - const boost::system::error_code& ec) { - ASSERT_EQ(ec.value(), 0) << "Accept handler should not be in error"; + auto accepted_lambda = + [this, &wait_closing_lambda](const boost::system::error_code& ec) { + ASSERT_EQ(ec.value(), 0) << "Accept handler should not be in error"; - io_service_client_.post(boost::bind(wait_closing_lambda, ec)); - }; + io_service_client_.post(boost::bind(wait_closing_lambda, ec)); + }; auto connected_lambda = [this, &fib_client, &connect_closed]( const boost::system::error_code& ec) { @@ -318,9 +305,6 @@ TEST_F(FiberTest, connectDisconnectFiberFromClient) { //----------------------------------------------------------------------------- TEST_F(FiberTest, multipleConnectDisconnectFiber) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - Wait(); const uint32_t number_of_connections = 1000; @@ -341,42 +325,42 @@ TEST_F(FiberTest, multipleConnectDisconnectFiber) { boost::bind(async_accept_h1, p_fiber, _1)); }; - async_accept_h1 = [this, &async_accept_h2, &number_of_accepted, - &number_of_connections, &accepted_all]( - std::shared_ptr p_fiber, - const boost::system::error_code& accept_ec) { - if (!accept_ec) { - p_fiber->close(); - ++number_of_accepted; - - if (number_of_accepted == number_of_connections) { - accepted_all.set_value(true); - return; - } - - this->io_service_server_.dispatch(async_accept_h2); - } else { - ASSERT_EQ(accept_ec.value(), 0) - << "Accept handler should not be in error: " << accept_ec.value(); - } - }; - - auto async_connect_h1 = [this, &number_of_connected, &number_of_connections, - &connected_all]( - std::shared_ptr p_fiber, - const boost::system::error_code& connect_ec) { - if (!connect_ec) { - p_fiber->close(); - ++number_of_connected; - - if (number_of_connected == number_of_connections) { - connected_all.set_value(true); - } - } else { - ASSERT_EQ(connect_ec.value(), 0) - << "Connect handler should not be in error"; - } - }; + async_accept_h1 = + [this, &async_accept_h2, &number_of_accepted, &number_of_connections, + &accepted_all](std::shared_ptr p_fiber, + const boost::system::error_code& accept_ec) { + if (!accept_ec) { + p_fiber->close(); + ++number_of_accepted; + + if (number_of_accepted == number_of_connections) { + accepted_all.set_value(true); + return; + } + + this->io_service_server_.dispatch(async_accept_h2); + } else { + ASSERT_EQ(accept_ec.value(), 0) + << "Accept handler should not be in error: " << accept_ec.value(); + } + }; + + auto async_connect_h1 = + [this, &number_of_connected, &number_of_connections, &connected_all]( + std::shared_ptr p_fiber, + const boost::system::error_code& connect_ec) { + if (!connect_ec) { + p_fiber->close(); + ++number_of_connected; + + if (number_of_connected == number_of_connections) { + connected_all.set_value(true); + } + } else { + ASSERT_EQ(connect_ec.value(), 0) + << "Connect handler should not be in error"; + } + }; boost::system::error_code ec_server; fiber_endpoint fib_server_endpoint( @@ -403,9 +387,6 @@ TEST_F(FiberTest, multipleConnectDisconnectFiber) { //----------------------------------------------------------------------------- TEST_F(FiberTest, exchangePackets) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - Wait(); const int32_t packet_number = 1000; @@ -421,9 +402,9 @@ TEST_F(FiberTest, exchangePackets) { bool result_server = true; int server_received = 0; - std::function + std::function async_receive_h1; - std::function + std::function async_send_h1; async_receive_h1 = [&, this](const boost::system::error_code& ec, @@ -471,9 +452,9 @@ TEST_F(FiberTest, exchangePackets) { boost::bind(async_receive_h1, _1, _2)); }; - std::function + std::function async_receive_h2; - std::function + std::function async_send_h2; async_receive_h2 = [&, this](const boost::system::error_code& ec, @@ -535,9 +516,6 @@ TEST_F(FiberTest, exchangePackets) { ////----------------------------------------------------------------------------- TEST_F(FiberTest, exchangePacketsFiveClients) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - Wait(); typedef std::array buffer_type; @@ -573,71 +551,71 @@ TEST_F(FiberTest, exchangePacketsFiveClients) { std::function&, fiber&, buffer_type&)> async_send_h1; - async_receive_h1 = [&](const boost::system::error_code& ec, std::size_t, - std::promise& closed, fiber& fib, - buffer_type& buffer_server) { - - if (ec) { - boost::recursive_mutex::scoped_lock lock_server_received( - server_received_mutex); - ASSERT_EQ(packet_number, server_received); - return; - } - - ASSERT_EQ(buffer_server[0], 'a'); - ASSERT_EQ(buffer_server[1], 'b'); - ASSERT_EQ(buffer_server[2], 'c'); - ASSERT_EQ(buffer_server[3], 'd'); - ASSERT_EQ(buffer_server[4], 'e'); - - buffer_server[0] = '1'; - buffer_server[1] = '2'; - buffer_server[2] = '3'; - buffer_server[3] = '4'; - buffer_server[4] = '5'; - - boost::recursive_mutex::scoped_lock lock(server_received_mutex); - if (++server_received < packet_number - 4) { - boost::asio::async_write( - fib, boost::asio::buffer(buffer_server), - boost::bind(async_send_h1, _1, _2, boost::ref(closed), - boost::ref(fib), boost::ref(buffer_server))); - } else if (server_received < packet_number) { - fib.close(); - closed.set_value(true); - } else { - finished = true; - fib.close(); - closed.set_value(true); - } - }; - - async_send_h1 = [&](const boost::system::error_code& ec, std::size_t, - std::promise& closed, fiber& fib, - buffer_type& buffer_server) { - ASSERT_EQ(ec.value(), 0); - boost::asio::async_read( - fib, boost::asio::buffer(buffer_server), - boost::bind(async_receive_h1, _1, _2, boost::ref(closed), - boost::ref(fib), boost::ref(buffer_server))); - }; - - auto async_accept_h = [&, this](const boost::system::error_code& ec, - std::promise& closed, fiber& fib, - buffer_type& buffer_server) { - EXPECT_EQ(ec.value(), 0); - - buffer_server[0] = '1'; - buffer_server[1] = '2'; - buffer_server[2] = '3'; - buffer_server[3] = '4'; - buffer_server[4] = '5'; - - boost::asio::async_read( - fib, boost::asio::buffer(buffer_server), - boost::bind(async_receive_h1, _1, _2, boost::ref(closed), - boost::ref(fib), boost::ref(buffer_server))); - }; + async_receive_h1 = + [&](const boost::system::error_code& ec, std::size_t, + std::promise& closed, fiber& fib, buffer_type& buffer_server) { + + if (ec) { + boost::recursive_mutex::scoped_lock lock_server_received( + server_received_mutex); + ASSERT_EQ(packet_number, server_received); + return; + } + + ASSERT_EQ(buffer_server[0], 'a'); + ASSERT_EQ(buffer_server[1], 'b'); + ASSERT_EQ(buffer_server[2], 'c'); + ASSERT_EQ(buffer_server[3], 'd'); + ASSERT_EQ(buffer_server[4], 'e'); + + buffer_server[0] = '1'; + buffer_server[1] = '2'; + buffer_server[2] = '3'; + buffer_server[3] = '4'; + buffer_server[4] = '5'; + + boost::recursive_mutex::scoped_lock lock(server_received_mutex); + if (++server_received < packet_number - 4) { + boost::asio::async_write( + fib, boost::asio::buffer(buffer_server), + boost::bind(async_send_h1, _1, _2, boost::ref(closed), + boost::ref(fib), boost::ref(buffer_server))); + } else if (server_received < packet_number) { + fib.close(); + closed.set_value(true); + } else { + finished = true; + fib.close(); + closed.set_value(true); + } + }; + + async_send_h1 = + [&](const boost::system::error_code& ec, std::size_t, + std::promise& closed, fiber& fib, buffer_type& buffer_server) { + ASSERT_EQ(ec.value(), 0); + boost::asio::async_read( + fib, boost::asio::buffer(buffer_server), + boost::bind(async_receive_h1, _1, _2, boost::ref(closed), + boost::ref(fib), boost::ref(buffer_server))); + }; + + auto async_accept_h = + [&, this](const boost::system::error_code& ec, std::promise& closed, + fiber& fib, buffer_type& buffer_server) { + EXPECT_EQ(ec.value(), 0); + + buffer_server[0] = '1'; + buffer_server[1] = '2'; + buffer_server[2] = '3'; + buffer_server[3] = '4'; + buffer_server[4] = '5'; + + boost::asio::async_read( + fib, boost::asio::buffer(buffer_server), + boost::bind(async_receive_h1, _1, _2, boost::ref(closed), + boost::ref(fib), boost::ref(buffer_server))); + }; std::function&, fiber&, buffer_type&)> @@ -645,57 +623,57 @@ TEST_F(FiberTest, exchangePacketsFiveClients) { std::function&, fiber&, buffer_type&)> async_send_h2; - async_receive_h2 = [&](const boost::system::error_code& ec, std::size_t, - std::promise& closed, fiber& fib, - buffer_type& buffer_client) { - if (!ec) { - ASSERT_EQ(buffer_client[0], '1'); - ASSERT_EQ(buffer_client[1], '2'); - ASSERT_EQ(buffer_client[2], '3'); - ASSERT_EQ(buffer_client[3], '4'); - ASSERT_EQ(buffer_client[4], '5'); - buffer_client[0] = 'a'; - buffer_client[1] = 'b'; - buffer_client[2] = 'c'; - buffer_client[3] = 'd'; - buffer_client[4] = 'e'; - - boost::asio::async_write( - fib, boost::asio::buffer(buffer_client), - boost::bind(async_send_h2, _1, _2, boost::ref(closed), - boost::ref(fib), boost::ref(buffer_client))); - } else { - fib.close(); - closed.set_value(true); - } - }; - - async_send_h2 = [&](const boost::system::error_code& ec, std::size_t, - std::promise& closed, fiber& fib, - buffer_type& buffer_client) { - EXPECT_EQ(ec.value(), 0); - boost::asio::async_read( - fib, boost::asio::buffer(buffer_client), - boost::bind(async_receive_h2, _1, _2, boost::ref(closed), - boost::ref(fib), boost::ref(buffer_client))); - }; - - auto async_connect_h = [&](std::promise& closed, fiber& fib, - buffer_type& buffer_client, - const boost::system::error_code& ec) { - ASSERT_EQ(ec.value(), 0); - - buffer_client[0] = 'a'; - buffer_client[1] = 'b'; - buffer_client[2] = 'c'; - buffer_client[3] = 'd'; - buffer_client[4] = 'e'; - - boost::asio::async_write( - fib, boost::asio::buffer(buffer_client), - boost::bind(async_send_h2, _1, _2, boost::ref(closed), - boost::ref(fib), boost::ref(buffer_client))); - }; + async_receive_h2 = + [&](const boost::system::error_code& ec, std::size_t, + std::promise& closed, fiber& fib, buffer_type& buffer_client) { + if (!ec) { + ASSERT_EQ(buffer_client[0], '1'); + ASSERT_EQ(buffer_client[1], '2'); + ASSERT_EQ(buffer_client[2], '3'); + ASSERT_EQ(buffer_client[3], '4'); + ASSERT_EQ(buffer_client[4], '5'); + buffer_client[0] = 'a'; + buffer_client[1] = 'b'; + buffer_client[2] = 'c'; + buffer_client[3] = 'd'; + buffer_client[4] = 'e'; + + boost::asio::async_write( + fib, boost::asio::buffer(buffer_client), + boost::bind(async_send_h2, _1, _2, boost::ref(closed), + boost::ref(fib), boost::ref(buffer_client))); + } else { + fib.close(); + closed.set_value(true); + } + }; + + async_send_h2 = + [&](const boost::system::error_code& ec, std::size_t, + std::promise& closed, fiber& fib, buffer_type& buffer_client) { + EXPECT_EQ(ec.value(), 0); + boost::asio::async_read( + fib, boost::asio::buffer(buffer_client), + boost::bind(async_receive_h2, _1, _2, boost::ref(closed), + boost::ref(fib), boost::ref(buffer_client))); + }; + + auto async_connect_h = + [&](std::promise& closed, fiber& fib, buffer_type& buffer_client, + const boost::system::error_code& ec) { + ASSERT_EQ(ec.value(), 0); + + buffer_client[0] = 'a'; + buffer_client[1] = 'b'; + buffer_client[2] = 'c'; + buffer_client[3] = 'd'; + buffer_client[4] = 'e'; + + boost::asio::async_write( + fib, boost::asio::buffer(buffer_client), + boost::bind(async_send_h2, _1, _2, boost::ref(closed), + boost::ref(fib), boost::ref(buffer_client))); + }; boost::system::error_code acceptor_ec; fiber_endpoint server_endpoint(boost::asio::fiber::stream_fiber::v1(), @@ -738,9 +716,6 @@ TEST_F(FiberTest, exchangePacketsFiveClients) { //----------------------------------------------------------------------------- TEST_F(FiberTest, tooSmallReceiveBuffer) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - Wait(); std::array buffer_client; @@ -758,18 +733,18 @@ TEST_F(FiberTest, tooSmallReceiveBuffer) { std::promise client_closed; std::promise server_closed; - auto void_handler_receive = [&](const boost::system::error_code& ec, - size_t s) { - boost::recursive_mutex::scoped_lock lock(received_mutex); - received += s; - ++count; + auto void_handler_receive = + [&](const boost::system::error_code& ec, size_t s) { + boost::recursive_mutex::scoped_lock lock(received_mutex); + received += s; + ++count; - if (count == 4) { - ASSERT_EQ(received, 5) << "Not received all data"; - ASSERT_EQ(fib_server.is_open(), false) << "Server fiber not closed"; - server_closed.set_value(true); - } - }; + if (count == 4) { + ASSERT_EQ(received, 5) << "Not received all data"; + ASSERT_EQ(fib_server.is_open(), false) << "Server fiber not closed"; + server_closed.set_value(true); + } + }; auto async_accept_h1 = [&, this](const boost::system::error_code& ec) { ASSERT_EQ(ec.value(), 0); @@ -786,8 +761,8 @@ TEST_F(FiberTest, tooSmallReceiveBuffer) { auto void_handler_send = [&](const boost::system::error_code& ec, size_t s) { EXPECT_EQ(ec.value(), 0); if (ec) { - client_closed.set_value(false); - return; + client_closed.set_value(false); + return; } sent += s; @@ -823,9 +798,7 @@ TEST_F(FiberTest, tooSmallReceiveBuffer) { client_closed.get_future().wait(); server_closed.get_future().wait(); - { - boost::recursive_mutex::scoped_lock lock(received_mutex); - } + { boost::recursive_mutex::scoped_lock lock(received_mutex); } EXPECT_EQ(received, 5); EXPECT_EQ(sent, 5); @@ -835,9 +808,6 @@ TEST_F(FiberTest, tooSmallReceiveBuffer) { //----------------------------------------------------------------------------- TEST_F(FiberTest, UDPfiber) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - Wait(); uint32_t rem_p = (1 << 16) + 1; @@ -859,33 +829,33 @@ TEST_F(FiberTest, UDPfiber) { boost::asio::fiber::datagram_fiber::v1(), demux_server_); std::array buffer_server; - auto sent_server = [&](const boost::system::error_code& sent_ec, - size_t length) { - ASSERT_EQ(sent_ec.value(), 0) << "Sent handler should not be in error"; - dgr_f_server.close(); - server_closed.set_value(true); - }; - - auto received_server = [&](const boost::system::error_code& received_ec, - size_t length) { - ASSERT_EQ(received_ec.value(), 0) - << "Received handler should not be in error"; - EXPECT_EQ(buffer_client[0], buffer_server[0]); - EXPECT_EQ(buffer_client[1], buffer_server[1]); - EXPECT_EQ(buffer_client[2], buffer_server[2]); - EXPECT_EQ(buffer_client[3], buffer_server[3]); - EXPECT_EQ(buffer_client[4], buffer_server[4]); - - buffer_server[0] = 10; - buffer_server[1] = 11; - buffer_server[2] = 12; - buffer_server[3] = 13; - buffer_server[4] = 14; - - dgr_f_server.async_send_to(boost::asio::buffer(buffer_server), - endpoint_server_from, - boost::bind(sent_server, _1, _2)); - }; + auto sent_server = + [&](const boost::system::error_code& sent_ec, size_t length) { + ASSERT_EQ(sent_ec.value(), 0) << "Sent handler should not be in error"; + dgr_f_server.close(); + server_closed.set_value(true); + }; + + auto received_server = + [&](const boost::system::error_code& received_ec, size_t length) { + ASSERT_EQ(received_ec.value(), 0) + << "Received handler should not be in error"; + EXPECT_EQ(buffer_client[0], buffer_server[0]); + EXPECT_EQ(buffer_client[1], buffer_server[1]); + EXPECT_EQ(buffer_client[2], buffer_server[2]); + EXPECT_EQ(buffer_client[3], buffer_server[3]); + EXPECT_EQ(buffer_client[4], buffer_server[4]); + + buffer_server[0] = 10; + buffer_server[1] = 11; + buffer_server[2] = 12; + buffer_server[3] = 13; + buffer_server[4] = 14; + + dgr_f_server.async_send_to(boost::asio::buffer(buffer_server), + endpoint_server_from, + boost::bind(sent_server, _1, _2)); + }; dgr_f_server.open(endpoint_server_local_port.protocol(), ec); dgr_f_server.bind(endpoint_server_local_port, ec); @@ -904,31 +874,31 @@ TEST_F(FiberTest, UDPfiber) { dgr_fiber_endpoint endpoint_client_new_remote_port( boost::asio::fiber::datagram_fiber::v1(), demux_client_); - auto received_client = [&](const boost::system::error_code& received_ec, - size_t length) { - ASSERT_EQ(received_ec.value(), 0) - << "Received handler should not be in error"; - EXPECT_EQ(buffer_client[0], buffer_server[0]); - EXPECT_EQ(buffer_client[1], buffer_server[1]); - EXPECT_EQ(buffer_client[2], buffer_server[2]); - EXPECT_EQ(buffer_client[3], buffer_server[3]); - EXPECT_EQ(buffer_client[4], buffer_server[4]); - - ASSERT_EQ(endpoint_client_new_remote_port.port(), - endpoint_client_remote_port.port()) - << "Endpoint ports should be equal"; - - dgr_f_client.close(); - client_closed.set_value(true); - }; - - auto sent_client = [&](const boost::system::error_code& sent_ec, - size_t length) { - ASSERT_EQ(sent_ec.value(), 0) << "Sent handler should not be in error"; - dgr_f_client.async_receive_from(boost::asio::buffer(buffer_client), - endpoint_client_new_remote_port, - boost::bind(received_client, _1, _2)); - }; + auto received_client = + [&](const boost::system::error_code& received_ec, size_t length) { + ASSERT_EQ(received_ec.value(), 0) + << "Received handler should not be in error"; + EXPECT_EQ(buffer_client[0], buffer_server[0]); + EXPECT_EQ(buffer_client[1], buffer_server[1]); + EXPECT_EQ(buffer_client[2], buffer_server[2]); + EXPECT_EQ(buffer_client[3], buffer_server[3]); + EXPECT_EQ(buffer_client[4], buffer_server[4]); + + ASSERT_EQ(endpoint_client_new_remote_port.port(), + endpoint_client_remote_port.port()) + << "Endpoint ports should be equal"; + + dgr_f_client.close(); + client_closed.set_value(true); + }; + + auto sent_client = + [&](const boost::system::error_code& sent_ec, size_t length) { + ASSERT_EQ(sent_ec.value(), 0) << "Sent handler should not be in error"; + dgr_f_client.async_receive_from( + boost::asio::buffer(buffer_client), endpoint_client_new_remote_port, + boost::bind(received_client, _1, _2)); + }; dgr_f_client.async_send_to(boost::asio::buffer(buffer_client), endpoint_client_remote_port, @@ -940,10 +910,6 @@ TEST_F(FiberTest, UDPfiber) { //---------------------------------------------------------------------------- TEST_F(FiberTest, SSLconnectDisconnectFiberFromClient) { - - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - Wait(); typedef boost::asio::ssl::stream ssl_fiber_t; @@ -951,31 +917,31 @@ TEST_F(FiberTest, SSLconnectDisconnectFiberFromClient) { std::function verify_callback = [](bool preverified, boost::asio::ssl::verify_context& ctx) { - BOOST_LOG_TRIVIAL(debug) << "------------------------------" << std::endl; - X509_STORE_CTX* cts = ctx.native_handle(); - X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); - - char subject_name[256]; - auto err = X509_STORE_CTX_get_error(ctx.native_handle()); - auto depth_err = X509_STORE_CTX_get_error_depth(ctx.native_handle()); - - BOOST_LOG_TRIVIAL(debug) << "Error " << X509_verify_cert_error_string(err) - << std::endl; - BOOST_LOG_TRIVIAL(debug) << "Depth " << depth_err << std::endl; - - X509* issuer = cts->current_issuer; - if (issuer) { - X509_NAME_oneline(X509_get_subject_name(issuer), subject_name, 256); - BOOST_LOG_TRIVIAL(debug) << "Issuer " << subject_name << "\n"; - } + SSF_LOG(kLogDebug) << "------------------------------"; + X509_STORE_CTX* cts = ctx.native_handle(); + X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); - X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); - BOOST_LOG_TRIVIAL(debug) << "Verifying " << subject_name << "\n"; + char subject_name[256]; + auto err = X509_STORE_CTX_get_error(ctx.native_handle()); + auto depth_err = X509_STORE_CTX_get_error_depth(ctx.native_handle()); - BOOST_LOG_TRIVIAL(debug) << "------------------------------" << std::endl; + SSF_LOG(kLogDebug) << "Error " << X509_verify_cert_error_string(err) + << std::endl; + SSF_LOG(kLogDebug) << "Depth " << depth_err; - return preverified; - }; + X509* issuer = cts->current_issuer; + if (issuer) { + X509_NAME_oneline(X509_get_subject_name(issuer), subject_name, 256); + SSF_LOG(kLogDebug) << "Issuer " << subject_name; + } + + X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); + SSF_LOG(kLogDebug) << "Verifying " << subject_name; + + SSF_LOG(kLogDebug) << "------------------------------"; + + return preverified; + }; ctx.set_verify_depth(100); @@ -1017,7 +983,7 @@ TEST_F(FiberTest, SSLconnectDisconnectFiberFromClient) { auto sent = [this, &server_closed, &ssl_fiber_server, &buffer_s]( const boost::system::error_code& ec, size_t length) { EXPECT_EQ(ec.value(), 0) << "Sent handler should not be in error"; - BOOST_LOG_TRIVIAL(debug) << "Server sent: " << buffer_s << std::endl; + SSF_LOG(kLogDebug) << "Server sent: " << buffer_s << std::endl; server_closed.set_value(true); }; @@ -1037,15 +1003,16 @@ TEST_F(FiberTest, SSLconnectDisconnectFiberFromClient) { boost::bind(async_handshaked_s, _1)); }; - auto received = [this, &client_closed, &ssl_fiber_client, &buffer_c, - &buffer_s](const boost::system::error_code& ec, - size_t length) { - EXPECT_EQ(ec.value(), 0) << "Received handler should not be in error " << ec.message(); - EXPECT_EQ(buffer_s, buffer_c); - BOOST_LOG_TRIVIAL(debug) << "client received: " << buffer_c << std::endl; - ssl_fiber_client.next_layer().close(); - client_closed.set_value(true); - }; + auto received = + [this, &client_closed, &ssl_fiber_client, &buffer_c, &buffer_s]( + const boost::system::error_code& ec, size_t length) { + EXPECT_EQ(ec.value(), 0) << "Received handler should not be in error " + << ec.message(); + EXPECT_EQ(buffer_s, buffer_c); + SSF_LOG(kLogDebug) << "client received: " << buffer_c << std::endl; + ssl_fiber_client.next_layer().close(); + client_closed.set_value(true); + }; auto async_handshaked_c = [this, &ssl_fiber_client, &buffer_c, &received]( const boost::system::error_code& ec) { diff --git a/src/tests/network/ssf_client_server_cipher_suites_tests.cpp b/src/tests/network/ssf_client_server_cipher_suites_tests.cpp new file mode 100644 index 00000000..64e5af65 --- /dev/null +++ b/src/tests/network/ssf_client_server_cipher_suites_tests.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "common/config/config.h" + +#include "core/network_protocol.h" +#include "core/client/client.h" +#include "core/server/server.h" + +#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" + +#include "services/initialisation.h" +#include "services/user_services/udp_port_forwarding.h" + +using NetworkProtocol = ssf::network::NetworkProtocol; + +class SSFClientServerCipherSuitesTest : public ::testing::Test { + public: + using Client = ssf::SSFClient; + using Server = ssf::SSFServer; + using demux = Client::Demux; + using BaseUserServicePtr = + ssf::services::BaseUserService::BaseUserServicePtr; + typedef boost::function ClientCallback; + + public: + SSFClientServerCipherSuitesTest() + : p_ssf_client_(nullptr), p_ssf_server_(nullptr) {} + + ~SSFClientServerCipherSuitesTest() {} + + virtual void TearDown() { + p_ssf_client_->Stop(); + p_ssf_server_->Stop(); + } + + void StartServer(const ssf::config::Config& config) { + auto endpoint_query = + NetworkProtocol::GenerateServerTLSQuery("", "8000", config); + + p_ssf_server_.reset(new Server(config.services())); + + boost::system::error_code run_ec; + p_ssf_server_->Run(endpoint_query, run_ec); + } + + void StartClient(const ssf::config::Config& config, + const ClientCallback& callback) { + std::vector client_options; + + auto endpoint_query = NetworkProtocol::GenerateClientTLSQuery( + "127.0.0.1", "8000", config, {}); + + p_ssf_client_.reset(new Client(client_options, config.services(), callback)); + + boost::system::error_code run_ec; + p_ssf_client_->Run(endpoint_query, run_ec); + } + + protected: + std::unique_ptr p_ssf_client_; + std::unique_ptr p_ssf_server_; +}; + +TEST_F(SSFClientServerCipherSuitesTest, connectDisconnectDifferentSuite) { + std::promise network_set; + std::promise transport_set; + + auto network_set_future = network_set.get_future(); + auto transport_set_future = transport_set.get_future(); + auto callback = [&network_set, &transport_set]( + ssf::services::initialisation::type type, + SSFClientServerCipherSuitesTest::BaseUserServicePtr p_user_service, + const boost::system::error_code& ec) { + if (type == ssf::services::initialisation::NETWORK) { + EXPECT_TRUE(!!ec); + network_set.set_value(!ec); + transport_set.set_value(false); + + return; + } + }; + ssf::config::Config client_config; + ssf::config::Config server_config; + server_config.tls().set_cipher_alg("DHE-RSA-AES256-GCM-SHA256"); + + StartServer(server_config); + StartClient(client_config, callback); + + network_set_future.wait(); + transport_set_future.wait(); +} + +TEST_F(SSFClientServerCipherSuitesTest, connectDisconnectTwoSuites) { + std::promise network_set; + std::promise transport_set; + + auto network_set_future = network_set.get_future(); + auto transport_set_future = transport_set.get_future(); + + auto callback = [&network_set, &transport_set]( + ssf::services::initialisation::type type, + SSFClientServerCipherSuitesTest::BaseUserServicePtr p_user_service, + const boost::system::error_code& ec) { + if (type == ssf::services::initialisation::NETWORK) { + EXPECT_TRUE(!ec); + network_set.set_value(!ec); + + return; + } else if (type == ssf::services::initialisation::TRANSPORT) { + EXPECT_TRUE(!ec); + transport_set.set_value(!ec); + + return; + } + }; + ssf::config::Config client_config; + ssf::config::Config server_config; + + client_config.tls().set_cipher_alg( + "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256^"); + server_config.tls().set_cipher_alg( + "ECDH-ECDSA-AES128-SHA256:DHE-RSA-AES128-SHA256"); + + StartServer(server_config); + StartClient(client_config, callback); + + network_set_future.wait(); + transport_set_future.wait(); +} diff --git a/src/tests/network/ssf_client_server_tests.cpp b/src/tests/network/ssf_client_server_tests.cpp new file mode 100644 index 00000000..b3e344a4 --- /dev/null +++ b/src/tests/network/ssf_client_server_tests.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "common/config/config.h" + +#include "core/network_protocol.h" +#include "core/client/client.h" +#include "core/server/server.h" + +#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" + +#include "services/initialisation.h" +#include "services/user_services/udp_port_forwarding.h" + +using NetworkProtocol = ssf::network::NetworkProtocol; + +class SSFClientServerTest : public ::testing::Test { + public: + using Client = + ssf::SSFClient; + using Server = + ssf::SSFServer; + + using demux = Client::Demux; + + using BaseUserServicePtr = + ssf::services::BaseUserService::BaseUserServicePtr; + + public: + SSFClientServerTest() : p_ssf_client_(nullptr), p_ssf_server_(nullptr) {} + + ~SSFClientServerTest() {} + + virtual void SetUp() { + StartServer(); + StartClient(); + } + + virtual void TearDown() { + p_ssf_client_->Stop(); + p_ssf_server_->Stop(); + } + + void StartServer() { + ssf::config::Config ssf_config; + + auto endpoint_query = + NetworkProtocol::GenerateServerQuery("", "8000", ssf_config); + p_ssf_server_.reset(new Server(ssf_config.services())); + + boost::system::error_code run_ec; + p_ssf_server_->Run(endpoint_query, run_ec); + } + + void StartClient() { + std::vector client_options; + + ssf::config::Config ssf_config; + + auto endpoint_query = NetworkProtocol::GenerateClientQuery( + "127.0.0.1", "8000", ssf_config, {}); + + p_ssf_client_.reset( + new Client(client_options, ssf_config.services(), + boost::bind(&SSFClientServerTest::SSFClientCallback, this, + _1, _2, _3))); + + boost::system::error_code run_ec; + p_ssf_client_->Run(endpoint_query, run_ec); + } + + bool Wait() { + auto network_set_future = network_set_.get_future(); + auto transport_set_future = transport_set_.get_future(); + + network_set_future.wait(); + transport_set_future.wait(); + + return network_set_future.get() && transport_set_future.get(); + } + + void SSFClientCallback(ssf::services::initialisation::type type, + BaseUserServicePtr p_user_service, + const boost::system::error_code& ec) { + if (type == ssf::services::initialisation::NETWORK) { + network_set_.set_value(!ec); + if (ec) { + transport_set_.set_value(false); + } + + return; + } + + if (type == ssf::services::initialisation::TRANSPORT) { + transport_set_.set_value(!ec); + + return; + } + } + + protected: + std::unique_ptr p_ssf_client_; + std::unique_ptr p_ssf_server_; + + std::promise network_set_; + std::promise transport_set_; +}; + +TEST_F(SSFClientServerTest, connectDisconnect) { ASSERT_TRUE(Wait()); } diff --git a/src/tests/network/ssf_server_tests.cpp b/src/tests/network/ssf_server_tests.cpp new file mode 100644 index 00000000..523b4971 --- /dev/null +++ b/src/tests/network/ssf_server_tests.cpp @@ -0,0 +1,61 @@ +#include + +#include "common/config/config.h" + +#include "core/network_protocol.h" +#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" +#include "core/server/server.h" + +using NetworkProtocol = ssf::network::NetworkProtocol; + +TEST(SSFServerTest, failListeningWrongInterface) { + using Server = + ssf::SSFServer; + + ssf::config::Config ssf_config; + + auto endpoint_query = + NetworkProtocol::GenerateServerQuery("1.1.1.1", "8000", ssf_config); + Server server(ssf_config.services()); + + boost::system::error_code run_ec; + server.Run(endpoint_query, run_ec); + + ASSERT_NE(0, run_ec.value()); +} + +TEST(SSFServerTest, listeningAllInterfaces) { + using Server = + ssf::SSFServer; + + ssf::config::Config ssf_config; + + auto endpoint_query = + NetworkProtocol::GenerateServerQuery("", "8000", ssf_config); + Server server(ssf_config.services()); + + boost::system::error_code run_ec; + server.Run(endpoint_query, run_ec); + + ASSERT_EQ(0, run_ec.value()); + + server.Stop(); +} + +TEST(SSFServerTest, listeningLocalhostInterface) { + using Server = + ssf::SSFServer; + + ssf::config::Config ssf_config; + + auto endpoint_query = + NetworkProtocol::GenerateServerQuery("127.0.0.1", "8000", ssf_config); + Server server(ssf_config.services()); + + boost::system::error_code run_ec; + server.Run(endpoint_query, run_ec); + + ASSERT_EQ(0, run_ec.value()); + + server.Stop(); +} diff --git a/src/tests/remote_socks_tests.cpp b/src/tests/remote_socks_tests.cpp deleted file mode 100644 index 92d01007..00000000 --- a/src/tests/remote_socks_tests.cpp +++ /dev/null @@ -1,538 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "common/config/config.h" - -#include "core/client/client.h" -#include "core/server/server.h" - -#include "core/network_virtual_layer_policies/link_policies/ssl_policy.h" -#include "core/network_virtual_layer_policies/link_policies/tcp_policy.h" -#include "core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h" -#include "core/network_virtual_layer_policies/bounce_protocol_policy.h" -#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" - -#include "services/initialisation.h" -#include "services/user_services/remote_socks.h" - -class request { - public: - enum command_type { connect = 0x01, bind = 0x02 }; - - request(command_type cmd, const boost::asio::ip::tcp::endpoint& endpoint, - const std::string& user_id) - : version_(0x04), command_(cmd), user_id_(user_id), null_byte_(0) { - // Only IPv4 is supported by the SOCKS 4 protocol. - if (endpoint.protocol() != boost::asio::ip::tcp::v4()) { - throw boost::system::system_error( - boost::asio::error::address_family_not_supported); - } - - // Convert port number to network byte order. - unsigned short port = endpoint.port(); - port_high_byte_ = (port >> 8) & 0xff; - port_low_byte_ = port & 0xff; - - // Save IP address in network byte order. - address_ = endpoint.address().to_v4().to_bytes(); - } - - std::array buffers() const { - std::array bufs = { - {boost::asio::buffer(&version_, 1), boost::asio::buffer(&command_, 1), - boost::asio::buffer(&port_high_byte_, 1), - boost::asio::buffer(&port_low_byte_, 1), boost::asio::buffer(address_), - boost::asio::buffer(user_id_), boost::asio::buffer(&null_byte_, 1)}}; - return bufs; - } - - private: - unsigned char version_; - unsigned char command_; - unsigned char port_high_byte_; - unsigned char port_low_byte_; - boost::asio::ip::address_v4::bytes_type address_; - std::string user_id_; - unsigned char null_byte_; -}; - -class reply { - public: - enum status_type { - request_granted = 0x5a, - request_failed = 0x5b, - request_failed_no_identd = 0x5c, - request_failed_bad_user_id = 0x5d - }; - - reply() : null_byte_(0), status_() {} - - std::array buffers() { - std::array bufs = { - {boost::asio::buffer(&null_byte_, 1), boost::asio::buffer(&status_, 1), - boost::asio::buffer(&port_high_byte_, 1), - boost::asio::buffer(&port_low_byte_, 1), - boost::asio::buffer(address_)}}; - return bufs; - } - - bool success() const { return null_byte_ == 0 && status_ == request_granted; } - - unsigned char status() const { return status_; } - - boost::asio::ip::tcp::endpoint endpoint() const { - unsigned short port = port_high_byte_; - port = (port << 8) & 0xff00; - port = port | port_low_byte_; - - boost::asio::ip::address_v4 address(address_); - - return boost::asio::ip::tcp::endpoint(address, port); - } - - private: - unsigned char null_byte_; - unsigned char status_; - unsigned char port_high_byte_; - unsigned char port_low_byte_; - boost::asio::ip::address_v4::bytes_type address_; -}; - -class DummyClient { - public: - DummyClient(std::size_t size) - : io_service_(), - p_worker_(new boost::asio::io_service::work(io_service_)), - socket_(io_service_), - size_(size) {} - - bool Init() { - t_ = boost::thread([&]() { io_service_.run(); }); - - boost::asio::ip::tcp::resolver r(io_service_); - boost::asio::ip::tcp::resolver::query q("127.0.0.1", "8081"); - boost::system::error_code ec; - boost::asio::connect(socket_, r.resolve(q), ec); - - if (ec) { - BOOST_LOG_TRIVIAL(error) << "dummy client : fail to connect " - << ec.value(); - Stop(); - } - - return !ec; - } - - bool InitSocks() { - boost::system::error_code ec; - - boost::asio::ip::tcp::resolver r2(io_service_); - boost::asio::ip::tcp::resolver::query q2("127.0.0.1", "8080"); - request req(request::command_type::connect, *r2.resolve(q2), "01"); - - boost::asio::write(socket_, req.buffers(), ec); - - if (ec) { - BOOST_LOG_TRIVIAL(error) << "dummy client : fail to write " << ec.value(); - Stop(); - return false; - } - - reply rep; - - boost::asio::read(socket_, rep.buffers(), ec); - - if (ec) { - BOOST_LOG_TRIVIAL(error) << "dummy client : fail to read " << ec.value(); - Stop(); - return false; - } - - if (!rep.success()) { - Stop(); - return false; - } - - boost::asio::write(socket_, boost::asio::buffer(&size_, sizeof(size_)), - ec); - - if (ec) { - BOOST_LOG_TRIVIAL(error) << "dummy client : fail to write " << ec.value(); - Stop(); - } - - return !ec; - } - - bool ReceiveOneBuffer() { - std::size_t received(0); - - while (received < size_) { - boost::system::error_code ec; - auto n = socket_.receive(boost::asio::buffer(one_buffer_), 0, ec); - - if (ec) { - Stop(); - return false; - } - - if (n == 0) { - Stop(); - return false; - } else { - received += n; - if (!CheckOneBuffer(n)) { - Stop(); - return false; - } - } - } - - Stop(); - return received == size_; - } - - void Stop() { - boost::system::error_code ec; - socket_.close(ec); - - p_worker_.reset(); - - t_.join(); - io_service_.stop(); - } - - private: - bool CheckOneBuffer(std::size_t n) { - for (std::size_t i = 0; i < n; ++i) { - if (one_buffer_[i] != 1) { - return false; - } - } - - return true; - } - - private: - boost::asio::io_service io_service_; - std::unique_ptr p_worker_; - boost::asio::ip::tcp::socket socket_; - boost::thread t_; - std::size_t size_; - std::array one_buffer_; -}; - -class DummyServer { - public: - DummyServer() - : io_service_(), - p_worker_(new boost::asio::io_service::work(io_service_)), - acceptor_(io_service_), - one_buffer_size_(10240) { - for (std::size_t i = 0; i < one_buffer_size_; ++i) { - one_buffer_[i] = 1; - } - } - - void Run() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - threads_.create_thread([&]() { io_service_.run(); }); - } - - boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 8080); - boost::asio::socket_base::reuse_address option(true); - - acceptor_.open(endpoint.protocol()); - acceptor_.set_option(option); - acceptor_.bind(endpoint); - acceptor_.listen(); - - DoAccept(); - } - - void Stop() { - sockets_.clear(); - boost::system::error_code ec; - acceptor_.close(ec); - - p_worker_.reset(); - - threads_.join_all(); - io_service_.stop(); - } - - private: - void DoAccept() { - auto p_socket = std::make_shared(io_service_); - sockets_.insert(p_socket); - acceptor_.async_accept( - *p_socket, boost::bind(&DummyServer::HandleAccept, this, p_socket, _1)); - } - - void HandleAccept(std::shared_ptr p_socket, - const boost::system::error_code& ec) { - if (!ec) { - auto p_size = std::make_shared(0); - boost::asio::async_read( - *p_socket, boost::asio::buffer(p_size.get(), sizeof(*p_size)), - boost::bind(&DummyServer::DoSendOnes, this, p_socket, p_size, _1, - _2)); - DoAccept(); - } else { - p_socket->close(); - } - } - - void DoSendOnes(std::shared_ptr p_socket, - std::shared_ptr p_size, - const boost::system::error_code& ec, size_t length) { - if (!ec) { - p_socket->async_send(boost::asio::buffer(one_buffer_, *p_size), - boost::bind(&DummyServer::HandleSend, this, p_socket, - p_size, _1, _2)); - } - - return; - } - - void HandleSend(std::shared_ptr p_socket, - std::shared_ptr p_size, - const boost::system::error_code& ec, size_t length) { - if (!ec) { - *p_size -= length; - if (*p_size == 0) { - return; - } else { - DoSendOnes(p_socket, p_size, boost::system::error_code(), 0); - return; - } - } else { - return; - } - } - - boost::asio::io_service io_service_; - std::unique_ptr p_worker_; - boost::asio::ip::tcp::acceptor acceptor_; - std::size_t one_buffer_size_; - std::array one_buffer_; - std::set> sockets_; - boost::thread_group threads_; -}; - -class RemoteSocksTest : public ::testing::Test -{ - public: - typedef boost::asio::ip::tcp::socket socket; - typedef ssf::SSLWrapper<> ssl_socket; - typedef boost::asio::fiber::basic_fiber_demux demux; - typedef ssf::services::BaseUserService::BaseUserServicePtr - BaseUserServicePtr; - - public: - RemoteSocksTest() - : client_io_service_(), - p_client_worker_(new boost::asio::io_service::work(client_io_service_)), - server_io_service_(), - p_server_worker_(new boost::asio::io_service::work(server_io_service_)), - p_ssf_client_(nullptr), - p_ssf_server_(nullptr) {} - - ~RemoteSocksTest() {} - - virtual void SetUp() { - StartServer(); - StartClient(); - } - - virtual void TearDown() { - StopServerThreads(); - StopClientThreads(); - } - - void StartServer() { - ssf::Config ssf_config; - - p_ssf_server_.reset(new ssf::SSFServer< - ssf::SSLPolicy, ssf::NullLinkAuthenticationPolicy, - ssf::BounceProtocolPolicy, ssf::TransportProtocolPolicy>( - server_io_service_, ssf_config, 8000)); - - StartServerThreads(); - p_ssf_server_->run(); - } - - void StartClient() { - std::vector client_options; - boost::system::error_code ec; - auto p_service = ssf::services::RemoteSocks::CreateServiceOptions( - "8081", ec); - - client_options.push_back(p_service); - - std::map params( - {{"remote_addr", "127.0.0.1"}, {"remote_port", "8000"}}); - - ssf::Config ssf_config; - - p_ssf_client_.reset(new ssf::SSFClient< - ssf::SSLPolicy, ssf::NullLinkAuthenticationPolicy, - ssf::BounceProtocolPolicy, ssf::TransportProtocolPolicy>( - client_io_service_, "127.0.0.1", "8000", ssf_config, client_options, - boost::bind(&RemoteSocksTest::SSFClientCallback, this, _1, _2, _3))); - StartClientThreads(); - p_ssf_client_->run(params); - } - - bool Wait() { - auto network_set_future = network_set_.get_future(); - auto service_set_future = service_set_.get_future(); - auto transport_set_future = transport_set_.get_future(); - - network_set_future.wait(); - service_set_future.wait(); - transport_set_future.wait(); - - return network_set_future.get() && service_set_future.get() && - transport_set_future.get(); - } - - void StartServerThreads() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - server_threads_.create_thread([&]() { server_io_service_.run(); }); - } - } - - void StartClientThreads() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - client_threads_.create_thread([&]() { client_io_service_.run(); }); - } - } - - void StopServerThreads() { - p_ssf_server_->stop(); - p_server_worker_.reset(); - server_threads_.join_all(); - server_io_service_.stop(); - } - - void StopClientThreads() { - p_ssf_client_->stop(); - p_client_worker_.reset(); - client_threads_.join_all(); - client_io_service_.stop(); - } - - void SSFClientCallback(ssf::services::initialisation::type type, - BaseUserServicePtr p_user_service, - const boost::system::error_code& ec) { - if (type == ssf::services::initialisation::NETWORK) { - network_set_.set_value(!ec); - if (ec) { - service_set_.set_value(false); - transport_set_.set_value(false); - } - - return; - } - - if (type == ssf::services::initialisation::TRANSPORT) { - transport_set_.set_value(!ec); - - return; - } - - if (type == ssf::services::initialisation::SERVICE && - p_user_service->GetName() == "remote_socks") { - service_set_.set_value(!ec); - - return; - } - } - - protected: - boost::asio::io_service client_io_service_; - std::unique_ptr p_client_worker_; - boost::thread_group client_threads_; - - boost::asio::io_service server_io_service_; - std::unique_ptr p_server_worker_; - boost::thread_group server_threads_; - std::unique_ptr> p_ssf_client_; - std::unique_ptr> p_ssf_server_; - - std::promise network_set_; - std::promise transport_set_; - std::promise service_set_; -}; - -//----------------------------------------------------------------------------- -TEST_F(RemoteSocksTest, startStopTransmitSSFRemoteSocks) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - - ASSERT_TRUE(Wait()); - - std::list> clients_finish; - - boost::recursive_mutex mutex; - - auto download = [&mutex](std::size_t size, std::promise& test_client) { - DummyClient client(size); - auto initiated = client.Init(); - - { - boost::recursive_mutex::scoped_lock lock(mutex); - EXPECT_TRUE(initiated); - } - - auto sent = client.InitSocks(); - - { - boost::recursive_mutex::scoped_lock lock(mutex); - EXPECT_TRUE(sent); - } - - auto received = client.ReceiveOneBuffer(); - - { - boost::recursive_mutex::scoped_lock lock(mutex); - EXPECT_TRUE(received); - } - - client.Stop(); - test_client.set_value(true); - }; - - DummyServer serv; - serv.Run(); - - boost::thread_group client_test_threads; - - for (std::size_t i = 0; i < 6; ++i) { - clients_finish.emplace_front(); - std::promise& client_finish = clients_finish.front(); - client_test_threads.create_thread(boost::bind( - download, 1024 * 1024 * i, boost::ref(client_finish))); - } - - client_test_threads.join_all(); - for (auto& client_finish : clients_finish) { - client_finish.get_future().wait(); - } - - serv.Stop(); -} diff --git a/src/tests/remote_stream_forwarding_tests.cpp b/src/tests/remote_stream_forwarding_tests.cpp deleted file mode 100644 index 0a032d46..00000000 --- a/src/tests/remote_stream_forwarding_tests.cpp +++ /dev/null @@ -1,399 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "common/config/config.h" - -#include "core/client/client.h" -#include "core/server/server.h" - -#include "core/network_virtual_layer_policies/link_policies/ssl_policy.h" -#include "core/network_virtual_layer_policies/link_policies/tcp_policy.h" -#include "core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h" -#include "core/network_virtual_layer_policies/bounce_protocol_policy.h" -#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" - -#include "services/initialisation.h" -#include "services/user_services/remote_port_forwarding.h" - -class DummyClient { - public: - DummyClient(size_t size) - : io_service_(), - p_worker_(new boost::asio::io_service::work(io_service_)), - socket_(io_service_), - size_(size) {} - - bool Run() { - t_ = boost::thread([&]() { io_service_.run(); }); - - boost::asio::ip::tcp::resolver r(io_service_); - boost::asio::ip::tcp::resolver::query q("127.0.0.1", "5454"); - boost::system::error_code ec; - boost::asio::connect(socket_, r.resolve(q), ec); - - if (ec) { - BOOST_LOG_TRIVIAL(error) << "dummy client : fail to connect " - << ec.value(); - return false; - } - - boost::asio::write(socket_, boost::asio::buffer(&size_, sizeof(size_t)), - ec); - - if (ec) { - BOOST_LOG_TRIVIAL(error) << "dummy client : fail to write " << ec.value(); - return false; - } - - size_t received(0); - size_t n(0); - while (received < size_) { - boost::system::error_code ec_read; - n = socket_.read_some(boost::asio::buffer(one_buffer_), ec_read); - - if (n == 0) { - return false; - } - - if (ec_read) { - return false; - } - - if (!CheckOneBuffer(n)) { - return false; - } - received += n; - } - - return received == size_; - } - - void Stop() { - boost::system::error_code ec; - socket_.close(ec); - - p_worker_.reset(); - - t_.join(); - io_service_.stop(); - } - - private: - bool CheckOneBuffer(size_t n) { - for (size_t i = 0; i < n; ++i) { - if (one_buffer_[i] != 1) { - return false; - } - } - - return true; - } - - boost::asio::io_service io_service_; - std::unique_ptr p_worker_; - boost::asio::ip::tcp::socket socket_; - boost::thread t_; - size_t size_; - std::array one_buffer_; -}; - -class DummyServer { - public: - DummyServer() - : io_service_(), - p_worker_(new boost::asio::io_service::work(io_service_)), - acceptor_(io_service_), - one_buffer_size_(10240), - one_buffer_(one_buffer_size_) { - for (size_t i = 0; i < one_buffer_size_; ++i) { - one_buffer_[i] = 1; - } - } - - void Run() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - threads_.create_thread([&]() { io_service_.run(); }); - } - - boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 5354); - boost::asio::socket_base::reuse_address option(true); - - acceptor_.open(endpoint.protocol()); - acceptor_.set_option(option); - acceptor_.bind(endpoint); - acceptor_.listen(); - - do_accept(); - } - - void Stop() { - boost::system::error_code ec; - acceptor_.close(ec); - - p_worker_.reset(); - - threads_.join_all(); - io_service_.stop(); - } - - private: - void do_accept() { - auto p_socket = std::make_shared(io_service_); - acceptor_.async_accept(*p_socket, boost::bind(&DummyServer::handle_accept, - this, p_socket, _1)); - } - - void handle_accept(std::shared_ptr p_socket, - const boost::system::error_code& ec) { - if (!ec) { - auto p_size = std::make_shared(0); - boost::asio::async_read(*p_socket, - boost::asio::buffer(p_size.get(), sizeof(size_t)), - boost::bind(&DummyServer::handle_send, this, - p_socket, p_size, true, _1, _2)); - do_accept(); - } else { - p_socket->close(); - } - } - - void handle_send(std::shared_ptr p_socket, - std::shared_ptr p_size, bool first, - const boost::system::error_code& ec, - size_t tranferred_bytes) { - if (!ec) { - if (!first) { - (*p_size) -= tranferred_bytes; - } - - if ((*p_size)) { - if ((*p_size) > one_buffer_size_) { - boost::asio::async_write( - *p_socket, boost::asio::buffer(one_buffer_, one_buffer_size_), - boost::bind(&DummyServer::handle_send, this, p_socket, p_size, - false, _1, _2)); - } else { - boost::asio::async_write( - *p_socket, boost::asio::buffer(one_buffer_, (*p_size)), - boost::bind(&DummyServer::handle_send, this, p_socket, p_size, - false, _1, _2)); - } - } else { - boost::asio::async_read(*p_socket, boost::asio::buffer(one_buffer_, 1), - [=](const boost::system::error_code&, - size_t) { p_socket->close(); }); - } - } else { - p_socket->close(); - } - } - - boost::asio::io_service io_service_; - std::unique_ptr p_worker_; - boost::asio::ip::tcp::acceptor acceptor_; - size_t one_buffer_size_; - std::vector one_buffer_; - boost::thread_group threads_; -}; - -class RemoteStreamForwardTest : public ::testing::Test { - public: - typedef boost::asio::ip::tcp::socket socket; - typedef ssf::SSLWrapper<> ssl_socket; - typedef boost::asio::fiber::basic_fiber_demux demux; - typedef ssf::services::BaseUserService::BaseUserServicePtr - BaseUserServicePtr; - public: - RemoteStreamForwardTest() - : client_io_service_(), - p_client_worker_(new boost::asio::io_service::work(client_io_service_)), - server_io_service_(), - p_server_worker_(new boost::asio::io_service::work(server_io_service_)), - p_ssf_client_(nullptr), - p_ssf_server_(nullptr) {} - - ~RemoteStreamForwardTest() {} - - virtual void SetUp() { - StartServer(); - StartClient(); - } - - virtual void TearDown() { - StopClientThreads(); - StopServerThreads(); - } - - void StartServer() { - ssf::Config ssf_config; - - p_ssf_server_.reset(new ssf::SSFServer< - ssf::SSLPolicy, ssf::NullLinkAuthenticationPolicy, - ssf::BounceProtocolPolicy, ssf::TransportProtocolPolicy>( - server_io_service_, ssf_config, 8000)); - - StartServerThreads(); - p_ssf_server_->run(); - } - - void StartClient() { - std::vector client_options; - boost::system::error_code ec; - auto p_service = - ssf::services::RemotePortForwading::CreateServiceOptions( - "5454:127.0.0.1:5354", ec); - - client_options.push_back(p_service); - - std::map params( - {{"remote_addr", "127.0.0.1"}, {"remote_port", "8000"}}); - - ssf::Config ssf_config; - - p_ssf_client_.reset(new ssf::SSFClient< - ssf::SSLPolicy, ssf::NullLinkAuthenticationPolicy, - ssf::BounceProtocolPolicy, ssf::TransportProtocolPolicy>( - client_io_service_, "127.0.0.1", "8000", ssf_config, client_options, - boost::bind(&RemoteStreamForwardTest::SSFClientCallback, this, _1, - _2, _3))); - StartClientThreads(); - p_ssf_client_->run(params); - } - - bool Wait() { - auto network_set_future = network_set_.get_future(); - auto service_set_future = service_set_.get_future(); - auto transport_set_future = transport_set_.get_future(); - - network_set_future.wait(); - service_set_future.wait(); - transport_set_future.wait(); - - return network_set_future.get() && service_set_future.get() && - transport_set_future.get(); - } - - void StartServerThreads() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - server_threads_.create_thread([&]() { server_io_service_.run(); }); - } - } - - void StartClientThreads() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - client_threads_.create_thread([&]() { client_io_service_.run(); }); - } - } - - void StopServerThreads() { - p_ssf_server_->stop(); - p_server_worker_.reset(); - server_threads_.join_all(); - server_io_service_.stop(); - } - - void StopClientThreads() { - p_ssf_client_->stop(); - p_client_worker_.reset(); - client_threads_.join_all(); - client_io_service_.stop(); - } - - void SSFClientCallback(ssf::services::initialisation::type type, - BaseUserServicePtr p_user_service, - const boost::system::error_code& ec) { - if (type == ssf::services::initialisation::NETWORK) { - network_set_.set_value(!ec); - if (ec) { - service_set_.set_value(false); - transport_set_.set_value(false); - } - - return; - } - - if (type == ssf::services::initialisation::TRANSPORT) { - transport_set_.set_value(!ec); - - return; - } - - if (type == ssf::services::initialisation::SERVICE && - p_user_service->GetName() == "remote") { - service_set_.set_value(!ec); - - return; - } - } - - protected: - boost::asio::io_service client_io_service_; - std::unique_ptr p_client_worker_; - boost::thread_group client_threads_; - - boost::asio::io_service server_io_service_; - std::unique_ptr p_server_worker_; - boost::thread_group server_threads_; - std::unique_ptr> p_ssf_client_; - std::unique_ptr> p_ssf_server_; - - std::promise network_set_; - std::promise transport_set_; - std::promise service_set_; -}; - -//----------------------------------------------------------------------------- -TEST_F(RemoteStreamForwardTest, transferOnesOverStream) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - - ASSERT_TRUE(Wait()); - - std::list> clients_finish; - - boost::recursive_mutex mutex; - - auto download = [&mutex](size_t size, std::promise& test_client) { - DummyClient client(size); - - { - boost::recursive_mutex::scoped_lock lock(mutex); - EXPECT_TRUE(client.Run()); - } - - client.Stop(); - test_client.set_value(true); - }; - - DummyServer serv; - serv.Run(); - - boost::thread_group client_test_threads; - - for (int i = 0; i < 6; ++i) { - clients_finish.emplace_front(); - std::promise& client_finish = clients_finish.front(); - client_test_threads.create_thread(boost::bind( - download, 1024 * 1024 * i, boost::ref(client_finish))); - } - - client_test_threads.join_all(); - for (auto& client_finish : clients_finish) { - client_finish.get_future().wait(); - } - - serv.Stop(); -} diff --git a/src/tests/remote_udp_forwarding_tests.cpp b/src/tests/remote_udp_forwarding_tests.cpp deleted file mode 100644 index 4315c7fc..00000000 --- a/src/tests/remote_udp_forwarding_tests.cpp +++ /dev/null @@ -1,410 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "common/config/config.h" - -#include "core/client/client.h" -#include "core/server/server.h" - -#include "core/network_virtual_layer_policies/link_policies/ssl_policy.h" -#include "core/network_virtual_layer_policies/link_policies/tcp_policy.h" -#include "core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h" -#include "core/network_virtual_layer_policies/bounce_protocol_policy.h" -#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" - -#include "services/initialisation.h" -#include "services/user_services/udp_remote_port_forwarding.h" - -class DummyClient { -public: - DummyClient(size_t size) - : io_service_(), - p_worker_(new boost::asio::io_service::work(io_service_)), - socket_(io_service_), - size_(size) {} - - bool Init() { - t_ = boost::thread([&]() { io_service_.run(); }); - - boost::asio::ip::udp::resolver r(io_service_); - boost::asio::ip::udp::resolver::query q("127.0.0.1", "5454"); - - auto it = r.resolve(q); - - endpoint_ = *it; - - boost::system::error_code ec; - socket_.open(endpoint_.protocol(), ec); - socket_.connect(endpoint_, ec); - - if (ec) { - return false; - } else { - return true; - } - } - - bool ReceiveOneBuffer() { - size_t received = 0; - - while (received < size_) { - boost::system::error_code ec; - size_t remaining_size = size_ - received; - socket_.send(boost::asio::buffer(&remaining_size, sizeof(remaining_size)), 0, ec); - - if (ec) { - return false; - } - - auto n = socket_.receive(boost::asio::buffer(one_buffer_), 0, ec); - - if (ec) { - return false; - } - - if (n == 0) { - return false; - } else { - received += n; - if (!CheckOneBuffer(n)) { - return false; - } - ResetBuffer(); - } - } - return received == size_; - } - - void Stop() { - boost::system::error_code ec; - socket_.close(ec); - - p_worker_.reset(); - - t_.join(); - io_service_.stop(); - } - -private: - void ResetBuffer() { - for (size_t i = 0; i < one_buffer_.size(); ++i) { - one_buffer_[i] = 0; - } - } - - bool CheckOneBuffer(size_t n) { - for (size_t i = 0; i < n; ++i) { - if (one_buffer_[i] != 1) { - return false; - } - } - - return true; - } - -private: - boost::asio::io_service io_service_; - std::unique_ptr p_worker_; - boost::asio::ip::udp::socket socket_; - boost::asio::ip::udp::endpoint endpoint_; - boost::thread t_; - size_t size_; - std::array one_buffer_; -}; - -class DummyServer { -public: - DummyServer() - : io_service_(), - p_worker_(new boost::asio::io_service::work(io_service_)), - socket_(io_service_), - one_buffer_(10240) { - for (size_t i = 0; i < 10240; ++i) { - one_buffer_[i] = 1; - } - } - - void Run() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - threads_.create_thread([&]() { io_service_.run(); }); - } - - boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::udp::v4(), 5354); - - socket_.open(endpoint.protocol()); - socket_.bind(endpoint); - - DoReceive(); - } - - void Stop() { - boost::system::error_code ec; - socket_.close(ec); - - p_worker_.reset(); - - threads_.join_all(); - io_service_.stop(); - } - -private: - void DoReceive() { - auto p_new_size_ = std::make_shared(0); - auto p_send_endpoint_ = std::make_shared(); - - socket_.async_receive_from( - boost::asio::buffer(&(*p_new_size_), sizeof((*p_new_size_))), - *p_send_endpoint_, boost::bind(&DummyServer::SizeReceivedHandler, this, - p_send_endpoint_, p_new_size_, _1, _2)); - } - - void SizeReceivedHandler( - std::shared_ptr p_endpoint, - std::shared_ptr p_size, const boost::system::error_code& ec, - size_t length) { - if (!ec) { - { - boost::recursive_mutex::scoped_lock lock(one_buffer_mutex_); - socket_.async_send_to(boost::asio::buffer(one_buffer_, *p_size), - *p_endpoint, - boost::bind(&DummyServer::OneBufferSentHandler, - this, p_endpoint, p_size, _1, _2)); - } - } - } - - void OneBufferSentHandler( - std::shared_ptr p_endpoint, - std::shared_ptr p_size, const boost::system::error_code& ec, - size_t length) { - if (ec.value() == ssf::error::message_too_long) { - { - boost::recursive_mutex::scoped_lock lock(one_buffer_mutex_); - one_buffer_.resize(one_buffer_.size() / 2); - } - if (one_buffer_.size()) { - SizeReceivedHandler(p_endpoint, p_size, boost::system::error_code(), 0); - return; - } - } else { - DoReceive(); - return; - } - } - - boost::asio::io_service io_service_; - std::unique_ptr p_worker_; - boost::asio::ip::udp::socket socket_; - boost::recursive_mutex one_buffer_mutex_; - std::vector one_buffer_; - boost::thread_group threads_; -}; - -class RemoteUdpForwardTest : public ::testing::Test { - public: - typedef boost::asio::ip::tcp::socket socket; - typedef ssf::SSLWrapper<> ssl_socket; - typedef boost::asio::fiber::basic_fiber_demux demux; - typedef ssf::services::BaseUserService::BaseUserServicePtr - BaseUserServicePtr; -public: - RemoteUdpForwardTest() - : client_io_service_(), - p_client_worker_(new boost::asio::io_service::work(client_io_service_)), - server_io_service_(), - p_server_worker_(new boost::asio::io_service::work(server_io_service_)), - p_ssf_client_(nullptr), - p_ssf_server_(nullptr) {} - - ~RemoteUdpForwardTest() {} - - virtual void SetUp() { - StartServer(); - StartClient(); - } - - virtual void TearDown() { - StopClientThreads(); - StopServerThreads(); - } - - void StartServer() { - ssf::Config ssf_config; - - p_ssf_server_.reset(new ssf::SSFServer< - ssf::SSLPolicy, ssf::NullLinkAuthenticationPolicy, - ssf::BounceProtocolPolicy, ssf::TransportProtocolPolicy>( - server_io_service_, ssf_config, 8000)); - - StartServerThreads(); - p_ssf_server_->run(); - } - - void StartClient() { - std::vector client_options; - boost::system::error_code ec; - auto p_service = - ssf::services::UdpRemotePortForwading::CreateServiceOptions( - "5454:127.0.0.1:5354", ec); - - client_options.push_back(p_service); - - std::map params( - {{"remote_addr", "127.0.0.1"}, {"remote_port", "8000"}}); - - ssf::Config ssf_config; - - p_ssf_client_.reset(new ssf::SSFClient< - ssf::SSLPolicy, ssf::NullLinkAuthenticationPolicy, - ssf::BounceProtocolPolicy, ssf::TransportProtocolPolicy>( - client_io_service_, "127.0.0.1", "8000", ssf_config, client_options, - boost::bind(&RemoteUdpForwardTest::SSFClientCallback, this, _1, _2, _3))); - StartClientThreads(); - p_ssf_client_->run(params); - } - - bool Wait() { - auto network_set_future = network_set_.get_future(); - auto service_set_future = service_set_.get_future(); - auto transport_set_future = transport_set_.get_future(); - - network_set_future.wait(); - service_set_future.wait(); - transport_set_future.wait(); - - return network_set_future.get() && service_set_future.get() && - transport_set_future.get(); - } - - void StartServerThreads() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - server_threads_.create_thread([&]() { server_io_service_.run(); }); - } - } - - void StartClientThreads() { - for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { - client_threads_.create_thread([&]() { client_io_service_.run(); }); - } - } - - void StopServerThreads() { - p_ssf_server_->stop(); - p_server_worker_.reset(); - server_threads_.join_all(); - server_io_service_.stop(); - } - - void StopClientThreads() { - p_ssf_client_->stop(); - p_client_worker_.reset(); - client_threads_.join_all(); - client_io_service_.stop(); - } - - void SSFClientCallback(ssf::services::initialisation::type type, - BaseUserServicePtr p_user_service, - const boost::system::error_code& ec) { - if (type == ssf::services::initialisation::NETWORK) { - network_set_.set_value(!ec); - if (ec) { - service_set_.set_value(false); - transport_set_.set_value(false); - } - - return; - } - - if (type == ssf::services::initialisation::TRANSPORT) { - transport_set_.set_value(!ec); - - return; - } - - if (type == ssf::services::initialisation::SERVICE && - p_user_service->GetName() == "udpremote") { - service_set_.set_value(!ec); - - return; - } - } - -protected: - boost::asio::io_service client_io_service_; - std::unique_ptr p_client_worker_; - boost::thread_group client_threads_; - - boost::asio::io_service server_io_service_; - std::unique_ptr p_server_worker_; - boost::thread_group server_threads_; - std::unique_ptr> p_ssf_client_; - std::unique_ptr> p_ssf_server_; - - std::promise network_set_; - std::promise transport_set_; - std::promise service_set_; -}; - -//----------------------------------------------------------------------------- -TEST_F(RemoteUdpForwardTest, transferOnesOverUdp) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - - ASSERT_TRUE(Wait()); - - std::list> clients_finish; - - boost::recursive_mutex mutex; - - auto download = [&mutex](size_t size, std::promise& test_client) { - DummyClient client(size); - auto initiated = client.Init(); - - { - boost::recursive_mutex::scoped_lock lock(mutex); - EXPECT_TRUE(initiated); - } - - auto received = client.ReceiveOneBuffer(); - - { - boost::recursive_mutex::scoped_lock lock(mutex); - EXPECT_TRUE(received); - } - - client.Stop(); - test_client.set_value(true); - }; - - DummyServer serv; - serv.Run(); - - boost::thread_group client_test_threads; - - for (int i = 0; i < 6; ++i) { - clients_finish.emplace_front(); - std::promise& client_finish = clients_finish.front(); - client_test_threads.create_thread(boost::bind( - download, 1024 * 1024 * i, boost::ref(client_finish))); - } - - client_test_threads.join_all(); - for (auto& client_finish : clients_finish) { - client_finish.get_future().wait(); - } - - serv.Stop(); -} diff --git a/src/tests/services/CMakeLists.txt b/src/tests/services/CMakeLists.txt new file mode 100644 index 00000000..66ecf8f7 --- /dev/null +++ b/src/tests/services/CMakeLists.txt @@ -0,0 +1,251 @@ +cmake_minimum_required(VERSION 2.8) + +set(services_tests_group_name "Unit Tests/Services") + +set(project_BINARY_SERVICES_TESTS_DIR "${project_BINARY_DIR}/src/tests/services") + +# --- Copy test certs for tests +set(project_BINARY_SERVICES_TESTS_CERT_DIR ${project_BINARY_SERVICES_TESTS_DIR}/certs) + +file(MAKE_DIRECTORY ${project_BINARY_SERVICES_TESTS_CERT_DIR}) +file(MAKE_DIRECTORY ${project_BINARY_SERVICES_TESTS_CERT_DIR}/trusted) + +file(COPY ${SSF_CERT_TEST_ROOT_FILES} + DESTINATION ${project_BINARY_SERVICES_TESTS_CERT_DIR}) +file(COPY ${SSF_CERT_TEST_TRUSTED_FILES} + DESTINATION ${project_BINARY_SERVICES_TESTS_CERT_DIR}/trusted) + +set(TCP_HELPERS_FILES + "tcp_helpers.h" + "tcp_helpers.cpp") + +set(UDP_HELPERS_FILES + "udp_helpers.h" + "udp_helpers.cpp") + +set(SOCKS_HELPERS_FILES + "socks_helpers.h" + "socks_helpers.cpp") + +set(SERVICE_TESTS_FILES + "service_fixture_test.h" + "process_fixture_test.h" +) + +# --- Service tests + +# --- Socks test +set(socks_test_source_files + "socks_tests.cpp" + ${SERVICE_TESTS_FILES} + ${SOCKS_HELPERS_FILES} + ${TCP_HELPERS_FILES} + ${SSF_SOURCES} +) + +add_target("socks_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${socks_test_source_files} +) +project_group(${services_tests_group_name} socks_tests) + +# --- Remote socks test +set(remote_socks_test_source_files + "remote_socks_tests.cpp" + ${SERVICE_TESTS_FILES} + ${SOCKS_HELPERS_FILES} + ${TCP_HELPERS_FILES} + ${SSF_SOURCES} +) + +add_target("remote_socks_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${remote_socks_test_source_files} +) +project_group(${services_tests_group_name} remote_socks_tests) + +# --- Stream forward test +set(stream_forwarding_test_source_files + "stream_forwarding_tests.cpp" + ${SERVICE_TESTS_FILES} + ${TCP_HELPERS_FILES} + ${SSF_SOURCES} +) + +add_target("stream_forward_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${stream_forwarding_test_source_files} +) +project_group(${services_tests_group_name} stream_forward_tests) + +# --- Remote stream forward test +set(remote_stream_forwarding_test_source_files + "remote_stream_forwarding_tests.cpp" + ${SERVICE_TESTS_FILES} + ${TCP_HELPERS_FILES} + ${SSF_SOURCES} +) + +add_target("remote_stream_forward_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${remote_stream_forwarding_test_source_files} +) +project_group(${services_tests_group_name} remote_stream_forward_tests) + +# --- UDP forward test +set(udp_test_source_files + "udp_forwarding_tests.cpp" + ${SERVICE_TESTS_FILES} + ${UDP_HELPERS_FILES} + ${SSF_SOURCES} +) + +add_target("udp_forward_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${udp_test_source_files} +) +project_group(${services_tests_group_name} udp_forward_tests) + +# --- Remote UDP forward test +set(remote_udp_test_source_files + "remote_udp_forwarding_tests.cpp" + ${SERVICE_TESTS_FILES} + ${UDP_HELPERS_FILES} + ${SSF_SOURCES} +) + +add_target("remote_udp_forward_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${remote_udp_test_source_files} +) +project_group(${services_tests_group_name} remote_udp_forward_tests) + +# --- File copy from client test +file(MAKE_DIRECTORY ${project_BINARY_DIR}/src/tests/services/files_to_copy) +file(MAKE_DIRECTORY ${project_BINARY_DIR}/src/tests/services/files_copied) + +FILE(GLOB_RECURSE TEST_FILES_TO_COPY + "${project_SRC_DIR}/tests/services/files_to_copy/*" +) + +file(COPY ${TEST_FILES_TO_COPY} DESTINATION ${project_BINARY_DIR}/src/tests/services/files_to_copy) + +set(file_copy_from_client_files + "file_copy_from_client_tests.cpp" + "file_copy_fixture_test.h" + "file_copy_fixture_test.cpp" + ${SSF_SOURCES} +) + +add_target("file_copy_from_client_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${file_copy_from_client_files} +) +project_group(${services_tests_group_name} file_copy_from_client_tests) + +# --- Process test +set(process_test_source_files + "process_tests.cpp" + ${SERVICE_TESTS_FILES} + ${SSF_SOURCES} +) + +add_target("process_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${process_test_source_files} +) +project_group(${services_tests_group_name} process_tests) + +# --- Remote process test +set(remote_process_test_source_files + "remote_process_tests.cpp" + ${SERVICE_TESTS_FILES} + ${SSF_SOURCES} +) + +add_target("remote_process_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${remote_process_test_source_files} +) +project_group(${services_tests_group_name} remote_process_tests) diff --git a/src/tests/services/file_copy_fixture_test.cpp b/src/tests/services/file_copy_fixture_test.cpp new file mode 100644 index 00000000..61997684 --- /dev/null +++ b/src/tests/services/file_copy_fixture_test.cpp @@ -0,0 +1,121 @@ +#include "tests/services/file_copy_fixture_test.h" + +FileCopyTestFixture::FileCopyTestFixture() + : p_ssf_client_(nullptr), p_ssf_server_(nullptr) {} + +FileCopyTestFixture::~FileCopyTestFixture() {} + +void FileCopyTestFixture::SetUp() { + StartServer(); + StartClient(); +} + +void FileCopyTestFixture::TearDown() { + StopClientThreads(); + StopServerThreads(); + + // Clean receiver path + boost::filesystem::path receiver_path(GetOutputPattern()); + if (boost::filesystem::is_directory(receiver_path)) { + for (boost::filesystem::directory_iterator end_it, it(receiver_path); + it != end_it; ++it) { + std::remove(it->path().string().c_str()); + } + return; + } + + if (boost::filesystem::is_regular_file(receiver_path)) { + std::remove(GetOutputPattern().c_str()); + } +} + +void FileCopyTestFixture::StartServer() { + ssf::config::Config ssf_config; + + auto endpoint_query = + NetworkProtocol::GenerateServerQuery("", "8000", ssf_config); + + p_ssf_server_.reset(new Server(ssf_config.services())); + + boost::system::error_code run_ec; + p_ssf_server_->Run(endpoint_query, run_ec); +} + +void FileCopyTestFixture::StartClient() { + std::vector client_services; + boost::system::error_code ec; + auto p_service = + ssf::services::CopyFileService::CreateServiceFromParams( + GetFromStdin(), GetFromLocalToRemote(), GetInputPattern(), + GetOutputPattern(), ec); + + client_services.push_back(p_service); + + ssf::config::Config ssf_config; + + auto endpoint_query = + NetworkProtocol::GenerateClientQuery("127.0.0.1", "8000", ssf_config, {}); + + p_ssf_client_.reset(new Client( + client_services, ssf_config.services(), + boost::bind(&FileCopyTestFixture::SSFClientCallback, this, _1, _2, _3))); + boost::system::error_code run_ec; + p_ssf_client_->Run(endpoint_query, run_ec); +} + +bool FileCopyTestFixture::Wait() { + auto network_set_future = network_set_.get_future(); + auto service_set_future = service_set_.get_future(); + auto transport_set_future = transport_set_.get_future(); + + network_set_future.wait(); + service_set_future.wait(); + transport_set_future.wait(); + + return network_set_future.get() && service_set_future.get() && + transport_set_future.get(); +} + +bool FileCopyTestFixture::WaitClose() { + auto close_future = close_set_.get_future(); + close_future.wait(); + return close_future.get(); +} + +std::string FileCopyTestFixture::GetOutputPattern() const { + return "files_copied/"; +} + +void FileCopyTestFixture::StopServerThreads() { p_ssf_server_->Stop(); } + +void FileCopyTestFixture::StopClientThreads() { p_ssf_client_->Stop(); } + +void FileCopyTestFixture::SSFClientCallback( + ssf::services::initialisation::type type, + FileCopyTestFixture::BaseUserServicePtr p_user_service, + const boost::system::error_code& ec) { + if (type == ssf::services::initialisation::NETWORK) { + network_set_.set_value(!ec); + if (ec) { + service_set_.set_value(false); + transport_set_.set_value(false); + } + return; + } + + if (type == ssf::services::initialisation::TRANSPORT) { + transport_set_.set_value(!ec); + return; + } + + if (type == ssf::services::initialisation::SERVICE && + p_user_service->GetName() == "copy_file") { + service_set_.set_value(!ec); + return; + } + + if (type == ssf::services::initialisation::CLOSE) { + close_set_.set_value(true); + return; + } +} \ No newline at end of file diff --git a/src/tests/services/file_copy_fixture_test.h b/src/tests/services/file_copy_fixture_test.h new file mode 100644 index 00000000..11924a1c --- /dev/null +++ b/src/tests/services/file_copy_fixture_test.h @@ -0,0 +1,153 @@ +#ifndef TESTS_SERVICES_COPY_FILE_FIXTURE_TEST_H_ +#define TESTS_SERVICES_COPY_FILE_FIXTURE_TEST_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common/config/config.h" + +#include "core/network_protocol.h" + +#include "core/client/client.h" +#include "core/server/server.h" + +#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" + +#include "services/initialisation.h" +#include "services/user_services/copy_file_service.h" + +using NetworkProtocol = ssf::network::NetworkProtocol; + +class FileCopyTestFixture : public ::testing::Test { + public: + using Client = + ssf::SSFClient; + using Server = + ssf::SSFServer; + using demux = Client::Demux; + using BaseUserServicePtr = + ssf::services::BaseUserService::BaseUserServicePtr; + + protected: + FileCopyTestFixture(); + + virtual ~FileCopyTestFixture(); + + void StartServer(); + bool Wait(); + bool WaitClose(); + void StopServerThreads(); + void StopClientThreads(); + void SSFClientCallback(ssf::services::initialisation::type type, + BaseUserServicePtr p_user_service, + const boost::system::error_code& ec); + + void SetUp() override; + void StartClient(); + void TearDown() override; + + virtual std::string GetOutputPattern() const; + virtual bool GetFromStdin() const = 0; + virtual bool GetFromLocalToRemote() const = 0; + virtual std::string GetInputPattern() const = 0; + + protected: + std::unique_ptr p_ssf_client_; + std::unique_ptr p_ssf_server_; + + std::promise network_set_; + std::promise transport_set_; + std::promise service_set_; + std::promise close_set_; +}; + +class CopyNoFileFromClientToRemoteTest : public FileCopyTestFixture { + public: + CopyNoFileFromClientToRemoteTest() : FileCopyTestFixture() {} + + bool GetFromStdin() const override { return false; } + + bool GetFromLocalToRemote() const override { return true; } + + std::string GetInputPattern() const override { + return "files_to_copy/test_filex.txt"; + } +}; + +class CopyUniqueFileFromClientToRemoteTest : public FileCopyTestFixture { + public: + CopyUniqueFileFromClientToRemoteTest() : FileCopyTestFixture() {} + + bool GetFromStdin() const override { return false; } + + bool GetFromLocalToRemote() const override { return true; } + + std::string GetInputPattern() const override { + return "files_to_copy/test_file1.txt"; + } +}; + +class CopyGlobFileFromClientToRemoteTest : public FileCopyTestFixture { + public: + CopyGlobFileFromClientToRemoteTest() : FileCopyTestFixture() {} + + bool GetFromStdin() const override { return false; } + + bool GetFromLocalToRemote() const override { return true; } + + std::string GetInputPattern() const override { + return "files_to_copy/test_*.txt"; + } +}; + +class CopyUniqueFileFromRemoteToClientTest : public FileCopyTestFixture { + public: + CopyUniqueFileFromRemoteToClientTest() : FileCopyTestFixture() {} + + bool GetFromStdin() const override { return false; } + + bool GetFromLocalToRemote() const override { return false; } + + std::string GetInputPattern() const override { + return "files_to_copy/test_file1.txt"; + } +}; + +class CopyGlobFileFromRemoteToClientTest : public FileCopyTestFixture { + public: + CopyGlobFileFromRemoteToClientTest() : FileCopyTestFixture() {} + + bool GetFromStdin() const override { return false; } + + bool GetFromLocalToRemote() const override { return false; } + + std::string GetInputPattern() const override { + return "files_to_copy/test_*.txt"; + } +}; + +class CopyStdinFromClientToRemoteTest : public FileCopyTestFixture { + public: + CopyStdinFromClientToRemoteTest() : FileCopyTestFixture() {} + + bool GetFromStdin() const override { return true; } + + bool GetFromLocalToRemote() const override { return true; } + + std::string GetInputPattern() const override { + return "files_to_copy/test_*.txt"; + } + + std::string GetOutputPattern() const override { + return "files_copied/test_file1.txt"; + } +}; + +#endif // TESTS_SERVICES_COPY_FILE_FIXTURE_TEST_H_ diff --git a/src/tests/file_copy_from_client_tests.cpp b/src/tests/services/file_copy_from_client_tests.cpp similarity index 82% rename from src/tests/file_copy_from_client_tests.cpp rename to src/tests/services/file_copy_from_client_tests.cpp index a6408c23..9af59ff9 100644 --- a/src/tests/file_copy_from_client_tests.cpp +++ b/src/tests/services/file_copy_from_client_tests.cpp @@ -1,4 +1,4 @@ -#include "tests/copy_file_test_fixture.h" +#include "tests/services/file_copy_fixture_test.h" #include #include @@ -16,7 +16,7 @@ Md5Digest GetMd5Sum(const std::string& filepath, std::ifstream file(filepath, std::ifstream::binary); MD5_CTX md5_context; - + if (!file.is_open()) { ec.assign(boost::system::errc::bad_file_descriptor, boost::system::get_system_category()); @@ -57,9 +57,6 @@ void FileExistsAndIdentical(const std::string& source_filepath, //----------------------------------------------------------------------------- TEST_F(CopyNoFileFromClientToRemoteTest, CopyTest) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - ASSERT_TRUE(Wait()); ASSERT_TRUE(WaitClose()); @@ -72,9 +69,6 @@ TEST_F(CopyNoFileFromClientToRemoteTest, CopyTest) { //----------------------------------------------------------------------------- TEST_F(CopyUniqueFileFromClientToRemoteTest, CopyTest) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - ASSERT_TRUE(Wait()); ASSERT_TRUE(WaitClose()); @@ -85,9 +79,6 @@ TEST_F(CopyUniqueFileFromClientToRemoteTest, CopyTest) { //----------------------------------------------------------------------------- TEST_F(CopyGlobFileFromClientToRemoteTest, CopyTest) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - ASSERT_TRUE(Wait()); ASSERT_TRUE(WaitClose()); @@ -100,9 +91,6 @@ TEST_F(CopyGlobFileFromClientToRemoteTest, CopyTest) { //----------------------------------------------------------------------------- TEST_F(CopyUniqueFileFromRemoteToClientTest, CopyTest) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - ASSERT_TRUE(Wait()); ASSERT_TRUE(WaitClose()); @@ -113,9 +101,6 @@ TEST_F(CopyUniqueFileFromRemoteToClientTest, CopyTest) { //----------------------------------------------------------------------------- TEST_F(CopyGlobFileFromRemoteToClientTest, CopyTest) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - ASSERT_TRUE(Wait()); ASSERT_TRUE(WaitClose()); @@ -128,9 +113,6 @@ TEST_F(CopyGlobFileFromRemoteToClientTest, CopyTest) { //----------------------------------------------------------------------------- TEST_F(CopyStdinFromClientToRemoteTest, CopyTest) { - boost::log::core::get()->set_filter(boost::log::trivial::severity >= - boost::log::trivial::info); - // stdin as test_file1.txt filebuf std::ifstream in("files_to_copy/test_file1.txt", std::ifstream::binary); std::streambuf* cinbuf = std::cin.rdbuf(); diff --git a/src/tests/files_to_copy/test_file1.txt b/src/tests/services/files_to_copy/test_file1.txt similarity index 100% rename from src/tests/files_to_copy/test_file1.txt rename to src/tests/services/files_to_copy/test_file1.txt diff --git a/src/tests/files_to_copy/test_file2.txt b/src/tests/services/files_to_copy/test_file2.txt similarity index 100% rename from src/tests/files_to_copy/test_file2.txt rename to src/tests/services/files_to_copy/test_file2.txt diff --git a/src/tests/services/process_fixture_test.h b/src/tests/services/process_fixture_test.h new file mode 100644 index 00000000..ef691e79 --- /dev/null +++ b/src/tests/services/process_fixture_test.h @@ -0,0 +1,78 @@ +#ifndef TESTS_SERVICES_PROCESS_FIXTURE_TEST_H_ +#define TESTS_SERVICES_PROCESS_FIXTURE_TEST_H_ + +#include +#include + +#include +#include +#include +#include + +#include + +#include "tests/services/service_fixture_test.h" + +template