From d7d3c88ff2146245ba30c9ad9bd883da89ccef1b Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 2 Aug 2018 15:49:00 -0700 Subject: [PATCH 1/3] Baseline SSF 1.1.0. --- ssf-1.1.0/.gitignore | 3 + ssf-1.1.0/CMakeLists.txt | 278 +++++ ssf-1.1.0/LICENSE.txt | 25 + ssf-1.1.0/README.md | 312 +++++ ssf-1.1.0/certs/WARNING | 2 + ssf-1.1.0/certs/certificate.crt | 65 + ssf-1.1.0/certs/dh4096.pem | 13 + ssf-1.1.0/certs/private.key | 51 + ssf-1.1.0/certs/trusted/ca.crt | 34 + ssf-1.1.0/certs/trusted/ca.key | 51 + ssf-1.1.0/certs/trusted/ca.srl | 1 + ssf-1.1.0/certs/trusted/extfile.ext | 7 + ssf-1.1.0/cmake-ms/CMakeLists.txt | 38 + ssf-1.1.0/cmake-unix/CMakeLists.txt | 38 + ssf-1.1.0/cmake/CMakeLists.txt | 27 + ssf-1.1.0/cmake/cmake-modules/LICENSE_1_0.txt | 23 + .../cmake-modules/ListCombinations.cmake | 53 + .../cmake-modules/MSVCStaticRuntime.cmake | 33 + ssf-1.1.0/cmake/cmake-modules/README.markdown | 107 ++ ssf-1.1.0/cmake/common/EnhancedList.cmake | 29 + ssf-1.1.0/cmake/common/FileEdit.cmake | 223 ++++ ssf-1.1.0/cmake/common/HelpersArguments.cmake | 107 ++ ssf-1.1.0/cmake/common/HelpersIdeTarget.cmake | 348 ++++++ ssf-1.1.0/cmake/common/MultiList.cmake | 67 + ssf-1.1.0/cmake/common/SystemTools.cmake | 226 ++++ ssf-1.1.0/cmake/external/CMakeLists.txt | 23 + .../external/ExternalPackageHelpers.cmake | 1025 ++++++++++++++++ ssf-1.1.0/cmake/external/README.md | 84 ++ .../cmake/external/boost/FindBoost.cmake | 268 ++++ ssf-1.1.0/cmake/external/boost/README.md | 53 + ssf-1.1.0/cmake/external/gtest/GTest.cmake | 31 + .../cmake/external/openssl/FindOpenSSL.cmake | 266 ++++ ssf-1.1.0/cmake/external/openssl/README.md | 47 + ssf-1.1.0/img/icon.icns | Bin 0 -> 42053 bytes ssf-1.1.0/img/icon.ico | Bin 0 -> 5822 bytes ssf-1.1.0/img/icon.rc | 1 + .../src/common/boost/fiber/basic_endpoint.hpp | 95 ++ .../common/boost/fiber/basic_fiber_demux.hpp | 266 ++++ .../boost/fiber/basic_fiber_demux_service.hpp | 283 +++++ .../boost/fiber/basic_fiber_demux_service.ipp | 736 +++++++++++ .../src/common/boost/fiber/datagram_fiber.hpp | 102 ++ .../boost/fiber/datagram_fiber_service.hpp | 457 +++++++ .../fiber/detail/basic_fiber_demux_impl.hpp | 119 ++ .../boost/fiber/detail/basic_fiber_impl.hpp | 654 ++++++++++ .../boost/fiber/detail/fiber_buffer.hpp | 231 ++++ .../boost/fiber/detail/fiber_header.hpp | 216 ++++ .../common/boost/fiber/detail/fiber_id.hpp | 180 +++ .../boost/fiber/detail/io_fiber_accept_op.hpp | 118 ++ .../fiber/detail/io_fiber_dgr_read_op.hpp | 148 +++ .../boost/fiber/detail/io_fiber_read_op.hpp | 162 +++ .../boost/fiber/detail/io_operation.hpp | 277 +++++ .../boost/fiber/detail/io_ssl_read_op.hpp | 139 +++ .../boost/fiber/fiber_acceptor_service.hpp | 220 ++++ .../src/common/boost/fiber/stream_fiber.hpp | 115 ++ .../boost/fiber/stream_fiber_service.hpp | 339 +++++ ssf-1.1.0/src/common/config/config.cpp | 67 + ssf-1.1.0/src/common/config/config.h | 35 + ssf-1.1.0/src/common/error/error.cpp | 70 ++ ssf-1.1.0/src/common/error/error.h | 50 + ssf-1.1.0/src/common/network/base_session.h | 26 + ssf-1.1.0/src/common/network/datagram_link.h | 209 ++++ .../common/network/datagram_link_operator.h | 108 ++ ssf-1.1.0/src/common/network/manager.h | 137 +++ .../common/network/network_policy_traits.h | 28 + .../src/common/network/session_forwarder.h | 123 ++ ssf-1.1.0/src/common/network/socket_link.h | 131 ++ ssf-1.1.0/src/common/utils/cleaner.h | 24 + ssf-1.1.0/src/core/CMakeLists.txt | 11 + ssf-1.1.0/src/core/client/CMakeLists.txt | 53 + ssf-1.1.0/src/core/client/client.h | 102 ++ ssf-1.1.0/src/core/client/client.ipp | 174 +++ ssf-1.1.0/src/core/client/main.cpp | 174 +++ ssf-1.1.0/src/core/client/main_cp.cpp | 165 +++ .../core/command_line/copy/command_line.cpp | 225 ++++ .../src/core/command_line/copy/command_line.h | 80 ++ .../command_line/standard/command_line.cpp | 146 +++ .../core/command_line/standard/command_line.h | 61 + .../src/core/factories/command_factory.h | 124 ++ .../src/core/factories/service_factory.h | 153 +++ .../core/factories/service_option_factory.h | 100 ++ .../factory_manager/service_factory_manager.h | 66 + .../bounce_protocol_policy.h | 731 +++++++++++ .../null_link_authentication_policy.h | 108 ++ .../link_policies/ssl_policy.h | 848 +++++++++++++ .../link_policies/tcp_policy.h | 252 ++++ ssf-1.1.0/src/core/parser/bounce_parser.cpp | 50 + ssf-1.1.0/src/core/parser/bounce_parser.h | 25 + ssf-1.1.0/src/core/server/CMakeLists.txt | 29 + ssf-1.1.0/src/core/server/main.cpp | 92 ++ ssf-1.1.0/src/core/server/server.h | 88 ++ ssf-1.1.0/src/core/server/server.ipp | 187 +++ .../core/service_manager/service_manager.h | 160 +++ .../init_packets/ssf_reply.cpp | 26 + .../init_packets/ssf_reply.h | 37 + .../init_packets/ssf_request.cpp | 34 + .../init_packets/ssf_request.h | 38 + .../transport_protocol_policy.h | 150 +++ ssf-1.1.0/src/services/CMakeLists.txt | 13 + ssf-1.1.0/src/services/admin/admin.h | 249 ++++ ssf-1.1.0/src/services/admin/admin.ipp | 467 +++++++ ssf-1.1.0/src/services/admin/admin_command.h | 51 + .../admin/requests/create_service_request.h | 142 +++ .../services/admin/requests/service_status.h | 147 +++ .../admin/requests/stop_service_request.h | 129 ++ ssf-1.1.0/src/services/base_service.h | 77 ++ .../copy_file/fiber_to_file/fiber_to_file.h | 127 ++ .../fiber_to_file/fiber_to_ofstream_session.h | 177 +++ .../copy_file/file_enquirer/file_enquirer.h | 141 +++ .../copy_file/file_to_fiber/file_to_fiber.h | 383 ++++++ .../file_to_fiber/istream_to_fiber_session.h | 180 +++ .../services/copy_file/filename_buffer.cpp | 38 + .../src/services/copy_file/filename_buffer.h | 38 + .../copy_file/filesystem/filesystem.cpp | 19 + .../copy_file/filesystem/filesystem.h | 28 + .../filesystem/linux/unix_filesystem.cpp | 26 + .../filesystem/windows/win_filesystem.cpp | 33 + .../src/services/copy_file/packet/packet.cpp | 99 ++ .../src/services/copy_file/packet/packet.h | 67 + .../datagrams_to_fibers/datagrams_to_fibers.h | 119 ++ .../datagrams_to_fibers.ipp | 87 ++ .../fibers_to_datagrams/fibers_to_datagrams.h | 126 ++ .../fibers_to_datagrams.ipp | 97 ++ .../fibers_to_sockets/fibers_to_sockets.h | 118 ++ .../fibers_to_sockets/fibers_to_sockets.ipp | 106 ++ ssf-1.1.0/src/services/initialisation.h | 20 + .../sockets_to_fibers/sockets_to_fibers.h | 113 ++ .../sockets_to_fibers/sockets_to_fibers.ipp | 127 ++ ssf-1.1.0/src/services/socks/socks_server.h | 116 ++ ssf-1.1.0/src/services/socks/socks_server.ipp | 128 ++ ssf-1.1.0/src/services/socks/socks_version.h | 33 + ssf-1.1.0/src/services/socks/v4/reply.cpp | 45 + ssf-1.1.0/src/services/socks/v4/reply.h | 60 + ssf-1.1.0/src/services/socks/v4/request.cpp | 81 ++ ssf-1.1.0/src/services/socks/v4/request.h | 128 ++ ssf-1.1.0/src/services/socks/v4/session.h | 76 ++ ssf-1.1.0/src/services/socks/v4/session.ipp | 168 +++ ssf-1.1.0/src/services/socks/v5/reply.cpp | 74 ++ ssf-1.1.0/src/services/socks/v5/reply.h | 86 ++ .../src/services/socks/v5/reply_auth.cpp | 25 + ssf-1.1.0/src/services/socks/v5/reply_auth.h | 38 + ssf-1.1.0/src/services/socks/v5/request.cpp | 85 ++ ssf-1.1.0/src/services/socks/v5/request.h | 143 +++ .../src/services/socks/v5/request_auth.cpp | 45 + .../src/services/socks/v5/request_auth.h | 98 ++ ssf-1.1.0/src/services/socks/v5/session.h | 88 ++ ssf-1.1.0/src/services/socks/v5/session.ipp | 254 ++++ .../user_services/base_user_service.h | 43 + .../user_services/copy_file_service.h | 183 +++ .../services/user_services/port_forwarding.h | 198 +++ .../user_services/remote_port_forwarding.h | 197 +++ .../src/services/user_services/remote_socks.h | 150 +++ ssf-1.1.0/src/services/user_services/socks.h | 148 +++ .../user_services/udp_port_forwarding.h | 195 +++ .../udp_remote_port_forwarding.h | 196 +++ ssf-1.1.0/src/tests/CMakeLists.txt | 318 +++++ ssf-1.1.0/src/tests/bouncing_tests.cpp | 169 +++ ssf-1.1.0/src/tests/copy_file_test_fixture.h | 283 +++++ ssf-1.1.0/src/tests/fiber_asio_tests.cpp | 1085 +++++++++++++++++ ssf-1.1.0/src/tests/fiber_basic_tests.cpp | 996 +++++++++++++++ .../src/tests/file_copy_from_client_tests.cpp | 148 +++ .../src/tests/files_to_copy/test_file1.txt | 3 + .../src/tests/files_to_copy/test_file2.txt | 3 + ssf-1.1.0/src/tests/load_config_tests.cpp | 107 ++ ssf-1.1.0/src/tests/remote_socks_tests.cpp | 538 ++++++++ .../tests/remote_stream_forwarding_tests.cpp | 399 ++++++ .../src/tests/remote_udp_forwarding_tests.cpp | 410 +++++++ ssf-1.1.0/src/tests/socks_tests.cpp | 536 ++++++++ .../ssf_client_server_cipher_suites_tests.cpp | 199 +++ .../src/tests/ssf_client_server_tests.cpp | 165 +++ .../src/tests/stream_forwarding_tests.cpp | 397 ++++++ ssf-1.1.0/src/tests/udp_forwarding_tests.cpp | 409 +++++++ ssf-1.1.0/src/versions.h.in | 25 + 172 files changed, 27128 insertions(+) create mode 100644 ssf-1.1.0/.gitignore create mode 100644 ssf-1.1.0/CMakeLists.txt create mode 100644 ssf-1.1.0/LICENSE.txt create mode 100644 ssf-1.1.0/README.md create mode 100644 ssf-1.1.0/certs/WARNING create mode 100644 ssf-1.1.0/certs/certificate.crt create mode 100644 ssf-1.1.0/certs/dh4096.pem create mode 100644 ssf-1.1.0/certs/private.key create mode 100644 ssf-1.1.0/certs/trusted/ca.crt create mode 100644 ssf-1.1.0/certs/trusted/ca.key create mode 100644 ssf-1.1.0/certs/trusted/ca.srl create mode 100644 ssf-1.1.0/certs/trusted/extfile.ext create mode 100644 ssf-1.1.0/cmake-ms/CMakeLists.txt create mode 100644 ssf-1.1.0/cmake-unix/CMakeLists.txt create mode 100644 ssf-1.1.0/cmake/CMakeLists.txt create mode 100644 ssf-1.1.0/cmake/cmake-modules/LICENSE_1_0.txt create mode 100644 ssf-1.1.0/cmake/cmake-modules/ListCombinations.cmake create mode 100644 ssf-1.1.0/cmake/cmake-modules/MSVCStaticRuntime.cmake create mode 100644 ssf-1.1.0/cmake/cmake-modules/README.markdown create mode 100644 ssf-1.1.0/cmake/common/EnhancedList.cmake create mode 100644 ssf-1.1.0/cmake/common/FileEdit.cmake create mode 100644 ssf-1.1.0/cmake/common/HelpersArguments.cmake create mode 100644 ssf-1.1.0/cmake/common/HelpersIdeTarget.cmake create mode 100644 ssf-1.1.0/cmake/common/MultiList.cmake create mode 100644 ssf-1.1.0/cmake/common/SystemTools.cmake create mode 100644 ssf-1.1.0/cmake/external/CMakeLists.txt create mode 100644 ssf-1.1.0/cmake/external/ExternalPackageHelpers.cmake create mode 100644 ssf-1.1.0/cmake/external/README.md create mode 100644 ssf-1.1.0/cmake/external/boost/FindBoost.cmake create mode 100644 ssf-1.1.0/cmake/external/boost/README.md create mode 100644 ssf-1.1.0/cmake/external/gtest/GTest.cmake create mode 100644 ssf-1.1.0/cmake/external/openssl/FindOpenSSL.cmake create mode 100644 ssf-1.1.0/cmake/external/openssl/README.md create mode 100644 ssf-1.1.0/img/icon.icns create mode 100644 ssf-1.1.0/img/icon.ico create mode 100644 ssf-1.1.0/img/icon.rc create mode 100644 ssf-1.1.0/src/common/boost/fiber/basic_endpoint.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux_service.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux_service.ipp create mode 100644 ssf-1.1.0/src/common/boost/fiber/datagram_fiber.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/datagram_fiber_service.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/detail/basic_fiber_demux_impl.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/detail/basic_fiber_impl.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/detail/fiber_buffer.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/detail/fiber_header.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/detail/fiber_id.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_accept_op.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_dgr_read_op.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_read_op.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/detail/io_operation.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/detail/io_ssl_read_op.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/fiber_acceptor_service.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/stream_fiber.hpp create mode 100644 ssf-1.1.0/src/common/boost/fiber/stream_fiber_service.hpp create mode 100644 ssf-1.1.0/src/common/config/config.cpp create mode 100644 ssf-1.1.0/src/common/config/config.h create mode 100644 ssf-1.1.0/src/common/error/error.cpp create mode 100644 ssf-1.1.0/src/common/error/error.h create mode 100644 ssf-1.1.0/src/common/network/base_session.h create mode 100644 ssf-1.1.0/src/common/network/datagram_link.h create mode 100644 ssf-1.1.0/src/common/network/datagram_link_operator.h create mode 100644 ssf-1.1.0/src/common/network/manager.h create mode 100644 ssf-1.1.0/src/common/network/network_policy_traits.h create mode 100644 ssf-1.1.0/src/common/network/session_forwarder.h create mode 100644 ssf-1.1.0/src/common/network/socket_link.h create mode 100644 ssf-1.1.0/src/common/utils/cleaner.h create mode 100644 ssf-1.1.0/src/core/CMakeLists.txt create mode 100644 ssf-1.1.0/src/core/client/CMakeLists.txt create mode 100644 ssf-1.1.0/src/core/client/client.h create mode 100644 ssf-1.1.0/src/core/client/client.ipp create mode 100644 ssf-1.1.0/src/core/client/main.cpp create mode 100644 ssf-1.1.0/src/core/client/main_cp.cpp create mode 100644 ssf-1.1.0/src/core/command_line/copy/command_line.cpp create mode 100644 ssf-1.1.0/src/core/command_line/copy/command_line.h create mode 100644 ssf-1.1.0/src/core/command_line/standard/command_line.cpp create mode 100644 ssf-1.1.0/src/core/command_line/standard/command_line.h create mode 100644 ssf-1.1.0/src/core/factories/command_factory.h create mode 100644 ssf-1.1.0/src/core/factories/service_factory.h create mode 100644 ssf-1.1.0/src/core/factories/service_option_factory.h create mode 100644 ssf-1.1.0/src/core/factory_manager/service_factory_manager.h create mode 100644 ssf-1.1.0/src/core/network_virtual_layer_policies/bounce_protocol_policy.h create mode 100644 ssf-1.1.0/src/core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h create mode 100644 ssf-1.1.0/src/core/network_virtual_layer_policies/link_policies/ssl_policy.h create mode 100644 ssf-1.1.0/src/core/network_virtual_layer_policies/link_policies/tcp_policy.h create mode 100644 ssf-1.1.0/src/core/parser/bounce_parser.cpp create mode 100644 ssf-1.1.0/src/core/parser/bounce_parser.h create mode 100644 ssf-1.1.0/src/core/server/CMakeLists.txt create mode 100644 ssf-1.1.0/src/core/server/main.cpp create mode 100644 ssf-1.1.0/src/core/server/server.h create mode 100644 ssf-1.1.0/src/core/server/server.ipp create mode 100644 ssf-1.1.0/src/core/service_manager/service_manager.h create mode 100644 ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.cpp create mode 100644 ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.h create mode 100644 ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_request.cpp create mode 100644 ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_request.h create mode 100644 ssf-1.1.0/src/core/transport_virtual_layer_policies/transport_protocol_policy.h create mode 100644 ssf-1.1.0/src/services/CMakeLists.txt create mode 100644 ssf-1.1.0/src/services/admin/admin.h create mode 100644 ssf-1.1.0/src/services/admin/admin.ipp create mode 100644 ssf-1.1.0/src/services/admin/admin_command.h create mode 100644 ssf-1.1.0/src/services/admin/requests/create_service_request.h create mode 100644 ssf-1.1.0/src/services/admin/requests/service_status.h create mode 100644 ssf-1.1.0/src/services/admin/requests/stop_service_request.h create mode 100644 ssf-1.1.0/src/services/base_service.h create mode 100644 ssf-1.1.0/src/services/copy_file/fiber_to_file/fiber_to_file.h create mode 100644 ssf-1.1.0/src/services/copy_file/fiber_to_file/fiber_to_ofstream_session.h create mode 100644 ssf-1.1.0/src/services/copy_file/file_enquirer/file_enquirer.h create mode 100644 ssf-1.1.0/src/services/copy_file/file_to_fiber/file_to_fiber.h create mode 100644 ssf-1.1.0/src/services/copy_file/file_to_fiber/istream_to_fiber_session.h create mode 100644 ssf-1.1.0/src/services/copy_file/filename_buffer.cpp create mode 100644 ssf-1.1.0/src/services/copy_file/filename_buffer.h create mode 100644 ssf-1.1.0/src/services/copy_file/filesystem/filesystem.cpp create mode 100644 ssf-1.1.0/src/services/copy_file/filesystem/filesystem.h create mode 100644 ssf-1.1.0/src/services/copy_file/filesystem/linux/unix_filesystem.cpp create mode 100644 ssf-1.1.0/src/services/copy_file/filesystem/windows/win_filesystem.cpp create mode 100644 ssf-1.1.0/src/services/copy_file/packet/packet.cpp create mode 100644 ssf-1.1.0/src/services/copy_file/packet/packet.h create mode 100644 ssf-1.1.0/src/services/datagrams_to_fibers/datagrams_to_fibers.h create mode 100644 ssf-1.1.0/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp create mode 100644 ssf-1.1.0/src/services/fibers_to_datagrams/fibers_to_datagrams.h create mode 100644 ssf-1.1.0/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp create mode 100644 ssf-1.1.0/src/services/fibers_to_sockets/fibers_to_sockets.h create mode 100644 ssf-1.1.0/src/services/fibers_to_sockets/fibers_to_sockets.ipp create mode 100644 ssf-1.1.0/src/services/initialisation.h create mode 100644 ssf-1.1.0/src/services/sockets_to_fibers/sockets_to_fibers.h create mode 100644 ssf-1.1.0/src/services/sockets_to_fibers/sockets_to_fibers.ipp create mode 100644 ssf-1.1.0/src/services/socks/socks_server.h create mode 100644 ssf-1.1.0/src/services/socks/socks_server.ipp create mode 100644 ssf-1.1.0/src/services/socks/socks_version.h create mode 100644 ssf-1.1.0/src/services/socks/v4/reply.cpp create mode 100644 ssf-1.1.0/src/services/socks/v4/reply.h create mode 100644 ssf-1.1.0/src/services/socks/v4/request.cpp create mode 100644 ssf-1.1.0/src/services/socks/v4/request.h create mode 100644 ssf-1.1.0/src/services/socks/v4/session.h create mode 100644 ssf-1.1.0/src/services/socks/v4/session.ipp create mode 100644 ssf-1.1.0/src/services/socks/v5/reply.cpp create mode 100644 ssf-1.1.0/src/services/socks/v5/reply.h create mode 100644 ssf-1.1.0/src/services/socks/v5/reply_auth.cpp create mode 100644 ssf-1.1.0/src/services/socks/v5/reply_auth.h create mode 100644 ssf-1.1.0/src/services/socks/v5/request.cpp create mode 100644 ssf-1.1.0/src/services/socks/v5/request.h create mode 100644 ssf-1.1.0/src/services/socks/v5/request_auth.cpp create mode 100644 ssf-1.1.0/src/services/socks/v5/request_auth.h create mode 100644 ssf-1.1.0/src/services/socks/v5/session.h create mode 100644 ssf-1.1.0/src/services/socks/v5/session.ipp create mode 100644 ssf-1.1.0/src/services/user_services/base_user_service.h create mode 100644 ssf-1.1.0/src/services/user_services/copy_file_service.h create mode 100644 ssf-1.1.0/src/services/user_services/port_forwarding.h create mode 100644 ssf-1.1.0/src/services/user_services/remote_port_forwarding.h create mode 100644 ssf-1.1.0/src/services/user_services/remote_socks.h create mode 100644 ssf-1.1.0/src/services/user_services/socks.h create mode 100644 ssf-1.1.0/src/services/user_services/udp_port_forwarding.h create mode 100644 ssf-1.1.0/src/services/user_services/udp_remote_port_forwarding.h create mode 100644 ssf-1.1.0/src/tests/CMakeLists.txt create mode 100644 ssf-1.1.0/src/tests/bouncing_tests.cpp create mode 100644 ssf-1.1.0/src/tests/copy_file_test_fixture.h create mode 100644 ssf-1.1.0/src/tests/fiber_asio_tests.cpp create mode 100644 ssf-1.1.0/src/tests/fiber_basic_tests.cpp create mode 100644 ssf-1.1.0/src/tests/file_copy_from_client_tests.cpp create mode 100644 ssf-1.1.0/src/tests/files_to_copy/test_file1.txt create mode 100644 ssf-1.1.0/src/tests/files_to_copy/test_file2.txt create mode 100644 ssf-1.1.0/src/tests/load_config_tests.cpp create mode 100644 ssf-1.1.0/src/tests/remote_socks_tests.cpp create mode 100644 ssf-1.1.0/src/tests/remote_stream_forwarding_tests.cpp create mode 100644 ssf-1.1.0/src/tests/remote_udp_forwarding_tests.cpp create mode 100644 ssf-1.1.0/src/tests/socks_tests.cpp create mode 100644 ssf-1.1.0/src/tests/ssf_client_server_cipher_suites_tests.cpp create mode 100644 ssf-1.1.0/src/tests/ssf_client_server_tests.cpp create mode 100644 ssf-1.1.0/src/tests/stream_forwarding_tests.cpp create mode 100644 ssf-1.1.0/src/tests/udp_forwarding_tests.cpp create mode 100644 ssf-1.1.0/src/versions.h.in diff --git a/ssf-1.1.0/.gitignore b/ssf-1.1.0/.gitignore new file mode 100644 index 000000000..979e8d654 --- /dev/null +++ b/ssf-1.1.0/.gitignore @@ -0,0 +1,3 @@ +/build +/build64 +/third_party \ No newline at end of file diff --git a/ssf-1.1.0/CMakeLists.txt b/ssf-1.1.0/CMakeLists.txt new file mode 100644 index 000000000..d2f702081 --- /dev/null +++ b/ssf-1.1.0/CMakeLists.txt @@ -0,0 +1,278 @@ +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_TRANSPORT 1) + +# --- Set client/server security +if(SSF_SECURITY STREQUAL "FORCE_TCP_ONLY") + message("** SSF_SECURITY is FORCE_TCP_ONLY") + add_definitions(-DTCP_ONLY_LINK) + set(SSF_VERSION_SECURITY 0) +else() + message("** SSF_SECURITY is STANDARD") + add_definitions(-DTLS_OVER_TCP_LINK) + set(SSF_VERSION_SECURITY 1) +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(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") + +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") +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 "") + +if (WIN32) + include(./cmake-ms/CMakeLists.txt) +elseif (UNIX) + include(./cmake-unix/CMakeLists.txt) +endif () + +# --- Define boost version if not set +if(NOT DEP_BOOST_VERSION) + set(DEP_BOOST_VERSION 1.56.0) +endif() + +# --- Setup Boost dependencies +set(Boost_USE_STATIC_LIBS ON) +set(Boost_USE_MULTITHREADED ON) +set(Boost_USE_STATIC_RUNTIME ON) + +# --- Boost components +find_package( + Boost REQUIRED + FLAGS + STATIC + ${BOOST_PLATFORM_FLAGS} + WITH_COMPONENTS + system + serialization + program_options + date_time + filesystem + log + regex + thread + chrono + ${BOOST_PLATFORM_COMPONENTS} +) + +# --- OpenSSL components +find_package( + OpenSSL REQUIRED + FLAGS + STATIC + ${OPENSSL_PLATFORM_FLAGS} +) + +# --- 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}) + + +# --- 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}) + +# --- 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} +) + +set (BUILD_UNIT_TESTS ON) +if (BUILD_UNIT_TESTS) + include(GTest) + + 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_subdirectory(${GTEST_ROOT_DIR}) + + # --- Add src test + add_subdirectory("${project_SRC_DIR}/tests") +endif (BUILD_UNIT_TESTS) + +add_subdirectory("${project_SRC_DIR}/core") diff --git a/ssf-1.1.0/LICENSE.txt b/ssf-1.1.0/LICENSE.txt new file mode 100644 index 000000000..de5ec05cf --- /dev/null +++ b/ssf-1.1.0/LICENSE.txt @@ -0,0 +1,25 @@ +Copyright (c) 2015 SSF + +This software is using: + * Boost project released with Boost Software License 1.0 + * 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 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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/ssf-1.1.0/README.md b/ssf-1.1.0/README.md new file mode 100644 index 000000000..a3267b5d5 --- /dev/null +++ b/ssf-1.1.0/README.md @@ -0,0 +1,312 @@ +# Secure Socket Funneling (SSF) + +## How to build + +### Requirements + + * Winrar >= 5.2.1 (Third party builds on windows) + * Boost >= 1.56.0 + * OpenSSL >= 1.0.2 + * 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.) + +SSF_SECURITY: + +* **STANDARD**: the project will be build with standard security features +* **FORCE_TCP_ONLY**: the project will be built without security features to facilitate debugging + +### Build SSF on Windows + +* Go in project directory + +```bash +cd PROJECT_PATH +``` + +* Copy [Boost archive](http://www.boost.org/users/download/) in ``third_party/boost`` + +```bash +cp boost_1_XX_Y.tar.bz2 PROJECT_PATH/third_party/boost +``` + +* Copy [OpenSSL archive](https://www.openssl.org/source/) in ``third_party/openssl`` + +```bash +cp openssl-1.0.XY.tar.gz PROJECT_PATH/third_party/openssl +``` + +If you are using *openssl-1.0.2a*, you need to fix the file ``crypto/x509v3/v3_scts.c``. It contains an incorrect ``#include`` line. +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`` + + ```bash + cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest + ``` + +* Generate project + + ```bash + mkdir PROJECT_PATH/build + cd PROJECT_PATH/build + cmake -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ + ``` + + * Build project + + ```bash + cd PROJECT_PATH/build + cmake --build `pwd` --config Debug|Release + ``` + +### Build SSF on Linux + +* Go in project directory + +```bash +cd PROJECT_PATH +``` + +* Copy [Boost archive](http://www.boost.org/users/download/) in ``third_party/boost`` + +```bash +cp boost_1_XX_Y.tar.bz2 PROJECT_PATH/third_party/boost +``` + +* Copy [OpenSSL archive](https://www.openssl.org/source/) in ``third_party/openssl`` + +```bash +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`` + +```bash +cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest +``` + +* Generate project + +```bash +mkdir PROJECT_PATH/build +cd PROJECT_PATH/build +cmake -DCMAKE_BUILD_TYPE=Release|Debug -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ +``` + +* Build project + +```bash +cmake --build PROJECT_PATH/build -- -j +``` + +### Build SSF on Mac OS X + +* Go in project directory + +```bash +cd PROJECT_PATH +``` + +* Copy [Boost archive](http://www.boost.org/users/download/) in ``third_party/boost`` + +```bash +cp boost_1_XX_Y.tar.bz2 PROJECT_PATH/third_party/boost +``` + +* Copy [OpenSSL archive](https://www.openssl.org/source/) in ``third_party/openssl`` + +```bash +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`` + +```bash +cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest +``` + +* Generate project + +```bash +mkdir PROJECT_PATH/build +cd PROJECT_PATH/build +cmake -DCMAKE_BUILD_TYPE=Release|Debug -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ +``` + +* Build project + +```bash +cmake --build PROJECT_PATH/build -- -j +``` + + +## How to configure + +### Generating certificates for TLS connections + +#### With tool script + +```bash +./tools/generate_cert.sh /path/to/store/certs +``` + +The first argument should be the directory where the CA and certificates will be generated + +#### Manually + +##### Generating Diffie-Hellman parameters + +```bash +openssl dhparam 4096 -outform PEM -out dh4096.pem +``` + +##### Generating a self-signed Certification Authority (CA) +First of all, create a file named *extfile.txt* containing the following lines: + +```plaintext +[ v3_req_p ] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +``` + +Then, generate a self-signed certificate (the CA) *ca.crt* and its private key *ca.key*: + +```bash +openssl req -x509 -nodes -newkey rsa:4096 -keyout ca.key -out ca.crt -days 3650 +``` + +##### Generating a certificate (signed with the CA) and its private key + +Generate a private key *private.key* and signing request *certificate.csr*: + +```bash +openssl req -newkey rsa:4096 -nodes -keyout private.key -out certificate.csr +``` + +Sign with the CA (*ca.crt*, *ca.key*) the signing request to get the certificate *certificate.pem* : + +```bash +openssl x509 -extfile extfile.txt -extensions v3_req_p -req -sha1 -days 3650 -CA ca.crt -CAkey ca.key -CAcreateserial -in certificate.csr -out certificate.pem +``` + +### Configuration file + +With default options, the following files and folders should be in the directory of execution of a client or a server: + +* ./certs/dh4096.pem +* ./certs/certificate.crt +* ./certs/private.key +* ./certs/trusted/ca.crt + +Where: + +* *dh4096.pem* contains the Diffie-Hellman parameters (see above for how to generate the file) +* *certificate.crt* and *private.key* are the certificate and the private key of the ssf server or client +* *ca.crt* is the concatenated list of certificates trusted by the ssf server or client + +However, if you want those files at different paths, it is possible to customize them with the configuration file option *-c*. + +An example is given in the file example section. + +### Relay chain file + +This file will contain the bounce servers and ports which will be used to establish the connection. +They will be listed as follow : + +```plaintext +SERVER1:PORT1 +SERVER2:PORT2 +SERVER3:PORT3 +``` + +The chain will be CLIENT -> SERVER1:PORT1 -> SERVER2:PORT2 -> SERVER3:PORT3 -> TARGET + +## How to use + +### 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] +``` + +* 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. +* -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. +* -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). + +### Copy command line + +```plaintext +ssfcp[.exe] [-h] [-b bounce_file] [-c config_file] [-p port] [-t] [host@]path [[host@]path] +``` + +* -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). +* -t : input from stdin + +#### Copy from local to remote destination : + +```plaintext +ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] path/to/file host@absolute/path/directory_destination +``` + +```plaintext +ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] path/to/file* host@absolute/path/directory_destination +``` + +#### From stdin to remote destination + +```plaintext +data_in_stdin | ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] -t host@path/to/destination/file_destination +``` + +#### Copy remote files to local destination : + +```plaintext +ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] remote_host@path/to/file absolute/path/directory_destination +``` + +```plaintext +ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] remote_host@path/to/file* absolute/path/directory_destination +``` + +### File example + +#### Bounce file (relay servers) + +```plaintext +127.0.0.1:8002 +127.0.0.1:8003 +``` + +#### Config file + +```plaintext +{ + "ssf": { + "tls": { + "ca_cert_path": "./certs/trusted/ca.crt", + "cert_path": "./certs/certificate.crt", + "key_path": "./certs/private.key", + "dh_path": "./certs/dh4096.pem", + "cipher_alg": "DHE-RSA-AES256-GCM-SHA384" + } + } +} +``` + +* *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 diff --git a/ssf-1.1.0/certs/WARNING b/ssf-1.1.0/certs/WARNING new file mode 100644 index 000000000..6dc7abcda --- /dev/null +++ b/ssf-1.1.0/certs/WARNING @@ -0,0 +1,2 @@ +These certificates are provided for test purpose only. +You should generate your own certificates as explained in the website. \ No newline at end of file diff --git a/ssf-1.1.0/certs/certificate.crt b/ssf-1.1.0/certs/certificate.crt new file mode 100644 index 000000000..e7bd77c91 --- /dev/null +++ b/ssf-1.1.0/certs/certificate.crt @@ -0,0 +1,65 @@ +-----BEGIN CERTIFICATE----- +MIIFQzCCAyugAwIBAgIJAK8e45jVbgd+MA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV +BAYTAkJFMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxCzAJBgNVBAMTAkNBMB4XDTE1MDYwMTAxMTkwOVoXDTI1 +MDUyOTAxMTkwOVowVDELMAkGA1UEBhMCQkUxEzARBgNVBAgTClNvbWUtU3RhdGUx +ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDENMAsGA1UEAxMEQ2Vy +dDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANUIAJQm7D9tLzS0U6F4 +hHbZcgLjtELZJQ8jmpROYYyA9MIBDdrIbgIHeSBS/q1VRNvoDfnJBCnVcplCDMBA +0uiuIGxHzO4JnyTZElWulujdoHKTfFmjxq1l46O6jb1zt1zyz92qzbOaENob2o65 +BZHLoPUt1UIIRLx/IGOzhh+Gmcz4gLs9PRuC7puacgwY3RCFLYyBk7R+kdUt30vQ +bPewTbdMmf5pGyAPBmUJpk3PpwHLiRA+qIiD3r/bR1RgaG000n57okbYpYwhHh8C +mZUTZGNg0Ymb+8TXHnzMH4cC3wdwRYwdeLuNgw99k5FrH2q3g3bzTHdD56a9iA4Z +RRSlkMpPXL1sCO66vPu6UB/RHZhC0hCSU/B+L+obW5UydG1o8C2Vz8h+KThzibCy +uSWh87piu9MHKi7vojH/cjMSBOpUhZ0jH9xkPbTYKg4ABvdv1WcwBDAbR7c6uJw5 +XIsxUQ9U2aEo7D+gIkXfVwHFqlclmYCG2rmD5yhV1wvsDFCoDKksGTEVW54aiF7G +imsuJo0OROTqNmBgRwhx7CKioiRg6XVjuCVb/FHYPcJxJrT5bOB+2wSa0ejzWZ7n +XYSIxVMkEYKGf7DpKGSmO82vg19lHVWr7vq+1M8XimOcVRosEWVjgSfLpLZRruyH +vxfGw9D2fDMQjYq7Vae5K1QLAgMBAAGjGjAYMAkGA1UdEwQCMAAwCwYDVR0PBAQD +AgXgMA0GCSqGSIb3DQEBBQUAA4ICAQAm6QoPEOQV9yfotbQRKnuH8qzDW+3UcNbc +MvhisrAhQPGukqTkFtenr1WK74KP2X7Y8HL7OIhwPCWvNDTW7XfrTQSFSWvIPZ/d +FqflmxQ3QMiYf4iMktUZ4E3Z/eVppKBgQnJ/gEk+3pxn/7APnOisoJII2A9hc5Lr +bfzrsrYxaAaRUOzXPXHqStnY52uGFV8FipBx8zwchR5oeIOsm/MHtEto9a/LOsJb +lEia5A/7Z43/ionqaKhlwFreSGVN3lhUKDXPidm6DjUHeNRGHzSGlGSyWgCRzv9q +Dh1dhwBbQ6g44KSXZWRFDS+aKJ80dTVggntbaVvDIHtGpWy/xOB5b2j6BIWrUgrQ +zhHVhM/rnqw0iICPrk1oQxdAJsIPbUEijM0T5dbAdV8/WkxkJ3pbX/Z2F8LDfKV0 +W+05IELxa7ZqpuSp1pGdhPVyPo+5qbwic8xMGlK3Pf3Mq0t6m8RHq78ZaH433i37 +Wd4M90hB+qWItiZ+MyI7999A1baHd3bJHxRFpegCDxdbTLfYc5q0pWpIonccLsqS +LDQfV1pus1vCvEnMarZKzmLRuX2dO8LJy77qe/zTcA30pAFYLq5WkErWZU63EDWy +MQcwd0vH4hljAk6C4SQPa73A+cp/ilkmrHuKkSMT0CjsbJ5EsLRBTaHKJ9N/+9c3 +Gm7FzklbMw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +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 +1aDJ/d7Oqd2Bda1uHMKMm4Y= +-----END CERTIFICATE----- diff --git a/ssf-1.1.0/certs/dh4096.pem b/ssf-1.1.0/certs/dh4096.pem new file mode 100644 index 000000000..4075f3b7f --- /dev/null +++ b/ssf-1.1.0/certs/dh4096.pem @@ -0,0 +1,13 @@ +-----BEGIN DH PARAMETERS----- +MIICCAKCAgEAseqOk+DzWryjmv42VdCyQ+7xOyzM5hq9fLpAuM5OWzl0ieTuKs7D +oDbYiWW61FwPupMThl1O7Sp6N2eFqsppnRDuR9ZGMHbEjJQCHwwQzWlXyB1u5xv9 +IOIxIZPXiR7IT22xGiB2YjS7oMZguRaAQWys8SG7N5eKeQ30G8FS+DZmS4wEGUac +Ah1qysta4pzyWeQNUDgwghyT+EsTrdTUuWt6ebbnIHnKbr3FQVs6SVZlp2IEun3V +4WrTs19tt2nwxn4Ttig+9/WHePgmxYzMM3KImJNEOO3iDwl7w0sJp3j5RGW5Dh/C +vVNtlbo1wZIWLVZTFBTlwVFVCMSRMoghwLoG5hC4GyQVT8rdj+zn0+VLXKCqyDXg +e3YzqnbMBeI1sp2ai4RFjd+d0jt1mXOaGXNV4dYSyQh6+7AFzptDwsSZ7/kfr/SY +w0xtMu/vqcj8TnQO8Lq3s2Et1oF+MFbh/kWCagYLcXG1eD8Lmxi104NwRMQYTsUz +IRcjLN5xdI2CtQVIbg1zdC3+Ciu+xJXfg1fYK9ZLBst31Pj/ZUaJlR0IOumboE4k +obGA87LOAbXB5LfTj7hROOhn4/NJLj/CHXXCPpXixE890Z5wo2f/PG+4umL4DTkK +hr/xGr7EJr1iLuv8ulDaApPR4MYbfEOUwpo3oqBRJFh1m5gahndmtqMCAQI= +-----END DH PARAMETERS----- diff --git a/ssf-1.1.0/certs/private.key b/ssf-1.1.0/certs/private.key new file mode 100644 index 000000000..cdb6f7023 --- /dev/null +++ b/ssf-1.1.0/certs/private.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/ssf-1.1.0/certs/trusted/ca.crt b/ssf-1.1.0/certs/trusted/ca.crt new file mode 100644 index 000000000..5a2b475cf --- /dev/null +++ b/ssf-1.1.0/certs/trusted/ca.crt @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF3TCCA8WgAwIBAgIJAPrG1z5hkxBBMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNV +BAYTAkJFMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQxCzAJBgNVBAMTAkNBMB4XDTE1MDYwMTAxMTgwMVoXDTI1 +MDUyOTAxMTgwMVowUjELMAkGA1UEBhMCQkUxEzARBgNVBAgTClNvbWUtU3RhdGUx +ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDELMAkGA1UEAxMCQ0Ew +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDmT2S1frBI4532bASwbuNH +gK5dlSv9PbpvEPTL8G4t4SMBTNXSMr2ScXZC0SRppTgrEyvK2P85YKaUucnX4T7R +r2i36j+UPC+509Oh+J9yp9TdBulNgM0VNiJCns+UFC2dRHVR7/0UX850YUhazISF +DeC8cnrDs4VZiImeDDGlFDUFfoHU7IX8ta/NWFc6cYHoDk2/Ye+FBJ1RM4iw7n5D +jW8sdTM886GaCjlyLt2R0iY9lVQnuAuMn/TzWCE86TWfpgYZz2uiBIs7ZhOckv37 +2Mij7D12kKG882StT2jqihMz1ncCkKquWdQoXZs+sTQfvlS9SKCTvIog4aMbBvIM +fzUkFUJMxAk0CBe49OFOdfNw7eTES7TuI90D08WB5cPBOrBLS66xAdw7DFIT49dx +A9tQTqzgp5wfyQUW6RhNiygNeamIY7HwPFQsFC7WKwIfYcXQWQAZDS0YdSXSvrqS +2paoilVfxLsi1SM2NdiQudUIbTPYAblAlP7Mbe90QizzeDbLgEJw5Anapyksw2SP +wyLSVM5uXFy/KS9fYAyBmSr0XEaTS7SC76uHPJa4Iffv+gKzJkB/UWNbz39PSF+R +Xuz/Dvc7DskOPnkZcm4KZtOiS/dzB/B8+0EWhFn6n2mIlq2eGwwXf62XFeUiQ0wo +zrzQRWoSUF6XiqZnU6SZ7wIDAQABo4G1MIGyMB0GA1UdDgQWBBRRdZlEXKYtmKsW +5HmAxoSyKH/YMjCBggYDVR0jBHsweYAUUXWZRFymLZirFuR5gMaEsih/2DKhVqRU +MFIxCzAJBgNVBAYTAkJFMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJ +bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxCzAJBgNVBAMTAkNBggkA+sbXPmGTEEEw +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAgEAmDjjNo2bLDoFeMhLGcV6 +1F4ZdB1wWJvfufGmFjHGjXTlgXA5u/L+fz1LqLqMoQHRxUczL0ZgE+8JwXaWsqdY +Wnz8F1h6u0HZemWb5YlaNDeHCrTcb4Xi8SS6A0AbEdL2M6ydOOxtjtDCTMxrTAAP +mDVJN9Kfl7+kmgjuAWbVdeRSP0sKVqocCGmENX61tWaT9zappUb9l6wxe7J48geu +bN8Q6hAzarrqdQFvNG8DkZkizHKXwGVO0v9SuFhRy8nGb41OCHiRbLunzkQbJfwC +WOL9kfH/u9R11Eli8Wq2A0pEOb7zx5m1B+RRhlBtOpXgsRpJaGrNBrj6ATa2gc0X +vU4f9aIr/urHKu1FO/q1K4D+yelS7E87HohXdMmnhKNYl3L/Y1ylQnDVwXb3uGK5 +HeWIf0EvcgPPQdabReQgh2Ybn40Ec5txtlkfQXQ76VBOosEZ+Kbep50J1szt5wrJ +e5MqhZIxvTSVwW3iwt2v2VYLzGX+zCcPaXFCVlEelLVF4VGXOxzm4pMmoBWe/0+B +JJkQoO0B8PWaDHIjxQTCHo8enBf1yWUOTBysuL+HA8rZ0VC5OOVvHWeqS5C3jlYa +lph+bp6iO/FAoIeigorNWkDQrmNqH6F+37i9Jse8vrPsbtxA1YwYfKeIDxO6CMHH +1aDJ/d7Oqd2Bda1uHMKMm4Y= +-----END CERTIFICATE----- diff --git a/ssf-1.1.0/certs/trusted/ca.key b/ssf-1.1.0/certs/trusted/ca.key new file mode 100644 index 000000000..7509670d0 --- /dev/null +++ b/ssf-1.1.0/certs/trusted/ca.key @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +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 +-----END RSA PRIVATE KEY----- diff --git a/ssf-1.1.0/certs/trusted/ca.srl b/ssf-1.1.0/certs/trusted/ca.srl new file mode 100644 index 000000000..f6d8f1d1d --- /dev/null +++ b/ssf-1.1.0/certs/trusted/ca.srl @@ -0,0 +1 @@ +AF1EE398D56E077E diff --git a/ssf-1.1.0/certs/trusted/extfile.ext b/ssf-1.1.0/certs/trusted/extfile.ext new file mode 100644 index 000000000..5539a4653 --- /dev/null +++ b/ssf-1.1.0/certs/trusted/extfile.ext @@ -0,0 +1,7 @@ +[ v3_req_p ] +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca_p ] +basicConstraints = CA:TRUE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment, keyCertSign diff --git a/ssf-1.1.0/cmake-ms/CMakeLists.txt b/ssf-1.1.0/cmake-ms/CMakeLists.txt new file mode 100644 index 000000000..87185f2aa --- /dev/null +++ b/ssf-1.1.0/cmake-ms/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 2.8) + +# --- Special settings to Windows platform +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 "") + + # --- 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") + add_definitions(-D_SCL_SECURE_NO_WARNINGS) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + endif(MSVC) +endif (WIN32) + diff --git a/ssf-1.1.0/cmake-unix/CMakeLists.txt b/ssf-1.1.0/cmake-unix/CMakeLists.txt new file mode 100644 index 000000000..834c603d8 --- /dev/null +++ b/ssf-1.1.0/cmake-unix/CMakeLists.txt @@ -0,0 +1,38 @@ +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(PLATFORM_SPECIFIC_LIB_DEP "pthread") + else() + 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}) + endif () +endif(UNIX) + diff --git a/ssf-1.1.0/cmake/CMakeLists.txt b/ssf-1.1.0/cmake/CMakeLists.txt new file mode 100644 index 000000000..510d0c0bc --- /dev/null +++ b/ssf-1.1.0/cmake/CMakeLists.txt @@ -0,0 +1,27 @@ +# ------------------------------------------------------------------------------ +# 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/ssf-1.1.0/cmake/cmake-modules/LICENSE_1_0.txt b/ssf-1.1.0/cmake/cmake-modules/LICENSE_1_0.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/ssf-1.1.0/cmake/cmake-modules/LICENSE_1_0.txt @@ -0,0 +1,23 @@ +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/ssf-1.1.0/cmake/cmake-modules/ListCombinations.cmake b/ssf-1.1.0/cmake/cmake-modules/ListCombinations.cmake new file mode 100644 index 000000000..4321f624a --- /dev/null +++ b/ssf-1.1.0/cmake/cmake-modules/ListCombinations.cmake @@ -0,0 +1,53 @@ +# - 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/ssf-1.1.0/cmake/cmake-modules/MSVCStaticRuntime.cmake b/ssf-1.1.0/cmake/cmake-modules/MSVCStaticRuntime.cmake new file mode 100644 index 000000000..e4ceea6f9 --- /dev/null +++ b/ssf-1.1.0/cmake/cmake-modules/MSVCStaticRuntime.cmake @@ -0,0 +1,33 @@ +# - 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/ssf-1.1.0/cmake/cmake-modules/README.markdown b/ssf-1.1.0/cmake/cmake-modules/README.markdown new file mode 100644 index 000000000..81ec9b22d --- /dev/null +++ b/ssf-1.1.0/cmake/cmake-modules/README.markdown @@ -0,0 +1,107 @@ +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/ssf-1.1.0/cmake/common/EnhancedList.cmake b/ssf-1.1.0/cmake/common/EnhancedList.cmake new file mode 100644 index 000000000..6db9503e0 --- /dev/null +++ b/ssf-1.1.0/cmake/common/EnhancedList.cmake @@ -0,0 +1,29 @@ +# +# 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/ssf-1.1.0/cmake/common/FileEdit.cmake b/ssf-1.1.0/cmake/common/FileEdit.cmake new file mode 100644 index 000000000..9a3959a26 --- /dev/null +++ b/ssf-1.1.0/cmake/common/FileEdit.cmake @@ -0,0 +1,223 @@ +# 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/ssf-1.1.0/cmake/common/HelpersArguments.cmake b/ssf-1.1.0/cmake/common/HelpersArguments.cmake new file mode 100644 index 000000000..a54f743c9 --- /dev/null +++ b/ssf-1.1.0/cmake/common/HelpersArguments.cmake @@ -0,0 +1,107 @@ +# +# 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/ssf-1.1.0/cmake/common/HelpersIdeTarget.cmake b/ssf-1.1.0/cmake/common/HelpersIdeTarget.cmake new file mode 100644 index 000000000..e08c3a991 --- /dev/null +++ b/ssf-1.1.0/cmake/common/HelpersIdeTarget.cmake @@ -0,0 +1,348 @@ +# +# 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/ssf-1.1.0/cmake/common/MultiList.cmake b/ssf-1.1.0/cmake/common/MultiList.cmake new file mode 100644 index 000000000..64c28d63f --- /dev/null +++ b/ssf-1.1.0/cmake/common/MultiList.cmake @@ -0,0 +1,67 @@ +# 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/ssf-1.1.0/cmake/common/SystemTools.cmake b/ssf-1.1.0/cmake/common/SystemTools.cmake new file mode 100644 index 000000000..9819c970c --- /dev/null +++ b/ssf-1.1.0/cmake/common/SystemTools.cmake @@ -0,0 +1,226 @@ +# 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/ssf-1.1.0/cmake/external/README.md b/ssf-1.1.0/cmake/external/README.md new file mode 100644 index 000000000..9f725369b --- /dev/null +++ b/ssf-1.1.0/cmake/external/README.md @@ -0,0 +1,84 @@ +## 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/ssf-1.1.0/cmake/external/boost/FindBoost.cmake b/ssf-1.1.0/cmake/external/boost/FindBoost.cmake new file mode 100644 index 000000000..b38dd6f21 --- /dev/null +++ b/ssf-1.1.0/cmake/external/boost/FindBoost.cmake @@ -0,0 +1,268 @@ +# 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/ssf-1.1.0/cmake/external/boost/README.md b/ssf-1.1.0/cmake/external/boost/README.md new file mode 100644 index 000000000..ef9d1ff0b --- /dev/null +++ b/ssf-1.1.0/cmake/external/boost/README.md @@ -0,0 +1,53 @@ +## 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/ssf-1.1.0/cmake/external/gtest/GTest.cmake b/ssf-1.1.0/cmake/external/gtest/GTest.cmake new file mode 100644 index 000000000..60ea61c93 --- /dev/null +++ b/ssf-1.1.0/cmake/external/gtest/GTest.cmake @@ -0,0 +1,31 @@ +# 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/ssf-1.1.0/cmake/external/openssl/FindOpenSSL.cmake b/ssf-1.1.0/cmake/external/openssl/FindOpenSSL.cmake new file mode 100644 index 000000000..b47670578 --- /dev/null +++ b/ssf-1.1.0/cmake/external/openssl/FindOpenSSL.cmake @@ -0,0 +1,266 @@ +# 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/ssf-1.1.0/cmake/external/openssl/README.md b/ssf-1.1.0/cmake/external/openssl/README.md new file mode 100644 index 000000000..32dffad29 --- /dev/null +++ b/ssf-1.1.0/cmake/external/openssl/README.md @@ -0,0 +1,47 @@ +## 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/ssf-1.1.0/img/icon.icns b/ssf-1.1.0/img/icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..dc04d0a3e86c4f2f4e9bb1c5796065602bcaecb3 GIT binary patch literal 42053 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

ncg}hAO%rb^hX+4qJMkoIh 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 literal 0 HcmV?d00001 diff --git a/ssf-1.1.0/img/icon.rc b/ssf-1.1.0/img/icon.rc new file mode 100644 index 000000000..ffcb6f872 --- /dev/null +++ b/ssf-1.1.0/img/icon.rc @@ -0,0 +1 @@ +IDR_MAINFRAME ICON DISCARDABLE "icon.ico" \ No newline at end of file diff --git a/ssf-1.1.0/src/common/boost/fiber/basic_endpoint.hpp b/ssf-1.1.0/src/common/boost/fiber/basic_endpoint.hpp new file mode 100644 index 000000000..4a0589d7b --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/basic_endpoint.hpp @@ -0,0 +1,95 @@ +// +// fiber/basic_endpoint.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_BASIC_ENDPOINT_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_BASIC_ENDPOINT_HPP_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include "common/boost/fiber/detail/fiber_id.hpp" + +namespace boost { +namespace asio { +namespace fiber { + +/// basic_endpoint class for fiber +/** +* An endpoint is composed of a demultiplexer and a remote fiber port +* +* @tparam Demux The type of the demultiplexer user +*/ +template +class basic_endpoint { + private: + /// Type of the demultiplexer + typedef typename Protocol::demux_type demux_type; + /// Type for the remote fiber port + typedef boost::asio::fiber::detail::fiber_id::port_type port_type; + + public: + basic_endpoint() : p_demux_(nullptr), port_(0) {} + + /// Constructor + /** + * @param protocol Protocol + * @param demux The demultiplexer on which to bind the endpoint + */ + basic_endpoint(Protocol protocol, demux_type& demux) + : p_demux_(&demux), port_(0) {} + + /// Constructor + /** + * @param demux The demultiplexer on which to bind the endpoint + * @param port The port on which to bind the endpoint + */ + basic_endpoint(demux_type& demux, port_type port) + : p_demux_(&demux), port_(port) {} + + bool operator<(const basic_endpoint& rhs) const { return port_ < rhs.port_; } + + /// Constructor + /** + * @param protocol Protocol + * @param demux The demultiplexer on which to bind the endpoint + * @param port The port on which to bind the endpoint + */ + basic_endpoint(Protocol protocol, demux_type& demux, port_type port) + : family_(protocol.family()), p_demux_(&demux), port_(port) {} + + basic_endpoint& operator=(const basic_endpoint& rhs) { + family_ = rhs.family_; + p_demux_ = rhs.p_demux_; + port_ = rhs.port_; + + return *this; + } + + /// The protocol associated with the endpoint. + Protocol protocol() const { return Protocol::v1(); } + + /// Get the demultiplexer + demux_type& demux() const { return *p_demux_; } + + /// Get the port + port_type port() const { return port_; } + + /// Get the port + port_type& port() { return port_; } + + private: + int family_; + demux_type* p_demux_; + port_type port_; +}; + +} // namespace fiber +} // namespace asio +} // namespace boost + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_BASIC_ENDPOINT_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux.hpp b/ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux.hpp new file mode 100644 index 000000000..d347686c3 --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux.hpp @@ -0,0 +1,266 @@ +// +// fiber/basic_fiber_demux.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_BASIC_FIBER_DEMUX_HPP_ +#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) + +#include + +#include +#include + +#include "common/boost/fiber/basic_fiber_demux_service.hpp" +#include "common/boost/fiber/detail/fiber_id.hpp" +#include "common/boost/fiber/detail/io_fiber_accept_op.hpp" + +#include + +namespace boost { +namespace asio { +namespace fiber { + +namespace detail { +template +class basic_fiber_impl; +} // namespace detail + +template > +class basic_fiber_demux : private boost::noncopyable { +public: + /// Type of the socket over which to demultiplex + typedef StreamSocket socket_type; + + /// Type of the service running the demux + typedef Service service_type; + + /// Type of the implementation class of the fiber demux. + typedef typename Service::implementation_deref_type implementation_deref_type; + + /// Type of a pointer to the implementation class of the fiber demux. + typedef typename Service::implementation_type implementation_type; + + /// Type of local ports used for demultiplexing fibers + typedef typename Service::local_port_type local_port_type; + + /// Type of remote ports used for demultiplexing fibers + typedef typename Service::remote_port_type remote_port_type; + + /// Type of the implementation class of the fiber. + typedef typename Service::fiber_impl_deref_type fiber_impl_deref_type; + + /// 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) + typedef typename Service::fiber_id fiber_id; + +private: + typedef boost::asio::fiber::detail::basic_pending_accept_operation accept_op; + typedef std::function close_handler_type; + +public: + + /// Construct a basic_fiber_demux object + /** + * This constructor creates a basic_fiber_demux object without any socket to + * demultiplex + * + * @param io_service The io_service object that the fiber will use to + * 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) + { + } + + /// 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() + { + return service_.get_io_service(); + } + + StreamSocket& socket() { return impl_->socket; } + + /// Start demultiplexing the stream socket + /** + * This function is used to initiate the demultiplexing on the stream socket + * + * @param socket The stream socket on which to demultiplex. + * + * @note Once this function has been called, the socket should not be directly + * 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"; + mtu = 60 * 1024; + } + + impl_ = implementation_deref_type::create(std::move(socket), close, mtu); + service_.fiberize(impl_); + } + + /// Bind a fiber acceptor to the given local fiber port. + /** + * 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 fib_impl The fiber acceptor implementation object. + * + * @param ec Set to indicate what error occurred, if any. + * + */ + void bind(local_port_type local_port, fiber_impl_type fib_impl, + boost::system::error_code& ec) { + service_.bind(impl_, local_port, fib_impl, ec); + } + + /// Open a fibert local port to be listened on + /** + * This function puts the local fiber port into the state where it may accept + * new connections. + * + * @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) + { + service_.listen(impl_, local_port, ec); + } + + /// Asynchronously starts an acknowledgement for a received connection + /** + * This function starts the acknowledgement for a received connection to + * complete the fiber connection handshake. + * + * @param id A reference to the fiber id + * + * @param fib_impl The implementation object of the fiber being connected + */ + void async_send_ack(fiber_impl_type fib_impl, accept_op* op) + { + service_.async_send_ack(impl_, fib_impl, op); + } + + /// Start an asynchronous connect. + /** + * 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 fib_impl The implementation object of the fiber. + * + * @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) + { + service_.async_connect(impl_, remote_port, fib_impl); + } + + /// Start an asynchronous receive. + /** + * This function is used to asynchronously receive data from a basic + * fiber. The function call always returns immediately. + * + * @param buffers One or more buffers into which the data will be received. + * Although the buffers object may be copied as necessary, ownership of the + * underlying memory blocks is retained by the caller, which must guarantee + * that they remain valid until the handler is called. + * + * @param handler The handler to be called when the receive operation + * completes. Copies will be made of the handler as required. The function + * signature of the handler must be: + * @code void handler( + * const boost::system::error_code& error, // Result of operation. + * std::size_t bytes_transferred // Number of bytes received. + * ); @endcode + * Regardless of whether the asynchronous operation completes immediately or + * not, the handler will not be invoked from within this function. Invocation + * of the handler will be performed in a manner equivalent to using + * boost::asio::io_service::post(). + */ + template + 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) + { + + 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); + } + + /// Close fiber. + /** + * This closes a fiber immediatly. It cancels all pending operations from this fiber. + */ + void close_fiber(fiber_impl_type fib_impl) + { + service_.close_fiber(impl_, fib_impl); + } + + /// Close the demultiplexer (does not close the underlaying socket) + /** + * This synchronously close the fiber demux. + */ + void close() + { + if (impl_.get()) + { + service_.close(impl_); + } + } + +private: + service_type& service_; + implementation_type impl_; + +}; +} // namespace fiber +} // namespace asio +} // namespace boost + +#include + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_BASIC_FIBER_DEMUX_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux_service.hpp b/ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux_service.hpp new file mode 100644 index 000000000..6d59f5629 --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux_service.hpp @@ -0,0 +1,283 @@ +// +// fiber/basic_fiber_demux_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_BASIC_FIBER_DEMUX_SERVICE_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_BASIC_FIBER_DEMUX_SERVICE_HPP_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include + +#include "common/boost/fiber/detail/basic_fiber_demux_impl.hpp" +#include "common/boost/fiber/detail/fiber_id.hpp" +#include "common/boost/fiber/detail/fiber_buffer.hpp" +#include "common/boost/fiber/detail/io_fiber_accept_op.hpp" + +#include + +namespace boost { +namespace asio { +namespace fiber { + +namespace detail { + template + class basic_fiber_impl; +} // namespace detail + +/// Class which implements all the demultiplexing abilities in an asio service +template +class basic_fiber_demux_service +#if defined(GENERATING_DOCUMENTATION) + : public boost::asio::io_service::service +#else + : public boost::asio::detail::service_base< + basic_fiber_demux_service > +#endif +{ +private: + /// The current service type. + typedef basic_fiber_demux_service service; + +public: + /// Type of the implementation class of the fiber. + typedef boost::asio::fiber::detail::basic_fiber_impl + fiber_impl_deref_type; + + /// Type of a pointer to the implementation class of the fiber. + typedef std::shared_ptr fiber_impl_type; + + /// Type of a remote fiber port. + typedef boost::asio::fiber::detail::fiber_id::remote_port_type remote_port_type; + + /// Type of a local fiber port. + typedef boost::asio::fiber::detail::fiber_id::local_port_type local_port_type; + + /// Type for the id class identifying fibers. + typedef boost::asio::fiber::detail::fiber_id fiber_id; + + /// Type for the buffer class used to receive packets from a fiber. + typedef boost::asio::fiber::detail::fiber_buffer fiber_buffer; + + /// Type of a pointer to the fiber buffer class + typedef boost::asio::fiber::detail::p_fiber_buffer p_fiber_buffer; + + /// Type of the headers of fiber packets. + typedef boost::asio::fiber::detail::fiber_header fiber_header; + + /// Type of the flag field in the headers of fiber packets. + typedef boost::asio::fiber::detail::fiber_header::flags_type flag_type; + + /// Type of a class used to store accepting operations (on a fiber acceptor). + typedef boost::asio::fiber::detail::basic_pending_accept_operation + accept_op; + +public: +#if defined(GENERATING_DOCUMENTATION) + static boost::asio::io_service::id id; +#endif + + /// Type of the implementation class of the fiber demux. + typedef boost::asio::fiber::detail::basic_fiber_demux_impl + implementation_deref_type; + + /// Type of a pointer to the implementation class of the fiber demux. + typedef std::shared_ptr implementation_type; + +public: + /// Construct a basic_fiber_demux_service object + /** + * This constructor creates a basic_fiber_demux_service object. + * + * @param io_service The io_service object that the fibers will use to + * dispatch handlers for any asynchronous operations performed on it. + */ + explicit basic_fiber_demux_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base(io_service), + io_service_(io_service) + { + } + + /// Destructor + ~basic_fiber_demux_service() {} + + + /// Get the io_service in charge of all asynchronous operations. + boost::asio::io_service& get_io_service() { return io_service_; } + + /// Start to demultiplex with a given demux + /** + * @param impl A pointer to the implementation of a demux. + */ + void fiberize(implementation_type impl); + + /// Bind a fiber to a fiber id and a demux + /** + * @param impl A pointer to the implementation of a demux. + * @param id A reference to a fiber id to which the fiber will be bound. + * @param fib_impl A pointer to the implementation of the fiber to be bound. + * @param ec The error code object in which any error will be stored. + */ + void bind(implementation_type impl, local_port_type local_port, + fiber_impl_type fib_impl, boost::system::error_code& ec); + + /// Check if a there is a fiber bound to a given demux and fiber id. + /** + * @param impl A pointer to the implementation of a demux. + * @param id A reference to a fiber id. + */ + bool is_bound(implementation_type impl, const fiber_id& id); + + /// Unbind any fiber bound to a given demux and id. + /** + * @param impl A pointer to the implementation of a demux. + * @param id A reference to a fiber id. + */ + void unbind(implementation_type impl, const fiber_id& id); + + /// Notify the service that a fiber acceptor is listening on a fiber port. + /** + * @param impl A pointer to the implementation of a demux. + * @param local_port The port on which the fiber acceptor is listening. + * @param ec The error code object in which any error will be stored. + */ + void listen(implementation_type impl, local_port_type local_port, + boost::system::error_code& ec); + + /// Check if a fiber acceptor is listening on a given demux and local port. + /** + * @param impl A pointer to the implementation of a demux. + * @param local_port The port on which the fiber acceptor is listening. + */ + bool is_listening(implementation_type impl, local_port_type local_port); + + /// Stop a fiber acceptor from listening on a given demux and local port. + /** + * @param impl A pointer to the implementation of a demux. + * @param local_port The port on which the fiber acceptor is listening. + */ + void stop_listening(implementation_type impl, local_port_type local_port); + + /// Start an asynchronous connect. + /** + * This function is used to asynchronously connect the fiber through a demux to + * a remote fiber port. + * + * @param impl A pointer to the implementation of the demux. + * @param remote_port The remote fiber port to connect to. + * @param fib_impl A pointer to the implementation of the fiber to connect. + */ + void async_connect(implementation_type impl, remote_port_type remote_port, + fiber_impl_type fib_impl); + + /// Start an asynchronous send of user data. + /** + * This function is used to asynchronously send data through the fiber bound to + * the given demux and fiber id. + * + * @param impl A pointer to the implementation of the demux. + * @param id The fiber id. + * @param buffers The user buffers to send. + * @param handler The user handler to execute when the data is sent. + */ + template + void async_send_data(implementation_type impl, fiber_id id, ConstBufferSequence& buffers, + Handler& handler) + { + async_send_push(impl, id, buffers, handler); + } + + /// Start an asynchronous send of user datagram. + /** + * This function is used to asynchronously send a datagram through the datagram + * fiber bound to the given demux and remote port. + * + * @param impl A pointer to the implementation of the demux. + * @param remote_port the remote port to which the datagram should be sent. + * @param fib_impl A pointer to the implementation of the datagram fiber. + * @param buffers The user buffers to send. + * @param handler The user handler to execute when the data is sent. + */ + template + void async_send_datagram(implementation_type impl, remote_port_type remote_port, + fiber_impl_type fib_impl, + ConstBufferSequence& buffers, Handler& handler) + { + async_send_dgr(impl, remote_port, fib_impl, buffers, handler); + } + + void close_fiber(implementation_type impl, fiber_impl_type fib_impl); + + void close(implementation_type impl); + + void shutdown_service(); + + void async_send_ack(implementation_type impl, fiber_impl_type fib_impl, accept_op* op); + +private: + enum + { + kFlagSyn = 1, + kFlagReset = 2, + kFlagAck = 4, + kFlagDatagram = 8, + kFlagPush = 16 + }; + void async_poll_packets(implementation_type impl); + template + void async_send_rst(implementation_type impl, fiber_id id, const Handler& handler); + void async_send_syn(implementation_type impl, fiber_id id); + + template + void async_send_push(implementation_type impl, fiber_id id, ConstBufferSequence& buffer, + Handler& handler); + + template + void async_send_dgr(implementation_type impl, remote_port_type remote_port, + fiber_impl_type fib_impl, ConstBufferSequence& buffer, + Handler& handler); + + void handle_dgr(implementation_type impl, p_fiber_buffer p_fiber_buff); + void handle_push(implementation_type impl, p_fiber_buffer p_fiber_buff); + void handle_ack(implementation_type impl, p_fiber_buffer p_fiber_buff); + void handle_syn(implementation_type impl, p_fiber_buffer p_fiber_buff); + void handle_rst(implementation_type impl, p_fiber_buffer p_fiber_buff); + + void async_push_packets(implementation_type impl); + void dispatch_buffer(implementation_type impl, p_fiber_buffer p_fiber_buff); + + local_port_type get_available_local_port(implementation_type impl); + + template + void async_send(implementation_type impl, fiber_id id, flag_type flags, + ConstBufferSequence& buffers, Handler handler, + uint8_t priority = 0); + + template + std::vector get_partial_buffer_sequence( + ConstBufferSequence buffers, size_t length); + + void close_all_fibers(implementation_type impl); + +private: + io_service& io_service_; + boost::random::mt19937 gen_; +}; + +} // namespace fiber +} // namespace asio +} // namespace boost + +#include + +#include "common/boost/fiber/basic_fiber_demux_service.ipp" + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_BASIC_FIBER_DEMUX_SERVICE_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux_service.ipp b/ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux_service.ipp new file mode 100644 index 000000000..5da1a9d3b --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/basic_fiber_demux_service.ipp @@ -0,0 +1,736 @@ +// +// fiber/basic_fiber_demux_service.ipp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_BASIC_FIBER_DEMUX_SERVICE_IMPL_IPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_BASIC_FIBER_DEMUX_SERVICE_IMPL_IPP_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#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" +#include "common/boost/fiber/detail/fiber_header.hpp" +#include "common/boost/fiber/detail/fiber_id.hpp" + +namespace boost { +namespace asio { +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; + return; + } + + BOOST_LOG_TRIVIAL(trace) << "demux: fiberizing"; + + async_poll_packets(impl); +} + +template +void basic_fiber_demux_service::bind(implementation_type impl, + local_port_type local_port, + 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()); + return; + } + + if (!local_port) { + local_port = get_available_local_port(impl); + } + + fib_impl->id.set_local_port(local_port); + fiber_id id = fib_impl->id; + + fiber_id receiving_id = id.returning_id(); + + { + 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 " + << 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"; + impl->bound[receiving_id] = fib_impl; + impl->used_ports.insert(id.local_port()); + + { + boost::recursive_mutex::scoped_lock lock_state(fib_impl->state_mutex); + fib_impl->closed = false; + } + + ec.assign(ssf::error::success, ssf::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()); + } + } +} + +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; + return false; + } + + boost::recursive_mutex::scoped_lock lock(impl->bound_mutex); + return !!impl->bound.count(id.returning_id()); +} + +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; + 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() << "," + << id.remote_port(); + + impl->bound.erase(id.returning_id()); + impl->used_ports.erase(id.local_port()); +} + +template +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()); + return; + } + + boost::recursive_mutex::scoped_lock lock(impl->listening_mutex); + + if (!impl->listening.count(local_port) && + is_bound(impl, fiber_id(0, local_port))) { + BOOST_LOG_TRIVIAL(debug) << "demux: listening on " << local_port; + + impl->listening.insert(local_port); + + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + } else if (impl->listening.count(local_port)) { + ec.assign(ssf::error::device_or_resource_busy, + ssf::error::get_ssf_category()); + } else { + ec.assign(ssf::error::protocol_error, ssf::error::get_ssf_category()); + } +} + +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; + return false; + } + + boost::recursive_mutex::scoped_lock lock(impl->listening_mutex); + return !!impl->listening.count(local_port); +} + +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; + 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; + + impl->listening.erase(local_port); + impl->used_ports.erase(local_port); +} + +template +void basic_fiber_demux_service::async_poll_packets( + implementation_type impl) { + auto p_fiber_buff = + std::make_shared(); + + /////////////////// BEGIN HANDLER /////////////////////////////// + auto dispatch_handler = [this, p_fiber_buff, impl]( + const boost::system::error_code& ec, std::size_t bytes_transferred) { + boost::recursive_mutex::scoped_lock lock(impl->closing_mutex); + if (impl->closing) { + return; + } + + if (!ec) { + this->dispatch_buffer(impl, p_fiber_buff); + this->async_poll_packets(impl); + } else { + BOOST_LOG_TRIVIAL(debug) << "demux: error in dispatch handler " << ec.value() + << ":" << ec.message() << " | " + << " " << bytes_transferred + << " bytes transferred"; + this->close(impl); + } + }; + //////////////////// END HANDLER /////////////////////////////// + + boost::recursive_mutex::scoped_lock lock(impl->closing_mutex); + if (!impl->closing) { + async_read_fiber_buffer(impl->socket, *p_fiber_buff, dispatch_handler); + } +} + +template +void basic_fiber_demux_service::async_push_packets( + implementation_type impl) { + boost::recursive_mutex::scoped_lock lock(impl->send_mutex); + const auto& toSendPriority = impl->toSendPriority.front(); + + std::function handler = [=]( + const boost::system::error_code& ec, size_t transferred_bytes) { + boost::recursive_mutex::scoped_lock lock(impl->send_mutex); + impl->toSendPriority.pop(); + impl->socket.get_io_service().post( + boost::bind(toSendPriority.handler, ec, transferred_bytes)); + if (!impl->toSendPriority.empty()) { + impl->socket.get_io_service().post(boost::bind( + &basic_fiber_demux_service::async_push_packets, this, impl)); + } + }; + + boost::recursive_mutex::scoped_lock lock2(impl->closing_mutex); + if (!impl->closing) { + 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()), + 0)); + } +} + +template +void basic_fiber_demux_service::dispatch_buffer( + implementation_type impl, p_fiber_buffer p_fiber_buff) { + const auto& header = p_fiber_buff->header(); + + const auto flags = header.flags(); + + BOOST_LOG_TRIVIAL(debug) << "demux: dispatch " << uint32_t(header.version()) << " " + << header.id().remote_port() << " " + << header.id().local_port() << " " << uint32_t(flags) + << " " << header.data_size(); + + switch (flags) { + case kFlagPush: + handle_push(impl, p_fiber_buff); + break; + case kFlagSyn: + handle_syn(impl, p_fiber_buff); + break; + case kFlagReset: + handle_rst(impl, p_fiber_buff); + break; + case kFlagAck: + handle_ack(impl, p_fiber_buff); + break; + case kFlagDatagram: + handle_dgr(impl, p_fiber_buff); + break; + default: + break; + } +} + +template +void basic_fiber_demux_service::handle_dgr(implementation_type impl, + p_fiber_buffer p_fiber_buff) { + BOOST_LOG_TRIVIAL(debug) << "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); + + if (impl->bound.count(full_id) != 0) { + if (impl->bound[full_id]->accepts_dgr) { + auto on_new_packet = impl->bound[full_id]->access_receive_dgr_handler(); + on_new_packet(p_fiber_buff->take_data(), + p_fiber_buff->header().id().local_port(), + p_fiber_buff->data_size()); + } + } else if (impl->bound.count(half_id) != 0) { + if (impl->bound[half_id]->accepts_dgr) { + auto on_new_packet = impl->bound[half_id]->access_receive_dgr_handler(); + on_new_packet(p_fiber_buff->take_data(), + p_fiber_buff->header().id().local_port(), + p_fiber_buff->data_size()); + } + } +} + +template +void basic_fiber_demux_service::handle_push(implementation_type impl, + p_fiber_buffer p_fiber_buff) { + BOOST_LOG_TRIVIAL(debug) << "demux: handle push"; + const auto& header = p_fiber_buff->header(); + boost::recursive_mutex::scoped_lock lock(impl->bound_mutex); + + if (impl->bound.count(header.id()) != 0) { + auto on_new_packet = impl->bound[header.id()]->access_receive_handler(); + on_new_packet(p_fiber_buff->take_data(), p_fiber_buff->data_size()); + } else { + async_send_rst(impl, header.id().returning_id(), + std::function([]() {})); + } +} + +template +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"; + + if (impl->bound.count(header.id())) { + auto p_fib_impl = impl->bound[header.id()]; + p_fib_impl->toggle_out(); + + boost::recursive_mutex::scoped_lock lock_state(p_fib_impl->state_mutex); + + 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())); + } + } else { + async_send_rst(impl, header.id().returning_id(), + std::function([]() {})); + } +} + +template +void basic_fiber_demux_service::handle_syn(implementation_type impl, + p_fiber_buffer p_fiber_buff) { + BOOST_LOG_TRIVIAL(debug) << "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); + + if (impl->listening.count(header.id().remote_port())) { + auto on_new_fiber = impl->bound[fiber_id(header.id().remote_port())] + ->access_accept_handler(); + io_service_.post(boost::bind(on_new_fiber, header.id().local_port())); + } else { + async_send_rst(impl, header.id().returning_id(), + std::function([]() {})); + } +} + +template +void basic_fiber_demux_service::handle_rst(implementation_type impl, + p_fiber_buffer p_fiber_buff) { + BOOST_LOG_TRIVIAL(debug) << "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); + if ((impl->bound).count(header.id())) { + auto p_fib_impl = impl->bound[header.id()]; + auto on_close = p_fib_impl->access_close_handler(); + boost::recursive_mutex::scoped_lock lock_state(p_fib_impl->state_mutex); + + p_fib_impl->closed = true; + + 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())); + } else { + p_fib_impl->set_disconnected(); + auto rst_sent = [this, impl, returning_id, p_fib_impl]() { + this->unbind(impl, returning_id); + p_fib_impl->access_close_handler()(); + }; + async_send_rst(impl, returning_id, std::move(rst_sent)); + } + } else if (p_fib_impl->disconnecting) { + p_fib_impl->set_disconnected(); + unbind(impl, returning_id); + on_close(); + } + } +} + +template +template +void basic_fiber_demux_service::async_send_push(implementation_type impl, + fiber_id id, + ConstBufferSequence& buffer, + Handler& handler) { + boost::recursive_mutex::scoped_lock lock1(impl->bound_mutex); + + if (impl->bound.count(id.returning_id())) { + auto p_fiber_impl = impl->bound[id.returning_id()]; + if (p_fiber_impl->ready_out) { + async_send(impl, id, kFlagPush, buffer, handler, p_fiber_impl->priority); + } else { + auto p_timer = std::make_shared(io_service_); + p_timer->expires_from_now(boost::posix_time::milliseconds(10)); + + auto lambda = [handler, p_timer](const boost::system::error_code&) mutable { + handler(boost::system::error_code(), 0); + }; + + p_timer->async_wait(lambda); + } + } else { + handler(boost::system::error_code(ssf::error::protocol_error, + ssf::error::get_ssf_category()), + 0); + } +} + +template +template +void basic_fiber_demux_service::async_send_dgr(implementation_type impl, + remote_port_type remote_port, + fiber_impl_type fib_impl, + ConstBufferSequence& buffer, + Handler& handler) { + boost::recursive_mutex::scoped_lock lock1(impl->bound_mutex); + + if (fib_impl->id.local_port() == 0) { + fib_impl->id.set_local_port(get_available_local_port(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(); + io_service_.post(boost::bind(handler, ec, 0)); + return; + } + } + + if (impl->bound.count(fib_impl->id.returning_id())) { + if (fib_impl->ready_out) { + async_send(impl, fiber_id(remote_port, fib_impl->id.local_port()), + kFlagDatagram, buffer, handler, fib_impl->priority); + } else { + auto p_timer = std::make_shared(io_service_); + p_timer->expires_from_now(boost::posix_time::milliseconds(10)); + + auto lambda = [handler, p_timer](const boost::system::error_code&) mutable { + handler(boost::system::error_code(), 0); + }; + + p_timer->async_wait(lambda); + } + } else { + io_service_.post(boost::bind( + handler, boost::system::error_code(ssf::error::protocol_error, + ssf::error::get_ssf_category()), + 0)); + } +} + +template +void basic_fiber_demux_service::async_send_ack(implementation_type impl, + fiber_impl_type fib_impl, + accept_op* op) { + fib_impl->toggle_in(); + + if (op) { + boost::system::error_code ec; + 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(); + 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(); + } else { + BOOST_LOG_TRIVIAL(trace) << "demux: ack sent"; + } + + op->complete(ec, 0); + }; + + boost::asio::const_buffer pre_buffer; + boost::asio::const_buffers_1 buffer(pre_buffer); + async_send(impl, fib_impl->id, kFlagAck, buffer, handler, 0); + } + } else { + boost::asio::const_buffer pre_buffer; + boost::asio::const_buffers_1 buffer(pre_buffer); + + auto lambda = [](const boost::system::error_code&, std::size_t) {}; + async_send(impl, fib_impl->id, kFlagAck, buffer, lambda, 0); + } +} + +template +void basic_fiber_demux_service::async_send_syn(implementation_type impl, + fiber_id id) { + boost::recursive_mutex::scoped_lock lock_bound(impl->bound_mutex); + // 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"; + + boost::recursive_mutex::scoped_lock lock_state(p_fib_impl->state_mutex); + + if (!p_fib_impl->connecting) { + 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(); + } else { + BOOST_LOG_TRIVIAL(trace) << "demux: syn sent"; + } + }; + + boost::asio::const_buffer pre_buffer; + boost::asio::const_buffers_1 buffer(pre_buffer); + async_send(impl, id, kFlagSyn, buffer, handler, 0); + } + } +} + +template +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"; + 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() << " : " + << ec.message(); + } else { + BOOST_LOG_TRIVIAL(trace) << "demux: rst sent " << id.local_port() << " " + << id.remote_port(); + } + + this->get_io_service().dispatch(close_handler); + }; + boost::asio::const_buffer pre_buffer; + boost::asio::const_buffers_1 buffer(pre_buffer); + + async_send(impl, id, kFlagReset, buffer, handler, 0); +} + +template +boost::asio::fiber::detail::fiber_id::local_port_type basic_fiber_demux_service< + S>::get_available_local_port(implementation_type impl) { + local_port_type new_port = (1 << 17) + 1024, + rand_port = 0; + boost::recursive_mutex::scoped_lock lock1(impl->used_ports_mutex); + + boost::random::uniform_int_distribution dist( + new_port, std::numeric_limits::max()); + for (uint32_t i = 0; i < 100; ++i) { + rand_port = dist(gen_); + if (impl->used_ports.count(rand_port) == 0) { + return rand_port; + } + } + + return 0; +} + +template +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 : " + << remote_port; + + fib_impl->id.set_remote_port(remote_port); + + boost::system::error_code ec; + + bind(impl, 0, fib_impl, ec); + + if (ec) { + auto connection_failed = [=]() { fib_impl->access_connect_handler()(ec); }; + impl->socket.get_io_service().post(connection_failed); + } else { + async_send_syn(impl, fib_impl->id); + } +} + +template +template +void basic_fiber_demux_service::async_send( + implementation_type impl, fiber_id id, + boost::asio::fiber::detail::fiber_header::flags_type flags, + ConstBufferSequence& buffers, Handler handler, uint8_t priority) { + auto buffers_size = boost::asio::buffer_size(buffers); + + 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)); + return; + } + buffers_size = impl->mtu; + } + + auto new_buffers = + get_partial_buffer_sequence(buffers, buffers_size); + + fiber_header header(id, flags, (uint16_t)buffers_size); + + auto p_fiber_buffer = std::make_shared(); + p_fiber_buffer->set_header(header); + auto raw_buffer_to_send = p_fiber_buffer->const_buffer(new_buffers); + + auto do_user_handler = [p_fiber_buffer, handler]( + const boost::system::error_code& ec, + size_t + transferred_bytes) mutable { handler(ec, + transferred_bytes - + fiber_header::pod_size()); }; + + detail::extended_raw_fiber_buffer toSend(raw_buffer_to_send, do_user_handler, + priority); + + auto do_push_packets = [this, toSend, impl]() { + boost::recursive_mutex::scoped_lock lock(impl->send_mutex); + impl->toSendPriority.push(toSend); + + if (impl->toSendPriority.size() > 1) { + return; + } + + this->async_push_packets(impl); + }; + + 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(); + + impl->socket.get_io_service().post(do_push_packets); +} + +template +template +std::vector basic_fiber_demux_service< + S>::get_partial_buffer_sequence(ConstBufferSequence buffers, + size_t length) { + std::vector new_buffers; + + for (auto& buffer : buffers) { + auto buffer_size = boost::asio::buffer_size(buffer); + if (length >= buffer_size) { + new_buffers.push_back(buffer); + length -= buffer_size; + } else { + const unsigned char* p1 = + boost::asio::buffer_cast(buffer); + new_buffers.push_back(boost::asio::const_buffer(p1, length)); + length = 0; + break; + } + } + + return new_buffers; +} + +template +void basic_fiber_demux_service::close_fiber(implementation_type impl, + fiber_impl_type fib_impl) { + + boost::recursive_mutex::scoped_lock lock_state(fib_impl->state_mutex); + + if (!fib_impl->disconnecting && !fib_impl->disconnected) { + if (fib_impl->id.remote_port() == 0) { + if (!fib_impl->closed) { + stop_listening(impl, fib_impl->id.local_port()); + unbind(impl, fib_impl->id); + + auto on_close = fib_impl->access_close_handler(); + fib_impl->closed = true; + impl->socket.get_io_service().post(on_close); + } + } else { + fib_impl->closed = true; + + if (fib_impl->connecting || fib_impl->connected) { + fib_impl->set_disconnecting(); + async_send_rst(impl, fib_impl->id, [] {}); + } + } + } +} + +template +void basic_fiber_demux_service::close_all_fibers(implementation_type impl) { + std::map fibers; + { + boost::recursive_mutex::scoped_lock lock1(impl->bound_mutex); + fibers = impl->bound; + } + + for (auto& fiber : fibers) { + close_fiber(impl, fiber.second); + } +} + +template +void basic_fiber_demux_service::close(implementation_type impl) { + if (impl) { + boost::recursive_mutex::scoped_lock lock(impl->closing_mutex); + if (!impl->closing) { + impl->closing = true; + close_all_fibers(impl); + impl->socket.get_io_service().post(impl->close_handler); + // not enough: have to close socket... + boost::system::error_code ec; + impl->socket.close(ec); + } + } +} + +template +void basic_fiber_demux_service::shutdown_service() {} + +} // namespace fiber +} // namespace asio +} // namespace boost + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_BASIC_FIBER_DEMUX_SERVICE_IMPL_IPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/datagram_fiber.hpp b/ssf-1.1.0/src/common/boost/fiber/datagram_fiber.hpp new file mode 100644 index 000000000..70026a33c --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/datagram_fiber.hpp @@ -0,0 +1,102 @@ +// +// fiber/datagram_fiber.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DATAGRAM_FIBER_HPP +#define SSF_COMMON_BOOST_ASIO_FIBER_DATAGRAM_FIBER_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include + +#include + +#include "common/boost/fiber/basic_endpoint.hpp" +#include "common/boost/fiber/basic_fiber_demux.hpp" +#include "common/boost/fiber/datagram_fiber_service.hpp" + +namespace boost { +namespace asio { +namespace fiber { + +/// Encapsulates the flags needed for datagram fiber. +/** + * The boost::asio::fiber::datagram_fiber class contains flags necessary for datagram fiber. + */ +template +class datagram_fiber +{ +public: + /// Socket type behind fiber + typedef Socket socket_type; + + /// Demultiplexer type + typedef typename boost::asio::fiber::basic_fiber_demux demux_type; + + /// The type of a datagram fiber endpoint. + typedef basic_endpoint endpoint; + + /// Construct to represent the version 1 Fiber protocol. + static datagram_fiber v1() + { + return datagram_fiber(BOOST_ASIO_OS_DEF(AF_INET)); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return BOOST_ASIO_OS_DEF(SOCK_DGRAM); + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return BOOST_ASIO_OS_DEF(IPPROTO_UDP); + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The datagram fiber type. + typedef boost::asio::basic_datagram_socket>> socket; + + /// Compare two protocols for equality. + friend bool operator==(const datagram_fiber& p1, const datagram_fiber& p2) + { + return p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const datagram_fiber& p1, const datagram_fiber& p2) + { + return p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit datagram_fiber(int protocol_family) + : family_(protocol_family) + { + } + + int family_; +}; + +} // namespace fiber +} // namespace asio +} // namespace boost + +#include + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DATAGRAM_FIBER_HPP diff --git a/ssf-1.1.0/src/common/boost/fiber/datagram_fiber_service.hpp b/ssf-1.1.0/src/common/boost/fiber/datagram_fiber_service.hpp new file mode 100644 index 000000000..5ddd2319b --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/datagram_fiber_service.hpp @@ -0,0 +1,457 @@ +// +// fiber/datagram_fiber_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DATAGRAM_FIBER_SERVICE_HPP +#define SSF_COMMON_BOOST_ASIO_FIBER_DATAGRAM_FIBER_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +# pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include +#include +#include + +#include "common/error/error.h" +#include "common/boost/fiber/detail/io_fiber_dgr_read_op.hpp" +#include "common/boost/fiber/basic_fiber_demux.hpp" +#include "common/boost/fiber/detail/fiber_id.hpp" +#include "common/boost/fiber/basic_endpoint.hpp" +#include "common/boost/fiber/detail/basic_fiber_impl.hpp" + +#include + +namespace boost { +namespace asio { +namespace fiber { + +/// Default service implementation for a datagram fiber. +template +class datagram_fiber_service +#if defined(GENERATING_DOCUMENTATION) + : public boost::asio::io_service::service +#else + : public boost::asio::detail::service_base > +#endif +{ +public: +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static boost::asio::io_service::id id; +#endif + + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Type of the demultiplexer. + typedef typename Protocol::demux_type demux_type; + + typedef boost::asio::fiber::detail::basic_fiber_impl + implementation_deref_type; + + /// Implementation type + typedef std::shared_ptr implementation_type; + + /// (Deprecated: Use native_handle_type.) The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef implementation_type native_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef implementation_type native_handle_type; +#endif + + /// Construct a new datagram fiber service for the specified io_service. + explicit datagram_fiber_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base< + datagram_fiber_service >(io_service) + { + } + + /// Construct a new datagram socket implementation. + void construct(implementation_type& impl) + { + impl = implementation_deref_type::create(); + impl->accepts_dgr = true; + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + } + + void move_assign(implementation_type& impl, + datagram_fiber_service& other_service, + implementation_type& other_impl) { + // + } + + /// Destroy a datagram socket implementation. + void destroy(implementation_type& impl) + { + } + + // Open a new datagram socket implementation. + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) + { + return ec; + } + + /// Determine whether the fiber is open. + bool is_open(const implementation_type& impl) const + { + boost::recursive_mutex::scoped_lock lock_state(impl->state_mutex); + return !impl->closed; + } + + /// Close a datagram fiber implementation. + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) + { + if (!impl->p_fib_demux) { + return ec; + } + + impl->p_fib_demux->close_fiber(impl); + return ec; + } + + /// (Deprecated: Use native_handle().) Get the native fiber implementation. + native_type native(implementation_type& impl) + { + return impl; + } + + /// Get the native socket implementation. + native_handle_type native_handle(implementation_type& impl) + { + return impl; + } + + /// Cancel all asynchronous operations associated with the datagram fiber. + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) + { + impl->cancel_operations(); + return ec; + } + + // Bind the datagram fiber to the specified local endpoint. + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) + { + impl->p_fib_demux = &(endpoint.demux()); + impl->p_fib_demux->bind(endpoint.port(), impl, ec); + + return ec; + } + + /// Start an asynchronous connect. + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, + void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + BOOST_ASIO_MOVE_ARG(ConnectHandler) handler) + { + boost::asio::detail::async_result_init< + ConnectHandler, void(boost::system::error_code)> init( + BOOST_ASIO_MOVE_CAST(ConnectHandler)(handler)); + + impl->p_fib_demux = &(peer_endpoint.demux()); + impl->id.set_remote_port(peer_endpoint.port()); + auto handler_to_post = [init]() mutable { + init.handler(boost::system::error_code()); + }; + this->get_io_service().post(handler_to_post); + return init.result.get(); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const + { + return endpoint_type(*(impl->p_fib_demux), impl->id.local_port()); + } + + /// Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const + { + return endpoint_type(*(impl->p_fib_demux), impl->id.remote_port()); + } + + /// Disable sends or receives on the datagram fiber. + boost::system::error_code shutdown(implementation_type& impl, + socket_base::shutdown_type what, + boost::system::error_code& ec) + { + return ec; + } + + /// Start an asynchronous send. + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> init( + BOOST_ASIO_MOVE_CAST(WriteHandler)(handler)); + + if (impl->id->remote_port() == 0) + { + auto handler_to_post = [init]() mutable { + init.handler( + boost::system::error_code(ssf::error::not_connected, + ssf::error::get_ssf_category()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + // Call handler immediatly if buffer size at 0 + 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()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + async_send_to(impl, + buffers, + remote_endpoint(impl, + boost::system::error_code()), + flags, + init.handler); + } + } + + return init.result.get(); + } + + /// Start an asynchronous send. + 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, + socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> init( + BOOST_ASIO_MOVE_CAST(WriteHandler)(handler)); + { + boost::recursive_mutex::scoped_lock lock_state(impl->state_mutex); + if (impl->closed) + { + impl->closed = false; + } + } + if (!impl->p_fib_demux) + { + impl->p_fib_demux = &(destination.demux()); + } + if (!impl->id.local_port() && (impl->id.remote_port() && + (impl->id.remote_port() != destination.port()))) + { + auto handler_to_post = [init]() mutable { + init.handler( + boost::system::error_code(ssf::error::not_connected, + ssf::error::get_ssf_category()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + // Call handler immediatly if buffer size at 0 + 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()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + impl->p_fib_demux->async_send_dgr(buffers, destination.port(), + impl, + init.handler); + } + } + + return init.result.get(); + } + + /// Start an asynchronous receive. + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> init( + BOOST_ASIO_MOVE_CAST(ReadHandler)(handler)); + { + boost::recursive_mutex::scoped_lock lock_state(impl->state_mutex); + if (impl->closed) + { + impl->closed = false; + } + } + if (!impl->id.local_port()) + { + auto handler_to_post = [init]() mutable { + init.handler( + boost::system::error_code(ssf::error::not_connected, + ssf::error::get_ssf_category()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + // Call handler immediatly if buffer size at 0 + 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()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + typedef detail::pending_dgr_read_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(impl->read_dgr_op_queue_mutex); + impl->read_dgr_op_queue.push(p.p); + } + p.v = p.p = 0; + impl->r_dgr_queues_handler(); + } + } + + return init.result.get(); + } + + /// Start an asynchronous receive that will get the endpoint of the sender. + 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, + socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> init( + BOOST_ASIO_MOVE_CAST(ReadHandler)(handler)); + { + boost::recursive_mutex::scoped_lock lock_state(impl->state_mutex); + if (impl->closed) + { + impl->closed = false; + } + } + if (!impl->id.local_port()) + { + auto handler_to_post = [init]() mutable { + init.handler( + boost::system::error_code(ssf::error::not_connected, + ssf::error::get_ssf_category()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + // Call handler immediatly if buffer size at 0 + 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()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + typedef detail::pending_dgr_read_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, &(sender_endpoint.port())); + + { + boost::recursive_mutex::scoped_lock lock(impl->read_dgr_op_queue_mutex); + impl->read_dgr_op_queue.push(p.p); + } + p.v = p.p = 0; + impl->r_dgr_queues_handler(); + } + } + + return init.result.get(); + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } +}; + +} // namespace fiber +} // namespace asio +} // namespace boost + +#include + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DATAGRAM_FIBER_SERVICE_HPP diff --git a/ssf-1.1.0/src/common/boost/fiber/detail/basic_fiber_demux_impl.hpp b/ssf-1.1.0/src/common/boost/fiber/detail/basic_fiber_demux_impl.hpp new file mode 100644 index 000000000..014a14d4f --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/detail/basic_fiber_demux_impl.hpp @@ -0,0 +1,119 @@ +// +// fiber/detail/basic_fiber_demux_impl.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_BASIC_FIBER_DEMUX_IMPL_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_BASIC_FIBER_DEMUX_IMPL_HPP_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include +#include + +#include "common/boost/fiber/detail/fiber_id.hpp" + +namespace boost { +namespace asio { +namespace fiber { +namespace detail { + +template +class basic_fiber_impl; + +/// Class used to handle QoS in fiber sendings +template +struct extended_buffer { + extended_buffer(const Buffer& b, const Handler& h, uint8_t p) + : buffer(b), handler(h), priority(p) {} + + bool operator<(const extended_buffer& rhs) const { + return priority < rhs.priority; + } + + Buffer buffer; + Handler handler; + uint8_t priority; +}; + +typedef extended_buffer, + std::function> extended_raw_fiber_buffer; + +/// Implementation class of the fiber demultiplexer +template +class basic_fiber_demux_impl : public std::enable_shared_from_this< + basic_fiber_demux_impl> { + private: + typedef boost::asio::fiber::detail::basic_fiber_impl + fiber_impl_deref_type; + typedef std::shared_ptr fiber_impl_type; + typedef std::map bind_map; + typedef std::set listen_set; + typedef std::set in_use_port_set; + typedef std::shared_ptr> p_impl; + + typedef std::function close_handler_type; + + private: + basic_fiber_demux_impl(StreamSocket s, close_handler_type close, size_t a_mtu) + : bound(), + listening(), + used_ports(), + socket(std::move(s)), + closing(false), + mtu(a_mtu), + close_handler(close) {} + + public: + ~basic_fiber_demux_impl() {} + + public: + static p_impl create(StreamSocket s, close_handler_type close, size_t mtu) { + return p_impl( + new basic_fiber_demux_impl(std::move(s), close, mtu)); + } + + void set_socket(StreamSocket s) { socket = std::move(s); } + + // Store the bound fibers + boost::recursive_mutex bound_mutex; + bind_map bound; + + /// Store the ports on which fibers are listening + boost::recursive_mutex listening_mutex; + listen_set listening; + + /// Store all currently used ports + boost::recursive_mutex used_ports_mutex; + in_use_port_set used_ports; + + StreamSocket socket; + + boost::recursive_mutex closing_mutex; + bool closing; + + boost::recursive_mutex send_mutex; + + /// maximum size of the payload of one packet + size_t mtu; + + close_handler_type close_handler; + + // std::priority_queue toSendPriority; + std::queue toSendPriority; +}; + +} // namespace detail +} // namespace fiber +} // namespace asio +} // namespace boost + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_BASIC_FIBER_DEMUX_IMPL_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/detail/basic_fiber_impl.hpp b/ssf-1.1.0/src/common/boost/fiber/detail/basic_fiber_impl.hpp new file mode 100644 index 000000000..82a0fc39b --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/detail/basic_fiber_impl.hpp @@ -0,0 +1,654 @@ +// +// fiber/detail/basic_fiber_impl.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_BASIC_FIBER_IMPL_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_BASIC_FIBER_IMPL_HPP_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include + +#include "common/error/error.h" +#include "common/boost/fiber/detail/fiber_id.hpp" +#include "common/boost/fiber/detail/fiber_header.hpp" +#include "common/boost/fiber/basic_fiber_demux.hpp" +#include "common/boost/fiber/detail/io_fiber_read_op.hpp" +#include "common/boost/fiber/detail/io_fiber_dgr_read_op.hpp" +#include "common/boost/fiber/detail/io_fiber_accept_op.hpp" + +namespace boost { +namespace asio { +namespace fiber { +namespace detail { + +template +struct make_asio_queue +{ + typedef boost::asio::detail::op_queue type; + typedef T* value_type; +}; + +template +struct make_queue +{ + typedef std::queue type; + typedef T value_type; +}; + +template +class basic_fiber_impl + : 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; + + /// 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; + + /// Type for an object storing the user read request + typedef boost::asio::fiber::detail::basic_pending_dgr_read_operation + dgr_read_op; + + /// Type for an object storing the user accept request + typedef boost::asio::fiber::detail::basic_pending_accept_operation + 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; + + /// Type for the structure used to store the data received + typedef boost::asio::streambuf data_queue_type; + + typedef make_queue>::type dgr_data_queue_type; + + /// Type for the queue storing the pending accept requests + typedef typename make_asio_queue::type accept_op_queue_type; + + /// Type for to store remote fiber ports (for accepting or datagrams) + typedef make_queue::type remote_port_queue_type; + + /// Type of the handler used when accepting a new fiber + typedef std::function accept_handler_type; + + /// Type of the handler used when connecting a new fiber + typedef std::function connect_handler_type; + + /// Type of the handler provided by the user when connecting a new fiber + typedef std::function + connect_user_handler_type; + + /// Type of the handler used when receiving a new packet + typedef std::function&&, std::size_t)> + 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; + + /// 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; + + /// Type of the handler used when an unknown error occurs + typedef std::function error_handler_type; + +private: + /// Constructor for a fiber implementation object + /** + * @param f_demux The demultiplexer used for this fiber + * @param remote_port The remote port to chich the fiber is to be bound + * @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: + /// Destructor + ~basic_fiber_impl() {} + +public: + /// Initialize the fiber impl by setting all its handler + void init() + { + accept_handler = [this](remote_port_type remote_port) { + { + boost::recursive_mutex::scoped_lock lock(this->port_queue_mutex); + this->port_queue.push(remote_port); + } + this->a_queues_handler(); + }; + + connect_handler = [this](boost::system::error_code ec) { + this->set_opened(); + + this->init_connect_in_out(); + + 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_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()); + cancel_operations(ec); + + BOOST_LOG_TRIVIAL(trace) << "fiber impl : close handler " + << this->id.remote_port() + << ":" << this->id.local_port(); + + this->set_closed(); + }; + + error_handler = [](boost::system::error_code) {}; + } + + /// Accessor for the 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); + }; + return lambda; + } + + + + /// Accessor for the 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); + }; + return lambda; + } + + /// Accessor for the 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) { + this->receive_handler(std::move(data), bytes_transfered); + }; + return lambda; + } + + /// Accessor for the receive handler for datagrams + 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) { + this->receive_dgr_handler(std::move(data), remote_port, bytes_transfered); + }; + return lambda; + } + + /// Accessor for the 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() + { + auto self = this->shared_from_this(); + auto lambda = [self, this]() { this->error_handler(); }; + return lambda; + } + + /// Create a new shared pointer to a fiber impl + /** + * @param f_demux The demultiplexer used for this fiber + * @param remote_port The remote port to chich the fiber is to be bound + * @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) + { + p_impl res = p_impl( + 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() + { + p_impl res = p_impl(new basic_fiber_impl()); + res->init(); + + return res; + } + + /// Handle the accept operations + /** + * @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::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()) + { + auto remote_port = port_queue.front(); + port_queue.pop(); + auto op = accept_op_queue.front(); + accept_op_queue.pop(); + op->set_remote_port(remote_port); + + op->get_p_fib()->init_accept_in_out(); + + p_fib_demux->async_send_ack(op->get_p_fib(), op); + + BOOST_LOG_TRIVIAL(debug) + << "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)); + } + } + else + { + if (!accept_op_queue.empty()) + { + auto op = accept_op_queue.front(); + accept_op_queue.pop(); + + op->complete(ec, 0); + a_queues_handler(ec); + } + } + } + + /// Handle the read operations + /** + * @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::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)) + { + 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()) + { + 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); + }; + 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)); + } + } + else + { + if (!read_op_queue.empty()) + { + auto op = read_op_queue.front(); + read_op_queue.pop(); + op->complete(ec, 0); + r_queues_handler(ec); + } + } + } + + /// Handle the read operations for datagrams + /** + * @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::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()) + { + auto op = read_dgr_op_queue.front(); + read_dgr_op_queue.pop(); + + auto remote_port = port_queue.front(); + port_queue.pop(); + + 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); + }; + 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)); + } + } + 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)); + } + } + } + + /// Cancel all pending operations immediatly + /** + * @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())) + { + r_queues_handler(ec); + a_queues_handler(ec); + } + + /// Make the fiber able to send and unable to receive + void init_accept_in_out() + { + boost::recursive_mutex::scoped_lock lock1(in_mutex); + boost::recursive_mutex::scoped_lock lock2(out_mutex); + + ready_in = false; + ready_out = true; + } + + /// Make the fiber able to send and receive + void init_connect_in_out() + { + boost::recursive_mutex::scoped_lock lock1(in_mutex); + boost::recursive_mutex::scoped_lock lock2(out_mutex); + + ready_in = true; + ready_out = true; + } + + /// Toggle the fiber ability to receive + void toggle_in() + { + boost::recursive_mutex::scoped_lock lock(in_mutex); + ready_in = !ready_in; + } + + /// Toggle the fiber ability to send + void toggle_out() + { + boost::recursive_mutex::scoped_lock lock(out_mutex); + ready_out = !ready_out; + } + + void set_opened() { + boost::recursive_mutex::scoped_lock lock_state(state_mutex); + closed = false; + } + + void set_closed() { + boost::recursive_mutex::scoped_lock lock_state(state_mutex); + closed = true; + } + + /// Set implementation in connecting state + void set_connecting() + { + boost::recursive_mutex::scoped_lock lock_state(state_mutex); + connecting = true; + connected = false; + disconnecting = false; + disconnected = false; + } + + /// Set implementation in connected state + void set_connected() + { + boost::recursive_mutex::scoped_lock lock_state(state_mutex); + connecting = false; + connected = true; + closed = false; + disconnecting = false; + disconnected = false; + } + + /// Set implementation in disconnecting state + void set_disconnecting() + { + boost::recursive_mutex::scoped_lock lock_state(state_mutex); + connecting = false; + connected = false; + disconnecting = true; + disconnected = false; + } + + /// Set implementation in disconnected state + void set_disconnected() + { + boost::recursive_mutex::scoped_lock lock_state(state_mutex); + connecting = false; + connected = false; + closed = true; + disconnecting = false; + disconnected = true; + } + + fiber_id id; + + fiber_demux_type* p_fib_demux; + + boost::recursive_mutex in_mutex; + boost::recursive_mutex out_mutex; + bool ready_in; + bool ready_out; + + uint8_t priority; + + boost::recursive_mutex state_mutex; + // States of the fiber + bool closed; + bool connecting; + bool connected; + bool disconnecting; + bool disconnected; + + boost::recursive_mutex read_op_queue_mutex; + + /// Store the pending read requests + read_op_queue_type read_op_queue; + + boost::recursive_mutex read_dgr_op_queue_mutex; + + /// Store the pending read requests for datagrams + read_dgr_op_queue_type read_dgr_op_queue; + + boost::recursive_mutex data_queue_mutex; + + /// Store the received data + data_queue_type data_queue; + + /// Store the dgr received data + dgr_data_queue_type dgr_data_queue_; + + boost::recursive_mutex accept_op_queue_mutex; + + /// Store the pending accept operation + accept_op_queue_type accept_op_queue; + + boost::recursive_mutex port_queue_mutex; + + /// Store the connecting remote port + remote_port_queue_type port_queue; + + /// Connect user handler + connect_user_handler_type connect_user_handler; + + /// Fiber accepts datagram + bool accepts_dgr; + +private: + accept_handler_type accept_handler; + connect_handler_type connect_handler; + receive_handler_type receive_handler; + receive_dgr_handler_type receive_dgr_handler; + close_handler_type close_handler; + error_handler_type error_handler; +}; + +} // namespace detail +} // namespace fiber +} // namespace asio +} // namespace boost + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_BASIC_FIBER_IMPL_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/detail/fiber_buffer.hpp b/ssf-1.1.0/src/common/boost/fiber/detail/fiber_buffer.hpp new file mode 100644 index 000000000..1cb6cd1fc --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/detail/fiber_buffer.hpp @@ -0,0 +1,231 @@ +// +// fiber/detail/fiber_buffer.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_FIBER_BUFFER_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_FIBER_BUFFER_HPP_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include + +#include "common/boost/fiber/detail/fiber_header.hpp" +#include "common/boost/fiber/detail/fiber_id.hpp" + +namespace boost { +namespace asio { +namespace fiber { +namespace detail { + +class fiber_buffer +{ +public: + /// Constructor for an empty fiber buffer of size 4096 bytes. + fiber_buffer() : data_(4096), data_bytes_transferred_(0) {} + + /// Get the header part of the fiber buffer in order to fill it. + fiber_header& header() + { + return header_; + }; + + /// Get the header part of the fiber buffer. + const fiber_header& header() const + { + return header_; + }; + + /// Get the fiber id in the header part of the fiber buffer. + const fiber_id fib_id() const { return header_.id(); } + + /// Set the header part of the fiber buffer. + /** + * @param header The header to put in the fiber buffer + */ + void set_header(const fiber_header& header) { header_ = header; } + + /// Get the data part of the fiber buffer. + const std::vector& cdata() const { return data_; } + + /// Get the data part of the fiber buffer to fill it. + std::vector& data() { return data_; } + + /// Move the received data out of the fiber buffer. + std::vector take_data() { return std::move(data_); } + + /// Set the size of the received payload. + /** + * @param effective_bytes_transferred The size of the received payload + */ + void set_bytes_transferred(std::size_t effective_bytes_transferred) + { + data_bytes_transferred_ = effective_bytes_transferred; + } + + /// Get the size of the payload received + std::size_t data_size() const { return data_bytes_transferred_; } + + /// Set the fiber id in the header part of the fiber buffer. + /** + * @param id The fiber id to set + */ + void set_id(const fiber_id& id) { header_.set_id(id); } + + /// Increase the size of the current buffer + /** + * @param new_size The new_size of the buffer + * (it has to be superior to the current size) + */ + void resize(std::size_t new_size) + { + assert(new_size > data_.size()); + data_.resize(new_size); + } + + /// Get the fiber buffer buffers for sending + /** + * @param buffers The buffers containing the payload for the fiber buffer. + */ + template + std::vector const_buffer( + ConstBufferSequence buffers) + { + std::vector buf; + + raw_ = header_.get_raw(); + buf.push_back(boost::asio::buffer((void*)&raw_, sizeof(raw_))); + + // header_.fill_const_buffer(buf); + + for (auto itor = buffers.begin(); itor != buffers.end(); ++itor) + { + buf.push_back(*itor); + } + + return buf; + } + + /// Get the fiber buffer buffer sequence for receiving. + std::vector buffer() + { + std::vector buf; + + header_.fill_buffer(buf); + buf.push_back(boost::asio::buffer(&data_, data_.size())); + + return buf; + } + +private: + fiber_header header_; + std::vector data_; + + std::size_t data_bytes_transferred_; + + fiber_header::raw_fiber_header raw_; +}; + +typedef std::shared_ptr p_fiber_buffer; + +/// Coroutine class to receive a fiber buffer from a stream +/** +* @tparam StreamSocket The stream type to receive from +* @tparam ReadHandler The type for the callback handler to call upon reception +*/ +template +class read_fiber_buf_op : public boost::asio::coroutine +{ +public: + /// Constructor + /** + * @param stream The stream to receive from + * @param buffer The fiber buffer to receive into + * @param handler The handler to be called upon reception + */ + read_fiber_buf_op(StreamSocket& stream, fiber_buffer& buffer, + ReadHandler& handler) + : stream_(stream), + buffer_(buffer), + handler_(handler), + total_transferred_(0), + data_transferred_(0) + { + } + +#include // NOLINT + void operator()(const boost::system::error_code& ec, std::size_t length) + { + auto& header = buffer_.header(); + + if (!ec) reenter(this) + { + // Receive the header + yield boost::asio::async_read(stream_, header.buffer(), + std::move(*this)); + total_transferred_ += length; + + // Resize the buffer if needed + if (header.data_size() > buffer_.cdata().size()) + { + buffer_.resize(header.data_size()); + } + + // Receive the payload + yield boost::asio::async_read( + stream_, boost::asio::buffer(buffer_.data(), header.data_size()), + std::move(*this)); + + data_transferred_ = length; + total_transferred_ += data_transferred_; + buffer_.set_bytes_transferred(data_transferred_); + buffer_.set_id(header.id()); + + handler_(ec, total_transferred_); + } + else + { + handler_(ec, total_transferred_); + } + } +#include // NOLINT + +private: + StreamSocket& stream_; + fiber_buffer& buffer_; + ReadHandler handler_; + std::size_t total_transferred_; + std::size_t data_transferred_; +}; + +/// Helper to receive a fiber packet in a fiber buffer +/** +* @tparam StreamSocket The stream type to receive from +* @tparam ReadHandler The type for the callback handler to call upon reception +* +* @param stream The stream to receive from +* @param buffer The fiber buffer to receive into +* @param handler The handler to be called upon reception +*/ +template +void async_read_fiber_buffer(StreamSocket& stream, fiber_buffer& buffer, + ReadHandler& handler) +{ + // Start the coroutine + read_fiber_buf_op(stream, buffer, handler)( + boost::system::error_code(), 0); +} + +} // namespace detail +} // namespace fiber +} // namespace asio +} // namespace boost + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_FIBER_BUFFER_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/detail/fiber_header.hpp b/ssf-1.1.0/src/common/boost/fiber/detail/fiber_header.hpp new file mode 100644 index 000000000..2c42cdbf6 --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/detail/fiber_header.hpp @@ -0,0 +1,216 @@ +// +// fiber/detail/fiber_header.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_FIBER_HEADER_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_FIBER_HEADER_HPP_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include + +#include "common/boost/fiber/detail/fiber_id.hpp" + +namespace boost { +namespace asio { +namespace fiber { +namespace detail { + +/// Class handling headers for fibers +class fiber_header +{ +public: + /// Type of the version field in the header + typedef uint8_t version_type; + + /// Type for the raw header + typedef fiber_id::raw_fiber_id raw_fiber_id; + + /// Type of the flags field in the header + typedef uint8_t flags_type; + + /// Type of the data size field in the header + typedef uint16_t data_size_type; + +public: + /// Constants used with the class + enum { fiber_verion = 1, field_number = 3 + fiber_id::field_number }; + +public: +#pragma pack(push) +#pragma pack(1) + /// Raw data structure to contain a fiber header + struct raw_fiber_header + { + /// Version field + version_type version; + + /// Raw id field + raw_fiber_id fiber_id; + + /// Flags field + flags_type flags; + + /// Data size field + data_size_type data_size; + }; +#pragma pack(pop) + + /// Get a raw header + raw_fiber_header get_raw() + { + raw_fiber_header raw; + + raw.version = version_; + raw.fiber_id = id_.get_raw(); + raw.flags = flags_; + raw.data_size = data_size_; + + return raw; + } + +public: + /// Default constructor + fiber_header() + : version_(fiber_verion), id_(0, 0), flags_(0), data_size_(0) + { + } + + /// Constructor with each port specified + /** + * @param local_port The local port field to be set in the id + * @param remote_port The remote port field to be set in the id + * @param flags The flags to set in the header + * @param data_size The data size to set in the header + */ + fiber_header(fiber_id::local_port_type local_port, + fiber_id::remote_port_type remote_port, flags_type flags, + data_size_type data_size) + : version_(fiber_verion), + id_(remote_port, local_port), + flags_(flags), + data_size_(data_size) + { + } + + /// Constructor with a fiber ID specified + /** + * @param id the fiber id to set in the header + * @param flags The flags to set in the header + * @param data_size The data size to set in the header + */ + fiber_header(fiber_id id, flags_type flags, data_size_type data_size) + : version_(fiber_verion), id_(id), flags_(flags), data_size_(data_size) + { + } + + /// Constructor with a fiber ID and no data size + /** + * @param id the fiber id to set in the header + * @param flags The flags to set in the header + */ + fiber_header(fiber_id id, flags_type flags) + : version_(fiber_verion), id_(id), flags_(flags), data_size_(0) + { + } + + /// Get the version + version_type version() const { return version_; } + + /// Get the id + fiber_id id() const { return id_; } + + /// Get the flags + flags_type flags() const { return flags_; } + + /// Get the data size + data_size_type data_size() const { return data_size_; } + + /// Set the id + /** + * @param id the fiber id to set in the header + */ + void set_id(const fiber_id& id) { id_ = id; } + + /// Set the data size + /** + * @param data_size The data size to set in the header + */ + void set_data_size(data_size_type data_size) { data_size_ = data_size; } + + /// Return the size of the header + static uint16_t pod_size() + { + return sizeof(version_type)+fiber_id::pod_size() + sizeof(flags_type)+ + sizeof(data_size_type); + } + + /// Get a buffer to receive a header + std::vector buffer() + { + std::vector buf; + + buf.push_back(boost::asio::buffer(&version_, sizeof(version_))); + id_.fill_buffer(buf); + buf.push_back(boost::asio::buffer(&flags_, sizeof(flags_))); + buf.push_back(boost::asio::buffer(&data_size_, sizeof(data_size_))); + + return buf; + } + + /// Get a buffer to send a header + std::vector const_buffer() + { + std::vector buf; + + buf.push_back(boost::asio::buffer(&version_, sizeof(version_))); + id_.fill_const_buffer(buf); + buf.push_back(boost::asio::buffer(&flags_, sizeof(flags_))); + buf.push_back(boost::asio::buffer(&data_size_, sizeof(data_size_))); + + return buf; + } + + /// Fill the given buffer vector with the fiber header fields + /** + * @param buffers The vector of buffers to fill + */ + void fill_buffer(std::vector& buffers) + { + buffers.push_back(boost::asio::buffer(&version_, sizeof(version_))); + id_.fill_buffer(buffers); + buffers.push_back(boost::asio::buffer(&flags_, sizeof(flags_))); + buffers.push_back(boost::asio::buffer(&data_size_, sizeof(data_size_))); + } + + /// Fill the given buffer vector with the fiber header fields + /** + * @param buffers The vector of buffers to fill + */ + void fill_const_buffer(std::vector& buffers) + { + buffers.push_back(boost::asio::buffer(&version_, sizeof(version_))); + id_.fill_const_buffer(buffers); + buffers.push_back(boost::asio::buffer(&flags_, sizeof(flags_))); + buffers.push_back(boost::asio::buffer(&data_size_, sizeof(data_size_))); + } + +private: + version_type version_; + fiber_id id_; + flags_type flags_; + data_size_type data_size_; +}; + +} // namespace detail +} // namespace fiber +} // namespace asio +} // namespace boost + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_FIBER_HEADER_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/detail/fiber_id.hpp b/ssf-1.1.0/src/common/boost/fiber/detail/fiber_id.hpp new file mode 100644 index 000000000..4fd074cd6 --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/detail/fiber_id.hpp @@ -0,0 +1,180 @@ +// +// fiber/detail/fiber_id.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_FIBER_ID_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_FIBER_ID_HPP_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include + +#include + +namespace boost { +namespace asio { +namespace fiber { +namespace detail { + +/// Class handling the id field of the fiber header +class fiber_id +{ +public: + /// Type for a port + typedef uint32_t port_type; + + /// Type for a remote fiber port + typedef port_type remote_port_type; + + /// Type for a local fiber port + typedef port_type local_port_type; + +public: + /// Constant used with the class + enum { field_number = 2 }; + +public: + /// Raw data structure to contain a fiber id + struct raw_fiber_id + { + /// Local port field + local_port_type local_port; + + /// Remote port field + remote_port_type remote_port; + }; + + /// Get a raw id + raw_fiber_id get_raw() + { + raw_fiber_id raw; + + raw.local_port = local_port_; + raw.remote_port = remote_port_; + + return raw; + } + +public: + /// Constructor setting only the remote port + /** + * @param remote_port The remote port to set in the id + */ + explicit fiber_id(remote_port_type remote_port) + : remote_port_(remote_port), local_port_(0) + { + } + + /// Constructor setting both ports + /** + * @param remote_port The remote port to set in the id + * @param local_port The local port to set in the id + */ + fiber_id(remote_port_type remote_port, local_port_type local_port) + : remote_port_(remote_port), local_port_(local_port) + { + } + + /// Operator< to enable std::map support + /** + * @param fib_id The fiber id to be compared to + */ + bool operator<(const fiber_id& fib_id) const + { + if (remote_port_ < fib_id.remote_port_) + { + return true; + } + else + { + return ((remote_port_ == fib_id.remote_port_) && + (local_port_ < fib_id.local_port_)); + } + } + + /// Get the return id + fiber_id returning_id() const { return fiber_id(local_port_, remote_port_); } + + /// Get the remote port + remote_port_type remote_port() const { return remote_port_; } + + /// Get the local port + local_port_type local_port() const { return local_port_; } + + /// Set the local port + /** + * @param src The local port to set in the id + */ + void set_local_port(local_port_type src) { local_port_ = src; } + + /// Set the remote port + /** + * @param dst The remote port to set in the id + */ + void set_remote_port(remote_port_type dst) { remote_port_ = dst; } + + /// Check if both port are set + bool is_set() const { return (remote_port_ != 0) && (local_port_ != 0); } + + /// Get a buffer to receive a fiber ID + std::array buffer() + { + std::array buf = { + { boost::asio::buffer(&local_port_, sizeof(local_port_)), + boost::asio::buffer(&remote_port_, sizeof(remote_port_)), } }; + return buf; + } + + /// Get a buffer to send a fiber ID + std::array const_buffer() + { + std::array buf = { + { boost::asio::buffer(&local_port_, sizeof(local_port_)), + boost::asio::buffer(&remote_port_, sizeof(remote_port_)), } }; + return buf; + } + + /// Fill the given buffer with the fiber id fields + /** + * @param buffers The vector of buffers to fill + */ + void fill_buffer(std::vector& buffers) + { + buffers.push_back(boost::asio::buffer(&local_port_, sizeof(local_port_))); + buffers.push_back(boost::asio::buffer(&remote_port_, sizeof(remote_port_))); + } + + /// Fill the given buffer with the fiber id fields + /** + * @param buffers The vector of buffers to fill + */ + void fill_const_buffer(std::vector& buffers) + { + buffers.push_back(boost::asio::buffer(&local_port_, sizeof(local_port_))); + buffers.push_back(boost::asio::buffer(&remote_port_, sizeof(remote_port_))); + } + + /// Get the size of a fiber ID + static uint16_t pod_size() + { + return sizeof(local_port_type)+sizeof(remote_port_type); + } + +private: + remote_port_type remote_port_; + local_port_type local_port_; +}; + +} // namespace detail +} // namespace fiber +} // namespace asio +} // namespace boost + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_FIBER_ID_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_accept_op.hpp b/ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_accept_op.hpp new file mode 100644 index 000000000..e8f725478 --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_accept_op.hpp @@ -0,0 +1,118 @@ +// +// fiber/detail/io_fiber_accept_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_FIBER_ACCEPT_OP_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_FIBER_ACCEPT_OP_HPP_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include + +#include +#include +#include +#include +#include +#include + +#include "common/boost/fiber/detail/io_operation.hpp" +#include "common/boost/fiber/detail/fiber_header.hpp" + +#include + +namespace boost { +namespace asio { +namespace fiber { +namespace detail { + +template +class basic_fiber_impl; + +/// 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 boost::asio::fiber::detail::basic_fiber_impl fiber_deref_impl; + typedef std::shared_ptr fiber_impl; + +public: + BOOST_ASIO_DEFINE_HANDLER_PTR(pending_accept_operation); + + /// Constructor + /** + * @param p_fib The fiber implementation of the accepted fiber + * @param p_id The fiber id of the accepted fiber + * @param handler The handler to call upon completion + */ + pending_accept_operation(fiber_impl p_fib, fiber_id* p_id, Handler& handler) + : basic_pending_accept_operation( + &pending_accept_operation::do_complete, p_fib, p_id), + handler_(BOOST_ASIO_MOVE_CAST(Handler)(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 + * @param bytes_transferred (Unused) The number of bytes handled + */ + static void do_complete(basic_pending_io_operation* base, bool destroy, + const boost::system::error_code& result_ec, + std::size_t /* bytes_transferred */) + { + 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_; +}; + +} // namespace detail +} // namespace fiber +} // namespace asio +} // namespace boost + +#include + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_FIBER_ACCEPT_OP_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_dgr_read_op.hpp b/ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_dgr_read_op.hpp new file mode 100644 index 000000000..fee1651f6 --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_dgr_read_op.hpp @@ -0,0 +1,148 @@ +// +// fiber/detail/io_fiber_read_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_FIBER_DGR_READ_OP_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_FIBER_DGR_READ_OP_HPP_ + +#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 "common/boost/fiber/detail/io_operation.hpp" +#include "common/boost/fiber/detail/fiber_id.hpp" + +#include + +namespace boost { +namespace asio { +namespace fiber { +namespace detail { + +/// Class to store buffers and read datagram operations on a fiber +/** +* @tparam MutableBufferSequence The type of the sequence of mutable buffers used +* @tparam Handler The type of the handler to be called upon completion +*/ +template +class pending_dgr_read_operation : public basic_pending_dgr_read_operation +{ + public: + BOOST_ASIO_DEFINE_HANDLER_PTR(basic_pending_dgr_read_operation); + + /// Constructor + /** + * @param buffers The reading buffers + * @param handler The handler to call upon completion + */ + pending_dgr_read_operation(const MutableBufferSequence& buffers, Handler& handler) + : basic_pending_dgr_read_operation(&pending_dgr_read_operation::do_complete, + &pending_dgr_read_operation::do_fill_buffers, + nullptr), + buffers_(buffers), + handler_(handler) + { + } + + /// Constructor + /** + * @param buffers The reading buffers + * @param handler The handler to call upon completion + * @param p_remote_port A pointer to the remote port from where the data was + * received + */ + pending_dgr_read_operation(const MutableBufferSequence& buffers, + Handler& handler, + fiber_id::remote_port_type* p_remote_port) + : basic_pending_dgr_read_operation( + &pending_dgr_read_operation::do_complete, + &pending_dgr_read_operation::do_fill_buffers, + p_remote_port), + buffers_(buffers), + handler_(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 + * @param bytes_transferred The number of bytes handled + */ + static void do_complete(basic_pending_io_operation* base, bool destroy, + const boost::system::error_code& result_ec, + std::size_t bytes_transferred) + { + boost::system::error_code ec(result_ec); + + // Take ownership of the operation object. + pending_dgr_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, bytes_transferred); + 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; + } + } + + /// Implementation of the filling buffer callback + /** + * @param base A pointer to the base class + * @param stream The buffer to copy the data to + */ + static size_t do_fill_buffers(basic_pending_io_operation* base, + const std::vector& data) + { + pending_dgr_read_operation* o(static_cast(base)); + return boost::asio::buffer_copy(o->buffers_, boost::asio::buffer(data)); + } + + private: + boost::asio::detail::consuming_buffers buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace fiber +} // namespace asio +} // namespace boost + +#include + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_FIBER_DGR_READ_OP_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_read_op.hpp b/ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_read_op.hpp new file mode 100644 index 000000000..9cc9e4203 --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/detail/io_fiber_read_op.hpp @@ -0,0 +1,162 @@ +// +// fiber/detail/io_fiber_read_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_FIBER_READ_OP_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_FIBER_READ_OP_HPP_ + +#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 "common/boost/fiber/detail/io_operation.hpp" +#include "common/boost/fiber/detail/fiber_id.hpp" + +#include + +namespace boost { +namespace asio { +namespace fiber { +namespace detail { + +/// Class to store buffers and read operations on a fiber +/** +* @tparam MutableBufferSequence The type of the sequence of mutable buffers used +* @tparam Handler The type of the handler to be called upon completion +*/ +template +class pending_read_operation : public basic_pending_read_operation +{ +public: + BOOST_ASIO_DEFINE_HANDLER_PTR(pending_read_operation); + + /// Constructor + /** + * @param buffers The reading buffers + * @param handler The handler to call upon completion + */ + pending_read_operation(const MutableBufferSequence& buffers, Handler& handler) + : basic_pending_read_operation(&pending_read_operation::do_complete, + &pending_read_operation::do_fill_buffers, + nullptr), + buffers_(buffers), + handler_(handler) + { + } + + /// Constructor + /** + * @param buffers The reading buffers + * @param handler The handler to call upon completion + * @param p_remote_port A pointer to the remote port from where the data was + * received + */ + pending_read_operation(const MutableBufferSequence& buffers, + Handler& handler, + fiber_id::remote_port_type* p_remote_port) + : basic_pending_read_operation(&pending_read_operation::do_complete, + &pending_read_operation::do_fill_buffers, + p_remote_port), + buffers_(buffers), + handler_(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 + * @param bytes_transferred The number of bytes handled + */ + static void do_complete(basic_pending_io_operation* base, bool destroy, + const boost::system::error_code& result_ec, + std::size_t bytes_transferred) + { + 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, bytes_transferred); + 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; + } + } + + /// Implementation of the filling buffer callback + /** + * @param base A pointer to the base class + * @param stream The buffer to copy the data to + */ + static size_t do_fill_buffers(basic_pending_io_operation* base, + boost::asio::streambuf& stream) + { + pending_read_operation* o(static_cast(base)); + + size_t copied = 0; + + for (auto it_buffer = o->buffers_.begin(); it_buffer != o->buffers_.end(); + ++it_buffer) + { + size_t just_copied = boost::asio::buffer_copy(*it_buffer, stream.data()); + stream.consume(just_copied); + copied += just_copied; + if (!stream.size()) + { + break; + } + } + + return copied; + } + +private: + boost::asio::detail::consuming_buffers buffers_; + Handler handler_; +}; + +} // namespace detail +} // namespace fiber +} // namespace asio +} // namespace boost + +#include + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_FIBER_READ_OP_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/detail/io_operation.hpp b/ssf-1.1.0/src/common/boost/fiber/detail/io_operation.hpp new file mode 100644 index 000000000..fc295a223 --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/detail/io_operation.hpp @@ -0,0 +1,277 @@ +// +// fiber/detail/io_operation.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_OPERATION_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_OPERATION_HPP_ + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#include +#include + +#include + +#include "common/boost/fiber/detail/fiber_id.hpp" + +namespace boost { +namespace asio { +namespace fiber { +namespace detail { + +template +class basic_fiber_impl; + +/// Base class for pending io operations +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 + * @param bytes_transferred The number of bytes read or written + */ + void complete(const boost::system::error_code& ec, + std::size_t bytes_transferred) + { + auto destroy = false; + func_(this, destroy, ec, bytes_transferred); + } + + void destroy() + { + auto destroy = true; + func_(this, destroy, boost::system::error_code(), 0); + } + +protected: + typedef void(*func_type)(basic_pending_io_operation*, bool, + const boost::system::error_code& ec, std::size_t); + + 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 fiber accept operations +template +class basic_pending_accept_operation : public basic_pending_io_operation +{ +private: + typedef boost::asio::fiber::detail::basic_fiber_impl fiber_deref_impl; + typedef std::shared_ptr fiber_impl; + +protected: + /// Constructor + /** + * @param func The completion handler + * @param p_fib The implementation of the accepted fiber + * @param p_fib_id The id of the accepted fiber + */ + basic_pending_accept_operation(basic_pending_io_operation::func_type func, + fiber_impl p_fib, fiber_id* p_fib_id) + : basic_pending_io_operation(func), + p_fib_(p_fib), + p_id_(p_fib_id) + { + } + +public: + /// Set the id of the accepted fiber + /** + * @param remote_port The remote port to set in the id + */ + void set_remote_port(fiber_id::remote_port_type remote_port) + { + p_id_->set_remote_port(remote_port); + } + + /// Get the accepted fiber implementation + fiber_impl get_p_fib() { return p_fib_; } + +private: + fiber_impl p_fib_; + fiber_id* p_id_; +}; + +//----------------------------------------------------------------------------- + +/// Base class for pending stream read operations +class basic_pending_read_operation : public basic_pending_io_operation +{ +private: + typedef size_t(*fill_buffer_func_type)(basic_pending_io_operation*, + boost::asio::streambuf&); + +protected: + /// Constructor + /** + * @param func The completion handler + * @param fill_buffer_func The function to fill the buffer with read data + * @param p_remote_port (Optional) The remote port from where the data was + * received + */ + basic_pending_read_operation( + basic_pending_io_operation::func_type func, + fill_buffer_func_type fill_buffer_func, + fiber_id::remote_port_type* p_remote_port = nullptr) + : basic_pending_io_operation(func), + fill_buffer_func_(fill_buffer_func), + p_remote_port_(p_remote_port) + { + } + +public: + /// Function called to fill the buffer with read data + /** + * @param buf The buffer where to store the read data + */ + size_t fill_buffers(boost::asio::streambuf& buf) + { + if (fill_buffer_func_) + { + return fill_buffer_func_(this, buf); + } + else + { + return 0; + } + } + + /// Set the remote port + /** + * @param remote_port The remote port value to set + */ + bool set_remote_port(const fiber_id::remote_port_type& remote_port) + { + if (p_remote_port_) + { + *p_remote_port_ = remote_port; + return true; + } + else + { + return false; + } + } + +private: + fill_buffer_func_type fill_buffer_func_; + fiber_id::remote_port_type* p_remote_port_; +}; + +/// Base class for pending dgr read operations +class basic_pending_dgr_read_operation : public basic_pending_io_operation +{ +private: + typedef size_t(*fill_dgr_buffer_func_type)(basic_pending_io_operation*, + const std::vector&); + +protected: + /// Constructor + /** + * @param func The completion handler + * @param fill_buffer_func The function to fill the buffer with read data + * @param p_remote_port (Optional) The remote port from where the data was + * received + */ + basic_pending_dgr_read_operation( + basic_pending_io_operation::func_type func, + fill_dgr_buffer_func_type fill_dgr_buffer_func, + fiber_id::remote_port_type* p_remote_port = nullptr) + : basic_pending_io_operation(func), + fill_dgr_buffer_func_(fill_dgr_buffer_func), + p_remote_port_(p_remote_port) { + } + +public: + /// Function called to fill the buffer with read data + /** + * @param buf The buffer where to store the read data + */ + size_t fill_buffers(const std::vector& buf) { + if (fill_dgr_buffer_func_) { + return fill_dgr_buffer_func_(this, buf); + } else { + return 0; + } + } + + /// Set the remote port + /** + * @param remote_port The remote port value to set + */ + bool set_remote_port(const fiber_id::remote_port_type& remote_port) { + if (p_remote_port_) { + *p_remote_port_ = remote_port; + return true; + } else { + return false; + } + } + +private: + fill_dgr_buffer_func_type fill_dgr_buffer_func_; + fiber_id::remote_port_type* p_remote_port_; +}; + +/// Base class for pending ssl read operations +class basic_pending_ssl_operation : public basic_pending_io_operation +{ +private: + typedef size_t(*fill_buffer_func_type)(basic_pending_ssl_operation*, + boost::asio::streambuf&); + +protected: + /// Constructor + /** + * @param func The completion handler + * @param fill_buffer_func The function to fill the buffer with read data + */ + basic_pending_ssl_operation(basic_pending_io_operation::func_type func, + fill_buffer_func_type fill_buffer_func) + : basic_pending_io_operation(func), + fill_buffer_func_(fill_buffer_func) + { + } + +public: + /// Function called to fill the buffer with read data + /** + * @param buf The buffer where to store the read data + */ + size_t fill_buffers(boost::asio::streambuf& buf) + { + if (fill_buffer_func_) + { + return fill_buffer_func_(this, buf); + } + else + { + return 0; + } + } + +private: + fill_buffer_func_type fill_buffer_func_; +}; + +} // namespace detail +} // namespace fiber +} // namespace asio +} // namespace boost + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_OPERATION_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/detail/io_ssl_read_op.hpp b/ssf-1.1.0/src/common/boost/fiber/detail/io_ssl_read_op.hpp new file mode 100644 index 000000000..bfa472f86 --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/detail/io_ssl_read_op.hpp @@ -0,0 +1,139 @@ +// +// fiber/detail/io_ssl_read_op.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_SSL_READ_OP_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_SSL_READ_OP_HPP_ + +#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 "common/boost/fiber/detail/io_operation.hpp" + +#include + +namespace boost { +namespace asio { +namespace fiber { +namespace detail { + +/// Class to store buffers and read operations on ssl stream +/** +* @tparam MutableBufferSequence The type of the sequence of mutable buffers used +* @tparam Handler The type of the handler to be called upon completion +*/ +template +class pending_ssl_read_operation : public basic_pending_ssl_operation +{ +public: + BOOST_ASIO_DEFINE_HANDLER_PTR(pending_ssl_read_operation); + + /// Constructor + /** + * @param buffers The reading buffers + * @param handler The handler to call upon completion + */ + pending_ssl_read_operation(const MutableBufferSequence& buffers, + const Handler& handler) + : basic_pending_ssl_operation(&pending_ssl_read_operation::do_complete, + &pending_ssl_read_operation::do_fill_buffers), + buffers_(buffers), + handler_(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 + * @param bytes_transferred The number of bytes handled + */ + static void do_complete(basic_pending_io_operation* base, + bool destroy, + const boost::system::error_code& result_ec, + std::size_t bytes_transferred) + { + boost::system::error_code ec(result_ec); + + // Take ownership of the operation object. + pending_ssl_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, bytes_transferred); + 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_buffers(basic_pending_ssl_operation* base, + boost::asio::streambuf& stream) + { + pending_ssl_read_operation* o(static_cast(base)); + + size_t copied = 0; + + for (auto it_buffer = o->buffers_.begin(); it_buffer != o->buffers_.end(); + ++it_buffer) + { + size_t just_copied = boost::asio::buffer_copy(*it_buffer, stream.data()); + stream.consume(just_copied); + copied += just_copied; + if (!stream.size()) + { + break; + } + } + + return copied; + } + +private: + boost::asio::detail::consuming_buffers buffers_; + Handler handler_; +}; + +} // detail +} // fiber +} // asio +} // boost + + +#include + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_DETAIL_IO_SSL_READ_OP_HPP_ diff --git a/ssf-1.1.0/src/common/boost/fiber/fiber_acceptor_service.hpp b/ssf-1.1.0/src/common/boost/fiber/fiber_acceptor_service.hpp new file mode 100644 index 000000000..56dff2d2d --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/fiber_acceptor_service.hpp @@ -0,0 +1,220 @@ +// +// fiber/fiber_acceptor_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_FIBER_ACCEPTOR_SERVICE_HPP +#define SSF_COMMON_BOOST_ASIO_FIBER_FIBER_ACCEPTOR_SERVICE_HPP + +#if defined(_MSC_VER) && (_MSC_VER >= 1200) +#pragma once +#endif // defined(_MSC_VER) && (_MSC_VER >= 1200) + +#include +#include +#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" +#include "common/boost/fiber/detail/basic_fiber_impl.hpp" + +namespace boost { +namespace asio { +namespace fiber { + +/// Default service implementation for a fiber acceptor. +template +class fiber_acceptor_service +#if defined(GENERATING_DOCUMENTATION) + : public boost::asio::io_service::service +#else + : public boost::asio::detail::service_base< + fiber_acceptor_service > +#endif + { + public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Type of the demultiplexer. + typedef typename Protocol::demux_type demux_type; + + typedef boost::asio::fiber::detail::basic_fiber_impl< + typename Protocol::socket_type> implementation_deref_type; + + /// Implementation type + typedef std::shared_ptr implementation_type; + +/// (Deprecated: Use native_handle_type.) The native fiber type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef implementation_type native_type; +#endif + +/// The native fiber type +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef implementation_type native_handle_type; +#endif + + /// Construct a new fiber acceptor service for the specified io_service. + explicit fiber_acceptor_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base >( + io_service) {} + + /// Construct a new fiber acceptor implementation. + void construct(implementation_type& impl) { + impl = implementation_deref_type::create(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + construct(other); + } + + void move_assign(implementation_type& impl, + fiber_acceptor_service& other_service, + implementation_type& other_impl) { + // + } + + /// Destroy a fiber acceptor implementation. + void destroy(implementation_type& impl) {} + + /// Open a new fiber acceptor implementation. + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) { + return ec; + } + + /// Determine whether the acceptor is open. + bool is_open(const implementation_type& impl) const { + boost::recursive_mutex::scoped_lock lock_state(impl->state_mutex); + return !impl->closed; + } + + /// Cancel all asynchronous operations associated with the acceptor. + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) { + impl->cancel_operations(); + return ec; + } + + /// Bind the fiber acceptor to the specified local endpoint. + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) { + impl->p_fib_demux = &(endpoint.demux()); + impl->p_fib_demux->bind(endpoint.port(), impl, ec); + + return ec; + } + + /// Place the fiber acceptor into the state where it will listen for new + /// connections. + boost::system::error_code listen(implementation_type& impl, int backlog, + boost::system::error_code& ec) { + impl->p_fib_demux->listen(impl->id.local_port(), ec); + return ec; + } + + /// Close a fiber acceptor implementation. + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) { + if (!impl->p_fib_demux) { + return ec; + } + + impl->p_fib_demux->close_fiber(impl); + return ec; + } + + /// (Deprecated: Use native_handle().) Get the native acceptor implementation. + native_type native(implementation_type& impl) { return impl; } + + /// Get the native acceptor implementation. + native_handle_type native_handle(implementation_type& impl) { return impl; } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const { + return endpoint_type(*(impl->p_fib_demux), impl->id.local_port()); + } + + /// Start an asynchronous accept. + template + BOOST_ASIO_INITFN_RESULT_TYPE(AcceptHandler, void(boost::system::error_code)) + async_accept(implementation_type& impl, + basic_socket& peer, + endpoint_type* peer_endpoint, + BOOST_ASIO_MOVE_ARG(AcceptHandler) handler, + typename enable_if< + is_convertible::value>::type* = 0) { + boost::asio::detail::async_result_init + 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())); + }; + this->get_io_service().post(handler_to_post); + + return init.result.get(); + } + } + + boost::system::error_code ec; + + auto fiber_impl = peer.native_handle(); + 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(); + + typedef detail::pending_accept_operation< + AcceptHandler, typename Protocol::socket_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(peer.native_handle(), &(peer.native_handle()->id), init.handler); + { + boost::recursive_mutex::scoped_lock lock(impl->accept_op_queue_mutex); + impl->accept_op_queue.push(p.p); + } + p.v = p.p = 0; + impl->a_queues_handler(); + + return init.result.get(); + } + + private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() {} +}; + +} // namespace fiber +} // namespace asio +} // namespace boost + +#include + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_FIBER_ACCEPTOR_SERVICE_HPP diff --git a/ssf-1.1.0/src/common/boost/fiber/stream_fiber.hpp b/ssf-1.1.0/src/common/boost/fiber/stream_fiber.hpp new file mode 100644 index 000000000..2b766d32d --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/stream_fiber.hpp @@ -0,0 +1,115 @@ +// +// fiber/stream_fiber.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_STREAM_FIBER_HPP +#define SSF_COMMON_BOOST_ASIO_FIBER_STREAM_FIBER_HPP + +#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 "common/boost/fiber/basic_endpoint.hpp" +#include "common/boost/fiber/basic_fiber_demux.hpp" +#include "common/boost/fiber/stream_fiber_service.hpp" +#include "common/boost/fiber/fiber_acceptor_service.hpp" + +namespace boost { +namespace asio { +namespace fiber { + +/// Encapsulates the flags needed for stream fiber. +/** +* The boost::asio::fiber::stream_fiber class contains flags necessary for stream fiber. +*/ +template +class stream_fiber +{ +public: + /// Socket type behind fiber + typedef Socket socket_type; + + /// Demultiplexer type + typedef typename boost::asio::fiber::basic_fiber_demux demux_type; + + /// The type of a stream fiber endpoint. + typedef basic_endpoint endpoint; + + /// Construct to represent the version 1 Fiber protocol. + static stream_fiber v1() + { + return stream_fiber(BOOST_ASIO_OS_DEF(AF_INET)); + } + + /// Obtain an identifier for the type of the protocol. + int type() const + { + return BOOST_ASIO_OS_DEF(SOCK_STREAM); + } + + /// Obtain an identifier for the protocol. + int protocol() const + { + return BOOST_ASIO_OS_DEF(IPPROTO_TCP); + } + + /// Obtain an identifier for the protocol family. + int family() const + { + return family_; + } + + /// The stream fiber type. + typedef boost::asio::basic_stream_socket< + stream_fiber, + boost::asio::fiber::stream_fiber_service>> socket; + + /// The stream fiber acceptor type. + typedef boost::asio::basic_socket_acceptor< + stream_fiber, boost::asio::fiber::fiber_acceptor_service< + stream_fiber>> acceptor; + + /// Compare two protocols for equality. + friend bool operator==(const stream_fiber& p1, const stream_fiber& p2) + { + return p1.family_ == p2.family_; + } + + /// Compare two protocols for inequality. + friend bool operator!=(const stream_fiber& p1, const stream_fiber& p2) + { + return p1.family_ != p2.family_; + } + +private: + // Construct with a specific family. + explicit stream_fiber(int protocol_family) + : family_(protocol_family) + { + } + + int family_; +}; + +} // namespace fiber +} // namespace asio +} // namespace boost + +#include + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_STREAM_FIBER_HPP diff --git a/ssf-1.1.0/src/common/boost/fiber/stream_fiber_service.hpp b/ssf-1.1.0/src/common/boost/fiber/stream_fiber_service.hpp new file mode 100644 index 000000000..78e0174a1 --- /dev/null +++ b/ssf-1.1.0/src/common/boost/fiber/stream_fiber_service.hpp @@ -0,0 +1,339 @@ +// +// fiber/stream_fiber_service.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2014-2015 +// + +#ifndef SSF_COMMON_BOOST_ASIO_FIBER_STREAM_FIBER_SERVICE_HPP_ +#define SSF_COMMON_BOOST_ASIO_FIBER_STREAM_FIBER_SERVICE_HPP_ + +#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 "common/boost/fiber/detail/io_fiber_read_op.hpp" + +#include "common/boost/fiber/basic_fiber_demux.hpp" +#include "common/boost/fiber/detail/fiber_id.hpp" +#include "common/boost/fiber/basic_endpoint.hpp" +#include "common/boost/fiber/detail/basic_fiber_impl.hpp" + +#include + +namespace boost { +namespace asio { +namespace fiber { + +/// Default service implementation for fiber +template +class stream_fiber_service +#if defined(GENERATING_DOCUMENTATION) + : public boost::asio::io_service::service +#else + : public boost::asio::detail::service_base > +#endif +{ +#if defined(GENERATING_DOCUMENTATION) + /// The unique service identifier. + static boost::asio::io_service::id id; +#endif + +public: + /// The protocol type. + typedef Protocol protocol_type; + + /// The endpoint type. + typedef typename Protocol::endpoint endpoint_type; + + /// Type of the demultiplexer. + typedef typename Protocol::demux_type demux_type; + + /// Deref of the implementation type + typedef boost::asio::fiber::detail::basic_fiber_impl + implementation_deref_type; + + /// Implementation type + typedef std::shared_ptr implementation_type; + + /// (Deprecated: Use native_handle_type.) The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_type; +#else + typedef implementation_type native_type; +#endif + + /// The native socket type. +#if defined(GENERATING_DOCUMENTATION) + typedef implementation_defined native_handle_type; +#else + typedef implementation_type native_handle_type; +#endif + +public: + /// Construct a stream_fiber_service object + /** + * This constructor creates a stream_fiber_service object. + * + * @param io_service The io_service object that the fibers will use to + * dispatch handlers for any asynchronous operations performed on it. + */ + explicit stream_fiber_service(boost::asio::io_service& io_service) + : boost::asio::detail::service_base>(io_service) {} + + /// Construct a new fiber implementation. + void construct(implementation_type& impl) + { + impl = implementation_deref_type::create(); + } + + void move_construct(implementation_type& impl, implementation_type& other) { + impl = std::move(other); + construct(other); + } + + void move_assign(implementation_type& impl, + stream_fiber_service& other_service, + implementation_type& other_impl) { + // + } + + /// Destroy a fiber implementation. + void destroy(implementation_type& impl) { impl.reset(); } + + /// Open a stream socket. + boost::system::error_code open(implementation_type& impl, + const protocol_type& protocol, + boost::system::error_code& ec) + { + return ec; + } + + /// Determine whether the fiber is open. + bool is_open(const implementation_type& impl) const + { + boost::recursive_mutex::scoped_lock lock_state(impl->state_mutex); + return !impl->closed; + } + + /// Close a fiber implementation. + boost::system::error_code close(implementation_type& impl, + boost::system::error_code& ec) + { + impl->p_fib_demux->close_fiber(impl); + return ec; + } + + /// Cancel all asynchronous operations associated with the stream fiber. + boost::system::error_code cancel(implementation_type& impl, + boost::system::error_code& ec) + { + impl->cancel_operations(); + return ec; + } + + /// Bind the fiber to the specified local/remote endpoint. + boost::system::error_code bind(implementation_type& impl, + const endpoint_type& endpoint, + boost::system::error_code& ec) + { + impl->p_fib_demux = &(endpoint.demux()); + impl->p_fib_demux->bind(endpoint.port(), impl, ec); + + return ec; + } + + /// Start an asynchronous connect. + template + BOOST_ASIO_INITFN_RESULT_TYPE(ConnectHandler, + void(boost::system::error_code)) + async_connect(implementation_type& impl, + const endpoint_type& peer_endpoint, + BOOST_ASIO_MOVE_ARG(ConnectHandler) handler) + { + boost::asio::detail::async_result_init + init(BOOST_ASIO_MOVE_CAST(ConnectHandler)(handler)); + + impl->connect_user_handler = init.handler; + impl->p_fib_demux = &(peer_endpoint.demux()); + impl->p_fib_demux->async_connect(peer_endpoint.port(), impl); + + return init.result.get(); + } + + /// Get the local endpoint. + endpoint_type local_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const + { + return endpoint_type(*(impl->p_fib_demux), impl->id.local_port()); + } + + /// Get the remote endpoint. + endpoint_type remote_endpoint(const implementation_type& impl, + boost::system::error_code& ec) const + { + return endpoint_type(*(impl->p_fib_demux), impl->id.remote_port()); + } + + /// (Deprecated: Use native_handle().) Get the native socket implementation. + native_type native(implementation_type& impl) + { + return impl; + } + + /// Get the native socket implementation. + native_handle_type native_handle(implementation_type& impl) + { + return impl; + } + + /// Start an asynchronous send. + template + BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler, + void(boost::system::error_code, std::size_t)) + async_send(implementation_type& impl, + const ConstBufferSequence& buffers, + socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(WriteHandler) handler) + { + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> init( + BOOST_ASIO_MOVE_CAST(WriteHandler)(handler)); + + if (!impl) + { + auto handler_to_post = [init]() mutable { + init.handler( + boost::system::error_code(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + + { + boost::recursive_mutex::scoped_lock lock_state(impl->state_mutex); + if (!impl->connected) { + auto handler_to_post = [init]() mutable { + init.handler( + boost::system::error_code(ssf::error::not_connected, + ssf::error::get_ssf_category()), + 0); + }; + this->get_io_service().post(handler_to_post); + return init.result.get(); + } + } + + // Call handler immediatly if buffer size at 0 + 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()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + // Service behaviour + impl->p_fib_demux->async_send(buffers, impl->id, init.handler); + } + } + + return init.result.get(); + } + + /// Start an asynchronous receive. + template + BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, + void(boost::system::error_code, std::size_t)) + async_receive(implementation_type& impl, + const MutableBufferSequence& buffers, + socket_base::message_flags flags, + BOOST_ASIO_MOVE_ARG(ReadHandler) handler) + { + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> init( + BOOST_ASIO_MOVE_CAST(ReadHandler)(handler)); + + { + boost::recursive_mutex::scoped_lock lock_state(impl->state_mutex); + if (!impl->connected) { + auto handler_to_post = [init]() mutable { + init.handler( + boost::system::error_code(ssf::error::not_connected, + ssf::error::get_ssf_category()), + 0); + }; + this->get_io_service().post(handler_to_post); + return init.result.get(); + } + } + + // Call handler immediatly if buffer size at 0 + 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()), + 0); + }; + this->get_io_service().post(handler_to_post); + } + else + { + typedef detail::pending_read_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(impl->read_op_queue_mutex); + impl->read_op_queue.push(p.p); + } + p.v = p.p = 0; + impl->r_queues_handler(); + } + + return init.result.get(); + } + + /// Disable sends or receives on the fiber. + boost::system::error_code shutdown(implementation_type& impl, + socket_base::shutdown_type what, + boost::system::error_code& ec) + { + return ec; + } + +private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } +}; + +} // namespace fiber +} // namespace asio +} // namespace boost + +#endif // SSF_COMMON_BOOST_ASIO_FIBER_STREAM_FIBER_SERVICE_HPP_ diff --git a/ssf-1.1.0/src/common/config/config.cpp b/ssf-1.1.0/src/common/config/config.cpp new file mode 100644 index 000000000..8e1c7ff0a --- /dev/null +++ b/ssf-1.1.0/src/common/config/config.cpp @@ -0,0 +1,67 @@ +#include "common/config/config.h" + +#include + +#include +#include +#include +#include + +#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; + + 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(); + } + } + } + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); + + return config; + } 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()); + + return ssf::Config(); + } +} diff --git a/ssf-1.1.0/src/common/config/config.h b/ssf-1.1.0/src/common/config/config.h new file mode 100644 index 000000000..691acdc83 --- /dev/null +++ b/ssf-1.1.0/src/common/config/config.h @@ -0,0 +1,35 @@ +#ifndef SSF_COMMON_CONFIG_CONFIG_H_ +#define SSF_COMMON_CONFIG_CONFIG_H_ + +#include + +#include + +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; +}; + +Config LoadConfig(const std::string& filepath, boost::system::error_code& ec); + +} // ssf + +#endif // SSF_COMMON_CONFIG_CONFIG_H_ diff --git a/ssf-1.1.0/src/common/error/error.cpp b/ssf-1.1.0/src/common/error/error.cpp new file mode 100644 index 000000000..014c3411e --- /dev/null +++ b/ssf-1.1.0/src/common/error/error.cpp @@ -0,0 +1,70 @@ +#include "error.h" + +const char* ssf::error::detail::ssf_category::name() const + BOOST_SYSTEM_NOEXCEPT { + return "ssf"; +} + +std::string ssf::error::detail::ssf_category::message(int value) const { + switch (value) { + case error::success: + return "success"; + break; + case error::interrupted: + return "connection interrupted"; + break; + case error::bad_file_descriptor: + return "bad file descriptor"; + break; + case error::device_or_resource_busy: + return "device or resource busy"; + break; + case error::invalid_argument: + return "invalid argument"; + break; + case error::not_a_socket: + return "no socket could be created"; + break; + case error::broken_pipe: + return "broken pipe"; + break; + case error::filename_too_long: + return "filename too long"; + break; + case error::message_too_long: + return "message too long"; + break; + case error::connection_aborted: + return "connection aborted"; + break; + case error::connection_refused: + return "connection refused"; + break; + case error::connection_reset: + return "connection reset"; + break; + case error::not_connected: + return "not connected"; + break; + case error::protocol_error: + return "protocol error"; + break; + case error::wrong_protocol_type: + return "wrong protocol type"; + break; + case error::operation_canceled: + return "operation canceled"; + break; + case error::service_not_found: + return "service not found"; + break; + case error::service_not_started: + return "service not started"; + break; + case error::out_of_range: + return "out of range"; + break; + default: + return "ssf error"; + } +} \ No newline at end of file diff --git a/ssf-1.1.0/src/common/error/error.h b/ssf-1.1.0/src/common/error/error.h new file mode 100644 index 000000000..dfc591228 --- /dev/null +++ b/ssf-1.1.0/src/common/error/error.h @@ -0,0 +1,50 @@ +#ifndef SSF_COMMON_ERROR_ERROR_H_ +#define SSF_COMMON_ERROR_ERROR_H_ + +#include + +#include +#include + +namespace ssf { +namespace error { + +enum errors { + success = boost::system::errc::success, + interrupted = boost::system::errc::interrupted, + 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, + message_too_long = boost::asio::error::basic_errors::message_size, + 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, + service_not_found = 10000, + service_not_started = 10001, + out_of_range = 10002 +}; + +namespace detail { +class ssf_category : public boost::system::error_category { + public: + const char* name() const BOOST_SYSTEM_NOEXCEPT; + + std::string message(int value) const; +}; +} // detail + +inline const boost::system::error_category& get_ssf_category() { + static detail::ssf_category instance; + return instance; +} + +} // error +} // ssf +#endif // SSF_COMMON_ERROR_ERROR_H_ diff --git a/ssf-1.1.0/src/common/network/base_session.h b/ssf-1.1.0/src/common/network/base_session.h new file mode 100644 index 000000000..2fe839fba --- /dev/null +++ b/ssf-1.1.0/src/common/network/base_session.h @@ -0,0 +1,26 @@ +#ifndef SSF_COMMON_NETWORK_BASE_SESSION_H_ +#define SSF_COMMON_NETWORK_BASE_SESSION_H_ + +#include + +#include + +namespace ssf { +/// Base class for ActionableItem types +class BaseSession : public std::enable_shared_from_this { + public: + BaseSession() {} + virtual void start(boost::system::error_code&) = 0; + virtual void stop(boost::system::error_code&) = 0; + virtual ~BaseSession() {} + + private: + // Make non-copyable + BaseSession(const BaseSession&); + BaseSession& operator=(const BaseSession&); +}; + +typedef std::shared_ptr BaseSessionPtr; +} // ssf + +#endif // SSF_COMMON_NETWORK_BASE_SESSION_H_ diff --git a/ssf-1.1.0/src/common/network/datagram_link.h b/ssf-1.1.0/src/common/network/datagram_link.h new file mode 100644 index 000000000..58a83725d --- /dev/null +++ b/ssf-1.1.0/src/common/network/datagram_link.h @@ -0,0 +1,209 @@ +#ifndef SSF_COMMON_NETWORK_DATAGRAM_LINK_H_ +#define SSF_COMMON_NETWORK_DATAGRAM_LINK_H_ + +#include + +#include // NOLINT +#include // NOLINT +#include // NOLINT + +#include +#include +#include // NOLINT +#include // NOLINT + +#include + +#include "common/network/datagram_link_operator.h" + +namespace ssf { +//----------------------------------------------------------------------------- + +/// Asynchronous Full Duplex Datagram Forwarder +template +struct DatagramLink : public std::enable_shared_from_this< + DatagramLink> { + private: + template + struct make_queue + { + typedef std::queue type; + typedef T value_type; + }; + + typedef std::shared_ptr DatagramLinkPtr; + + /// Types for the DatagramLink manager + typedef DatagramLinkOperator 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)); + } + + /// Feed data to be sent to the "left endpoint" + void Feed(boost::asio::const_buffer buffer, size_t length) { + boost::recursive_mutex::scoped_lock lock(datagram_queue_mutex_); + + // Copy the data into the Datagram queue + Datagram dgr(length); + boost::asio::buffer_copy(boost::asio::buffer(dgr), buffer); + dgr_queue_.push(dgr); + + // Notify that new data is available + timer_.cancel(); + } + +#include // NOLINT + /// Loop that sends data to the "left endpoint" + void ExternalFeed( + const boost::system::error_code& ec = boost::system::error_code(), + std::size_t n = 0) { + boost::recursive_mutex::scoped_lock lock(datagram_queue_mutex_); + + // Stop the loop if stopping the DatagramLink + if (stopping_) { + return; + } + + 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)); + yield timer_.async_wait(boost::bind(&DatagramLink::ExternalFeed, + this->shared_from_this(), _1, 0)); + } + + // if no other error than a canceled timer has occured and that some + // data is available, we send it, else we stop + if ((!ec || ec.value() == boost::asio::error::operation_aborted) && + !dgr_queue_.empty()) { + if (l_.is_open()) { + yield l_.async_send_to( + boost::asio::buffer(dgr_queue_.front()), remote_endpoint_left_, + boost::bind(&DatagramLink::ExternalFeed, + this->shared_from_this(), _1, _2)); + dgr_queue_.pop(); + if (!auto_feeding_) { + auto_feeding_ = true; + AutoFeed(); + } + } else { + Shutdown(); + return; + } + } else { + Shutdown(); + return; + } + } + } + } +#include // NOLINT + +#include // NOLINT + /// Loop that automatically forward the data from left to right + void AutoFeed( + const boost::system::error_code& ec = boost::system::error_code(), + std::size_t n = 0) { + if (stopping_) { + return; + } + if (!ec && r_.is_open() && l_.is_open()) reenter(coro_auto_) { + for (;;) { + yield l_.async_receive_from( + boost::asio::buffer(array_buffer_), remote_endpoint_left_, + boost::bind(&DatagramLink::AutoFeed, this->shared_from_this(), _1, + _2)); + bytes_to_transfer_ = n; + transferred_bytes_ = 0; + while (transferred_bytes_ < bytes_to_transfer_) { + yield r_.async_send_to( + boost::asio::buffer(array_buffer_, + bytes_to_transfer_ - transferred_bytes_), + remote_endpoint_right_, + boost::bind(&DatagramLink::AutoFeed, this->shared_from_this(), + _1, _2)); + + transferred_bytes_ += n; + } + } + } + else { + Shutdown(); + } + } +#include // NOLINT + + void Stop() { + boost::system::error_code ec; + l_.close(ec); + timer_.cancel(ec); + stopping_ = true; + } + + 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) { + } + + void Shutdown() { + boost::recursive_mutex::scoped_lock lock(p_operator_mutex_); + if (p_operator_ && !stopping_) { + p_operator_->Stop(this->shared_from_this()); + p_operator_.reset(); + } + } + + public: + boost::asio::coroutine coro_auto_; + boost::asio::coroutine coro_external_; + RightEndSocket& r_; + LeftEndSocket l_; + DatagramQueue dgr_queue_; + std::array array_buffer_; + size_t bytes_to_transfer_; + size_t transferred_bytes_; + boost::asio::deadline_timer timer_; + RemoteEndpointRight remote_endpoint_right_; + RemoteEndpointLeft remote_endpoint_left_; + boost::recursive_mutex datagram_queue_mutex_; + boost::recursive_mutex p_operator_mutex_; + bool stopping_; + DatagramLinkOperatorSpecPtr p_operator_; + bool auto_feeding_; +}; + +} // ssf + +#endif // SSF_COMMON_NETWORK_DATAGRAM_LINK_H_ diff --git a/ssf-1.1.0/src/common/network/datagram_link_operator.h b/ssf-1.1.0/src/common/network/datagram_link_operator.h new file mode 100644 index 000000000..9e2809a2d --- /dev/null +++ b/ssf-1.1.0/src/common/network/datagram_link_operator.h @@ -0,0 +1,108 @@ +#ifndef SSF_COMMON_NETWORK_DATAGRAM_LINK_OPERATOR_H_ +#define SSF_COMMON_NETWORK_DATAGRAM_LINK_OPERATOR_H_ + +#include +#include +#include + +#include +#include + +namespace ssf { + +template +struct DatagramLink; + +/// Class to manager DatagramLinks +template +class DatagramLinkOperator + : public std::enable_shared_from_this< + DatagramLinkOperator> { +private: + // Types for DatagramLinks + 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)); + } + + ~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_; + std::map links_; + RightEndSocket& right_end_socket_; +}; + +} // ssf + +#endif // SSF_COMMON_NETWORK_DATAGRAM_LINK_OPERATOR_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/common/network/manager.h b/ssf-1.1.0/src/common/network/manager.h new file mode 100644 index 000000000..676debe2b --- /dev/null +++ b/ssf-1.1.0/src/common/network/manager.h @@ -0,0 +1,137 @@ +#ifndef SSF_COMMON_NETWORK_MANAGER_H +#define SSF_COMMON_NETWORK_MANAGER_H + +#include + +#include +#include +#include + +#include +#include +#include + +#include "common/error/error.h" + +namespace ssf { +/// Manage actionable items +/** Items must support start() and stop() interfaces and +* implement operator== +*/ +template +class ItemManager : private boost::noncopyable { + + public: + /// Type for the unique instance id given to each item managed + typedef uint32_t instance_id_type; + + public: + ItemManager() : id_map_mutex_(), id_map_() {} + + ~ItemManager() {} + + /// Activate an Item and return a unique ID + instance_id_type start(ActionableItem item, boost::system::error_code& ec) { + return do_start(item, ec); + } + + /// Stop an item + void stop(ActionableItem item, boost::system::error_code& ec) { + do_stop(find_id_from_item(item), ec); + } + + /// Stop the item given its unique ID + void stop_with_id(instance_id_type id, boost::system::error_code& ec) { + do_stop(id, ec); + } + + /// Stop all items + void stop_all() { + do_stop_all(); + } + + private: + /// Find the unique ID of the given item + instance_id_type find_id_from_item(ActionableItem item) { + boost::recursive_mutex::scoped_lock lock(id_map_mutex_); + + for (const auto& item_pair : id_map_) { + if (item_pair.second == item) { + return item_pair.first; + } + } + + return 0; + } + + /// Activate the item and return a unique ID + instance_id_type do_start(ActionableItem item, + boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(id_map_mutex_); + + /// Get an available ID + instance_id_type new_id = get_available_id(); + + /// If new_id == 0, no ID was available + if (!new_id) { + ec.assign(ssf::error::device_or_resource_busy, + ssf::error::get_ssf_category()); + return 0; + } else { + item->start(ec); + if (!ec) { + id_map_.insert(std::make_pair(new_id, std::move(item))); + return new_id; + } else { + return 0; + } + } + } + + /// Stop the item associated to the given unique ID + void do_stop(instance_id_type id, boost::system::error_code& ec) { + boost::recursive_mutex::scoped_lock lock(id_map_mutex_); + + auto it = id_map_.find(id); + + if (it != std::end(id_map_)) { + it->second->stop(ec); + if (!ec) { + id_map_.erase(id); + } + } else { + ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + } + } + + /// Stop all items + void do_stop_all() { + boost::recursive_mutex::scoped_lock lock(id_map_mutex_); + + boost::system::error_code ec; + for (auto& item : id_map_) { + item.second->stop(ec); + } + id_map_.clear(); + } + + /// Return the next available ID and 0 if no ID is available + instance_id_type get_available_id() { + boost::recursive_mutex::scoped_lock lock(id_map_mutex_); + + for (instance_id_type i = 1; i < std::numeric_limits::max(); + ++i) { + if (!id_map_.count(i)) { + return i; + } + } + return 0; + } + + private: + boost::recursive_mutex id_map_mutex_; + std::map id_map_; +}; +} // ssf + +#endif // SSF_COMMON_NETWORK_MANAGER_H diff --git a/ssf-1.1.0/src/common/network/network_policy_traits.h b/ssf-1.1.0/src/common/network/network_policy_traits.h new file mode 100644 index 000000000..c5c815b62 --- /dev/null +++ b/ssf-1.1.0/src/common/network/network_policy_traits.h @@ -0,0 +1,28 @@ +#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/ssf-1.1.0/src/common/network/session_forwarder.h b/ssf-1.1.0/src/common/network/session_forwarder.h new file mode 100644 index 000000000..81fd1fcda --- /dev/null +++ b/ssf-1.1.0/src/common/network/session_forwarder.h @@ -0,0 +1,123 @@ +#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/ssf-1.1.0/src/common/network/socket_link.h b/ssf-1.1.0/src/common/network/socket_link.h new file mode 100644 index 000000000..200a0d6c1 --- /dev/null +++ b/ssf-1.1.0/src/common/network/socket_link.h @@ -0,0 +1,131 @@ +#ifndef SSF_COMMON_NETWORK_SOCKET_LINK_H_ +#define SSF_COMMON_NETWORK_SOCKET_LINK_H_ + +#include +#include +#include +#include + +namespace ssf { +//----------------------------------------------------------------------------- + +/// Async Half Duplex Stream Socket Forwarder +/** +* @tparam Handler type of the callback handler +* @tparam ReadFromSocketType type of the input socket +* @tparam WriteToSocketType type od the output socket +*/ +template +struct AsyncHDSocketLinker : boost::asio::coroutine { + public: + /// Constructor + /** + * @param read_from input socket + * @param write_to output socket + * @param working_buffer a single buffer to receive and send + * @param handler the callback to call when the transfer stops + */ + AsyncHDSocketLinker(ReadFromSocketType& read_from, + WriteToSocketType& write_to, + boost::asio::mutable_buffers_1 working_buffer, + Handler handler) + : r_(read_from), + w_(write_to), + working_buffer_(working_buffer), + handler_(handler), + transfered_bytes_(0) { } + +#include +#include // NOLINT + + /// Operator() + /** + * This is function is its own handler for asynchronous operations it calls + * + * @param ec an error code describing the status of the operation + * @param n number of bytes handled + */ + void operator() (const boost::system::error_code& ec, std::size_t n) { + if (!ec && r_.is_open() && w_.is_open()) reenter(this) { + for (;;) { + // Receive some data + yield r_.async_read_some(working_buffer_, std::move(*this)); + transfered_bytes_ = n; + + // Keep sending until the number of sent bytes is not null (if some + // bytes were received previously). + do { + // Send the received data + yield boost::asio::async_write( + w_, boost::asio::buffer(working_buffer_, transfered_bytes_), + std::move(*this)); + } while (!n && transfered_bytes_); + } + } + else { + boost::get<0>(handler_)(ec); + } + } +#include // NOLINT + + ReadFromSocketType& r_; + WriteToSocketType& w_; + boost::asio::mutable_buffers_1 working_buffer_; + Handler handler_; + size_t transfered_bytes_; +}; + +/// Wrapper for the Stream Socket to read from +template +struct ReadFromHelper { + typedef SocketType type; + + explicit ReadFromHelper(SocketType& ref) + : read_from_(ref) { } + + SocketType& read_from_; +}; + +/// Function to create a ReadFromHelper +template +ReadFromHelper ReadFrom(SocketType& s) { + return ReadFromHelper(s); +} + +/// Wrapper for the Stream Socket to write to +template +struct WriteToHelper { + typedef SocketType type; + + explicit WriteToHelper(SocketType& ref) + : write_to_(ref) { } + + SocketType& write_to_; +}; + +/// Function to create a WriteToHelper +template +WriteToHelper WriteTo(SocketType& s) { + return WriteToHelper(s); +} + +/// Establish a Half Duplex Link +template +void AsyncEstablishHDLink(ReadFrom rf, WriteTo wt, + boost::asio::mutable_buffers_1 working_buffer, + Handler handler) { + AsyncHDSocketLinker, + typename ReadFrom::type, + typename WriteTo::type> AsyncTransfer( + rf.read_from_, + wt.write_to_, + working_buffer, + boost::make_tuple(handler)); + + AsyncTransfer(boost::system::error_code(), 0); +} +} // ssf + +#endif // SSF_COMMON_NETWORK_SOCKET_LINK_H_ diff --git a/ssf-1.1.0/src/common/utils/cleaner.h b/ssf-1.1.0/src/common/utils/cleaner.h new file mode 100644 index 000000000..6f6669345 --- /dev/null +++ b/ssf-1.1.0/src/common/utils/cleaner.h @@ -0,0 +1,24 @@ +#ifndef SSF_COMMON_UTILS_CLEANER_H_ +#define SSF_COMMON_UTILS_CLEANER_H_ + +#include + +class ScopeCleaner { + private: + typedef std::function Handler; + + public: + ScopeCleaner(Handler handler) : handler_(std::move(handler)) {} + ~ScopeCleaner() { + try { + handler_(); + } catch (...) { + // Swallows exceptions + } + } + + private: + Handler handler_; +}; + +#endif // SSF_COMMON_UTILS_CLEANER_H_ diff --git a/ssf-1.1.0/src/core/CMakeLists.txt b/ssf-1.1.0/src/core/CMakeLists.txt new file mode 100644 index 000000000..02bfe4ed3 --- /dev/null +++ b/ssf-1.1.0/src/core/CMakeLists.txt @@ -0,0 +1,11 @@ +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/ssf-1.1.0/src/core/client/CMakeLists.txt b/ssf-1.1.0/src/core/client/CMakeLists.txt new file mode 100644 index 000000000..fdc0a33e9 --- /dev/null +++ b/ssf-1.1.0/src/core/client/CMakeLists.txt @@ -0,0 +1,53 @@ +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} + TYPE + executable ${EXEC_FLAG} + FILES + ${CLIENT_FILES} + ${SSF_SOURCES} + ${CORE_COMMAND_LINE_STANDARD_FILES} + ${ICON_RC} + 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}) + +# --- SSF_Client_cp +add_target(${CLIENT_CP_PROJECT_NAME} + TYPE + executable ${EXEC_FLAG} + FILES + ${CLIENT_CP_FILES} + ${SSF_SOURCES} + ${CORE_COMMAND_LINE_COPY_FILES} + ${ICON_RC} + 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}) diff --git a/ssf-1.1.0/src/core/client/client.h b/ssf-1.1.0/src/core/client/client.h new file mode 100644 index 000000000..de89f3f98 --- /dev/null +++ b/ssf-1.1.0/src/core/client/client.h @@ -0,0 +1,102 @@ +#ifndef SSF_CORE_CLIENT_CLIENT_H_ +#define SSF_CORE_CLIENT_CLIENT_H_ + +#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/service_manager/service_manager.h" +#include "services/base_service.h" + + +namespace ssf { +template class LinkAuthenticationPolicy, + template class> + class NetworkVirtualLayerPolicy, + template class TransportVirtualLayerPolicy> +class SSFClient : public NetworkVirtualLayerPolicy, + public TransportVirtualLayerPolicy< + typename PhysicalVirtualLayer::socket_type> { + + 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; + + 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; + + 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); + + void run(Parameters& parameters); + + void stop(); + + private: + 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 OnDemuxClose(); + + void NetworkToTransport(p_socket_type p_socket, vector_error_code_type v_ec); + bool PrintErrorVector(vector_error_code_type v_ec); + + 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))); + } + } + + private: + boost::asio::io_service& io_service_; + demux fiber_demux_; + + std::vector user_services_; + + std::string remote_addr_; + std::string remote_port_; + + client_callback_type callback_; +}; +} // ssf + +#include "core/client/client.ipp" + +#endif // SSF_CORE_CLIENT_CLIENT_H_ diff --git a/ssf-1.1.0/src/core/client/client.ipp b/ssf-1.1.0/src/core/client/client.ipp new file mode 100644 index 000000000..c522716b6 --- /dev/null +++ b/ssf-1.1.0/src/core/client/client.ipp @@ -0,0 +1,174 @@ +#ifndef SSF_CORE_CLIENT_CLIENT_IPP_ +#define SSF_CORE_CLIENT_CLIENT_IPP_ + +#include + +#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/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" + +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), + user_services_(user_services), + remote_addr_(remote_addr), + remote_port_(port), + 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 L, + template class> class N, + template class T> +void SSFClient::stop() { + fiber_demux_.close(); +} + +//------------------------------------------------------------------------------- +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)) { + 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; + } + } + } +} +//------------------------------------------------------------------------------ + +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; + } + } + return true; +} + +//------------------------------------------------------------------------------ +template class L, + template class> class N, + template class T> +void SSFClient::DoSSFStart(p_socket_type 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"; + boost::system::error_code ec2; + this->DoFiberize(p_socket, ec2); + + Notify(ssf::services::initialisation::TRANSPORT, nullptr, ec); + } else { + BOOST_LOG_TRIVIAL(error) << "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) { + // Register supported admin commands + services::admin::CreateServiceRequest::RegisterToCommandFactory(); + services::admin::StopServiceRequest::RegisterToCommandFactory(); + services::admin::ServiceStatus::RegisterToCommandFactory(); + + auto close_demux_handler = [this]() { this->OnDemuxClose(); }; + fiber_demux_.fiberize(std::move(*p_socket), close_demux_handler); + + // Make a new service manager + 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); + + // Register supported micro services + services::socks::SocksServer::RegisterToServiceFactory( + p_service_factory); + services::fibers_to_sockets::FibersToSockets::RegisterToServiceFactory( + p_service_factory); + services::sockets_to_fibers::SocketsToFibers::RegisterToServiceFactory( + p_service_factory); + services::fibers_to_datagrams::FibersToDatagrams< + demux>::RegisterToServiceFactory(p_service_factory); + services::datagrams_to_fibers::DatagramsToFibers< + demux>::RegisterToServiceFactory(p_service_factory); + services::copy_file::file_to_fiber::FileToFiber< + demux>::RegisterToServiceFactory(p_service_factory); + services::copy_file::fiber_to_file::FiberToFile< + demux>::RegisterToServiceFactory(p_service_factory); + services::copy_file::file_enquirer::FileEnquirer< + demux>::RegisterToServiceFactory(p_service_factory); + + // Start the admin micro service + std::map empty_map; + + auto p_admin_service = services::admin::Admin::Create( + 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() { + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&fiber_demux_); + if (p_service_factory) { + p_service_factory->Destroy(); + } + Notify(ssf::services::initialisation::CLOSE, nullptr, + boost::system::error_code()); +} + +} // ssf + +#endif // SSF_CORE_CLIENT_CLIENT_IPP_ diff --git a/ssf-1.1.0/src/core/client/main.cpp b/ssf-1.1.0/src/core/client/main.cpp new file mode 100644 index 000000000..e104e3182 --- /dev/null +++ b/ssf-1.1.0/src/core/client/main.cpp @@ -0,0 +1,174 @@ +#include + +#include +#include +#include +#include +#include + +#include "common/config/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/transport_virtual_layer_policies/transport_protocol_policy.h" + +#include "services/initialisation.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/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); +} + +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(); + + boost::program_options::options_description options = + 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); + + if (ec) { + BOOST_LOG_TRIVIAL(error) << "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); + + 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.IsAddrSet()) { + BOOST_LOG_TRIVIAL(error) << "client: no hostname provided -- Exiting"; + return 1; + } + + if (!cmd.IsPortSet()) { + BOOST_LOG_TRIVIAL(error) << "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); + + if (ec_config) { + BOOST_LOG_TRIVIAL(error) << "client: invalid config file format" + << std::endl; + return 0; + } + + // 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(); + } + }; + + threads.create_thread(lambda); + } + + auto callback = [](ssf::services::initialisation::type, BaseUserServicePtr, + const boost::system::error_code&) {}; + + // Initiate and start client + Client client(io_service, cmd.addr(), std::to_string(cmd.port()), ssf_config, + user_services, callback); + + std::map params; + + std::list bouncers = + BounceParser::ParseBounceFile(cmd.bounce_file()); + + 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()); + } + + std::ostringstream ostrs; + boost::archive::text_oarchive ar(ostrs); + ar << BOOST_SERIALIZATION_NVP(bouncers); + + params["bouncing_nodes"] = ostrs.str(); + + client.run(params); + + getchar(); + + client.stop(); + threads.join_all(); + io_service.stop(); + + return 0; +} diff --git a/ssf-1.1.0/src/core/client/main_cp.cpp b/ssf-1.1.0/src/core/client/main_cp.cpp new file mode 100644 index 000000000..b7b531b8e --- /dev/null +++ b/ssf-1.1.0/src/core/client/main_cp.cpp @@ -0,0 +1,165 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include "common/config/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/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); +} + +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(); + + // Parse the command line + ssf::command_line::copy::CommandLine cmd; + std::vector user_services; + + boost::system::error_code ec; + + cmd.parse(argc, argv, ec); + + if (ec) { + BOOST_LOG_TRIVIAL(error) << "client: wrong command line arguments"; + return 1; + } + + // Create and initialize copy user service + auto p_copy_service = + ssf::services::CopyFileService::CreateServiceFromParams( + cmd.from_stdin(), cmd.from_local_to_remote(), cmd.input_pattern(), + cmd.output_pattern(), ec); + + if (ec) { + BOOST_LOG_TRIVIAL(error) << "client: copy service could not be created"; + return 1; + } + + user_services.push_back(p_copy_service); + + if (!cmd.IsAddrSet()) { + BOOST_LOG_TRIVIAL(error) << "client: no remote host provided -- Exiting"; + return 1; + } + + if (!cmd.IsPortSet()) { + BOOST_LOG_TRIVIAL(error) << "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); + + if (ec_config) { + BOOST_LOG_TRIVIAL(error) << "client: invalid config file format" + << std::endl; + return 0; + } + + // 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; + 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(); + } + }; + + threads.create_thread(lambda); + } + + 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; + } + }; + + // Initiating and starting the client + Client client(io_service, cmd.addr(), std::to_string(cmd.port()), ssf_config, + user_services, callback); + + std::map params; + + std::list bouncers = + BounceParser::ParseBounceFile(cmd.bounce_file()); + + 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()); + } + + std::ostringstream ostrs; + boost::archive::text_oarchive ar(ostrs); + ar << BOOST_SERIALIZATION_NVP(bouncers); + + params["bouncing_nodes"] = ostrs.str(); + + client.run(params); + + // wait end transfer + closed.get_future().get(); + + p_worker.reset(); + + threads.join_all(); + + client.stop(); + io_service.stop(); + + return 0; +} diff --git a/ssf-1.1.0/src/core/command_line/copy/command_line.cpp b/ssf-1.1.0/src/core/command_line/copy/command_line.cpp new file mode 100644 index 000000000..e3cc015d3 --- /dev/null +++ b/ssf-1.1.0/src/core/command_line/copy/command_line.cpp @@ -0,0 +1,225 @@ +#include "core/command_line/copy/command_line.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common/error/error.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; + } + + 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()); + } +} + +uint16_t CommandLine::port() const { return port_; } + +std::string CommandLine::addr() const { return addr_; } + +std::string CommandLine::bounce_file() const { return bounce_file_; } + +std::string CommandLine::config_file() const { return config_file_; } + +bool CommandLine::from_stdin() const { return from_stdin_; } + +bool CommandLine::from_local_to_remote() const { return from_local_to_remote_; } + +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::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()) { + 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; + } + + auto second_arg_it = vm.find("arg2"); + if (second_arg_it != vm.end()) { + ParseSecondArgument(second_arg_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()); + } +} + +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); + if (!parse_ec) { + addr_set_ = true; + from_local_to_remote_ = true; + } + } else { + // expecting host:dirpath or filepath syntax + boost::system::error_code extract_ec; + ExtractHostPattern(first_arg, &addr_, &input_pattern_, extract_ec); + if (!extract_ec) { + addr_set_ = true; + from_local_to_remote_ = false; + } else { + // not host:dirpath syntax so it is filepath syntax + input_pattern_ = first_arg; + from_local_to_remote_ = true; + } + } +} + +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; + } + + // 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_ += '/'; + } + } +} + +void CommandLine::ExtractHostPattern(const std::string& string, + std::string* p_host, + std::string* p_pattern, + 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()); + return; + } + + *p_host = string.substr(0, found); + *p_pattern = string.substr(found + 1); + ec.assign(ssf::error::success, ssf::error::get_ssf_category()); +} + +char CommandLine::GetHostDirectorySeparator() const { return '@'; } + +} // copy +} // command_line +} // ssf diff --git a/ssf-1.1.0/src/core/command_line/copy/command_line.h b/ssf-1.1.0/src/core/command_line/copy/command_line.h new file mode 100644 index 000000000..858d84938 --- /dev/null +++ b/ssf-1.1.0/src/core/command_line/copy/command_line.h @@ -0,0 +1,80 @@ +#ifndef SSF_CORE_COMMAND_LINE_COPY_COMMAND_LINE_H_ +#define SSF_CORE_COMMAND_LINE_COPY_COMMAND_LINE_H_ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace ssf { +namespace command_line { +namespace copy { + +class CommandLine { + 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; + + bool from_stdin() const; + + bool from_local_to_remote() const; + + std::string input_pattern() const; + + std::string output_pattern() const; + + bool IsPortSet() const; + + bool IsAddrSet() const; + + 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); + + void ParseSecondArgument(const std::string& second_arg, + boost::system::error_code& parse_ec); + + void ExtractHostPattern(const std::string& string, std::string* p_host, + std::string* p_pattern, + boost::system::error_code& ec) const; + + 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 +} // command_line +} // ssf + +#endif // SSF_CORE_COMMAND_LINE_COPY_COMMAND_LINE_H_ diff --git a/ssf-1.1.0/src/core/command_line/standard/command_line.cpp b/ssf-1.1.0/src/core/command_line/standard/command_line.cpp new file mode 100644 index 000000000..b3fc025da --- /dev/null +++ b/ssf-1.1.0/src/core/command_line/standard/command_line.cpp @@ -0,0 +1,146 @@ +#include "core/command_line/standard/command_line.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common/error/error.h" + +namespace ssf { +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"); + } + + options.add_options() + ("config,c", + boost::program_options::value(), + "Set config file"); + // clang-format on + + 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>(); + } +} + +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_; } + +bool CommandLine::IsAddrSet() { return addr_set_; } + +std::map> CommandLine::InternalParsing( + const boost::program_options::variables_map& vm, + boost::system::error_code& ec) { + std::map> result; + + 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>(); + } + } + + return result; +} + +} // standard +} // command_line +} // ssf diff --git a/ssf-1.1.0/src/core/command_line/standard/command_line.h b/ssf-1.1.0/src/core/command_line/standard/command_line.h new file mode 100644 index 000000000..a92950b11 --- /dev/null +++ b/ssf-1.1.0/src/core/command_line/standard/command_line.h @@ -0,0 +1,61 @@ +#ifndef SSF_CORE_COMMAND_LINE_STANDARD_COMMAND_LINE_H +#define SSF_CORE_COMMAND_LINE_STANDARD_COMMAND_LINE_H + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace ssf { +namespace command_line { +namespace standard { + +class CommandLine { + 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); + + uint16_t port(); + + std::string addr(); + + std::string bounce_file(); + + std::string config_file(); + + bool IsPortSet(); + + bool IsAddrSet(); + + 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_; +}; + +} // standard +} // command_line +} // ssf + +#endif // SSF_CORE_COMMAND_LINE_STANDARD_COMMAND_LINE_H diff --git a/ssf-1.1.0/src/core/factories/command_factory.h b/ssf-1.1.0/src/core/factories/command_factory.h new file mode 100644 index 000000000..cf3456c4b --- /dev/null +++ b/ssf-1.1.0/src/core/factories/command_factory.h @@ -0,0 +1,124 @@ +#ifndef SSF_CORE_FACTORIES_COMMAND_FACTORY_H_ +#define SSF_CORE_FACTORIES_COMMAND_FACTORY_H_ + +#include + +#include +#include +#include + +#include +#include + + +namespace boost { +namespace archive { + class text_iarchive; +} // archive +} // boost + + +namespace ssf { +template +class CommandFactory { + public: + typedef std::function CommandExecuterType; + + typedef std::function CommandReplierType; + private: + typedef std::map CommandExecuterMap; + typedef std::map CommandReplierMap; + typedef std::map CommandReplyIndexMap; + + public: + 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))); + return inserted.second; + } + + static bool RegisterOnReplyCommand(uint32_t index, + CommandReplierType replier) { + boost::recursive_mutex::scoped_lock lock(replier_mutex_); + auto inserted = + command_repliers_.insert(std::make_pair(index, std::move(replier))); + return inserted.second; + } + + 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))); + return inserted.second; + } + + static CommandExecuterType* GetExecuter(uint32_t index) { + boost::recursive_mutex::scoped_lock lock(executer_mutex_); + + auto it = command_executers_.find(index); + + if (it != std::end(command_executers_)) { + return &(it->second); + } else { + return nullptr; + } + } + + static CommandReplierType* GetReplier(uint32_t index) { + boost::recursive_mutex::scoped_lock lock(replier_mutex_); + + auto it = command_repliers_.find(index); + + if (it != std::end(command_repliers_)) { + return &(it->second); + } else { + return nullptr; + } + } + + static uint32_t* GetReplyCommandIndex(uint32_t index) { + boost::recursive_mutex::scoped_lock lock(command_reply_mutex_); + + auto it = command_reply_indexes.find(index); + + if (it != std::end(command_reply_indexes)) { + return &(it->second); + } else { + return nullptr; + } + } + + private: + static boost::recursive_mutex executer_mutex_; + static boost::recursive_mutex replier_mutex_; + static boost::recursive_mutex command_reply_mutex_; + static CommandExecuterMap command_executers_; + static CommandReplierMap command_repliers_; + static CommandReplyIndexMap command_reply_indexes; +}; + + +template +boost::recursive_mutex CommandFactory::executer_mutex_; +template +boost::recursive_mutex CommandFactory::replier_mutex_; +template +boost::recursive_mutex CommandFactory::command_reply_mutex_; + +template +typename CommandFactory::CommandExecuterMap CommandFactory::command_executers_; +template +typename CommandFactory::CommandReplierMap CommandFactory::command_repliers_; +template +typename CommandFactory::CommandReplyIndexMap CommandFactory::command_reply_indexes; + +} // ssf +#endif // SSF_CORE_FACTORIES_COMMAND_FACTORY_H_ diff --git a/ssf-1.1.0/src/core/factories/service_factory.h b/ssf-1.1.0/src/core/factories/service_factory.h new file mode 100644 index 000000000..b39c872a9 --- /dev/null +++ b/ssf-1.1.0/src/core/factories/service_factory.h @@ -0,0 +1,153 @@ +#ifndef SSF_CORE_FACTORIES_SERVICE_FACTORY_H_ +#define SSF_CORE_FACTORIES_SERVICE_FACTORY_H_ + +#include + +#include +#include +#include + +#include +#include +#include + +#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); + } + + return std::move(p_service_factory); + } + + ~ServiceFactory() {} + + bool RegisterServiceCreator(uint32_t index, ServiceCreator creator) { + boost::recursive_mutex::scoped_lock lock(service_creators_mutex_); + if (service_creators_.count(index)) { + return false; + } else { + service_creators_[index] = creator; + return true; + } + } + + 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); + 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()); + return 0; + } + } else { + ec.assign(ssf::error::service_not_found, ssf::error::get_ssf_category()); + return 0; + } + } + + void StopService(uint32_t id) { + boost::system::error_code ec; + p_service_manager_->stop_with_id(id, ec); + } + + void StopAllLocalServices() { + p_service_manager_->stop_all(); + } + + void Destroy() { + ServiceFactoryManager::UnregisterServiceFactory(&demux_); + } + + 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); + } + + 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); + } + + 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 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: + 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 diff --git a/ssf-1.1.0/src/core/factories/service_option_factory.h b/ssf-1.1.0/src/core/factories/service_option_factory.h new file mode 100644 index 000000000..96f9c8039 --- /dev/null +++ b/ssf-1.1.0/src/core/factories/service_option_factory.h @@ -0,0 +1,100 @@ +#ifndef SSF_CORE_FACTORIES_SERVICE_OPTION_FACTORY_H_ +#define SSF_CORE_FACTORIES_SERVICE_OPTION_FACTORY_H_ + +#include +#include +#include + +#include + +#include + +#include + +#include "common/error/error.h" +#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; + + struct ParserDescriptor { + ServiceParserType parser; + std::string fullname; + std::string description; + }; + + typedef std::map ServiceParserMap; + +public: + static bool RegisterUserServiceParser(std::string index, std::string full_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)}; + return true; + } + } + + 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; + } + } + + static boost::program_options::options_description GetOptionDescriptions() { + 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()); + } + + return desc; + } + + static std::shared_ptr> + 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()); + return std::shared_ptr>(nullptr); + } + } + +private: + static boost::recursive_mutex service_options_mutex_; + static ServiceParserMap service_options_; +}; + +template +boost::recursive_mutex ServiceOptionFactory::service_options_mutex_; + +template +typename ServiceOptionFactory::ServiceParserMap ServiceOptionFactory::service_options_; + +} // ssf + + +#endif // SSF_CORE_FACTORIES_SERVICE_OPTION_FACTORY_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/core/factory_manager/service_factory_manager.h b/ssf-1.1.0/src/core/factory_manager/service_factory_manager.h new file mode 100644 index 000000000..466d81540 --- /dev/null +++ b/ssf-1.1.0/src/core/factory_manager/service_factory_manager.h @@ -0,0 +1,66 @@ +#ifndef SSF_CORE_FACTORY_MANAGER_SERVICE_FACTORY_MANAGER_H_ +#define SSF_CORE_FACTORY_MANAGER_SERVICE_FACTORY_MANAGER_H_ + +#include +#include + +#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; + } + + static bool UnregisterServiceFactory(Demux* index) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + auto it = service_factories_.find(index); + + if (it != std::end(service_factories_)) { + auto& p_service_factory = it->second; + p_service_factory->StopAllLocalServices(); + service_factories_.erase(index); + return true; + } else { + return false; + } + } + + static ServiceFactoryPtr GetServiceFactory(Demux* index) { + boost::recursive_mutex::scoped_lock lock(mutex_); + + auto it = service_factories_.find(index); + + if (it != std::end(service_factories_)) { + return it->second; + } else { + return nullptr; + } + } + +private: + static boost::recursive_mutex mutex_; + static ServiceFactoryMap service_factories_; +}; + +template +boost::recursive_mutex ServiceFactoryManager::mutex_; + +template +typename ServiceFactoryManager::ServiceFactoryMap ServiceFactoryManager::service_factories_; + +} // ssf +#endif // SSF_CORE_FACTORY_MANAGER_SERVICE_FACTORY_MANAGER_H_ diff --git a/ssf-1.1.0/src/core/network_virtual_layer_policies/bounce_protocol_policy.h b/ssf-1.1.0/src/core/network_virtual_layer_policies/bounce_protocol_policy.h new file mode 100644 index 000000000..705eb48a6 --- /dev/null +++ b/ssf-1.1.0/src/core/network_virtual_layer_policies/bounce_protocol_policy.h @@ -0,0 +1,731 @@ +#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/ssf-1.1.0/src/core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h b/ssf-1.1.0/src/core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h new file mode 100644 index 000000000..f5b6d8b4d --- /dev/null +++ b/ssf-1.1.0/src/core/network_virtual_layer_policies/link_authentication_policies/null_link_authentication_policy.h @@ -0,0 +1,108 @@ +#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/ssf-1.1.0/src/core/network_virtual_layer_policies/link_policies/ssl_policy.h b/ssf-1.1.0/src/core/network_virtual_layer_policies/link_policies/ssl_policy.h new file mode 100644 index 000000000..70da68a01 --- /dev/null +++ b/ssf-1.1.0/src/core/network_virtual_layer_policies/link_policies/ssl_policy.h @@ -0,0 +1,848 @@ +#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/ssf-1.1.0/src/core/network_virtual_layer_policies/link_policies/tcp_policy.h b/ssf-1.1.0/src/core/network_virtual_layer_policies/link_policies/tcp_policy.h new file mode 100644 index 000000000..a95e20a2e --- /dev/null +++ b/ssf-1.1.0/src/core/network_virtual_layer_policies/link_policies/tcp_policy.h @@ -0,0 +1,252 @@ +#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/ssf-1.1.0/src/core/parser/bounce_parser.cpp b/ssf-1.1.0/src/core/parser/bounce_parser.cpp new file mode 100644 index 000000000..8b4b022ac --- /dev/null +++ b/ssf-1.1.0/src/core/parser/bounce_parser.cpp @@ -0,0 +1,50 @@ +#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/ssf-1.1.0/src/core/parser/bounce_parser.h b/ssf-1.1.0/src/core/parser/bounce_parser.h new file mode 100644 index 000000000..ef283a1ed --- /dev/null +++ b/ssf-1.1.0/src/core/parser/bounce_parser.h @@ -0,0 +1,25 @@ +#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/ssf-1.1.0/src/core/server/CMakeLists.txt b/ssf-1.1.0/src/core/server/CMakeLists.txt new file mode 100644 index 000000000..0fc2d339e --- /dev/null +++ b/ssf-1.1.0/src/core/server/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 2.8) + +set(project_NAME "ssfs") +project(${project_NAME}) + +set(SERVER_FILES + "${project_SRC_DIR}/core/server/main.cpp") + +include_directories( + ${OpenSSL_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS}) + +add_target(${project_NAME} + TYPE + executable ${EXEC_FLAG} + FILES + ${SERVER_FILES} + ${CORE_COMMAND_LINE_STANDARD_FILES} + ${SSF_SOURCES} + ${ICON_RC} + 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}) diff --git a/ssf-1.1.0/src/core/server/main.cpp b/ssf-1.1.0/src/core/server/main.cpp new file mode 100644 index 000000000..102d7d0c2 --- /dev/null +++ b/ssf-1.1.0/src/core/server/main.cpp @@ -0,0 +1,92 @@ +#include + +#include +#include +#include +#include +#include + +#include "common/config/config.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/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); +} + +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(); + + // 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); + + if (ec) { + BOOST_LOG_TRIVIAL(error) << "server: wrong arguments" << std::endl; + return 1; + } + + // Load SSF config if any + boost::system::error_code ec_config; + ssf::Config ssf_config = ssf::LoadConfig(cmd.config_file(), ec_config); + + if (ec_config) { + BOOST_LOG_TRIVIAL(error) << "server: invalid config file format" + << std::endl; + 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(); + + // Initiate and start the server + Server server(io_service, ssf_config, cmd.port()); + + server.run(); + + getchar(); + server.stop(); + io_service.stop(); + threads.join_all(); + + return 0; +} \ No newline at end of file diff --git a/ssf-1.1.0/src/core/server/server.h b/ssf-1.1.0/src/core/server/server.h new file mode 100644 index 000000000..d7fffe467 --- /dev/null +++ b/ssf-1.1.0/src/core/server/server.h @@ -0,0 +1,88 @@ +#ifndef SSF_CORE_SERVER_SERVER_H_ +#define SSF_CORE_SERVER_SERVER_H_ + +#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/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/factories/service_factory.h" + +namespace ssf { +template class LinkAuthenticationPolicy, + template class> + class NetworkVirtualLayerPolicy, + template class TransportVirtualLayerPolicy> +class SSFServer : public NetworkVirtualLayerPolicy, + public TransportVirtualLayerPolicy< + typename PhysicalVirtualLayer::socket_type> { + private: + typedef typename PhysicalVirtualLayer::socket_type socket_type; + typedef typename PhysicalVirtualLayer::p_socket_type p_socket_type; + + typedef std::vector vector_error_code_type; + + public: + typedef boost::asio::fiber::basic_fiber_demux demux; + + private: + typedef std::shared_ptr p_demux; + + typedef std::shared_ptr> p_ServiceManager; + + typedef std::set demux_set; + typedef std::map socket_map; + typedef std::map service_manager_map; + + public: + SSFServer(boost::asio::io_service& io_service, const ssf::Config& ssf_config, + uint16_t local_port); + + void run(); + void stop(); + + private: + void AddDemux(p_demux p_fiber_demux, p_ServiceManager p_service_manager); + void RemoveDemux(p_demux 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_; + + demux_set p_fiber_demuxes_; + service_manager_map p_service_managers_; + + boost::recursive_mutex storage_mutex_; +}; + +} // ssf + +#include "core/server/server.ipp" + +#endif // SSF_CORE_SERVER_SERVER_H_ diff --git a/ssf-1.1.0/src/core/server/server.ipp b/ssf-1.1.0/src/core/server/server.ipp new file mode 100644 index 000000000..55ab15d57 --- /dev/null +++ b/ssf-1.1.0/src/core/server/server.ipp @@ -0,0 +1,187 @@ +#ifndef SSF_CORE_SERVER_SERVER_IPP_ +#define SSF_CORE_SERVER_SERVER_IPP_ + +#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" + +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) {} + +/// 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)); +} + +/// 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 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]) { + this->DoSSFInitiateReceive(p_socket); + } else { + BOOST_LOG_TRIVIAL(error) << "server: network error: " << v_ec[0].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) { + if (!ec) { + BOOST_LOG_TRIVIAL(trace) << "server: SSF reply ok"; + boost::system::error_code ec2; + this->DoFiberize(p_socket, ec2); + } else { + BOOST_LOG_TRIVIAL(error) << "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) { + // 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 close_demux_handler = + [this, p_fiber_demux]() { this->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); + + // Make a new service factory + auto p_service_factory = ServiceFactory::Create( + io_service_, *p_fiber_demux, p_service_manager); + + // Register supported micro services + services::socks::SocksServer::RegisterToServiceFactory( + p_service_factory); + services::fibers_to_sockets::FibersToSockets::RegisterToServiceFactory( + p_service_factory); + services::sockets_to_fibers::SocketsToFibers::RegisterToServiceFactory( + p_service_factory); + services::fibers_to_datagrams::FibersToDatagrams< + demux>::RegisterToServiceFactory(p_service_factory); + services::datagrams_to_fibers::DatagramsToFibers< + demux>::RegisterToServiceFactory(p_service_factory); + services::copy_file::file_to_fiber::FileToFiber< + demux>::RegisterToServiceFactory(p_service_factory); + services::copy_file::fiber_to_file::FiberToFile< + demux>::RegisterToServiceFactory(p_service_factory); + + // Start the admin micro service + std::map empty_map; + + auto p_admin_service = services::admin::Admin::Create( + 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) { + boost::recursive_mutex::scoped_lock lock(storage_mutex_); + BOOST_LOG_TRIVIAL(trace) << "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) { + boost::recursive_mutex::scoped_lock lock(storage_mutex_); + BOOST_LOG_TRIVIAL(trace) << "server: removing a demux"; + + p_fiber_demux->close(); + p_fiber_demuxes_.erase(p_fiber_demux); + + if (p_service_managers_.count(p_fiber_demux)) { + auto p_service_manager = p_service_managers_[p_fiber_demux]; + p_service_manager->stop_all(); + p_service_managers_.erase(p_fiber_demux); + } + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(p_fiber_demux.get()); + if (p_service_factory) { + p_service_factory->Destroy(); + } +} + +template class L, + template class> class N, + template class T> +void SSFServer::RemoveAllDemuxes() { + boost::recursive_mutex::scoped_lock lock(storage_mutex_); + BOOST_LOG_TRIVIAL(trace) << "server: removing all demuxes"; + + for (auto& p_fiber_demux : p_fiber_demuxes_) { + 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_managers_.erase(p_fiber_demux); + } + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(p_fiber_demux.get()); + if (p_service_factory) { + p_service_factory->Destroy(); + } + } + + p_fiber_demuxes_.clear(); +} + +} // ssf + +#endif // SSF_CORE_SERVER_SERVER_H_ diff --git a/ssf-1.1.0/src/core/service_manager/service_manager.h b/ssf-1.1.0/src/core/service_manager/service_manager.h new file mode 100644 index 000000000..099f4c709 --- /dev/null +++ b/ssf-1.1.0/src/core/service_manager/service_manager.h @@ -0,0 +1,160 @@ +#ifndef SSF_CORE_SERVICE_MANAGER_SERVICE_MANAGER_H_ +#define SSF_CORE_SERVICE_MANAGER_SERVICE_MANAGER_H_ + +#include + +#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: + uint32_t get_id(uint32_t service_type_id, Parameters parameters) { + boost::recursive_mutex::scoped_lock lock(status_n_instance_list_mutex_); + + auto& instance_list = intance_lists_[service_type_id]; + + for (const auto& instance : instance_list) { + if (instance.first == parameters) { + return instance.second; + } + } + + return 0; + } + + uint32_t find_error(uint32_t service_type_id, Parameters parameters) { + boost::recursive_mutex::scoped_lock lock(status_n_instance_list_mutex_); + + auto& error_list = error_lists_[service_type_id]; + + for (const auto& error : error_list) { + if (error.first == parameters) { + return error.second; + } + } + + return 0; + } + + uint32_t get_status(uint32_t id) { + if (status_.count(id)) { + return status_[id]; + } else { + return 0xFFFFFFFF; // Undefined + } + } + + uint32_t get_status(uint32_t service_type_id, Parameters parameters, + uint32_t id) { + if (status_.count(id)) { + return status_[id]; + } else { + auto error = find_error(service_type_id, parameters); + if (error) { + return error; + } else { + return 0xFFFFFFFF; // Undefined + } + } + } + + 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_); + + if (!status_.count(id)) { + add_remote(id, service_type_id, error_code_value, parameters); + return true; + } else { + return update_remote(id, error_code_value, ec); + } + } + + 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 + status_.erase(id); + remove_id_from_instances(service_ids_[id], id); + service_ids_.erase(id); + } else { + status_[id] = error_code_value; + } + return true; + } + } + +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)); + 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)); + if (instance_list.size() > 100) { + instance_list.pop_back(); + } + } + } + + void remove_id_from_instances(uint32_t service_type_id, uint32_t id) { + boost::recursive_mutex::scoped_lock lock(status_n_instance_list_mutex_); + + auto& instance_list = intance_lists_[service_type_id]; + + for (auto it = instance_list.begin(); it != instance_list.end(); ++it) { + if ((*it).second == id) { + instance_list.erase(it); + break; + } + } + } + +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_; +}; + +} // ssf + +#endif // SSF_CORE_SERVICE_MANAGER_SERVICE_MANAGER_H_ diff --git a/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.cpp b/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.cpp new file mode 100644 index 000000000..dc48ff762 --- /dev/null +++ b/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.cpp @@ -0,0 +1,26 @@ +#include "ssf_reply.h" + +namespace ssf { + +SSFReply::SSFReply() : result_(false) {} +SSFReply::SSFReply(bool result) : result_(result) {} + +bool SSFReply::result() const { return result_; } + +std::array +SSFReply::const_buffer() const { + std::array buf = { + {boost::asio::const_buffer(&result_, sizeof(result_))}}; + + return buf; +} + +std::array +SSFReply::buffer() { + std::array buf = { + {boost::asio::mutable_buffer(&result_, sizeof(result_))}}; + + return buf; +} + +} // ssf diff --git a/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.h b/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.h new file mode 100644 index 000000000..0c70479f9 --- /dev/null +++ b/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_reply.h @@ -0,0 +1,37 @@ +#ifndef SSF_CORE_TRANSPORT_VIRTUAL_LAYER_POLICIES_INIT_PACKETS_SSF_REPLY_H_ +#define SSF_CORE_TRANSPORT_VIRTUAL_LAYER_POLICIES_INIT_PACKETS_SSF_REPLY_H_ + +#include +#include +#include + +#include + +namespace ssf { + +class SSFReply { + public: + enum { + field_number = 1, + total_size = sizeof(bool) + }; + + public: + SSFReply(); + SSFReply(bool result); + + bool result() const; + + std::array const_buffer() const; + + std::array buffer(); + + private: + bool result_; +}; + +typedef std::shared_ptr SSFReplyPtr; + +} // ssf + +#endif // SSF_CORE_TRANSPORT_VIRTUAL_LAYER_POLICIES_INIT_PACKETS_SSF_REPLY_H_ diff --git a/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_request.cpp b/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_request.cpp new file mode 100644 index 000000000..50af6faab --- /dev/null +++ b/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_request.cpp @@ -0,0 +1,34 @@ +#include "ssf_request.h" + + +namespace ssf { + +SSFRequest::SSFRequest() : version_(0) {} + +SSFRequest::SSFRequest(version_field_type version) + : version_(version) {} + +SSFRequest::version_field_type SSFRequest::version() const { return version_; } + +std::array +SSFRequest::const_buffer() const { + std::array buf = { + { + boost::asio::const_buffer(&version_, sizeof(version_)) + } + }; + + return buf; +} + +std::array SSFRequest::buffer() { + std::array buf = { + { + boost::asio::mutable_buffer(&version_, sizeof(version_)) + } + }; + + return buf; +} + +} // ssf diff --git a/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_request.h b/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_request.h new file mode 100644 index 000000000..b7cf6df2c --- /dev/null +++ b/ssf-1.1.0/src/core/transport_virtual_layer_policies/init_packets/ssf_request.h @@ -0,0 +1,38 @@ +#ifndef SSF_CORE_TRANSPORT_VIRTUAL_LAYER_POLICIES_INIT_PACKETS_SSF_REQUEST_H_ +#define SSF_CORE_TRANSPORT_VIRTUAL_LAYER_POLICIES_INIT_PACKETS_SSF_REQUEST_H_ + +#include +#include +#include + +#include + +namespace ssf { + +class SSFRequest { + public: + typedef uint32_t version_field_type; + + enum { + field_number = 1, + total_size = sizeof(version_field_type) + }; + +public: + SSFRequest(); + SSFRequest(version_field_type version); + + version_field_type version() const; + + std::array const_buffer() const; + std::array buffer(); + +private: + version_field_type version_; +}; + +typedef std::shared_ptr SSFRequestPtr; + +} // ssf + +#endif // SSF_CORE_TRANSPORT_VIRTUAL_LAYER_POLICIES_INIT_PACKETS_SSF_REQUEST_H_ diff --git a/ssf-1.1.0/src/core/transport_virtual_layer_policies/transport_protocol_policy.h b/ssf-1.1.0/src/core/transport_virtual_layer_policies/transport_protocol_policy.h new file mode 100644 index 000000000..3242284c0 --- /dev/null +++ b/ssf-1.1.0/src/core/transport_virtual_layer_policies/transport_protocol_policy.h @@ -0,0 +1,150 @@ +#ifndef SSF_CORE_TRANSPORT_VIRTUAL_LAYER_POLICIES_TRANSPORT_PROTOCOL_POLICY_H +#define SSF_CORE_TRANSPORT_VIRTUAL_LAYER_POLICIES_TRANSPORT_PROTOCOL_POLICY_H + +#include + +#include +#include + +#include +#include + +#include "core/transport_virtual_layer_policies/init_packets/ssf_reply.h" +#include "core/transport_virtual_layer_policies/init_packets/ssf_request.h" + +#include "versions.h" + +namespace ssf { + +template +class TransportProtocolPolicy { + private: + typedef std::shared_ptr p_socket_type; + typedef std::function + callback_type; + + public: + TransportProtocolPolicy(callback_type callback) : callback_(callback) {} + + void DoSSFInitiate(p_socket_type p_socket) { + BOOST_LOG_TRIVIAL(info) << "transport: starting SSF protocol"; + + uint32_t version = GetVersion(); + auto p_ssf_request = std::make_shared(version); + + boost::asio::async_write( + *p_socket, p_ssf_request->const_buffer(), + boost::bind(&TransportProtocolPolicy::DoSSFValidReceive, this, + p_ssf_request, p_socket, _1, _2)); + } + + void DoSSFInitiateReceive(p_socket_type p_socket) { + auto p_ssf_request = std::make_shared(); + boost::asio::async_read( + *p_socket, p_ssf_request->buffer(), + boost::bind(&TransportProtocolPolicy::DoSSFValid, this, + 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) { + if (!ec) { + uint32_t version = p_ssf_request->version(); + + BOOST_LOG_TRIVIAL(trace) << "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)); + } 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()); + callback_(p_socket, result_ec); + } + } else { + BOOST_LOG_TRIVIAL(error) << "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) { + if (!ec) { + BOOST_LOG_TRIVIAL(info) << "transport: SSF request sent"; + + auto p_ssf_reply = std::make_shared(); + + boost::asio::async_read( + *p_socket, p_ssf_reply->buffer(), + 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(); + callback_(p_socket, ec); + } + } + + void DoSSFProtocolFinished(SSFReplyPtr p_ssf_reply, p_socket_type 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"; + 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(); + callback_(p_socket, result_ec); + } + } else { + BOOST_LOG_TRIVIAL(error) << "transport: could NOT read SSF reply " << ec.message(); + callback_(p_socket, ec); + } + } + + uint32_t GetVersion() { + uint32_t version = versions::major; + version = version << 8; + + version |= versions::minor; + version = version << 8; + + version |= versions::transport; + 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 transport = (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) && + (transport == versions::transport) && + (serialization == boost::archive::BOOST_ARCHIVE_VERSION()); + } + + private: + callback_type callback_; +}; +} //ssf + +#endif // SSF_CORE_TRANSPORT_VIRTUAL_LAYER_POLICIES_TRANSPORT_PROTOCOL_POLICY_H diff --git a/ssf-1.1.0/src/services/CMakeLists.txt b/ssf-1.1.0/src/services/CMakeLists.txt new file mode 100644 index 000000000..79172c5fe --- /dev/null +++ b/ssf-1.1.0/src/services/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 2.8) + +if(NOT DEP_BOOST_VERSION) + set(DEP_BOOST_VERSION 1.55.0) +endif() + +add_subdirectory(./socks) +add_subdirectory(./fibers_to_sockets) +add_subdirectory(./sockets_to_fibers) + + + + diff --git a/ssf-1.1.0/src/services/admin/admin.h b/ssf-1.1.0/src/services/admin/admin.h new file mode 100644 index 000000000..0e251ddeb --- /dev/null +++ b/ssf-1.1.0/src/services/admin/admin.h @@ -0,0 +1,249 @@ +#ifndef SSF_SERVICES_ADMIN_ADMIN_H_ +#define SSF_SERVICES_ADMIN_ADMIN_H_ + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include // NOLINT + +#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" + +#include "services/admin/requests/create_service_request.h" +#include "services/admin/admin_command.h" +#include "services/admin/requests/stop_service_request.h" + +#include "core/factory_manager/service_factory_manager.h" +#include "core/factories/service_factory.h" + +#include "services/user_services/base_user_service.h" + +namespace ssf { namespace services { namespace admin { + +template +class Admin : public BaseService { +public: + typedef typename ssf::services::BaseUserService::BaseUserServicePtr + BaseUserServicePtr; + typedef std::function AdminCallbackType; + + private: + typedef typename Demux::local_port_type local_port_type; + + typedef std::shared_ptr AdminPtr; + typedef ItemManager::BaseServicePtr> + ServiceManager; + + typedef typename ssf::BaseService::Parameters Parameters; + typedef typename ssf::BaseService::demux demux; + typedef typename ssf::BaseService::endpoint endpoint; + + typedef std::function CommandHandler; + typedef std::map IdToCommandHandlerMap; + + public: + static AdminPtr Create(boost::asio::io_service& io_service, + Demux& fiber_demux, Parameters parameters) { + return std::shared_ptr(new Admin(io_service, fiber_demux)); + } + + ~Admin() {} + + enum { + factory_id = 1, + 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 + }; + + static void RegisterToServiceFactory( + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, &Admin::Create); + } + + void set_server(); + 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) { + std::string parameters_buff_to_send = request.OnSending(); + + auto serial = GetAvailableSerial(); + InsertHandler(serial, handler); + + auto p_command = std::make_shared( + serial, request.command_id, (uint32_t)parameters_buff_to_send.size(), + parameters_buff_to_send); + + auto do_handler = + [p_command](const boost::system::error_code& ec, size_t length) {}; + + async_SendCommand(*p_command, do_handler); + } + + void InsertHandler(uint32_t serial, CommandHandler command_handler) { + boost::recursive_mutex::scoped_lock lock1(command_handlers_mutex_); + command_handlers_[serial] = command_handler; + } + + // 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->EraseHandler(command_serial_received_); + } + } + + void EraseHandler(uint32_t serial) { + boost::recursive_mutex::scoped_lock lock1(command_handlers_mutex_); + command_handlers_.erase(serial); + } + + uint32_t GetAvailableSerial() { + boost::recursive_mutex::scoped_lock lock1(command_handlers_mutex_); + + for (uint32_t serial = 3; serial < std::numeric_limits::max(); + ++serial) { + if (command_handlers_.count(serial + !!is_server_) == 0) { + command_handlers_[serial + !!is_server_] = + [](const boost::system::error_code&) {}; + return serial + !!is_server_; + } + } + return 0; + } + + private: + Admin(boost::asio::io_service& io_service, Demux& fiber_demux); + + void StartAccept(); + void HandleAccept(const boost::system::error_code& ec); + void StartConnect(); + void HandleConnect(const boost::system::error_code& ec); + void HandleStop(); + void Initialize(); + void StartRemoteService( + const admin::CreateServiceRequest& create_request, + const CommandHandler& handler); + void StopRemoteService(const admin::StopServiceRequest& stop_request, + const CommandHandler& handler); + void InitializeRemoteServices(const boost::system::error_code& ec); + void ListenForCommand(); + void DoAdmin( + const boost::system::error_code& ec = boost::system::error_code(), + size_t length = 0); + void PostKeepAlive(const boost::system::error_code& ec, size_t length); + void SendKeepAlive(const boost::system::error_code& ec); + void ReceiveInstructionHeader(); + void ReceiveInstructionParameters(); + void TreatInstructionId(); + void CreateNewService(); + void ShutdownServices(); + + template + void async_SendCommand(const AdminCommand& command, Handler handler) { + auto do_handler = [handler](const boost::system::error_code& ec, + size_t length) { handler(ec, length); }; + + boost::asio::async_write(fiber_, command.const_buffers(), do_handler); + } + + void Notify(ssf::services::initialisation::type type, + BaseUserServicePtr p_user_service, boost::system::error_code ec) { + if (callback_) { + this->get_io_service().post(boost::bind(callback_, std::move(type), + p_user_service, std::move(ec))); + } + } + + template + auto Then(Handler handler, + This me) -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { + return boost::bind(handler, me->SelfFromThis(), _1); + } + + std::shared_ptr SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); + } + + private: + // Version information + uint8_t admin_version_; + + // For initiating fiber connection + bool is_server_; + typename ssf::BaseService::fiber_acceptor fiber_acceptor_; + typename ssf::BaseService::fiber fiber_; + + // The status in which the admin service is + uint32_t status_; + + // Buffers to receive commands + uint32_t command_serial_received_; + uint32_t command_id_received_; + uint32_t command_size_received_; + std::vector parameters_buff_received_; + + // Keep alives + uint32_t reserved_keep_alive_id_; + uint32_t reserved_keep_alive_size_; + std::string reserved_keep_alive_parameters_; + boost::asio::deadline_timer reserved_keep_alive_timer_; + + // List of user services + std::vector user_services_; + + // Connection attempts + uint8_t retries_; + + // Is stopped? + boost::recursive_mutex stopping_mutex_; + bool stopped_; + + // Initialize services + boost::asio::coroutine coroutine_; + size_t i_; + std::vector> create_request_vector_; + size_t j_; + uint32_t remote_all_started_; + uint16_t init_retries_; + std::vector> stop_request_vector_; + bool local_all_started_; + boost::system::error_code init_ec_; + + boost::recursive_mutex command_handlers_mutex_; + IdToCommandHandlerMap command_handlers_; + + AdminCallbackType callback_; +}; + +} // admin +} // services +} // ssf + +#include "services/admin/admin.ipp" + +#endif // SSF_SERVICES_ADMIN_ADMIN_H_ diff --git a/ssf-1.1.0/src/services/admin/admin.ipp b/ssf-1.1.0/src/services/admin/admin.ipp new file mode 100644 index 000000000..6ea846924 --- /dev/null +++ b/ssf-1.1.0/src/services/admin/admin.ipp @@ -0,0 +1,467 @@ +#ifndef SSF_SERVICES_ADMIN_ADMIN_IPP_ +#define SSF_SERVICES_ADMIN_ADMIN_IPP_ + +#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), + admin_version_(1), + is_server_(0), + fiber_acceptor_(io_service), + fiber_(io_service), + reserved_keep_alive_id_(0), + reserved_keep_alive_size_(0), + reserved_keep_alive_parameters_(), + reserved_keep_alive_timer_(io_service), + retries_(0), + stopping_mutex_(), + stopped_(false), + callback_() {} + +//----------------------------------------------------------------------------- +template +void Admin::set_server() { + is_server_ = 1; + endpoint ep(this->get_demux(), service_port); + fiber_acceptor_.bind(ep, init_ec_); + fiber_acceptor_.listen(boost::asio::socket_base::max_connections, init_ec_); +} + +//----------------------------------------------------------------------------- +template +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_; + + if (!init_ec_) { + if (is_server_) { + StartAccept(); + } else { + StartConnect(); + } + } +} + +//----------------------------------------------------------------------------- +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()); + + 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"; + + if (!fiber_acceptor_.is_open()) { + return; + } + + if (ec) { + BOOST_LOG_TRIVIAL(error) << "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"; + + if (!fiber_.is_open() || ec) { + BOOST_LOG_TRIVIAL(error) + << "service admin: no new connection: " << ec.message() << " " + << ec.value(); + // Retry to connect if failed to open the fiber + if (retries_ < 50) { + this->StartConnect(); + ++retries_; + } + return; + } else { + this->Initialize(); + this->InitializeRemoteServices(boost::system::error_code()); + } +} + +//----------------------------------------------------------------------------- +template +void Admin::Initialize() { + // Initialize the command reception processes + this->ListenForCommand(); + + // Initialize the keep alive processes + this->PostKeepAlive(boost::system::error_code(), 0); +} + +//----------------------------------------------------------------------------- +#include // NOLINT + +/// Initialize micro services (local and remote) asked by the client +template +void Admin::InitializeRemoteServices( + const boost::system::error_code& ec) { + if (!ec) { + 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(); + + // 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)); + } + + // At this point, all remote services have responded with their statuses + remote_all_started_ = + user_services_[i_]->CheckRemoteServiceStatus(this->get_demux()); + + // If something went wrong remote_all_started_ > 0 + if (remote_all_started_) { + BOOST_LOG_TRIVIAL(warning) << "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())); + + // Get the remote micro services to stop + stop_request_vector_ = + user_services_[i_]->GetRemoteServiceStopVector(this->get_demux()); + for (j_ = 0; j_ < stop_request_vector_.size(); ++j_) { + // Send a service stop request + yield StopRemoteService( + stop_request_vector_[j_], + boost::bind(&Admin::InitializeRemoteServices, + this->SelfFromThis(), _1)); + } + + return; + } + + // Start local associated services + local_all_started_ = + user_services_[i_]->StartLocalServices(this->get_demux()); + + // If something went wrong local_all_started_ == false + if (!local_all_started_) { + BOOST_LOG_TRIVIAL(warning) << "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())); + + // Get the remote micro services to stop + stop_request_vector_ = + user_services_[i_]->GetRemoteServiceStopVector(this->get_demux()); + for (j_ = 0; j_ < stop_request_vector_.size(); ++j_) { + // Send a service stop request + yield StopRemoteService( + stop_request_vector_[j_], + boost::bind(&Admin::InitializeRemoteServices, + this->SelfFromThis(), _1)); + } + // 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())); + } + } + } else { + BOOST_LOG_TRIVIAL(debug) << "service admin: ec intializing " << ec.value(); + } +} +#include // NOLINT + +//----------------------------------------------------------------------------- +template +void Admin::ListenForCommand() { + status_ = 101; + this->get_io_service().post(boost::bind(&Admin::DoAdmin, this->SelfFromThis(), + boost::system::error_code(), 0)); +} + +//----------------------------------------------------------------------------- +template +void Admin::DoAdmin(const boost::system::error_code& ec, size_t length) { + switch (status_) { + case 101: + if (!ec) { + this->ReceiveInstructionHeader(); + } else { + this->get_io_service().post( + boost::bind(&Admin::ShutdownServices, SelfFromThis())); + return; + } + break; + + case 102: + if (!ec) { + if (command_id_received_) { + this->ReceiveInstructionParameters(); + } else { + this->ListenForCommand(); + } + } else { + this->get_io_service().post( + boost::bind(&Admin::ShutdownServices, SelfFromThis())); + return; + } + break; + + case 103: + if (!ec) { + this->TreatInstructionId(); + } else { + this->get_io_service().post( + boost::bind(&Admin::ShutdownServices, SelfFromThis())); + return; + } + break; + + default: + /*get_io_service().post(boost::bind(&Admin::DoAdmin, + SelfFromThis(), + boost::system::error_code(), + 0));*/ + break; + } +} + +//----------------------------------------------------------------------------- +template +void Admin::ReceiveInstructionHeader() { + status_ = 102; // Receive current instruction parameter size + + std::array buff = { + {boost::asio::buffer(&command_serial_received_, + sizeof(command_serial_received_)), + boost::asio::buffer(&command_id_received_, sizeof(command_id_received_)), + boost::asio::buffer(&command_size_received_, + sizeof(command_size_received_))}}; + + // receive the command header + boost::asio::async_read(fiber_, buff, + boost::bind(&Admin::DoAdmin, SelfFromThis(), _1, _2)); +} + +//----------------------------------------------------------------------------- +template +void Admin::ReceiveInstructionParameters() { + status_ = 103; // Handler current instruction + + // @todo check size + parameters_buff_received_.resize(command_size_received_); + + // Receive the command parameters + boost::asio::async_read( + fiber_, boost::asio::buffer(parameters_buff_received_), + boost::bind(&Admin::DoAdmin, this->SelfFromThis(), _1, _2)); +} + +/// 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; + CommandExecuterType* p_executer = + ssf::CommandFactory::GetExecuter(command_id_received_); + + const std::string serialized(¶meters_buff_received_[0], + parameters_buff_received_.size()); + std::istringstream istrs(serialized); + boost::archive::text_iarchive ar(istrs); + + Demux& d = this->get_demux(); + + // @todo Add a not found error ? + if (p_executer) { + // Execute and get the result + boost::system::error_code ec; + std::string serialized_result = (*p_executer)(ar, &d, ec); + + // Get the right function to reply + using CommandReplierType = + typename ssf::CommandFactory::CommandReplierType; + CommandReplierType* p_replier = + ssf::CommandFactory::GetReplier(command_id_received_); + + std::istringstream istrs2(serialized); + boost::archive::text_iarchive ar2(istrs2); + + // Get the reply + std::string reply = (*p_replier)(ar2, &d, ec, serialized_result); + + // If there is something to send back + if (reply.size() > 0) { + uint32_t* p_reply_command_index = + ssf::CommandFactory::GetReplyCommandIndex( + 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); + + this->async_SendCommand( + *p_command, [p_command](const boost::system::error_code&, size_t) {}); + } + } + + // Execute an handler bound to the serial command (e.g, callback after a + // service started) + this->ExecuteAndRemoveCommandHandler(command_serial_received_); + + this->ListenForCommand(); +} + +//----------------------------------------------------------------------------- +template +void Admin::StartRemoteService( + const admin::CreateServiceRequest& create_request, + const CommandHandler& handler) { + this->Command(create_request, handler); +} + +//----------------------------------------------------------------------------- +template +void Admin::StopRemoteService( + const admin::StopServiceRequest& stop_request, + const CommandHandler& handler) { + this->Command(stop_request, handler); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +template +void Admin::SendKeepAlive(const boost::system::error_code& ec) { + if (!ec) { + auto p_command = std::make_shared( + 0, reserved_keep_alive_id_, reserved_keep_alive_size_, + reserved_keep_alive_parameters_); + + auto self = this->SelfFromThis(); + + 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())); + return; + } +} + +//----------------------------------------------------------------------------- +template +void Admin::PostKeepAlive(const boost::system::error_code& ec, + 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)); + } else { + this->get_io_service().post( + boost::bind(&Admin::ShutdownServices, SelfFromThis())); + return; + } +} + +//----------------------------------------------------------------------------- +template +void Admin::ShutdownServices() { + boost::recursive_mutex::scoped_lock lock(stopping_mutex_); + if (!stopped_) { + demux& d = this->get_demux(); + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&d); + if (p_service_factory) { + p_service_factory->Destroy(); + } + stopped_ = true; + } +} + +//----------------------------------------------------------------------------- +template +void Admin::HandleStop() { + reserved_keep_alive_timer_.cancel(); + fiber_acceptor_.close(); + fiber_.close(); +} + +} // admin +} // services +} // ssf + +#endif // SSF_SERVICES_ADMIN_ADMIN_IPP_ diff --git a/ssf-1.1.0/src/services/admin/admin_command.h b/ssf-1.1.0/src/services/admin/admin_command.h new file mode 100644 index 000000000..6b229b29a --- /dev/null +++ b/ssf-1.1.0/src/services/admin/admin_command.h @@ -0,0 +1,51 @@ +#ifndef SSF_SERVICES_ADMIN_ADMIN_COMMAND_H_ +#define SSF_SERVICES_ADMIN_ADMIN_COMMAND_H_ + +#include + +#include +#include +#include +#include + +#include + +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) {} + + 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_) + } + }; + + return buf; + } + +private: + uint32_t serial_; + uint32_t command_id_; + uint32_t serialize_arguments_size_; + std::string serialized_arguments_; +}; + +} // admin +} // services +} // ssf + +#endif // SSF_SERVICES_ADMIN_ADMIN_COMMAND_H_ diff --git a/ssf-1.1.0/src/services/admin/requests/create_service_request.h b/ssf-1.1.0/src/services/admin/requests/create_service_request.h new file mode 100644 index 000000000..4d6f8bb81 --- /dev/null +++ b/ssf-1.1.0/src/services/admin/requests/create_service_request.h @@ -0,0 +1,142 @@ +#ifndef SSF_SERVICES_ADMIN_REQUESTS_CREATE_SERVICE_REQUEST_H_ +#define SSF_SERVICES_ADMIN_REQUESTS_CREATE_SERVICE_REQUEST_H_ + +#include + +#include +#include +#include +#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" + + +#include "services/admin/requests/service_status.h" + +namespace ssf { namespace services { namespace admin { + +template +class CreateServiceRequest { +private: + typedef std::map Parameters; +public: + CreateServiceRequest() {} + + CreateServiceRequest(uint32_t service_id) + : service_id_(service_id) {} + + CreateServiceRequest(uint32_t service_id, Parameters parameters) + : service_id_(service_id), parameters_(parameters) {} + + enum { + command_id = 1, + reply_id = 2 + }; + + static void RegisterToCommandFactory() { + 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) { + CreateServiceRequest request; + + try { + ar >> request; + } catch (const std::exception&) { + return std::string(); + } + + 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(); + + std::stringstream ss; + std::string result; + + ss << id; + ss >> result; + + return result; + } + + static std::string OnReply(boost::archive::text_iarchive& ar, + Demux* p_demux, + const boost::system::error_code& ec, + std::string serialized_result) { + CreateServiceRequest request; + + try { + ar >> request; + } catch (const std::exception&) { + return std::string(); + } + + ServiceStatus reply(std::stoul(serialized_result), + request.service_id(), + ec.value(), + request.parameters()); + + return reply.OnSending(); + } + + std::string OnSending() const { + std::ostringstream ostrs; + boost::archive::text_oarchive ar(ostrs); + + ar << *this; + + return ostrs.str(); + } + + uint32_t service_id() { + return service_id_; + } + + Parameters parameters() { + return parameters_; + } + + void add_parameter(const std::string& key, const std::string& value) { + parameters_[key] = value; + } + +private: + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & service_id_; + ar & BOOST_SERIALIZATION_NVP(parameters_); + } + +private: + uint32_t service_id_; + Parameters parameters_; +}; + +} // admin +} // services +} // ssf + + +#endif // SSF_SERVICES_ADMIN_REQUESTS_CREATE_SERVICE_REQUEST_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/services/admin/requests/service_status.h b/ssf-1.1.0/src/services/admin/requests/service_status.h new file mode 100644 index 000000000..37d6e9862 --- /dev/null +++ b/ssf-1.1.0/src/services/admin/requests/service_status.h @@ -0,0 +1,147 @@ +#ifndef SSF_SERVICES_ADMIN_REQUESTS_SERVICE_STATUS_H_ +#define SSF_SERVICES_ADMIN_REQUESTS_SERVICE_STATUS_H_ + +#include + +#include +#include +#include + +#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 { + +template +class ServiceStatus { +private: + typedef std::map Parameters; +public: + ServiceStatus() {} + + 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 + }; + + static void RegisterToCommandFactory() { + CommandFactory::RegisterOnReceiveCommand(command_id, + &ServiceStatus::OnReceive); + CommandFactory::RegisterOnReplyCommand(command_id, + &ServiceStatus::OnReply); + CommandFactory::RegisterReplyCommandIndex(command_id, reply_id); + } + + 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&) { + return std::string(); + } + + 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); + } else { + 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(); + + 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) { + return std::string(); + } + + std::string OnSending() const { + std::ostringstream ostrs; + boost::archive::text_oarchive ar(ostrs); + + ar << *this; + + return ostrs.str(); + } + + uint32_t id() { + return id_; + } + + uint32_t service_id() { + return service_id_; + } + + Parameters parameters() { + return parameters_; + } + + uint32_t error_code_value() { + return error_code_value_; + } + + void add_parameter(std::string key, std::string value) { + parameters_[key] = value; + } + +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_); + } + +private: + uint32_t id_; + uint32_t service_id_; + uint32_t error_code_value_; + Parameters parameters_; +}; + +} // admin +} // services +} // ssf + + +#endif // SSF_SERVICES_ADMIN_REQUESTS_SERVICE_STATUS_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/services/admin/requests/stop_service_request.h b/ssf-1.1.0/src/services/admin/requests/stop_service_request.h new file mode 100644 index 000000000..b376e0829 --- /dev/null +++ b/ssf-1.1.0/src/services/admin/requests/stop_service_request.h @@ -0,0 +1,129 @@ +#ifndef SSF_SERVICES_ADMIN_REQUESTS_STOP_SERVICE_REQUEST_H_ +#define SSF_SERVICES_ADMIN_REQUESTS_STOP_SERVICE_REQUEST_H_ + +#include + +#include +#include +#include +#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" + +#include "services/admin/requests/service_status.h" + +namespace ssf { namespace services { namespace admin { + +template +class StopServiceRequest { +private: + typedef std::map Parameters; +public: + StopServiceRequest() {} + + StopServiceRequest(uint32_t unique_id) + : unique_id_(unique_id) {} + + enum { + command_id = 3, + reply_id = 2 + }; + + static void RegisterToCommandFactory() { + 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) { + StopServiceRequest request; + + try { + ar >> request; + } + catch (const std::exception&) { + return std::string(); + } + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(p_demux); + + p_service_factory->StopService(request.unique_id()); + + BOOST_LOG_TRIVIAL(debug) << "service status: stop request"; + + ec.assign(boost::system::errc::interrupted, + boost::system::system_category()); + + std::stringstream ss; + std::string result; + + ss << request.unique_id(); + ss >> result; + + return result; + } + + static std::string OnReply(boost::archive::text_iarchive& ar, + Demux* p_demux, + const boost::system::error_code& ec, + std::string serialized_result) { + StopServiceRequest request; + + try { + ar >> request; + } catch (const std::exception&) { + return std::string(); + } + + ServiceStatus reply(std::stoul(serialized_result), + 0, + ec.value(), + Parameters()); + + return reply.OnSending(); + } + + std::string OnSending() const { + std::ostringstream ostrs; + boost::archive::text_oarchive ar(ostrs); + + ar << *this; + + return ostrs.str(); + } + + uint32_t unique_id() { + return unique_id_; + } + +private: + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & unique_id_; + } + +private: + uint32_t unique_id_; +}; + +} // admin +} // services +} // ssf + + +#endif // SSF_SERVICES_ADMIN_REQUESTS_STOP_SERVICE_REQUEST_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/services/base_service.h b/ssf-1.1.0/src/services/base_service.h new file mode 100644 index 000000000..323e6b041 --- /dev/null +++ b/ssf-1.1.0/src/services/base_service.h @@ -0,0 +1,77 @@ +#ifndef SSF_SERVICES_BASE_SERVICE_H_ +#define SSF_SERVICES_BASE_SERVICE_H_ + +#include + +#include +#include +#include + +#include +#include + +#include "common/boost/fiber/stream_fiber.hpp" +#include "common/boost/fiber/datagram_fiber.hpp" + +namespace ssf { +//---------------------------------------------------------------------------- +/// Base class for services +template +class BaseService : public std::enable_shared_from_this> { + public: + typedef std::shared_ptr> BaseServicePtr; + + typedef typename Demux::socket_type socket_type; + typedef typename boost::asio::fiber::stream_fiber::socket fiber; + typedef typename boost::asio::fiber::datagram_fiber::socket + fiber_datagram; + typedef typename boost::asio::fiber::stream_fiber::acceptor + fiber_acceptor; + typedef + typename boost::asio::fiber::stream_fiber::endpoint endpoint; + typedef typename boost::asio::fiber::datagram_fiber::endpoint + datagram_endpoint; + typedef Demux demux; + + typedef std::map Parameters; + + public: + virtual void start(boost::system::error_code&) = 0; + virtual void stop(boost::system::error_code&) = 0; + + virtual uint32_t service_type_id() = 0; + + virtual ~BaseService() {} + + void set_local_id(uint32_t local_id) { local_id_ = local_id; } + + uint32_t local_id() { return local_id_; } + + protected: + /// Constructor + /** + * @param io_service the io_service used for all asynchronous operations + * @param demux the demultiplexer used to send and receive data + */ + BaseService(boost::asio::io_service& io_service, demux& demux) + : io_service_(io_service), demux_(demux) {} + + /// Accessor for the io_service + boost::asio::io_service& get_io_service() { return io_service_; } + + /// Accessor for the demultiplexer + demux& get_demux() { return demux_; } + + private: + BaseService(const BaseService&) = delete; + BaseService& operator=(const BaseService&) = delete; + + private: + boost::asio::io_service& io_service_; + demux& demux_; + uint32_t local_id_; +}; + +} // ssf + +#endif // SSF_SERVICES_BASE_SERVICE_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/services/copy_file/fiber_to_file/fiber_to_file.h b/ssf-1.1.0/src/services/copy_file/fiber_to_file/fiber_to_file.h new file mode 100644 index 000000000..5c9af465a --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/fiber_to_file/fiber_to_file.h @@ -0,0 +1,127 @@ +#ifndef SSF_SERVICES_COPY_FILE_FIBER_TO_FILE_FIBER_TO_FILE_H_ +#define SSF_SERVICES_COPY_FILE_FIBER_TO_FILE_FIBER_TO_FILE_H_ + +#include + +#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" + +namespace ssf { +namespace services { +namespace copy_file { +namespace fiber_to_file { + +template +class FiberToFile : public BaseService { + private: + using FiberToFilePtr = std::shared_ptr; + using SessionManager = ItemManager; + using Parameters = typename ssf::BaseService::Parameters; + using fiber_port = typename Demux::local_port_type; + using demux = typename ssf::BaseService::demux; + using fiber = typename ssf::BaseService::fiber; + using endpoint = typename ssf::BaseService::endpoint; + using fiber_acceptor = typename ssf::BaseService::fiber_acceptor; + + public: + enum { factory_id = 7, kServicePort = 40 }; + + public: + // Factory method to create the service + static FiberToFilePtr Create(boost::asio::io_service& io_service, + demux& fiber_demux, Parameters parameters) { + return FiberToFilePtr(new FiberToFile(io_service, fiber_demux)); + } + + virtual ~FiberToFile() {} + + // 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; + fiber_acceptor_.bind(ep, ec); + fiber_acceptor_.listen(boost::asio::socket_base::max_connections, ec); + if (ec) { + return; + } + StartAccept(); + } + + // Stop service + virtual void stop(boost::system::error_code& ec) { + BOOST_LOG_TRIVIAL(info) << "service fiber to file: stopping"; + manager_.stop_all(); + fiber_acceptor_.close(ec); + fiber_.close(ec); + } + + virtual uint32_t service_type_id() { return factory_id; } + + static void RegisterToServiceFactory( + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, &FiberToFile::Create); + } + + static ssf::services::admin::CreateServiceRequest GetCreateRequest() { + ssf::services::admin::CreateServiceRequest create(factory_id); + + return create; + } + + private: + FiberToFile(boost::asio::io_service& io_service, demux& fiber_demux) + : BaseService(io_service, fiber_demux), + fiber_acceptor_(io_service), + fiber_(io_service) {} + + void StartAccept() { + if (fiber_acceptor_.is_open()) { + fiber_acceptor_.async_accept( + fiber_, Then(&FiberToFile::StartDataForwarderSessionHandler, + this->SelfFromThis())); + } + } + + // 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"; + return; + } + + auto p_session = FiberToOstreamSession::Create( + &manager_, std::move(fiber_)); + boost::system::error_code start_ec; + manager_.start(p_session, start_ec); + StartAccept(); + } + + template + auto Then(Handler handler, + This me) -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { + return boost::bind(handler, me->SelfFromThis(), _1); + } + + std::shared_ptr SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); + } + + private: + bool accept_; + fiber_acceptor fiber_acceptor_; + fiber fiber_; + SessionManager manager_; +}; + +} // fiber_to_file +} // copy_file +} // services +} // ssf + +#endif // SSF_SERVICES_COPY_FILE_FIBER_TO_FILE_FIBER_TO_FILE_H_ diff --git a/ssf-1.1.0/src/services/copy_file/fiber_to_file/fiber_to_ofstream_session.h b/ssf-1.1.0/src/services/copy_file/fiber_to_file/fiber_to_ofstream_session.h new file mode 100644 index 000000000..6cb2baf39 --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/fiber_to_file/fiber_to_ofstream_session.h @@ -0,0 +1,177 @@ +#ifndef SRC_SERVICES_COPY_FILE_FIBER_TO_FILE_FIBER_TO_OSTREAM_SESSION_H_ +#define SRC_SERVICES_COPY_FILE_FIBER_TO_FILE_FIBER_TO_OSTREAM_SESSION_H_ + +#include + +#include +#include +#include + +#include + +#include "common/network/base_session.h" // NOLINT + +#include "common/network/manager.h" +#include "common/network/base_session.h" + +#include "services/copy_file/filename_buffer.h" +#include "services/copy_file/packet/packet.h" + +namespace ssf { +namespace services { +namespace copy_file { +namespace fiber_to_file { + +template +class FiberToOstreamSession : public ssf::BaseSession { + public: + using StopHandler = std::function; + using FiberToOstreamSessionPtr = std::shared_ptr; + + // Type for the class managing the different forwarding links + using SessionManager = ItemManager; + // Buffer for the transiting data + using Packet = ssf::services::copy_file::packet::Packet; + + public: + template + static FiberToOstreamSessionPtr Create(Args&&... args) { + return std::shared_ptr( + new FiberToOstreamSession(std::forward(args)...)); + } + + // Start read from stream socket and write to output stream + virtual void start(boost::system::error_code&) { + ForwardData(boost::system::error_code(), 0); + } + + // Stop session + virtual void stop(boost::system::error_code& ec) { + BOOST_LOG_TRIVIAL(debug) << "session fiber to file : stopped"; + input_socket_stream_.close(ec); + // connection interrupted without prior notification (broken pipe) + // delete output file + if (output_stream_.is_open()) { + output_stream_.close(); + std::remove(request_.GetFilename().c_str()); + } + } + + private: + FiberToOstreamSession(SessionManager* p_manager, + InputSocketStream input_socket_stream) + : input_socket_stream_(std::move(input_socket_stream)), + p_manager_(p_manager), + output_stream_ok_(true) {} + +#include // NOLINT + // Read output filename + // Transfer data from fiber to file + void ForwardData(const boost::system::error_code& ec, std::size_t length) { + if (ec) { + boost::system::error_code stop_ec; + // connection interrupted without prior notification (broken pipe) + if (output_stream_.is_open()) { + output_stream_.close(); + std::remove(request_.GetFilename().c_str()); + } + p_manager_->stop(this->SelfFromThis(), stop_ec); + + return; + } + + reenter(coroutine_) { + // read filename_size from request + yield boost::asio::async_read( + input_socket_stream_, request_.GetFilenameSizeMutBuffers(), + boost::bind(&FiberToOstreamSession::ForwardData, this->SelfFromThis(), + _1, _2)); + + // read filename from request + yield boost::asio::async_read( + input_socket_stream_, request_.GetFilenameMutBuffers(), + boost::bind(&FiberToOstreamSession::ForwardData, this->SelfFromThis(), + _1, _2)); + + BOOST_LOG_TRIVIAL(info) + << "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"; + boost::system::error_code stop_ec; + p_manager_->stop(this->SelfFromThis(), stop_ec); + + return; + } + + while (input_socket_stream_.is_open()) { + // Read type packet + yield boost::asio::async_read( + input_socket_stream_, packet_.GetTypeMutBuf(), + boost::bind(&FiberToOstreamSession::ForwardData, + this->SelfFromThis(), _1, _2)); + + if (packet_.IsDataPacket()) { + // Read size packet + yield boost::asio::async_read( + input_socket_stream_, packet_.GetSizeMutBuf(), + boost::bind(&FiberToOstreamSession::ForwardData, + this->SelfFromThis(), _1, _2)); + // Read binary data + yield boost::asio::async_read( + input_socket_stream_, packet_.GetPayloadMutBuf(), + boost::bind(&FiberToOstreamSession::ForwardData, + this->SelfFromThis(), _1, _2)); + // Write in input file + output_stream_.write(packet_.buffer().data(), packet_.size()); + } else { + yield boost::asio::async_read( + input_socket_stream_, packet_.GetSignalMutBuf(), + boost::bind(&FiberToOstreamSession::ForwardData, + this->SelfFromThis(), _1, _2)); + + // Close file handler + output_stream_.close(); + if (packet_.signal() == Packet::kInterrupted) { + // delete file -> transmission interrupted + std::remove(request_.GetFilename().c_str()); + } + break; + } + } + + // stop session + boost::system::error_code stop_ec; + p_manager_->stop(this->SelfFromThis(), stop_ec); + } + } +#include // NOLINT + + FiberToOstreamSessionPtr SelfFromThis() { + return std::static_pointer_cast( + this->shared_from_this()); + } + + private: + InputSocketStream input_socket_stream_; + SessionManager* p_manager_; + + // Coroutine variables + boost::asio::coroutine coroutine_; + bool output_stream_ok_; + std::ofstream output_stream_; + Packet packet_; + FilenameBuffer request_; +}; + +} // fiber_to_file +} // copy_file +} // services +} // ssf + +#endif // SRC_SERVICES_COPY_FILE_FIBER_TO_FILE_FIBER_TO_OSTREAM_SESSION_H_ diff --git a/ssf-1.1.0/src/services/copy_file/file_enquirer/file_enquirer.h b/ssf-1.1.0/src/services/copy_file/file_enquirer/file_enquirer.h new file mode 100644 index 000000000..812230a4f --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/file_enquirer/file_enquirer.h @@ -0,0 +1,141 @@ +#ifndef SSF_SERVICES_COPY_FILE_FILE_ENQUIRER_FILE_ENQUIRER_H_ +#define SSF_SERVICES_COPY_FILE_FILE_ENQUIRER_FILE_ENQUIRER_H_ + +#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" + +namespace ssf { +namespace services { +namespace copy_file { +namespace file_enquirer { + +template +class FileEnquirer : public BaseService { + private: + using Endpoint = typename ssf::BaseService::endpoint; + using FileEnquirerPtr = std::shared_ptr; + using FiberPort = typename Demux::local_port_type; + using Fiber = typename ssf::BaseService::fiber; + using FiberPtr = std::shared_ptr; + using FilenameBuffer = ssf::services::copy_file::FilenameBuffer; + using Parameters = typename ssf::BaseService::Parameters; + + public: + enum { factory_id = 9 }; + + static FileEnquirerPtr Create(boost::asio::io_service& io_service, + Demux& fiber_demux, Parameters parameters) { + if (parameters.count("input_pattern") != 0 && + parameters.count("output_pattern") != 0) { + return FileEnquirerPtr(new FileEnquirer(io_service, fiber_demux, + parameters["input_pattern"], + parameters["output_pattern"])); + } + + return nullptr; + } + + virtual ~FileEnquirer() {} + + virtual void start(boost::system::error_code& ec) { + SendRequest(boost::system::error_code(), 0); + } + + virtual void stop(boost::system::error_code& ec) { fiber_.close(ec); } + + virtual uint32_t service_type_id() { return factory_id; } + + static void RegisterToServiceFactory( + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, &FileEnquirer::Create); + } + + static ssf::services::admin::CreateServiceRequest GetCreateRequest( + const std::string& input_pattern, const std::string& output_pattern) { + ssf::services::admin::CreateServiceRequest create(factory_id); + create.add_parameter("input_pattern", input_pattern); + create.add_parameter("output_pattern", output_pattern); + + return create; + } + + private: + FileEnquirer(boost::asio::io_service& io_service, Demux& fiber_demux, + const std::string& input_pattern, + const std::string& output_pattern) + : BaseService(io_service, fiber_demux), + fiber_(io_service), + input_request_(input_pattern), + output_request_(output_pattern), + remote_endpoint_(fiber_demux, + ssf::services::copy_file::file_to_fiber::FileToFiber< + Demux>::kServicePort) {} + +#include + // Connect to remote file to fiber service and send input and output pattern + void SendRequest(const boost::system::error_code& ec, std::size_t length) { + if (ec) { + boost::system::error_code close_ec; + fiber_.close(close_ec); + return; + } + + reenter(coroutine_) { + BOOST_LOG_TRIVIAL(debug) + << "service file enquirer: connect to remote fiber acceptor port " + << remote_endpoint_.port(); + + yield fiber_.async_connect( + remote_endpoint_, + boost::bind(&FileEnquirer::SendRequest, this->SelfFromThis(), _1, 0)); + + // write input pattern size from request + yield boost::asio::async_write( + fiber_, input_request_.GetFilenameSizeConstBuffers(), + boost::bind(&FileEnquirer::SendRequest, this->SelfFromThis(), _1, + _2)); + + // write input pattern from request + yield boost::asio::async_write(fiber_, + input_request_.GetFilenameConstBuffers(), + boost::bind(&FileEnquirer::SendRequest, + this->SelfFromThis(), _1, _2)); + + // write output pattern size from request + yield boost::asio::async_write( + fiber_, output_request_.GetFilenameSizeConstBuffers(), + boost::bind(&FileEnquirer::SendRequest, this->SelfFromThis(), _1, + _2)); + + // write output pattern from request + yield boost::asio::async_write(fiber_, + output_request_.GetFilenameConstBuffers(), + boost::bind(&FileEnquirer::SendRequest, + this->SelfFromThis(), _1, _2)); + } + } +#include + + FileEnquirerPtr SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); + } + + private: + Fiber fiber_; + + // coroutine variables + boost::asio::coroutine coroutine_; + FilenameBuffer input_request_; + FilenameBuffer output_request_; + Endpoint remote_endpoint_; +}; + +} // file_enquirer +} // copy_file +} // services +} // ssf + +#endif // SSF_SERVICES_COPY_FILE_FILE_ENQUIRER_FILE_ENQUIRER_H_ diff --git a/ssf-1.1.0/src/services/copy_file/file_to_fiber/file_to_fiber.h b/ssf-1.1.0/src/services/copy_file/file_to_fiber/file_to_fiber.h new file mode 100644 index 000000000..f90803a3b --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/file_to_fiber/file_to_fiber.h @@ -0,0 +1,383 @@ +#ifndef SSF_SERVICES_COPY_FILE_FILE_TO_FIBER_FILE_TO_FIBER_H_ +#define SSF_SERVICES_COPY_FILE_FILE_TO_FIBER_FILE_TO_FIBER_H_ + +#ifdef WIN32 +#include +#include +#endif + +#include + +#include +#include +#include + +#include +#include +#include +#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" + +#include "services/copy_file/filename_buffer.h" +#include "services/copy_file/filesystem/filesystem.h" +#include "services/copy_file/fiber_to_file/fiber_to_file.h" + +namespace ssf { +namespace services { +namespace copy_file { +namespace file_to_fiber { + +template +class FileToFiber : public BaseService { + private: + using FileToFiberPtr = std::shared_ptr; + using SessionManager = ItemManager; + using Parameters = typename ssf::BaseService::Parameters; + using fiber_port = typename Demux::local_port_type; + using demux = typename ssf::BaseService::demux; + using fiber = typename ssf::BaseService::fiber; + using FiberPtr = std::shared_ptr; + using endpoint = typename ssf::BaseService::endpoint; + using fiber_acceptor = typename ssf::BaseService::fiber_acceptor; + using FilesList = std::list; + using FilesListPtr = std::shared_ptr; + using InputOutputFilesMap = std::map; + using StopSessionHandler = std::function; + using Filesystem = ssf::services::copy_file::filesystem::Filesystem; + using FilenameBuffer = ssf::services::copy_file::FilenameBuffer; + + public: + enum { factory_id = 8, kServicePort = 41 }; + + public: + // Factory method to create the service + static FileToFiberPtr Create(boost::asio::io_service& io_service, + demux& fiber_demux, Parameters parameters) { + if (parameters.count("accept") != 0) { + return FileToFiberPtr(new FileToFiber(io_service, fiber_demux)); + } + + if (parameters.count("from_stdin") != 0 && + parameters.count("input_pattern") != 0 && + parameters.count("output_pattern") != 0) { + return FileToFiberPtr(new FileToFiber( + io_service, fiber_demux, parameters["from_stdin"] == "true", + parameters["input_pattern"], parameters["output_pattern"])); + } + + return nullptr; + } + + virtual ~FileToFiber() {} + + // Start service + virtual void start(boost::system::error_code& ec) { +// set stdin in binary mode +#ifdef WIN32 + if (_setmode(_fileno(stdin), _O_BINARY) == -1) { + return; + } +#endif + + if (accept_) { + endpoint ep(this->get_demux(), kServicePort); + BOOST_LOG_TRIVIAL(info) + << "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) { + return; + } + StartAccept(); + } else { + StartTransfer(); + } + } + + // Stop service + virtual void stop(boost::system::error_code& ec) { + BOOST_LOG_TRIVIAL(info) << "service file to fiber: stopping"; + manager_.stop_all(); + boost::system::error_code close_ec; + fiber_acceptor_.close(close_ec); + +// reset stdin in text mode +#ifdef WIN32 + if (_setmode(_fileno(stdin), _O_TEXT) == -1) { + return; + } +#endif + } + + virtual uint32_t service_type_id() { return factory_id; } + + static void RegisterToServiceFactory( + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, &FileToFiber::Create); + } + + static ssf::services::admin::CreateServiceRequest GetCreateRequest( + bool server, bool from_stdin, const std::string& input_pattern, + const std::string& output_pattern) { + ssf::services::admin::CreateServiceRequest create(factory_id); + if (server) { + create.add_parameter("accept", "true"); + } else { + create.add_parameter("from_stdin", from_stdin ? "true" : "false"); + create.add_parameter("input_pattern", input_pattern); + create.add_parameter("output_pattern", output_pattern); + } + + return create; + } + + private: + FileToFiber(boost::asio::io_service& io_service, demux& fiber_demux) + : BaseService(io_service, fiber_demux), + accept_(true), + from_stdin_(false), + input_pattern_(""), + output_pattern_(""), + fiber_acceptor_(io_service) {} + + FileToFiber(boost::asio::io_service& io_service, demux& fiber_demux, + bool from_stdin, const std::string& input_pattern, + const std::string& output_pattern) + : BaseService(io_service, fiber_demux), + accept_(false), + from_stdin_(from_stdin), + input_pattern_(input_pattern), + output_pattern_(output_pattern), + fiber_acceptor_(io_service) {} + + // Start accepting new fiber (remote files to local) + // Input and output patterns will be given through the fiber + void StartAccept() { + if (fiber_acceptor_.is_open()) { + auto p_fiber = + std::make_shared(this->get_demux().get_io_service()); + fiber_acceptor_.async_accept( + *p_fiber, boost::bind(&FileToFiber::AcceptFiberHandler, + this->SelfFromThis(), _1, p_fiber)); + } + } + + void AcceptFiberHandler(const boost::system::error_code& ec, + FiberPtr p_fiber) { + if (!ec) { + StartAccept(); + ConfigureSessions(p_fiber, boost::system::error_code(), 0); + } + } + +#include + // Read input and output pattern from fiber + // Connect sessions to remote fiber to file service + void ConfigureSessions(FiberPtr p_fiber, const boost::system::error_code& ec, + std::size_t length) { + if (ec) { + boost::system::error_code close_ec; + p_fiber->close(close_ec); + return; + } + reenter(coroutine_) { + // read input pattern size from request + yield boost::asio::async_read( + *p_fiber, input_request_.GetFilenameSizeMutBuffers(), + boost::bind(&FileToFiber::ConfigureSessions, this->SelfFromThis(), + p_fiber, _1, _2)); + + // read input pattern from request + yield boost::asio::async_read( + *p_fiber, input_request_.GetFilenameMutBuffers(), + boost::bind(&FileToFiber::ConfigureSessions, this->SelfFromThis(), + p_fiber, _1, _2)); + + // read output pattern size from request + yield boost::asio::async_read( + *p_fiber, output_request_.GetFilenameSizeMutBuffers(), + boost::bind(&FileToFiber::ConfigureSessions, this->SelfFromThis(), + p_fiber, _1, _2)); + + // read output pattern from request + yield boost::asio::async_read( + *p_fiber, output_request_.GetFilenameMutBuffers(), + boost::bind(&FileToFiber::ConfigureSessions, this->SelfFromThis(), + p_fiber, _1, _2)); + + ProcessInputOutputPattern(input_request_.GetFilename(), + output_request_.GetFilename()); + } + } +#include + + // Start transfer data + void StartTransfer() { + if (!from_stdin_) { + ProcessInputOutputPattern(input_pattern_, output_pattern_); + } else { + auto p_output_files_list = std::make_shared(); + p_output_files_list->push_back(output_pattern_); + auto finish_callback = + boost::bind(&FileToFiber::FinishSentSession, this->SelfFromThis(), _1, + p_output_files_list); + AsyncConnectFileFiberSession(true, "", output_pattern_, finish_callback); + } + } + + // Get the file list and generate one transfer session per input file + void ProcessInputOutputPattern(const std::string& input_pattern, + const std::string& output_pattern) { + FilesList input_files_list; + auto p_output_files_list = std::make_shared(); + + InputOutputFilesMap input_output_files; + ParseInputOutputPatterns(input_pattern, output_pattern, + &input_output_files); + + for (auto& input_output_file_pair : input_output_files) { + p_output_files_list->push_back(input_output_file_pair.second); + } + + // If no file, callback finish handler + if (input_output_files.empty()) { + this->get_io_service().post(boost::bind(&FileToFiber::FinishSentSession, + this->SelfFromThis(), "", + p_output_files_list)); + return; + }; + + auto finish_callback = + boost::bind(&FileToFiber::FinishSentSession, this->SelfFromThis(), _1, + p_output_files_list); + + // Start one session per input file + for (auto& input_output_file_pair : input_output_files) { + AsyncConnectFileFiberSession(false, input_output_file_pair.first, + input_output_file_pair.second, + finish_callback); + } + } + + // Connect a new fiber to the fiber to file service and start an input stream + // to fiber session + void AsyncConnectFileFiberSession(bool from_stdin, + const std::string& input_file, + const std::string& output_file, + StopSessionHandler stop_handler) { + 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::services::copy_file::fiber_to_file::FiberToFile< + Demux>::kServicePort; + + endpoint ep(this->get_demux(), + ssf::services::copy_file::fiber_to_file::FiberToFile< + Demux>::kServicePort); + + p_fiber->async_connect( + ep, boost::bind(&FileToFiber::StartDataForwarderSessionHandler, + this->SelfFromThis(), _1, p_fiber, from_stdin, + input_file, output_file, stop_handler)); + } + + // Start the data forward session + void StartDataForwarderSessionHandler(const boost::system::error_code& ec, + FiberPtr p_fiber, bool from_stdin, + const std::string& input_file, + const std::string& output_file, + StopSessionHandler stop_handler) { + if (ec) { + boost::system::error_code close_ec; + p_fiber->close(close_ec); + return; + } + + if (from_stdin) { + BOOST_LOG_TRIVIAL(info) + << "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; + } + + if (!from_stdin) { + auto p_session = IstreamToFiberSession::Create( + &manager_, input_file, std::move(*p_fiber), + output_file, stop_handler); + boost::system::error_code start_ec; + manager_.start(p_session, start_ec); + } else { + auto p_session = IstreamToFiberSession::Create( + &manager_, std::move(*p_fiber), output_file, stop_handler); + boost::system::error_code start_ec; + manager_.start(p_session, start_ec); + } + } + + // This callback remove the output file from the file list and closes the + // demux service once the file list is empty + void FinishSentSession(const std::string output_file, + FilesListPtr p_files_list) { + boost::mutex::scoped_lock lock_files_set(files_set_mutex_); + p_files_list->remove(output_file); + + if (p_files_list->empty()) { + auto& demux = this->get_demux(); + auto close_demux_callback = [&demux]() { demux.close(); }; + this->get_demux().get_io_service().post(close_demux_callback); + } + } + + // Extract input filename from input pattern + // Generate output filename from input file and output_pattern + void ParseInputOutputPatterns(const std::string& input_pattern, + const std::string& output_pattern, + InputOutputFilesMap* p_input_output_files) { + auto input_files = filesystem::Filesystem::Glob(input_pattern); + for (auto& input_file : input_files) { + (*p_input_output_files)[input_file] = + output_pattern + Filesystem::GetFilename(input_file); + } + } + + template + auto Then(Handler handler, + This me) -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { + return boost::bind(handler, me->SelfFromThis(), _1); + } + + std::shared_ptr SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); + } + + private: + bool accept_; + bool from_stdin_; + std::string input_pattern_; + std::string output_pattern_; + SessionManager manager_; + fiber_acceptor fiber_acceptor_; + boost::mutex files_set_mutex_; + // Coroutine variables + boost::asio::coroutine coroutine_; + FilenameBuffer input_request_; + FilenameBuffer output_request_; +}; + +} // file_to_fiber +} // copy_file +} // services +} // ssf + +#endif // SSF_SERVICES_COPY_FILE_FILE_TO_FIBER_FILE_TO_FIBER_H_ diff --git a/ssf-1.1.0/src/services/copy_file/file_to_fiber/istream_to_fiber_session.h b/ssf-1.1.0/src/services/copy_file/file_to_fiber/istream_to_fiber_session.h new file mode 100644 index 000000000..dbc6f3f1e --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/file_to_fiber/istream_to_fiber_session.h @@ -0,0 +1,180 @@ +#ifndef SRC_SERVICES_COPY_FILE_FILE_TO_FIBER_ISTREAM_TO_FIBER_SESSION_H_ +#define SRC_SERVICES_COPY_FILE_FILE_TO_FIBER_ISTREAM_TO_FIBER_SESSION_H_ + +#include +#include +#include + +#include +#include +#include + +#include + +#include "common/network/base_session.h" + +#include "common/network/manager.h" +#include "common/network/base_session.h" + +#include "services/copy_file/filename_buffer.h" +#include "services/copy_file/packet/packet.h" + +namespace ssf { +namespace services { +namespace copy_file { +namespace file_to_fiber { + +template +class IstreamToFiberSession : public ssf::BaseSession { + public: + using IstreamToFiberSessionPtr = std::shared_ptr; + // Type for the class managing the different forwarding links + using SessionManager = ItemManager; + // Buffer for the transiting data + using FilenameBuffer = ssf::services::copy_file::FilenameBuffer; + using Packet = ssf::services::copy_file::packet::Packet; + using StopSessionHandler = std::function; + + public: + template + static IstreamToFiberSessionPtr Create(Args&&... args) { + return std::shared_ptr( + new IstreamToFiberSession(std::forward(args)...)); + } + + virtual ~IstreamToFiberSession() {} + + // Start read from input stream and write to output socket stream + virtual void start(boost::system::error_code& ec) { + 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()); + stop_handler_(output_file_); + + return; + } + } + + ForwardData(boost::system::error_code(), 0); + } + + // Stop session + virtual void stop(boost::system::error_code& ec) { + output_socket_stream_.close(ec); + input_stream_.close(); + } + + private: + IstreamToFiberSession(SessionManager* p_manager, const std::string& input_file, + OutputSocketStream&& output_socket_stream, + const std::string& output_file, + StopSessionHandler stop_handler) + : from_stdin_(false), + input_file_(input_file), + input_stream_(), + output_socket_stream_(std::move(output_socket_stream)), + output_file_(output_file), + p_manager_(p_manager), + stop_handler_(stop_handler), + output_request_(output_file) {} + + IstreamToFiberSession(SessionManager* p_manager, + OutputSocketStream&& output_socket_stream, + const std::string& output_file, + StopSessionHandler stop_handler) + : from_stdin_(true), + output_socket_stream_(std::move(output_socket_stream)), + output_file_(output_file), + p_manager_(p_manager), + stop_handler_(stop_handler), + output_request_(output_file) {} + +#include // NOLINT + // Write filename + // Forward file through fiber + void ForwardData(const boost::system::error_code& ec, std::size_t length) { + auto& input = from_stdin_ ? std::cin : input_stream_; + + if (ec) { + boost::system::error_code stop_ec; + p_manager_->stop(this->SelfFromThis(), stop_ec); + stop_handler_(output_file_); + + return; + } + + reenter(coroutine_) { + // async send output file name size + yield boost::asio::async_write( + output_socket_stream_, output_request_.GetFilenameSizeConstBuffers(), + boost::bind(&IstreamToFiberSession::ForwardData, this->SelfFromThis(), + _1, _2)); + + // async send output file name + yield boost::asio::async_write( + output_socket_stream_, output_request_.GetFilenameConstBuffers(), + boost::bind(&IstreamToFiberSession::ForwardData, this->SelfFromThis(), + _1, _2)); + + while (input.good()) { + packet_.set_type(Packet::kData); + input.read(packet_.buffer().data(), packet_.buffer().size()); + packet_.set_size(static_cast(input.gcount())); + + yield boost::asio::async_write( + output_socket_stream_, packet_.GetConstBuf(), + boost::bind(&IstreamToFiberSession::ForwardData, + this->SelfFromThis(), _1, _2)); + } + + if (input.eof()) { + packet_.set_type(Packet::kCtrl); + packet_.set_signal(Packet::kEof); + } else { + packet_.set_type(Packet::kCtrl); + packet_.set_signal(Packet::kInterrupted); + } + + yield boost::asio::async_write( + output_socket_stream_, packet_.GetConstBuf(), + boost::bind(&IstreamToFiberSession::ForwardData, this->SelfFromThis(), + _1, _2)); + + yield boost::asio::async_read( + output_socket_stream_, packet_.GetTypeMutBuf(), + boost::bind(&IstreamToFiberSession::ForwardData, this->SelfFromThis(), + _1, _2)); + } + } +#include // NOLINT + + IstreamToFiberSessionPtr SelfFromThis() { + return std::static_pointer_cast( + this->shared_from_this()); + } + + private: + bool from_stdin_; + std::string input_file_; + InputStream input_stream_; + OutputSocketStream output_socket_stream_; + std::string output_file_; + SessionManager* p_manager_; + StopSessionHandler stop_handler_; + + // Coroutine variables + boost::asio::coroutine coroutine_; + Packet packet_; + std::size_t file_read_length_; + FilenameBuffer output_request_; +}; + +} // file_to_fiber +} // copy_file +} // services +} // ssf + +#endif // SRC_SERVICES_COPY_FILE_FILE_TO_FIBER_ISTREAM_TO_FIBER_SESSION_H_ diff --git a/ssf-1.1.0/src/services/copy_file/filename_buffer.cpp b/ssf-1.1.0/src/services/copy_file/filename_buffer.cpp new file mode 100644 index 000000000..a247e4de6 --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/filename_buffer.cpp @@ -0,0 +1,38 @@ +#include "services/copy_file/filename_buffer.h" + +namespace ssf { +namespace services { +namespace copy_file { + +FilenameBuffer::FilenameBuffer() : filename_size_(0), filename_() {} + +FilenameBuffer::FilenameBuffer(const std::string& filename) + : filename_size_(static_cast(filename.length())), + filename_(filename.cbegin(), filename.cend()) {} + +std::string FilenameBuffer::GetFilename() const { + return std::string(filename_.cbegin(), filename_.cend()); +} + +boost::asio::const_buffers_1 FilenameBuffer::GetFilenameSizeConstBuffers() + const { + return boost::asio::const_buffers_1( + boost::asio::buffer(&filename_size_, sizeof(filename_size_))); +} + +boost::asio::mutable_buffers_1 FilenameBuffer::GetFilenameSizeMutBuffers() { + return boost::asio::buffer(&filename_size_, sizeof(filename_size_)); +} + +boost::asio::const_buffers_1 FilenameBuffer::GetFilenameConstBuffers() const { + return boost::asio::const_buffers_1(boost::asio::buffer(filename_)); +} + +boost::asio::mutable_buffers_1 FilenameBuffer::GetFilenameMutBuffers() { + filename_.resize(filename_size_); + return boost::asio::buffer(filename_); +} + +} // copy_file +} // services +} // ssf \ No newline at end of file diff --git a/ssf-1.1.0/src/services/copy_file/filename_buffer.h b/ssf-1.1.0/src/services/copy_file/filename_buffer.h new file mode 100644 index 000000000..530a8b5a3 --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/filename_buffer.h @@ -0,0 +1,38 @@ +#ifndef SSF_SERVICES_COPY_FILE_FILENAME_BUFFER_H_ +#define SSF_SERVICES_COPY_FILE_FILENAME_BUFFER_H_ + +#include + +#include +#include +#include + +#include + +namespace ssf { +namespace services { +namespace copy_file { + +class FilenameBuffer { + public: + FilenameBuffer(); + FilenameBuffer(const std::string& filename); + + std::string GetFilename() const; + + boost::asio::const_buffers_1 GetFilenameSizeConstBuffers() const; + boost::asio::mutable_buffers_1 GetFilenameSizeMutBuffers(); + + boost::asio::const_buffers_1 GetFilenameConstBuffers() const; + boost::asio::mutable_buffers_1 GetFilenameMutBuffers(); + + private: + uint32_t filename_size_; + std::vector filename_; +}; + +} // copy_file +} // services +} // ssf + +#endif // SSF_SERVICES_COPY_FILE_FILENAME_BUFFER_H_ diff --git a/ssf-1.1.0/src/services/copy_file/filesystem/filesystem.cpp b/ssf-1.1.0/src/services/copy_file/filesystem/filesystem.cpp new file mode 100644 index 000000000..ad39463eb --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/filesystem/filesystem.cpp @@ -0,0 +1,19 @@ +#include "services/copy_file/filesystem/filesystem.h" + +namespace ssf { +namespace services { +namespace copy_file { +namespace filesystem { + +std::string Filesystem::GetFilename(const std::string& path) { + return path.substr(path.find_last_of("/\\") + 1); +} + +std::string Filesystem::GetParentPath(const std::string& path) { + return path.substr(0, path.find_last_of("/\\")); +} + +} // ssf +} // services +} // copy_file +} // ssf diff --git a/ssf-1.1.0/src/services/copy_file/filesystem/filesystem.h b/ssf-1.1.0/src/services/copy_file/filesystem/filesystem.h new file mode 100644 index 000000000..c7d37cbd7 --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/filesystem/filesystem.h @@ -0,0 +1,28 @@ +#ifndef SSF_SERVICES_COPY_FILE_FILESYSTEM_FILESYSTEM_H_ +#define SSF_SERVICES_COPY_FILE_FILESYSTEM_FILESYSTEM_H_ + +#include + +#include +#include + +namespace ssf { +namespace services { +namespace copy_file { +namespace filesystem { + +class Filesystem { + public: + static std::list Glob(const std::string& path); + + static std::string GetFilename(const std::string& path); + + static std::string GetParentPath(const std::string& path); +}; + +} // filesystem +} // copy_file +} // services +} // ssf + +#endif // SSF_SERVICES_COPY_FILE_FILESYSTEM_FILESYSTEM_H_ diff --git a/ssf-1.1.0/src/services/copy_file/filesystem/linux/unix_filesystem.cpp b/ssf-1.1.0/src/services/copy_file/filesystem/linux/unix_filesystem.cpp new file mode 100644 index 000000000..3efe46f84 --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/filesystem/linux/unix_filesystem.cpp @@ -0,0 +1,26 @@ +#include "services/copy_file/filesystem/filesystem.h" + +#include + +namespace ssf { +namespace services { +namespace copy_file { +namespace filesystem { + +std::list Filesystem::Glob(const std::string& path) { + std::list result; + + glob_t glob_result; + glob(path.c_str(), GLOB_TILDE, NULL, &glob_result); + for (uint32_t i = 0; i < glob_result.gl_pathc; ++i) { + result.push_back(std::string(glob_result.gl_pathv[i])); + } + globfree(&glob_result); + + return result; +} + +} // filesystem +} // copy_file +} // services +} // ssf diff --git a/ssf-1.1.0/src/services/copy_file/filesystem/windows/win_filesystem.cpp b/ssf-1.1.0/src/services/copy_file/filesystem/windows/win_filesystem.cpp new file mode 100644 index 000000000..4f1ba4dca --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/filesystem/windows/win_filesystem.cpp @@ -0,0 +1,33 @@ +#include "services/copy_file/filesystem/filesystem.h" + +#include + +#include + +#include + +namespace ssf { +namespace services { +namespace copy_file { +namespace filesystem { + +std::list Filesystem::Glob(const std::string& path) { + std::list result; + HANDLE file_handle; + WIN32_FIND_DATA data; + auto parent_dir_path = Filesystem::GetParentPath(path); + + file_handle = FindFirstFile(path.c_str(), &data); + if (file_handle != INVALID_HANDLE_VALUE) { + do { + result.push_back(parent_dir_path + '/' + data.cFileName); + } while (FindNextFile(file_handle, &data) != 0); + } + + return result; +} + +} // filesystem +} // copy_file +} // services +} // ssf diff --git a/ssf-1.1.0/src/services/copy_file/packet/packet.cpp b/ssf-1.1.0/src/services/copy_file/packet/packet.cpp new file mode 100644 index 000000000..c71a24475 --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/packet/packet.cpp @@ -0,0 +1,99 @@ +#include "services/copy_file/packet/packet.h" + +namespace ssf { +namespace services { +namespace copy_file { +namespace packet { + +Packet::Packet() : type_(kCtrl), signal_(kInterrupted), size_(0), buffer_() {} + +Packet::Type Packet::type() const { return type_; } + +void Packet::set_type(Type type) { type_ = type; } + +boost::asio::const_buffers_1 Packet::GetTypeConstBuf() const { + return boost::asio::const_buffers_1( + boost::asio::buffer(&type_, sizeof(type_))); +} + +boost::asio::mutable_buffers_1 Packet::GetTypeMutBuf() { + return boost::asio::buffer(&type_, sizeof(type_)); +} + +bool Packet::IsDataPacket() { return type_ == kData; } + +Packet::Size Packet::size() const { return size_; } + +void Packet::set_size(Size size) { size_ = size; } + +boost::asio::const_buffers_1 Packet::GetSizeConstBuf() const { + return boost::asio::const_buffers_1( + boost::asio::buffer(&size_, sizeof(size_))); +} + +boost::asio::mutable_buffers_1 Packet::GetSizeMutBuf() { + return boost::asio::buffer(&size_, sizeof(size_)); +} + +Packet::Signal Packet::signal() const { return signal_; } + +void Packet::set_signal(Signal signal) { signal_ = signal; } + +boost::asio::const_buffers_1 Packet::GetSignalConstBuf() const { + return boost::asio::const_buffers_1( + boost::asio::buffer(&signal_, sizeof(signal_))); +} + +boost::asio::mutable_buffers_1 Packet::GetSignalMutBuf() { + return boost::asio::buffer(&signal_, sizeof(signal_)); +} + +Packet::Buffer& Packet::buffer() { return buffer_; } + +Packet::ConstBufSeq Packet::GetConstBuf() const { + ConstBufSeq buf_seq; + auto header_buf = GetHeaderConstBuf(); + auto payload_buf = GetPayloadConstBuf(); + + buf_seq.insert(buf_seq.end(), header_buf.begin(), header_buf.end()); + buf_seq.insert(buf_seq.end(), payload_buf.begin(), payload_buf.end()); + + return buf_seq; +} + +Packet::ConstBufSeq Packet::GetHeaderConstBuf() const { + ConstBufSeq buf_seq; + buf_seq.push_back(boost::asio::buffer(&type_, sizeof(type_))); + if (type_ == kData) { + buf_seq.push_back(boost::asio::buffer(&size_, sizeof(size_))); + } + + return buf_seq; +} + +Packet::ConstBufSeq Packet::GetPayloadConstBuf() const { + ConstBufSeq buf_seq; + if (type_ == kCtrl) { + buf_seq.push_back(boost::asio::buffer(&signal_, sizeof(signal_))); + } else { + buf_seq.push_back(boost::asio::buffer(buffer_, size_)); + } + + return buf_seq; +} + +Packet::MutBufSeq Packet::GetPayloadMutBuf() { + MutBufSeq buf_seq; + if (type_ == kCtrl) { + buf_seq.push_back(boost::asio::buffer(&signal_, sizeof(signal_))); + } else { + buf_seq.push_back(boost::asio::buffer(buffer_, size_)); + } + + return buf_seq; +} + +} // packet +} // copy_file +} // services +} // ssf diff --git a/ssf-1.1.0/src/services/copy_file/packet/packet.h b/ssf-1.1.0/src/services/copy_file/packet/packet.h new file mode 100644 index 000000000..16a980c3c --- /dev/null +++ b/ssf-1.1.0/src/services/copy_file/packet/packet.h @@ -0,0 +1,67 @@ +#ifndef SRC_SERVICES_COPY_FILE_PACKET_PACKET_H_ +#define SRC_SERVICES_COPY_FILE_PACKET_PACKET_H_ + +#include + +#include + +#include + +namespace ssf { +namespace services { +namespace copy_file { +namespace packet { + +class Packet { + public: + using Size = uint32_t; + using Buffer = std::array; + + using ConstBufSeq = std::vector; + using MutBufSeq = std::vector; + + enum Type : uint8_t { kCtrl = 0, kData = 1 }; + + enum Signal : uint8_t { kEof = 0, kInterrupted = 1 }; + + public: + Packet(); + Type type() const; + void set_type(Type type); + boost::asio::const_buffers_1 GetTypeConstBuf() const; + boost::asio::mutable_buffers_1 GetTypeMutBuf(); + + bool IsDataPacket(); + + Size size() const; + void set_size(Size size); + boost::asio::const_buffers_1 GetSizeConstBuf() const; + boost::asio::mutable_buffers_1 GetSizeMutBuf(); + + Signal signal() const; + void set_signal(Signal signal); + boost::asio::const_buffers_1 GetSignalConstBuf() const; + boost::asio::mutable_buffers_1 GetSignalMutBuf(); + + Buffer& buffer(); + + ConstBufSeq GetConstBuf() const; + + ConstBufSeq GetHeaderConstBuf() const; + + ConstBufSeq GetPayloadConstBuf() const; + MutBufSeq GetPayloadMutBuf(); + + private: + Type type_; + Signal signal_; + Size size_; + Buffer buffer_; +}; + +} // packet +} // copy_file +} // services +} // ssf + +#endif // SRC_SERVICES_COPY_FILE_PACKET_PACKET_H_ diff --git a/ssf-1.1.0/src/services/datagrams_to_fibers/datagrams_to_fibers.h b/ssf-1.1.0/src/services/datagrams_to_fibers/datagrams_to_fibers.h new file mode 100644 index 000000000..73f781f82 --- /dev/null +++ b/ssf-1.1.0/src/services/datagrams_to_fibers/datagrams_to_fibers.h @@ -0,0 +1,119 @@ +#ifndef SSF_SERVICES_DATAGRAMS_TO_FIBERS_DATAGRAMS_TO_FIBERS_H_ +#define SSF_SERVICES_DATAGRAMS_TO_FIBERS_DATAGRAMS_TO_FIBERS_H_ + +#include + +#include + +#include + +#include "common/boost/fiber/datagram_fiber.hpp" +#include "common/boost/fiber/basic_fiber_demux.hpp" + +#include "common/network/datagram_link.h" +#include "common/network/datagram_link_operator.h" + +#include "services/base_service.h" + +#include "core/factories/service_factory.h" + +#include "services/admin/requests/create_service_request.h" + +namespace ssf { namespace services { namespace datagrams_to_fibers { + +template +class DatagramsToFibers : public BaseService { + public: + typedef typename Demux::local_port_type local_port_type; + typedef typename Demux::remote_port_type remote_port_type; + + typedef boost::asio::ip::udp::socket::endpoint_type remote_udp_endpoint_type; + typedef boost::asio::ip::udp::socket socket; + + 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 DatagramLinkOperator UdpOperator; + typedef std::shared_ptr UdpOperatorPtr; + typedef std::shared_ptr DatagramsToFibersPtr; + + typedef std::array WorkingBufferType; + + public: + static DatagramsToFibersPtr Create(boost::asio::io_service& io_service, + 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"]))); + } + } + + enum { + factory_id = 6 + }; + + static void RegisterToServiceFactory( + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, &DatagramsToFibers::Create); + } + + virtual void start(boost::system::error_code& ec); + virtual void stop(boost::system::error_code& ec); + virtual uint32_t service_type_id(); + + static ssf::services::admin::CreateServiceRequest GetCreateRequest( + 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)); + + return create; + } + + private: + DatagramsToFibers(boost::asio::io_service& io_service, Demux& fiber_demux, + uint16_t local, remote_port_type remote_port); + + private: + void StartReceivingDatagrams(); + void SocketReceiveHandler(const boost::system::error_code& ec, size_t length); + + template + auto Then(Handler handler, This me) + -> decltype(boost::bind(handler, me->SelfFromThis(), _1, _2)) { + return boost::bind(handler, me->SelfFromThis(), _1, _2); + } + + std::shared_ptr SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); + } + + uint16_t local_port_; + remote_port_type remote_port_; + socket socket_; + + boost::asio::ip::udp::endpoint endpoint_; + datagram_endpoint send_to_; + + WorkingBufferType working_buffer_; + + UdpOperatorPtr p_udp_operator_; +}; + +} // datagrams_to_fibers +} // services +} // ssf + +#include "services/datagrams_to_fibers/datagrams_to_fibers.ipp" + +#endif // SSF_SERVICES_DATAGRAMS_TO_FIBERS_DATAGRAMS_TO_FIBERS_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp b/ssf-1.1.0/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp new file mode 100644 index 000000000..deb94fcec --- /dev/null +++ b/ssf-1.1.0/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp @@ -0,0 +1,87 @@ +#ifndef SSF_SERVICES_DATAGRAMS_TO_FIBERS_DATAGRAMS_TO_FIBERS_IPP_ +#define SSF_SERVICES_DATAGRAMS_TO_FIBERS_DATAGRAMS_TO_FIBERS_IPP_ + +#include + +#include "common/network/session_forwarder.h" + +namespace ssf { namespace services { namespace datagrams_to_fibers { +template +DatagramsToFibers::DatagramsToFibers(boost::asio::io_service& io_service, + Demux& fiber_demux, + uint16_t local_port, + remote_port_type remote_port) + : ssf::BaseService::BaseService(io_service, fiber_demux), + local_port_(local_port), + remote_port_(remote_port), + socket_(io_service), + endpoint_(boost::asio::ip::udp::v4(), local_port_), + send_to_(fiber_demux, remote_port), + 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_; + + // Listen on all interfaces + socket_.open(boost::asio::ip::udp::v4(), ec); + socket_.bind(endpoint_, ec); + + if (!ec) { + this->StartReceivingDatagrams(); + } +} + +template +void DatagramsToFibers::stop(boost::system::error_code& ec) { + BOOST_LOG_TRIVIAL(info) << "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; + } + + p_udp_operator_->StopAll(); +} + +template +uint32_t DatagramsToFibers::service_type_id() { return factory_id; } + +template +void DatagramsToFibers::StartReceivingDatagrams() { + BOOST_LOG_TRIVIAL(trace) << "service datagrams to fibers: receiving new datagrams"; + + socket_.async_receive_from( + boost::asio::buffer(working_buffer_), endpoint_, + Then(&DatagramsToFibers::SocketReceiveHandler, this->SelfFromThis())); +} + +template +void DatagramsToFibers::SocketReceiveHandler( + const boost::system::error_code& ec, size_t length) { + if (!ec) { + auto already_in = p_udp_operator_->Feed( + endpoint_, boost::asio::buffer(working_buffer_), length); + + if (!already_in) { + 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()); + p_udp_operator_->Feed(endpoint_, boost::asio::buffer(working_buffer_), + length); + } + + this->StartReceivingDatagrams(); + } else { + // boost::system::error_code ec; + // stop(ec); + } +} + +} // datagrams_to_fibers +} // services +} // ssf + +#endif // SSF_SERVICES_DATAGRAMS_TO_FIBERS_DATAGRAMS_TO_FIBERS_IPP_ diff --git a/ssf-1.1.0/src/services/fibers_to_datagrams/fibers_to_datagrams.h b/ssf-1.1.0/src/services/fibers_to_datagrams/fibers_to_datagrams.h new file mode 100644 index 000000000..f73642742 --- /dev/null +++ b/ssf-1.1.0/src/services/fibers_to_datagrams/fibers_to_datagrams.h @@ -0,0 +1,126 @@ +#ifndef SSF_SERVICES_FIBERS_TO_DATAGRAMS_FIBERS_TO_DATAGRAMS_H_ +#define SSF_SERVICES_FIBERS_TO_DATAGRAMS_FIBERS_TO_DATAGRAMS_H_ + +#include + +#include + +#include + +#include "common/boost/fiber/datagram_fiber.hpp" +#include "common/boost/fiber/basic_fiber_demux.hpp" + +#include "common/network/datagram_link.h" +#include "common/network/datagram_link_operator.h" + +#include "services/base_service.h" + +#include "core/factories/service_factory.h" + +#include "services/admin/requests/create_service_request.h" + +namespace ssf { namespace services { namespace fibers_to_datagrams { + +template +class FibersToDatagrams : public BaseService { + private: + typedef typename Demux::local_port_type local_port_type; + typedef typename Demux::remote_port_type remote_port_type; + + typedef boost::asio::ip::udp::socket::endpoint_type remote_udp_endpoint_type; + typedef boost::asio::ip::udp::socket socket; + 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 DatagramLinkOperator UdpOperator; + typedef std::shared_ptr UdpOperatorPtr; + + typedef std::shared_ptr FibersToDatagramsPtr; + + typedef std::array WorkingBufferType; + + public: + static FibersToDatagramsPtr create(boost::asio::io_service& io_service, + 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"]))); + } + } + + enum { + factory_id = 5 + }; + + static void RegisterToServiceFactory( + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, &FibersToDatagrams::create); + } + + virtual void start(boost::system::error_code& ec); + virtual void stop(boost::system::error_code& ec); + 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) { + ssf::services::admin::CreateServiceRequest create(factory_id); + create.add_parameter("local_port", std::to_string(local_port)); + create.add_parameter("remote_ip", remote_addr); + create.add_parameter("remote_port", std::to_string(remote_port)); + + return create; + } + + private: + FibersToDatagrams(boost::asio::io_service& io_service, Demux& fiber_demux, + local_port_type local, const std::string& ip, + uint16_t remote_port); + + private: + void StartReceivingDatagrams(); + void FiberReceiveHandler(const boost::system::error_code& ec, size_t length); + + template + auto Then(Handler handler, This me) + -> decltype(boost::bind(handler, me->SelfFromThis(), _1, _2)) { + return boost::bind(handler, me->SelfFromThis(), _1, _2); + } + + std::shared_ptr SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); + } + + private: + uint16_t remote_port_; + std::string ip_; + local_port_type local_port_; + fiber_datagram fiber_; + + boost::asio::ip::udp::endpoint endpoint_; + datagram_endpoint received_from_; + + WorkingBufferType working_buffer_; + + UdpOperatorPtr p_udp_operator_; +}; + +} // fibers_to_datagrams +} // services +} // ssf + +#include "services/fibers_to_datagrams/fibers_to_datagrams.ipp" + +#endif // SSF_SERVICES_FIBERS_TO_DATAGRAMS_FIBERS_TO_DATAGRAMS_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp b/ssf-1.1.0/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp new file mode 100644 index 000000000..aa305aee3 --- /dev/null +++ b/ssf-1.1.0/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp @@ -0,0 +1,97 @@ +#ifndef SSF_SERVICES_FIBERS_TO_DATAGRAMS_FIBERS_TO_DATAGRAMS_IPP_ +#define SSF_SERVICES_FIBERS_TO_DATAGRAMS_FIBERS_TO_DATAGRAMS_IPP_ + +#include + +#include "common/error/error.h" + +namespace ssf { +namespace services { +namespace fibers_to_datagrams { +template +FibersToDatagrams::FibersToDatagrams(boost::asio::io_service& io_service, + Demux& fiber_demux, + local_port_type local_port, + const std::string& ip, + uint16_t remote_port) + : ssf::BaseService::BaseService(io_service, fiber_demux), + remote_port_(remote_port), + ip_(ip), + local_port_(local_port), + fiber_(io_service), + received_from_(fiber_demux, 0), + 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_; + + // fiber.open() + fiber_.bind(datagram_endpoint(this->get_demux(), local_port_), ec); + + // Resolve the given address + boost::asio::ip::udp::resolver resolver(this->get_io_service()); + boost::asio::ip::udp::resolver::query query(ip_, + std::to_string(remote_port_)); + boost::asio::ip::udp::resolver::iterator iterator( + resolver.resolve(query, ec)); + + if (!ec) { + endpoint_ = *iterator; + this->StartReceivingDatagrams(); + } +} + +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()); + + fiber_.close(); + p_udp_operator_->StopAll(); +} + +template +uint32_t FibersToDatagrams::service_type_id() { + return factory_id; +} + +template +void FibersToDatagrams::StartReceivingDatagrams() { + BOOST_LOG_TRIVIAL(trace) << "service fibers to datagrams: receiving new datagrams"; + + fiber_.async_receive_from( + boost::asio::buffer(working_buffer_), received_from_, + Then(&FibersToDatagrams::FiberReceiveHandler, this->SelfFromThis())); +} + +template +void FibersToDatagrams::FiberReceiveHandler( + const boost::system::error_code& ec, size_t length) { + if (!ec) { + auto already_in = p_udp_operator_->Feed( + received_from_, boost::asio::buffer(working_buffer_), length); + + if (!already_in) { + boost::asio::ip::udp::socket left( + 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()); + p_udp_operator_->Feed(received_from_, + boost::asio::buffer(working_buffer_), length); + } + + this->StartReceivingDatagrams(); + } else { + // boost::system::error_code ec; + // stop(ec); + } +} + +} // fibers_to_datagrams +} // services +} // ssf + +#endif // SSF_SERVICES_FIBERS_TO_DATAGRAMS_FIBERS_TO_DATAGRAMS_IPP_ diff --git a/ssf-1.1.0/src/services/fibers_to_sockets/fibers_to_sockets.h b/ssf-1.1.0/src/services/fibers_to_sockets/fibers_to_sockets.h new file mode 100644 index 000000000..f4ffc5057 --- /dev/null +++ b/ssf-1.1.0/src/services/fibers_to_sockets/fibers_to_sockets.h @@ -0,0 +1,118 @@ +#ifndef SSF_SERVICES_FIBERS_TO_SOCKETS_FIBERS_TO_SOCKETS_H_ +#define SSF_SERVICES_FIBERS_TO_SOCKETS_FIBERS_TO_SOCKETS_H_ + +#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 "services/base_service.h" + +#include "core/factories/service_factory.h" + +#include "services/admin/requests/create_service_request.h" + +namespace ssf { namespace services { namespace fibers_to_sockets { + +template +class FibersToSockets : public BaseService { + private: + typedef typename Demux::local_port_type local_port_type; + + typedef std::shared_ptr FibersToSocketsPtr; + typedef ItemManager SessionManager; + typedef typename ssf::BaseService::Parameters Parameters; + typedef typename ssf::BaseService::demux demux; + typedef typename ssf::BaseService::fiber fiber; + typedef typename ssf::BaseService::endpoint endpoint; + typedef typename ssf::BaseService::fiber_acceptor fiber_acceptor; + + typedef boost::asio::ip::tcp::socket socket; + + public: + static FibersToSocketsPtr create(boost::asio::io_service& io_service, + demux& fiber_demux, Parameters parameters) { + if (!parameters.count("local_port") || + !parameters.count("remote_ip") || + !parameters.count("remote_port")) { + return FibersToSocketsPtr(nullptr); + } else { + return std::shared_ptr(new FibersToSockets( + io_service, fiber_demux, std::stoul(parameters["local_port"]), + parameters["remote_ip"], + (uint16_t)std::stoul(parameters["remote_port"]))); + } + } + + enum { + factory_id = 3 + }; + + static void RegisterToServiceFactory( + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, + &FibersToSockets::create); + } + + virtual void start(boost::system::error_code& ec); + virtual void stop(boost::system::error_code& ec); + 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) { + ssf::services::admin::CreateServiceRequest create(factory_id); + create.add_parameter("local_port", std::to_string(local_port)); + create.add_parameter("remote_ip", remote_addr); + create.add_parameter("remote_port", std::to_string(remote_port)); + + return create; + } + + private: + FibersToSockets(boost::asio::io_service& io_service, demux& fiber_demux, + local_port_type local, const std::string& ip, + uint16_t remote_port); + + private: + //No check on prioror presence implemented + void StartAcceptFibers(); + + void FiberAcceptHandler(const boost::system::error_code& ec); + + void SocketConnectHandler(const boost::system::error_code& ec); + + template + auto Then(Handler handler, + This me) -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { + return boost::bind(handler, me->SelfFromThis(), _1); + } + + std::shared_ptr SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); + } + + uint16_t remote_port_; + std::string ip_; + local_port_type local_port_; + fiber_acceptor fiber_acceptor_; + fiber fiber_; + socket socket_; + + boost::asio::ip::tcp::endpoint endpoint_; + + SessionManager manager_; +}; + +} // fibers_to_sockets +} // services +} // ssf + +#include "services/fibers_to_sockets/fibers_to_sockets.ipp" + +#endif // SSF_SERVICES_FIBERS_TO_SOCKETS_FIBERS_TO_SOCKETS_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/services/fibers_to_sockets/fibers_to_sockets.ipp b/ssf-1.1.0/src/services/fibers_to_sockets/fibers_to_sockets.ipp new file mode 100644 index 000000000..f75a68b6d --- /dev/null +++ b/ssf-1.1.0/src/services/fibers_to_sockets/fibers_to_sockets.ipp @@ -0,0 +1,106 @@ +#ifndef SSF_SERVICES_FIBERS_TO_SOCKETS_FIBERS_TO_SOCKETS_IPP_ +#define SSF_SERVICES_FIBERS_TO_SOCKETS_FIBERS_TO_SOCKETS_IPP_ + +#include + +#include "common/error/error.h" +#include "common/network/session_forwarder.h" + +namespace ssf { namespace services { namespace fibers_to_sockets { + +template +FibersToSockets::FibersToSockets(boost::asio::io_service& io_service, + demux& fiber_demux, + local_port_type local_port, + const std::string& ip, + uint16_t remote_port) + : ssf::BaseService::BaseService(io_service, fiber_demux), + remote_port_(remote_port), + ip_(ip), + local_port_(local_port), + fiber_acceptor_(io_service), + fiber_(io_service), + socket_(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 " + << local_port_; + + endpoint ep(this->get_demux(), local_port_); + fiber_acceptor_.bind(ep, ec); + fiber_acceptor_.listen(boost::asio::socket_base::max_connections, ec); + + // Resolve the given address + boost::asio::ip::tcp::resolver resolver(this->get_io_service()); + boost::asio::ip::tcp::resolver::query query(ip_, + std::to_string(remote_port_)); + boost::asio::ip::tcp::resolver::iterator iterator( + resolver.resolve(query, ec)); + + if (!ec) { + endpoint_ = *iterator; + this->StartAcceptFibers(); + } +} + +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()); + + fiber_acceptor_.close(); + manager_.stop_all(); +} + +template +uint32_t FibersToSockets::service_type_id() { + return factory_id; +} + +template +void FibersToSockets::StartAcceptFibers() { + BOOST_LOG_TRIVIAL(trace) << "service fibers to sockets: accepting new clients"; + + fiber_acceptor_.async_accept( + fiber_, Then(&FibersToSockets::FiberAcceptHandler, this->SelfFromThis())); +} + +template +void FibersToSockets::FiberAcceptHandler( + const boost::system::error_code& ec) { + BOOST_LOG_TRIVIAL(trace) << "service fibers to sockets: accept handler"; + + if (!fiber_acceptor_.is_open()) { + return; + } + + if (!ec) { + 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"; + + if (!ec) { + auto session = SessionForwarder::create( + &manager_, std::move(fiber_), std::move(socket_)); + boost::system::error_code e; + manager_.start(session, e); + } + + this->StartAcceptFibers(); +} + +} // fibers_to_sockets +} // services +} // ssf + +#endif // SSF_SERVICES_FIBERS_TO_SOCKETS_FIBERS_TO_SOCKETS_IPP_ diff --git a/ssf-1.1.0/src/services/initialisation.h b/ssf-1.1.0/src/services/initialisation.h new file mode 100644 index 000000000..a8a7e80c1 --- /dev/null +++ b/ssf-1.1.0/src/services/initialisation.h @@ -0,0 +1,20 @@ +#ifndef SSF_SERVICES_INITIALISATION_H_ +#define SSF_SERVICES_INITIALISATION_H_ + +namespace ssf { +namespace services { +namespace initialisation { + + enum type + { + NETWORK = 1, + TRANSPORT = 2, + SERVICE = 3, + CLOSE = 4 + }; + +} // initialisation +} // services +} // ssf + +#endif // SSF_SERVICES_INITIALISATION_H_ diff --git a/ssf-1.1.0/src/services/sockets_to_fibers/sockets_to_fibers.h b/ssf-1.1.0/src/services/sockets_to_fibers/sockets_to_fibers.h new file mode 100644 index 000000000..8907653dd --- /dev/null +++ b/ssf-1.1.0/src/services/sockets_to_fibers/sockets_to_fibers.h @@ -0,0 +1,113 @@ +#ifndef SSF_SERVICES_SOCKETS_TO_FIBERS_SOCKETS_TO_FIBERS_H_ +#define SSF_SERVICES_SOCKETS_TO_FIBERS_SOCKETS_TO_FIBERS_H_ + +#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 "services/base_service.h" + +#include "core/factories/service_factory.h" + +#include "services/admin/requests/create_service_request.h" + +namespace ssf { +namespace services { +namespace sockets_to_fibers { + +template +class SocketsToFibers : public BaseService { +public: + typedef typename Demux::remote_port_type remote_port_type; + + typedef std::shared_ptr SocketsToFibersPtr; + typedef ItemManager SessionManager; + typedef typename ssf::BaseService::Parameters Parameters; + typedef typename ssf::BaseService::demux demux; + typedef typename ssf::BaseService::fiber fiber; + typedef typename ssf::BaseService::endpoint endpoint; + + typedef boost::asio::ip::tcp::socket socket; + typedef boost::asio::ip::tcp::acceptor socket_acceptor; + +public: + static SocketsToFibersPtr Create(boost::asio::io_service& io_service, + demux& fiber_demux, + Parameters parameters) { + if (!parameters.count("local_port") || + !parameters.count("remote_port")) { + return SocketsToFibersPtr(nullptr); + } else { + return std::shared_ptr( + new SocketsToFibers(io_service, fiber_demux, + (uint16_t)std::stoul(parameters["local_port"]), + std::stoul(parameters["remote_port"]))); + } + } + + enum { + factory_id = 4 + }; + + static void RegisterToServiceFactory( + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, + &SocketsToFibers::Create); + } + + virtual void start(boost::system::error_code& ec); + virtual void stop(boost::system::error_code& ec); + virtual uint32_t service_type_id(); + + static ssf::services::admin::CreateServiceRequest GetCreateRequest( + 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)); + + return create; + } + +private: + SocketsToFibers(boost::asio::io_service& io_service, demux& fiber_demux, + uint16_t local, remote_port_type remote_port); + +private: + void StartAcceptSockets(); + + void SocketAcceptHandler(const boost::system::error_code& ec); + + void FiberConnectHandler(const boost::system::error_code& ec); + + template + auto Then(Handler handler, + This me) -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { + return boost::bind(handler, me->SelfFromThis(), _1); + } + + std::shared_ptr SelfFromThis() { + return std::static_pointer_cast( + this->shared_from_this()); + } + + uint16_t local_port_; + remote_port_type remote_port_; + socket_acceptor socket_acceptor_; + socket socket_; + fiber fiber_; + + SessionManager manager_; +}; + +} // sockets_to_fibers +} // services +} // ssf + +#include "services/sockets_to_fibers/sockets_to_fibers.ipp" + +#endif // SSF_SERVICES_SOCKETS_TO_FIBERS_SOCKETS_TO_FIBERS_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/services/sockets_to_fibers/sockets_to_fibers.ipp b/ssf-1.1.0/src/services/sockets_to_fibers/sockets_to_fibers.ipp new file mode 100644 index 000000000..ba1fff33c --- /dev/null +++ b/ssf-1.1.0/src/services/sockets_to_fibers/sockets_to_fibers.ipp @@ -0,0 +1,127 @@ +#ifndef SSF_SERVICES_SOCKETS_TO_FIBERS_SOCKETS_TO_FIBERS_IPP_ +#define SSF_SERVICES_SOCKETS_TO_FIBERS_SOCKETS_TO_FIBERS_IPP_ + +#include + +#include "common/network/session_forwarder.h" + +namespace ssf { namespace services { namespace sockets_to_fibers { + +template +SocketsToFibers::SocketsToFibers(boost::asio::io_service& io_service, + demux& fiber_demux, uint16_t local_port, + remote_port_type remote_port) + : ssf::BaseService::BaseService(io_service, fiber_demux), + local_port_(local_port), + remote_port_(remote_port), + socket_acceptor_(io_service), + socket_(io_service), + fiber_(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 " + << 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_); + + socket_acceptor_.open(endpoint.protocol(), ec); + if (ec) { + socket_acceptor_.close(close_ec); + return; + } + + boost::asio::socket_base::reuse_address option(true); + + socket_acceptor_.bind(endpoint, ec); + if (ec) { + socket_acceptor_.close(close_ec); + return ; + } + + socket_acceptor_.set_option(option, ec); + if (ec) { + socket_acceptor_.close(close_ec); + return ; + } + + socket_acceptor_.listen(boost::asio::socket_base::max_connections, ec); + if (ec) { + socket_acceptor_.close(close_ec); + return ; + } + + this->StartAcceptSockets(); +} + +template +void SocketsToFibers::stop(boost::system::error_code& ec) { + BOOST_LOG_TRIVIAL(info) << "service sockets to fibers: stopping"; + socket_acceptor_.close(ec); + if (ec) { + BOOST_LOG_TRIVIAL(debug) << "service sockets to fibers: " << ec.message() + << std::endl; + } + manager_.stop_all(); +} + +template +uint32_t SocketsToFibers::service_type_id() { + return factory_id; +} + +template +void SocketsToFibers::StartAcceptSockets() { + BOOST_LOG_TRIVIAL(trace) + << "service sockets to fibers: accepting new clients"; + + if (!socket_acceptor_.is_open()) { + return; + } + + socket_acceptor_.async_accept( + socket_, + Then(&SocketsToFibers::SocketAcceptHandler, this->SelfFromThis())); +} + +template +void SocketsToFibers::SocketAcceptHandler( + const boost::system::error_code& ec) { + BOOST_LOG_TRIVIAL(trace) << "service sockets to fibers: accept handler"; + + if (!socket_acceptor_.is_open()) { + return; + } + + if (!ec) { + endpoint ep(this->get_demux(), remote_port_); + fiber_.async_connect( + 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"; + + if (!ec) { + auto session = SessionForwarder::create( + &manager_, std::move(socket_), std::move(fiber_)); + boost::system::error_code e; + manager_.start(session, e); + } + + StartAcceptSockets(); +} + +} // sockets_to_fibers +} // services +} // ssf + +#endif // SSF_SERVICES_SOCKETS_TO_FIBERS_SOCKETS_TO_FIBERS_IPP_ diff --git a/ssf-1.1.0/src/services/socks/socks_server.h b/ssf-1.1.0/src/services/socks/socks_server.h new file mode 100644 index 000000000..dbc687085 --- /dev/null +++ b/ssf-1.1.0/src/services/socks/socks_server.h @@ -0,0 +1,116 @@ +#ifndef SSF_SERVICES_SOCKS_SOCKS_SERVER_H_ +#define SSF_SERVICES_SOCKS_SOCKS_SERVER_H_ + +#include +#include + +#include +#include // NOLINT + +#include "common/network/manager.h" +#include "common/network/base_session.h" + +#include "services/base_service.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" + +namespace ssf { +namespace services { +namespace socks { +//----------------------------------------------------------------------------- +template +class SocksServer : public BaseService { + private: + typedef typename Demux::local_port_type local_port_type; + + typedef std::shared_ptr SocksServerPtr; + typedef ItemManager SessionManager; + typedef typename ssf::BaseService::Parameters Parameters; + typedef typename ssf::BaseService::demux demux; + typedef typename ssf::BaseService::fiber fiber; + typedef typename ssf::BaseService::fiber_acceptor fiber_acceptor; + typedef typename ssf::BaseService::endpoint endpoint; + + public: + 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, + demux& fiber_demux, Parameters parameters) { + if (!parameters.count("local_port")) { + return SocksServerPtr(nullptr); + } else { + return std::shared_ptr(new SocksServer( + io_service, fiber_demux, std::stoul(parameters["local_port"]))); + } + } + + // SSF service ID for identification in the service factory + enum { factory_id = 2 }; + + /// Function used to register the micro service to the given factory + static void RegisterToServiceFactory( + std::shared_ptr> p_factory) { + p_factory->RegisterServiceCreator(factory_id, &SocksServer::create); + } + + /// Start the service instance + virtual void start(boost::system::error_code& ec); + + /// Stop the service instance + virtual void stop(boost::system::error_code& ec); + + /// Return the type of the service + virtual uint32_t service_type_id(); + + /// 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 std::move(create); + } + + private: + SocksServer(boost::asio::io_service& io_service, demux& fiber_demux, + const local_port_type& port); + + private: + void StartAccept(); + void HandleAccept(const boost::system::error_code& e); + void HandleStop(); + + template + auto Then(Handler handler, This me) + -> decltype(boost::bind(handler, me->SelfFromThis(), _1)) { + return boost::bind(handler, me->SelfFromThis(), _1); + } + + std::shared_ptr SelfFromThis() { + return std::static_pointer_cast(this->shared_from_this()); + } + + private: + fiber_acceptor fiber_acceptor_; + + private: + SessionManager session_manager_; + fiber new_connection_; + boost::system::error_code init_ec_; + local_port_type local_port_; +}; + +} // socks +} // services +} // ssf + +#include "services/socks/socks_server.ipp" + +#endif // SSF_SERVICES_SOCKS_SOCKS_SERVER_H_ diff --git a/ssf-1.1.0/src/services/socks/socks_server.ipp b/ssf-1.1.0/src/services/socks/socks_server.ipp new file mode 100644 index 000000000..2ee02c4cd --- /dev/null +++ b/ssf-1.1.0/src/services/socks/socks_server.ipp @@ -0,0 +1,128 @@ +#ifndef SSF_SERVICES_SOCKS_SOCKS_SERVER_IPP_ +#define SSF_SERVICES_SOCKS_SOCKS_SERVER_IPP_ + +#include + +#include // NOLINT +#include // NOLINT +#include //NOLINT +#include + +#include "services/socks/socks_version.h" // NOLINT +#include "services/socks/v4/session.h" // NOLINT +#include "services/socks/v5/session.h" // NOLINT + +namespace ssf { namespace services { namespace socks { +//----------------------------------------------------------------------------- +template +SocksServer::SocksServer(boost::asio::io_service& io_service, + demux& fiber_demux, const local_port_type& port) + : ssf::BaseService::BaseService(io_service, fiber_demux), + fiber_acceptor_(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(); + 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_; + ec = init_ec_; + + if (!init_ec_) { + this->StartAccept(); + } +} + +//----------------------------------------------------------------------------- +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 ####"; + this->HandleStop(); +} + +//----------------------------------------------------------------------------- +template +uint32_t SocksServer::service_type_id() { return factory_id; } + +//----------------------------------------------------------------------------- +template +void SocksServer::StartAccept() { + fiber_acceptor_.async_accept( + new_connection_, Then(&SocksServer::HandleAccept, this->SelfFromThis())); +} + +//----------------------------------------------------------------------------- +template +void SocksServer::HandleAccept(const boost::system::error_code& ec) { + BOOST_LOG_TRIVIAL(trace) << "HandleAccept"; + + if (!fiber_acceptor_.is_open()) { + return; + } + + if (ec) { + fprintf(stderr, "[!] Error accepting new connection: %s %d", + ec.message().c_str(), ec.value()); + this->StartAccept(); + } + + std::shared_ptr p_version(new Version()); + + auto self = this->SelfFromThis(); + 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()); + fiber fib = std::move(this->new_connection_); + this->StartAccept(); + } else if (p_version->Number() == 4) { + BOOST_LOG_TRIVIAL(trace) << "Version accepted: v4"; + ssf::BaseSessionPtr new_socks_session = + 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::BaseSessionPtr new_socks_session = + 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 { + fprintf(stderr, "Protocol not supported yet: %X\n", p_version->Number()); + this->new_connection_.close(); + fiber fib = std::move(this->new_connection_); + this->StartAccept(); + } + }; + + // Read the version field of the SOCKS header + boost::asio::async_read(new_connection_, p_version->Buffer(), start_handler); +} + +//----------------------------------------------------------------------------- +template +void SocksServer::HandleStop() { + fiber_acceptor_.close(); + session_manager_.stop_all(); +} + +} // socks +} // services +} // ssf + +#endif // SSF_SERVICES_SOCKS_SOCKS_SERVER_IPP_ diff --git a/ssf-1.1.0/src/services/socks/socks_version.h b/ssf-1.1.0/src/services/socks/socks_version.h new file mode 100644 index 000000000..28c465225 --- /dev/null +++ b/ssf-1.1.0/src/services/socks/socks_version.h @@ -0,0 +1,33 @@ +#ifndef SSF_SOCKS_VERSION_H_ +#define SSF_SOCKS_VERSION_H_ + +#include +#include +#include + +namespace ssf { namespace services { namespace socks { +//----------------------------------------------------------------------------- + +class Version { + public: + uint8_t Number() const { return version_number_; } + + std::array Buffer() { + std::array buf = { + { + boost::asio::buffer(&version_number_, 1) + } + }; + return buf; + } + + private: + uint8_t version_number_; +}; + +} // socks +} // services +} // ssf + + +#endif // SSF_SOCKS_VERSION_H_ diff --git a/ssf-1.1.0/src/services/socks/v4/reply.cpp b/ssf-1.1.0/src/services/socks/v4/reply.cpp new file mode 100644 index 000000000..1f35e789d --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v4/reply.cpp @@ -0,0 +1,45 @@ +#include "services/socks/v4/reply.h" + +#include // NOLINT + +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)) { + 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_) + } + }; + return buf; +} + + +//----------------------------------------------------------------------------- +Reply::Status Reply::ErrorCodeToStatus(const boost::system::error_code& err) { + Status s = kFailed; + + if (!err) { + s = kGranted; + } + + return s; +} +} // v4 +} // socks +} // ssf \ No newline at end of file diff --git a/ssf-1.1.0/src/services/socks/v4/reply.h b/ssf-1.1.0/src/services/socks/v4/reply.h new file mode 100644 index 000000000..02e8ea4a4 --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v4/reply.h @@ -0,0 +1,60 @@ +#ifndef SSF_V4_REPLY_H_ +#define SSF_V4_REPLY_H_ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +namespace ssf { namespace socks { namespace v4 { +//----------------------------------------------------------------------------- + +class Reply { + public: + // Status constants + enum Status { + kGranted = 0x5a, + kFailed = 0x5b, + kFailedNoIdentd = 0x5c, + kFailedBadUser = 0x5d + }; + + public: + Reply(const boost::system::error_code&, + const boost::asio::ip::tcp::endpoint&); + + std::array Buffer() const; + + private: + Status ErrorCodeToStatus(const boost::system::error_code& err); + + private: + uint8_t null_byte_; + uint8_t status_; + uint8_t port_high_byte_; + uint8_t port_low_byte_; + boost::asio::ip::address_v4::bytes_type address_; +}; + + +//----------------------------------------------------------------------------- +// S E N D R E P L Y +//----------------------------------------------------------------------------- +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_ diff --git a/ssf-1.1.0/src/services/socks/v4/request.cpp b/ssf-1.1.0/src/services/socks/v4/request.cpp new file mode 100644 index 000000000..a5ca0049f --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v4/request.cpp @@ -0,0 +1,81 @@ +#include "services/socks/v4/request.h" + +#include // NOLINT +#include // NOLINT + +#include // NOLINT +#include // NOLINT + + +namespace ssf { namespace socks { namespace v4 { +//----------------------------------------------------------------------------- +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 { + boost::asio::ip::address_v4 address(address_); + return boost::asio::ip::tcp::endpoint(address, port); + } +} + + +//----------------------------------------------------------------------------- +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_) + } + }; + return buf; +} + + +//----------------------------------------------------------------------------- +std::string Request::Name() const { + return name_; +} + +std::string Request::Domain() const { + return domain_; +} + +uint16_t Request::Port() const { + uint16_t port = port_high_byte_; + port = (port << 8) & 0xff00; + port = port | port_low_byte_; + + return port; +} + +//----------------------------------------------------------------------------- +void Request::SetName(const std::string& name) { + name_ = name; +} + +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); +} + +} // v4 +} // socks +} // ssf + diff --git a/ssf-1.1.0/src/services/socks/v4/request.h b/ssf-1.1.0/src/services/socks/v4/request.h new file mode 100644 index 000000000..1bdf9a79f --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v4/request.h @@ -0,0 +1,128 @@ +#ifndef SSF_V4_REQUEST_H_ +#define SSF_V4_REQUEST_H_ + +#include +#include +#include // NOLINT +#include // NOLINT + +#include +#include +#include +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include + +#include // NOLINT +#include // NOLINT + +namespace ssf { namespace socks { namespace v4 { +//----------------------------------------------------------------------------- +class Request { + public: + enum CommandId { + kConnect = 0x01, + kBind = 0x02 + }; + + public: + uint8_t Command() const; + + boost::asio::ip::tcp::endpoint Endpoint() const; + + std::array Buffer(); + + std::string Name() const; + std::string Domain() const; + uint16_t Port() const; + + void SetName(const std::string& name); + void SetDomain(const std::string& domain); + + bool is_4a_version() const; + + private: + uint8_t command_; + uint8_t port_high_byte_; + uint8_t port_low_byte_; + boost::asio::ip::address_v4::bytes_type address_; + std::string name_; + std::string domain_; +}; + + +//----------------------------------------------------------------------------- +// R E A D R E Q U E S T +//----------------------------------------------------------------------------- + +template +class ReadRequestCoro : public boost::asio::coroutine { +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){ } + +#include // NOLINT + void operator()(const boost::system::error_code& ec, std::size_t length) { + std::istream is(p_stream_.get()); + std::string name; + 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)); + 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_); + } + } +#include // NOLINT + + private: + StreamSocket& c_; + Request& r_; + VerifyHandler handler_; + std::size_t total_length_; + std::shared_ptr p_stream_; +}; + + +template +void AsyncReadRequest(StreamSocket& c, Request* p_r, VerifyHandler 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 + + +#endif // SSF_V4_REQUEST_H_ diff --git a/ssf-1.1.0/src/services/socks/v4/session.h b/ssf-1.1.0/src/services/socks/v4/session.h new file mode 100644 index 000000000..2ed959004 --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v4/session.h @@ -0,0 +1,76 @@ +#ifndef SRC_SERVICES_SOCKS_V4_SESSION_H_ +#define SRC_SERVICES_SOCKS_V4_SESSION_H_ + +#include + +#include // NOLINT +#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 "services/socks/v4/request.h" // NOLINT + +#include "common/boost/fiber/stream_fiber.hpp" + +namespace ssf { namespace socks { namespace v4 { + +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; + + public: + Session(SessionManager* sm, fiber client); + + public: + 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); + + void DoConnectRequest(); + + void DoBindRequest(); + + void HandleApplicationServerConnect(const boost::system::error_code&); + + void EstablishLink(); + + void HandleStop(); + + private: + std::shared_ptr self_shared_from_this() { + return std::static_pointer_cast(shared_from_this()); + } + + private: + boost::asio::io_service& io_service_; + SessionManager* p_session_manager_; + + fiber client_; + socket app_server_; + Request request_; + + std::shared_ptr upstream_; + std::shared_ptr downstream_; +}; +} // v4 +} // socks +} // ssf + +#include "services/socks/v4/session.ipp" + +#endif // SRC_SERVICES_SOCKS_V4_SESSION_H_ diff --git a/ssf-1.1.0/src/services/socks/v4/session.ipp b/ssf-1.1.0/src/services/socks/v4/session.ipp new file mode 100644 index 000000000..247e34841 --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v4/session.ipp @@ -0,0 +1,168 @@ +#ifndef SRC_SERVICES_SOCKS_V4_SESSION_IPP_ +#define SRC_SERVICES_SOCKS_V4_SESSION_IPP_ + +#include +#include +#include + +#include "services/socks/v4/request.h" +#include "services/socks/v4/reply.h" + +#include + +namespace ssf { namespace socks { namespace v4 { +//----------------------------------------------------------------------------- +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()) {} + +//----------------------------------------------------------------------------- +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; + } +} + + +//----------------------------------------------------------------------------- +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)); +} + + +//----------------------------------------------------------------------------- +template +void Session::HandleRequestDispatch(const boost::system::error_code& ec, + std::size_t) { + if (ec) { + HandleStop(); + return; + } + + // Dispatch request according to it's command id + switch (request_.Command()) { + case Request::kConnect: + DoConnectRequest(); + break; + + case Request::kBind: + DoBindRequest(); + break; + + default: + fprintf(stderr, "[!][Session][0x%p] Invalid v4 command\n", (void*)this); + break; + } +} + + +//----------------------------------------------------------------------------- +template +void Session::DoConnectRequest() { + 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; + + if (request_.is_4a_version()) { + // In the 4a version, the address needs to be resolved + boost::asio::ip::tcp::resolver resolver(io_service_); + boost::asio::ip::tcp::resolver::query query( + request_.Domain(), std::to_string(request_.Port())); + boost::asio::ip::tcp::resolver::iterator iterator( + resolver.resolve(query, ec)); + + if (!ec) { + endpoint = *iterator; + } + } else { + endpoint = request_.Endpoint(); + } + + if (ec) { + HandleApplicationServerConnect(ec); + } else { + app_server_.async_connect(endpoint, handler); + } +} + + +//----------------------------------------------------------------------------- +template +void Session::DoBindRequest() { + fprintf(stderr, "[!][Session][0x%p] Bind Not implemented yet\n", (void*)this); + HandleStop(); +} + + +//----------------------------------------------------------------------------- +template +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(); + }); + } 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(); + }); + } +} + + +//----------------------------------------------------------------------------- +template +void Session::EstablishLink() { + auto self = self_shared_from_this(); + + upstream_.reset(new StreamBuff); + downstream_.reset(new StreamBuff); + + + // Two half duplex links + AsyncEstablishHDLink(ssf::ReadFrom(client_), ssf::WriteTo(app_server_), + boost::asio::buffer(*upstream_), + boost::bind(&Session::HandleStop, self)); + + AsyncEstablishHDLink(ssf::ReadFrom(app_server_), ssf::WriteTo(client_), + boost::asio::buffer(*downstream_), + boost::bind(&Session::HandleStop, self)); +} +} // v4 +} // socks +} // ssf + +#endif // SRC_SERVICES_SOCKS_V4_SESSION_IPP_ diff --git a/ssf-1.1.0/src/services/socks/v5/reply.cpp b/ssf-1.1.0/src/services/socks/v5/reply.cpp new file mode 100644 index 000000000..bebcb941e --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v5/reply.cpp @@ -0,0 +1,74 @@ +#include "services/socks/v5/reply.h" + +#include // NOLINT + +namespace ssf { namespace socks { namespace v5 { +//----------------------------------------------------------------------------- +Reply::Reply(const boost::system::error_code& err) + : version_(0x05), + status_(ErrorCodeToStatus(err)), + reserved_(0x00) { } + +//----------------------------------------------------------------------------- +void Reply::set_ipv4(boost::asio::ip::address_v4::bytes_type ipv4) { + addr_type_ = 0x01; + ipv4_ = ipv4; +} + +void Reply::set_domain(std::vector domain) { + addr_type_ = 0x03; + domainLength_ = (uint8_t)domain.size(); + domain_ = domain; +} + +void Reply::set_ipv6(boost::asio::ip::address_v6::bytes_type ipv6) { + addr_type_ = 0x04; + ipv6_ = ipv6; +} + +void Reply::set_port(uint16_t port) { + port_high_byte_ = (port >> 8) & 0xff; + port_low_byte_ = port & 0xff; +} + +//----------------------------------------------------------------------------- +std::vector Reply::Buffers() const { + std::vector buf; + + buf.push_back(boost::asio::buffer(&version_, 1)); + buf.push_back(boost::asio::buffer(&status_, 1)); + buf.push_back(boost::asio::buffer(&reserved_, 1)); + 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; + } + + 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; + + if (!err) { + s = kGranted; + } + return s; +} +} // v5 +} // socks +} // ssf diff --git a/ssf-1.1.0/src/services/socks/v5/reply.h b/ssf-1.1.0/src/services/socks/v5/reply.h new file mode 100644 index 000000000..2844626a2 --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v5/reply.h @@ -0,0 +1,86 @@ +#ifndef SSF_V5_REPLY_H_ +#define SSF_V5_REPLY_H_ + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +namespace ssf { namespace socks { namespace v5 { +//----------------------------------------------------------------------------- + +class Reply { + public: + // Status constants + enum Status { + kGranted = 0x00, + kFailed = 0x01, + kConnNotAllowed = 0x02, + kNetUnreach = 0x03, + kHostUnreach = 0x04, + kConnRefused = 0x05, + kTTLEx = 0x06, + kError = 0x07, + kBadAddrType = 0x08 + }; + + // Address type constants + enum AddressTypeId { + kIPv4 = 0x01, + kDNS = 0x03, + kIPv6 = 0x04 + }; + + public: + Reply(const boost::system::error_code&); + + void set_ipv4(boost::asio::ip::address_v4::bytes_type ipv4); + void set_domain(std::vector domain); + void set_ipv6(boost::asio::ip::address_v6::bytes_type ipv6); + + void set_port(uint16_t port); + + std::vector Buffers() const; + + private: + Status ErrorCodeToStatus(const boost::system::error_code& err); + + private: + uint8_t version_; + uint8_t status_; + uint8_t reserved_; + uint8_t addr_type_; + + boost::asio::ip::address_v4::bytes_type ipv4_; + + uint8_t domainLength_; + std::vector domain_; + + boost::asio::ip::address_v6::bytes_type ipv6_; + + uint8_t port_high_byte_; + uint8_t port_low_byte_; +}; + + +//----------------------------------------------------------------------------- +// S E N D R E P L Y +//----------------------------------------------------------------------------- +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_ diff --git a/ssf-1.1.0/src/services/socks/v5/reply_auth.cpp b/ssf-1.1.0/src/services/socks/v5/reply_auth.cpp new file mode 100644 index 000000000..4205ace9f --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v5/reply_auth.cpp @@ -0,0 +1,25 @@ +#include "services/socks/v5/reply_auth.h" + +#include // NOLINT + +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), + } + }; + return buf; +} + +} // v5 +} // socks +} // ssf diff --git a/ssf-1.1.0/src/services/socks/v5/reply_auth.h b/ssf-1.1.0/src/services/socks/v5/reply_auth.h new file mode 100644 index 000000000..70bb587ce --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v5/reply_auth.h @@ -0,0 +1,38 @@ +#ifndef SSF_V5_REPLY_AUTH_H_ +#define SSF_V5_REPLY_AUTH_H_ + +#include +#include + +#include +#include + +namespace ssf { namespace socks { namespace v5 { +//----------------------------------------------------------------------------- + +class AuthReply { + public: + AuthReply(uint8_t authMethod); + + std::array Buffer() const; + + private: + uint8_t version_; + uint8_t authMethod_; +}; + + +//----------------------------------------------------------------------------- +// S E N D R E P L Y +//----------------------------------------------------------------------------- +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_ diff --git a/ssf-1.1.0/src/services/socks/v5/request.cpp b/ssf-1.1.0/src/services/socks/v5/request.cpp new file mode 100644 index 000000000..25f998c46 --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v5/request.cpp @@ -0,0 +1,85 @@ +#include "services/socks/v5/request.h" + +#include // NOLINT +#include // NOLINT + +#include // NOLINT +#include // NOLINT + + +namespace ssf { namespace socks { namespace v5 { +//----------------------------------------------------------------------------- +uint8_t Request::version() const { return version_; } + +uint8_t Request::command() const { return command_; } + +uint8_t Request::addressType() const { return addressType_; } + +boost::asio::ip::address_v4::bytes_type Request::ipv4() const { return ipv4_; } + +uint8_t Request::domainLength() const { return domainLength_; } + +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 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) + } + }; + return buf; +} + +boost::asio::mutable_buffers_1 Request::Domain_Length_Buffer() { + return boost::asio::mutable_buffers_1(&domainLength_, 1); +} + +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_)); + } + + return buf; +} + +std::array Request::Port_Buffers() { + std::array buf = { + { + boost::asio::buffer(&port_high_byte_, 1), + boost::asio::buffer(&port_low_byte_, 1) + } + }; + return buf; +} + +} // v5 +} // socks +} // ssf + diff --git a/ssf-1.1.0/src/services/socks/v5/request.h b/ssf-1.1.0/src/services/socks/v5/request.h new file mode 100644 index 000000000..edff9bfbc --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v5/request.h @@ -0,0 +1,143 @@ +#ifndef SSF_V5_REQUEST_H_ +#define SSF_V5_REQUEST_H_ + +#include +#include +#include // NOLINT +#include // NOLINT + +#include +#include +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include + +#include // NOLINT +#include // NOLINT + +#include "common/error/error.h" + +namespace ssf { namespace socks { namespace v5 { +//----------------------------------------------------------------------------- +class Request { + public: + // Command types constants + enum CommandId { + kConnect = 0x01, + kBind = 0x02, + kUDP = 0x03 + }; + + // Address type constants + enum AddressTypeId { + kIPv4 = 0x01, + kDNS = 0x03, + kIPv6 = 0x04 + }; + + public: + uint8_t version() const; + uint8_t command() const; + uint8_t addressType() const; + boost::asio::ip::address_v4::bytes_type ipv4() const; + uint8_t domainLength() const; + std::vector domain() const; + boost::asio::ip::address_v6::bytes_type ipv6() const; + uint16_t port() const; + + std::array First_Part_Buffers(); + boost::asio::mutable_buffers_1 Domain_Length_Buffer(); + std::vector Address_Buffer(); + std::array Port_Buffers(); + + private: + uint8_t version_; + uint8_t command_; + uint8_t reserved_; + uint8_t addressType_; + + boost::asio::ip::address_v4::bytes_type ipv4_; + + uint8_t domainLength_; + std::vector domain_; + + boost::asio::ip::address_v6::bytes_type ipv6_; + + uint8_t port_high_byte_; + uint8_t port_low_byte_; +}; + + +//----------------------------------------------------------------------------- +// R E A D R E Q U E S T +//----------------------------------------------------------------------------- + +template +class ReadRequestCoro : public boost::asio::coroutine { +public: + ReadRequestCoro(StreamSocket& c, Request* p_r, VerifyHandler h) + : 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)); + 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; + + boost::get<0>(handler_)(ec, total_length_); + } else { + boost::get<0>(handler_)(ec, total_length_); + } + } +#include // NOLINT + + private: + StreamSocket& c_; + Request& r_; + VerifyHandler handler_; + std::size_t total_length_; +}; + + +template +void AsyncReadRequest(StreamSocket& c, Request* p_r, VerifyHandler handler) { + ReadRequestCoro, StreamSocket> + RequestReader(c, p_r, boost::make_tuple(handler)); + + RequestReader(boost::system::error_code(), 0); +} + +} // v5 +} // socks +} // ssf + + +#endif // SSF_V5_REQUEST_H_ diff --git a/ssf-1.1.0/src/services/socks/v5/request_auth.cpp b/ssf-1.1.0/src/services/socks/v5/request_auth.cpp new file mode 100644 index 000000000..dc9057ff2 --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v5/request_auth.cpp @@ -0,0 +1,45 @@ +#include "services/socks/v5/request_auth.h" + +#include // NOLINT +#include // NOLINT + +#include // NOLINT +#include // NOLINT + + +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 = { + { + 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) { + return true; + } + } + + return false; +} + +} // v5 +} // socks +} // ssf + diff --git a/ssf-1.1.0/src/services/socks/v5/request_auth.h b/ssf-1.1.0/src/services/socks/v5/request_auth.h new file mode 100644 index 000000000..d11131a07 --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v5/request_auth.h @@ -0,0 +1,98 @@ +#ifndef SSF_V5_REQUEST_AUTH_H_ +#define SSF_V5_REQUEST_AUTH_H_ + +#include +#include // NOLINT + +#include +#include +#include // NOLINT + +#include // NOLINT +#include + +namespace ssf { namespace socks { namespace v5 { +//----------------------------------------------------------------------------- +class RequestAuth { + public: + // Authentication method constants + enum AuthMethodIds { + kNoAuth = 0x00, + kGSSAPI = 0x01, + kUserPassword = 0x02, + kError = 0xFF + }; + + public: + uint8_t numberOfAuthSupported() const; + void addAuthMethod(uint8_t authMethod); + + std::array Buffers(); + + bool is_no_auth_present(); + + private: + uint8_t numberOfAuthSupported_; + 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 +class ReadRequestAuthCoro : public boost::asio::coroutine { +public: + ReadRequestAuthCoro(StreamSocket& c, RequestAuth* p_r, VerifyHandler h) + : c_(c), r_(*p_r), handler_(h), total_length_(0) { } + +#include // NOLINT + void operator()(const boost::system::error_code& ec, std::size_t length) { + uint8_t numberOfMethods = 0; + 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); + total_length_ += length; + } + + boost::get<0>(handler_)(ec, total_length_); + } else { + boost::get<0>(handler_)(ec, total_length_); + } + } +#include // NOLINT + + private: + StreamSocket& c_; + RequestAuth& r_; + VerifyHandler handler_; + std::size_t total_length_; +}; + + +template +void AsyncReadRequestAuth(StreamSocket& c, RequestAuth* p_r, VerifyHandler handler) { + ReadRequestAuthCoro, StreamSocket> + RequestAuthReader(c, p_r, boost::make_tuple(handler)); + + RequestAuthReader(boost::system::error_code(), 0); +} + +//----------------------------------------------------------------------------- +} // v5 +} // socks +} // ssf + + +#endif // SSF_V5_REQUEST_AUTH_H_ diff --git a/ssf-1.1.0/src/services/socks/v5/session.h b/ssf-1.1.0/src/services/socks/v5/session.h new file mode 100644 index 000000000..5fdeb9bbf --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v5/session.h @@ -0,0 +1,88 @@ +#ifndef SRC_SERVICES_SOCKS_V5_SESSION_H_ +#define SRC_SERVICES_SOCKS_V5_SESSION_H_ + +#include + +#include // NOLINT +#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 "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 { + +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; + + public: + Session(SessionManager* sm, fiber client); + + public: + virtual void start(boost::system::error_code&); + + virtual void stop(boost::system::error_code&); + + private: + void HandleRequestAuthDispatch(const boost::system::error_code& ec, std::size_t); + + void DoNoAuth(); + void DoErrorAuth(); + + void HandleReplyAuthSent(); + + void HandleRequestDispatch(const boost::system::error_code&, std::size_t); + + void DoConnectRequest(); + + void DoBindRequest(); + + void DoUDPRequest(); + + void HandleApplicationServerConnect(const boost::system::error_code&); + + void EstablishLink(); + + void HandleStop(); + + private: + std::shared_ptr self_shared_from_this() { + return std::static_pointer_cast(shared_from_this()); + } + + + private: + boost::asio::io_service& io_service_; + SessionManager* p_session_manager_; + + fiber client_; + socket app_server_; + RequestAuth request_auth_; + Request request_; + + std::shared_ptr upstream_; + std::shared_ptr downstream_; +}; +} // v5 +} // socks +} // ssf + +#include "services/socks/v5/session.ipp" + +#endif // SRC_SERVICES_SOCKS_V5_SESSION_H_ diff --git a/ssf-1.1.0/src/services/socks/v5/session.ipp b/ssf-1.1.0/src/services/socks/v5/session.ipp new file mode 100644 index 000000000..1f965984f --- /dev/null +++ b/ssf-1.1.0/src/services/socks/v5/session.ipp @@ -0,0 +1,254 @@ +#ifndef SRC_SERVICES_SOCKS_V5_SESSION_IPP_ +#define SRC_SERVICES_SOCKS_V5_SESSION_IPP_ + +#include +#include +#include + +#include "services/socks/v5/request.h" +#include "services/socks/v5/request_auth.h" +#include "services/socks/v5/reply.h" +#include "services/socks/v5/reply_auth.h" + +#include + +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()) { } + +//----------------------------------------------------------------------------- +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; + } +} + + +//----------------------------------------------------------------------------- +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)); +} + + +//----------------------------------------------------------------------------- +template +void Session::HandleRequestAuthDispatch(const boost::system::error_code& ec, + std::size_t) { + if (ec) { + HandleStop(); + return; + } + + // Check for compatible authentications + if (request_auth_.is_no_auth_present()) { + DoNoAuth(); + } else { + DoErrorAuth(); + } +} + +//----------------------------------------------------------------------------- +template +void Session::DoNoAuth() { + auto self = self_shared_from_this(); + auto p_reply = std::make_shared(0x00); + + AsyncSendAuthReply(client_, *p_reply, + [this, self, p_reply](boost::system::error_code ec, + std::size_t transferred) { + if (ec) { + HandleStop(); + return; + } + HandleReplyAuthSent(); + }); +} + +template +void Session::DoErrorAuth() { + auto self = self_shared_from_this(); + auto p_reply = std::make_shared(0xFF); + + AsyncSendAuthReply(client_, *p_reply, + [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)); +} + +//----------------------------------------------------------------------------- +template +void Session::HandleRequestDispatch(const boost::system::error_code& ec, + std::size_t) { + if (ec) { + HandleStop(); + return; + } + + // 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; + } +} + +//----------------------------------------------------------------------------- +template +void Session::DoConnectRequest() { + 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)); + if (ec) { + handler(ec); + } else { + app_server_.async_connect(*iterator, handler); + } + } 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), + 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)); + if (ec) { + handler(ec); + } else { + app_server_.async_connect(*iterator, handler); + } + } +} + +template +void Session::DoBindRequest() { + fprintf(stderr, "[!][Session][0x%p] Bind Not implemented yet\n", (void*)this); + HandleStop(); +} + +template +void Session::DoUDPRequest() { + fprintf(stderr, "[!][Session][0x%p] UDP Not implemented yet\n", (void*)this); + HandleStop(); +} + +//----------------------------------------------------------------------------- +template +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; + } + + 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(); + }); + } 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(); + }); + } +} + + +//----------------------------------------------------------------------------- +template +void Session::EstablishLink() { + auto self = self_shared_from_this(); + + upstream_.reset(new StreamBuff); + downstream_.reset(new StreamBuff); + + AsyncEstablishHDLink(ssf::ReadFrom(client_), ssf::WriteTo(app_server_), + boost::asio::buffer(*upstream_), + boost::bind(&Session::HandleStop, self)); + + AsyncEstablishHDLink(ssf::ReadFrom(app_server_), ssf::WriteTo(client_), + boost::asio::buffer(*downstream_), + boost::bind(&Session::HandleStop, self)); +} +} // v5 +} // socks +} // ssf + +#endif // SRC_SERVICES_SOCKS_V5_SESSION_IPP_ diff --git a/ssf-1.1.0/src/services/user_services/base_user_service.h b/ssf-1.1.0/src/services/user_services/base_user_service.h new file mode 100644 index 000000000..a15d1c782 --- /dev/null +++ b/ssf-1.1.0/src/services/user_services/base_user_service.h @@ -0,0 +1,43 @@ +#ifndef SSF_SERVICES_USER_SERVICES_BASE_USER_SERVICE_H_ +#define SSF_SERVICES_USER_SERVICES_BASE_USER_SERVICE_H_ + +#include +#include +#include + +#include "services/admin/requests/create_service_request.h" +#include "services/admin/requests/stop_service_request.h" + +namespace ssf { +namespace services { + +//---------------------------------------------------------------------------- +template +class BaseUserService + : public std::enable_shared_from_this> { + public: + typedef typename std::shared_ptr> BaseUserServicePtr; + + virtual std::vector> + GetRemoteServiceCreateVector() = 0; + virtual std::vector> + GetRemoteServiceStopVector(Demux& demux) = 0; + virtual uint32_t CheckRemoteServiceStatus(Demux& demux) = 0; + + virtual std::string GetName() = 0; + + virtual bool StartLocalServices(Demux& demux) = 0; + virtual void StopLocalServices(Demux& demux) = 0; + + BaseUserService() {} + virtual ~BaseUserService() {} + + private: + BaseUserService(const BaseService&) = delete; + BaseUserService& operator=(const BaseService&) = delete; +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_USER_SERVICES_BASE_USER_SERVICE_H_ \ No newline at end of file diff --git a/ssf-1.1.0/src/services/user_services/copy_file_service.h b/ssf-1.1.0/src/services/user_services/copy_file_service.h new file mode 100644 index 000000000..dae663d24 --- /dev/null +++ b/ssf-1.1.0/src/services/user_services/copy_file_service.h @@ -0,0 +1,183 @@ +#ifndef SSF_SERVICES_USER_SERVICES_COPY_FILE_SERVICE_H_ +#define SSF_SERVICES_USER_SERVICES_COPY_FILE_SERVICE_H_ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "services/user_services/base_user_service.h" +#include "services/copy_file/file_enquirer/file_enquirer.h" +#include "services/copy_file/file_to_fiber/file_to_fiber.h" +#include "services/copy_file/fiber_to_file/fiber_to_file.h" + +namespace ssf { +namespace services { + +template +class CopyFileService : public BaseUserService { + public: + using ServiceId = uint32_t; + using ServideIdSet = std::set; + using UserParameters = std::map>; + using CopyFileServicePtr = std::shared_ptr; + + public: + // Create user service from parameters (factory method) + static std::shared_ptr CreateServiceFromParams( + 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()); + return CopyFileServicePtr(new CopyFileService( + from_stdin, from_local_to_remote, input_pattern, output_pattern)); + } + + virtual std::string GetName() { return "copy_file"; } + + virtual std::vector> + GetRemoteServiceCreateVector() { + std::vector> result; + result.push_back(GetRemoteServiceRequest()); + return result; + } + + virtual std::vector> + GetRemoteServiceStopVector(Demux& demux) { + std::vector> result; + + return result; + } + + virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { + services::admin::CreateServiceRequest remote_service_request = + GetRemoteServiceRequest(); + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + auto status = p_service_factory->GetStatus( + remote_service_request.service_id(), + remote_service_request.parameters(), GetRemoteServiceId(demux)); + + return status; + } + + virtual bool StartLocalServices(Demux& demux) { + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + boost::system::error_code ec; + + if (from_local_to_remote_) { + // Local to remote : file to fiber only + services::admin::CreateServiceRequest local_service_request; + local_service_request = services::copy_file::file_to_fiber::FileToFiber< + Demux>::GetCreateRequest(false, from_stdin_, input_pattern_, + output_pattern_); + + local_service_ids_.insert(p_service_factory->CreateRunNewService( + local_service_request.service_id(), + local_service_request.parameters(), ec)); + return !ec; + } + + // Remote to local : fiber to file, file enquirer + services::admin::CreateServiceRequest fiber_to_file_request = + services::copy_file::fiber_to_file::FiberToFile< + Demux>::GetCreateRequest(); + + services::admin::CreateServiceRequest file_enquirer_request = + services::copy_file::file_enquirer::FileEnquirer< + Demux>::GetCreateRequest(input_pattern_, output_pattern_); + + local_service_ids_.insert(p_service_factory->CreateRunNewService( + fiber_to_file_request.service_id(), fiber_to_file_request.parameters(), + ec)); + + local_service_ids_.insert(p_service_factory->CreateRunNewService( + file_enquirer_request.service_id(), file_enquirer_request.parameters(), + ec)); + + return !ec; + } + + virtual void StopLocalServices(Demux& demux) { + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + for (auto& local_service_id : local_service_ids_) { + p_service_factory->StopService(local_service_id); + } + local_service_ids_.clear(); + } + + virtual ~CopyFileService() {} + + private: + CopyFileService(bool from_stdin, bool from_local_to_remote, + const std::string& input_pattern, + const std::string& output_pattern) + : from_stdin_(from_stdin), + from_local_to_remote_(from_local_to_remote || from_stdin), + input_pattern_(input_pattern), + output_pattern_(output_pattern), + remote_service_id_(0), + local_service_ids_() {} + + services::admin::CreateServiceRequest GetRemoteServiceRequest() { + services::admin::CreateServiceRequest remote_service_request; + if (from_local_to_remote_) { + remote_service_request = services::copy_file::fiber_to_file::FiberToFile< + Demux>::GetCreateRequest(); + } else { + remote_service_request = services::copy_file::file_to_fiber::FileToFiber< + Demux>::GetCreateRequest(true, false, "", ""); + } + return remote_service_request; + } + + services::admin::CreateServiceRequest GetLocalServiceRequest() { + services::admin::CreateServiceRequest local_service_request; + if (from_local_to_remote_) { + local_service_request = services::copy_file::file_to_fiber::FileToFiber< + Demux>::GetCreateRequest(false, from_stdin_, input_pattern_, + output_pattern_); + } else { + local_service_request = services::copy_file::fiber_to_file::FiberToFile< + Demux>::GetCreateRequest(); + } + return local_service_request; + } + + uint32_t GetRemoteServiceId(Demux& demux) { + if (remote_service_id_) { + return remote_service_id_; + } else { + auto remote_service_request = GetRemoteServiceRequest(); + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + auto id = p_service_factory->GetIdFromParameters( + remote_service_request.service_id(), + remote_service_request.parameters()); + remote_service_id_ = id; + return id; + } + } + + private: + bool from_stdin_; + bool from_local_to_remote_; + std::string input_pattern_; + std::string output_pattern_; + ServiceId remote_service_id_; + ServideIdSet local_service_ids_; +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_USER_SERVICES_COPY_FILE_SERVICE_H_ diff --git a/ssf-1.1.0/src/services/user_services/port_forwarding.h b/ssf-1.1.0/src/services/user_services/port_forwarding.h new file mode 100644 index 000000000..0518292eb --- /dev/null +++ b/ssf-1.1.0/src/services/user_services/port_forwarding.h @@ -0,0 +1,198 @@ +#ifndef SSF_SERVICES_USER_SERVICES_PORT_FORWARDING_H_ +#define SSF_SERVICES_USER_SERVICES_PORT_FORWARDING_H_ + +#include + +#include +#include +#include +#include + +#ifndef BOOST_SPIRIT_USE_PHOENIX_V3 +#define BOOST_SPIRIT_USE_PHOENIX_V3 1 +#endif +#include +#include +#include + +#include + +#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 "core/factories/service_option_factory.h" + +#include "common/boost/fiber/detail/fiber_id.hpp" + +#include "services/sockets_to_fibers/sockets_to_fibers.h" +#include "services/fibers_to_sockets/fibers_to_sockets.h" + +namespace ssf { +namespace services { + +template +class PortForwading : 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, + uint16_t remote_port) + : 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 GetParseName() { + return "forward"; + } + + static std::string GetParseDesc() { + return "Forward local port on given target"; + } + + public: + 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_; + using boost::spirit::qi::rule; + typedef std::string::const_iterator str_it; + + 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); + + if (parsed) { + return std::shared_ptr( + new PortForwading(listening_port, target_addr, target_port)); + } else { + ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + return std::shared_ptr(nullptr); + } + } + + static void RegisterToServiceOptionFactory() { + ServiceOptionFactory::RegisterUserServiceParser( + GetParseName(), GetFullParseName(), GetParseDesc(), + &PortForwading::CreateServiceOptions); + } + + virtual std::string GetName() { + return "forward"; + } + + virtual std::vector> + GetRemoteServiceCreateVector() { + std::vector> result; + + services::admin::CreateServiceRequest r_socks( + services::fibers_to_sockets::FibersToSockets< + Demux>::GetCreateRequest(relay_fiber_port_, remote_addr_, + remote_port_)); + + result.push_back(r_socks); + + return result; + } + + virtual std::vector> + GetRemoteServiceStopVector(Demux& demux) { + std::vector> result; + + auto id = GetRemoteServiceId(demux); + + if (id) { + result.push_back(admin::StopServiceRequest(id)); + } + + return result; + } + + virtual bool StartLocalServices(Demux& demux) { + services::admin::CreateServiceRequest l_forward( + services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( + local_port_, relay_fiber_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); + 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_)); + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + auto status = p_service_factory->GetStatus( + r_socks.service_id(), r_socks.parameters(), GetRemoteServiceId(demux)); + + return status; + } + + virtual 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::fibers_to_sockets::FibersToSockets< + Demux>::GetCreateRequest(relay_fiber_port_, remote_addr_, + 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; + } + } + + uint16_t local_port_; + std::string remote_addr_; + uint16_t remote_port_; + + local_port_type relay_fiber_port_; + + uint32_t remoteServiceId_; + uint32_t localServiceId_; +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_USER_SERVICES_PORT_FORWARDING_H_ diff --git a/ssf-1.1.0/src/services/user_services/remote_port_forwarding.h b/ssf-1.1.0/src/services/user_services/remote_port_forwarding.h new file mode 100644 index 000000000..76fc1d6b8 --- /dev/null +++ b/ssf-1.1.0/src/services/user_services/remote_port_forwarding.h @@ -0,0 +1,197 @@ +#ifndef SSF_SERVICES_USER_SERVICES_REMOTE_PORT_FORWARDING_H_ +#define SSF_SERVICES_USER_SERVICES_REMOTE_PORT_FORWARDING_H_ + +#include + +#include +#include +#include +#include + +#ifndef BOOST_SPIRIT_USE_PHOENIX_V3 +#define BOOST_SPIRIT_USE_PHOENIX_V3 1 +#endif +#include +#include +#include + +#include + +#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 "core/factories/service_option_factory.h" + +#include "common/boost/fiber/detail/fiber_id.hpp" + +#include "services/sockets_to_fibers/sockets_to_fibers.h" +#include "services/fibers_to_sockets/fibers_to_sockets.h" + +namespace ssf { +namespace services { + +template +class RemotePortForwading : 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, + 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_; + } + + public: + static std::string GetFullParseName() { + return "remote,R"; + } + + static std::string GetParseName() { + return "remote"; + } + + static std::string GetParseDesc() { + return "Forward remote port on given target"; + } + + public: + 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_; + using boost::spirit::qi::rule; + typedef std::string::const_iterator str_it; + + 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); + + if (parsed) { + return std::shared_ptr( + new RemotePortForwading(listening_port, target_addr, target_port)); + } else { + ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + return std::shared_ptr(nullptr); + } + } + + static void RegisterToServiceOptionFactory() { + ServiceOptionFactory::RegisterUserServiceParser( + GetParseName(), GetFullParseName(), GetParseDesc(), + &RemotePortForwading::CreateServiceOptions); + } + + virtual std::string GetName() { + return "remote"; + } + + virtual std::vector> + GetRemoteServiceCreateVector() { + std::vector> result; + + services::admin::CreateServiceRequest r_forward( + services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( + remote_port_, relay_fiber_port_)); + + result.push_back(r_forward); + + return result; + } + + virtual std::vector> GetRemoteServiceStopVector( + Demux& demux) { + std::vector> result; + + auto id = GetRemoteServiceId(demux); + + if (id) { + result.push_back(admin::StopServiceRequest(id)); + } + + return result; + } + + virtual bool StartLocalServices(Demux& demux) { + services::admin::CreateServiceRequest l_forward( + services::fibers_to_sockets::FibersToSockets< + Demux>::GetCreateRequest(relay_fiber_port_, local_addr_, + 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); + return !ec; + } + + virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { + services::admin::CreateServiceRequest r_forward( + services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( + remote_port_, relay_fiber_port_)); + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + auto status = p_service_factory->GetStatus(r_forward.service_id(), + r_forward.parameters(), + GetRemoteServiceId(demux)); + + return status; + } + + virtual 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_, relay_fiber_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; + } + } + + uint16_t local_port_; + std::string local_addr_; + uint16_t remote_port_; + + local_port_type relay_fiber_port_; + + uint32_t remoteServiceId_; + uint32_t localServiceId_; +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_USER_SERVICES_REMOTE_PORT_FORWARDING_H_ diff --git a/ssf-1.1.0/src/services/user_services/remote_socks.h b/ssf-1.1.0/src/services/user_services/remote_socks.h new file mode 100644 index 000000000..e0c664d4c --- /dev/null +++ b/ssf-1.1.0/src/services/user_services/remote_socks.h @@ -0,0 +1,150 @@ +#ifndef SSF_SERVICES_USER_SERVICES_REMOTE_SOCKS_H_ +#define SSF_SERVICES_USER_SERVICES_REMOTE_SOCKS_H_ + +#include + +#include +#include +#include + +#include + +#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 "core/factories/service_option_factory.h" + +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) {} + + public: + static std::string GetFullParseName() { + return "remote_socks,F"; + } + + static std::string GetParseName() { + return "remote_socks"; + } + + 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) { + 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()); + return std::shared_ptr(nullptr); + } catch (const std::out_of_range&) { + ec.assign(ssf::error::out_of_range, ssf::error::get_ssf_category()); + return std::shared_ptr(nullptr); + } + } + + static void RegisterToServiceOptionFactory() { + ServiceOptionFactory::RegisterUserServiceParser( + GetParseName(), GetFullParseName(), GetParseDesc(), + &RemoteSocks::CreateServiceOptions); + } + + virtual std::string GetName() { + return "remote_socks"; + } + + virtual 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; + } + + virtual std::vector> GetRemoteServiceStopVector( + Demux& demux) { + std::vector> result; + + auto id = GetRemoteServiceId(demux); + + if (id) { + result.push_back(admin::StopServiceRequest(id)); + } + + return result; + } + + virtual bool StartLocalServices(Demux& demux) { + services::admin::CreateServiceRequest l_socks( + services::socks::SocksServer::GetCreateRequest(remote_port_)); + + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + boost::system::error_code ec; + local_service_id_ = p_service_factory->CreateRunNewService( + l_socks.service_id(), l_socks.parameters(), ec); + return !ec; + } + + virtual 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); + auto status = p_service_factory->GetStatus(r_forward.service_id(), + r_forward.parameters(), + GetRemoteServiceId(demux)); + + return status; + } + + virtual void StopLocalServices(Demux& demux) { + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + p_service_factory->StopService(local_service_id_); + } + + private: + uint32_t GetRemoteServiceId(Demux& demux) { + if (remote_service_id_) { + return remote_service_id_; + } 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()); + remote_service_id_ = id; + return id; + } + } + + uint16_t remote_port_; + uint32_t remote_service_id_; + uint32_t local_service_id_; +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_USER_SERVICES_REMOTE_SOCKS_H_ diff --git a/ssf-1.1.0/src/services/user_services/socks.h b/ssf-1.1.0/src/services/user_services/socks.h new file mode 100644 index 000000000..72db7b88a --- /dev/null +++ b/ssf-1.1.0/src/services/user_services/socks.h @@ -0,0 +1,148 @@ +#ifndef SSF_SERVICES_USER_SERVICES_SOCKS_H_ +#define SSF_SERVICES_USER_SERVICES_SOCKS_H_ + +#include + +#include +#include +#include + +#include + +#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 "core/factories/service_option_factory.h" + +namespace ssf { namespace services { + +template +class Socks : public BaseUserService { +private: + Socks(uint16_t local_port) + : local_port_(local_port), remoteServiceId_(0), localServiceId_(0) {} + +public: + static std::string GetFullParseName() { + return "socks,D"; + } + + static std::string GetParseName() { + return "socks"; + } + + static std::string GetParseDesc() { + return "Run a proxy socks on remote host"; + } + +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()); + return std::shared_ptr(nullptr); + } catch (const std::out_of_range&) { + ec.assign(ssf::error::out_of_range, ssf::error::get_ssf_category()); + return std::shared_ptr(nullptr); + } + } + + static void RegisterToServiceOptionFactory() { + ServiceOptionFactory::RegisterUserServiceParser( + GetParseName(), GetFullParseName(), GetParseDesc(), + &Socks::CreateServiceOptions); + } + + virtual std::string GetName() { + return "socks"; + } + + virtual std::vector> + GetRemoteServiceCreateVector() { + std::vector> result; + + services::admin::CreateServiceRequest r_socks( + services::socks::SocksServer::GetCreateRequest(local_port_)); + + result.push_back(r_socks); + + return result; + } + + virtual std::vector> GetRemoteServiceStopVector( + Demux& demux) { + std::vector> result; + + auto id = GetRemoteServiceId(demux); + + if (id) { + result.push_back(admin::StopServiceRequest(id)); + } + + return result; + } + + virtual 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); + return !ec; + } + + virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { + services::admin::CreateServiceRequest r_socks( + services::socks::SocksServer::GetCreateRequest(local_port_)); + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + auto status = p_service_factory->GetStatus( + r_socks.service_id(), r_socks.parameters(), GetRemoteServiceId(demux)); + + return status; + } + + virtual 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::socks::SocksServer::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; + } + } + + uint16_t local_port_; + uint32_t remoteServiceId_; + uint32_t localServiceId_; +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_USER_SERVICES_SOCKS_H_ diff --git a/ssf-1.1.0/src/services/user_services/udp_port_forwarding.h b/ssf-1.1.0/src/services/user_services/udp_port_forwarding.h new file mode 100644 index 000000000..9557c18ee --- /dev/null +++ b/ssf-1.1.0/src/services/user_services/udp_port_forwarding.h @@ -0,0 +1,195 @@ +#ifndef SSF_SERVICES_USER_SERVICES_UDP_PORT_FORWARDING_H_ +#define SSF_SERVICES_USER_SERVICES_UDP_PORT_FORWARDING_H_ + +#include + +#include +#include +#include +#include + +#ifndef BOOST_SPIRIT_USE_PHOENIX_V3 +#define BOOST_SPIRIT_USE_PHOENIX_V3 1 +#endif +#include +#include +#include + +#include + +#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 "core/factories/service_option_factory.h" + +#include "common/boost/fiber/detail/fiber_id.hpp" + +#include "services/datagrams_to_fibers/datagrams_to_fibers.h" +#include "services/fibers_to_datagrams/fibers_to_datagrams.h" + +namespace ssf { +namespace services { + +template +class UdpPortForwading : 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) { + relay_fiber_port_ = remote_port_ + (1 << 16); + } + + public: + static std::string GetFullParseName() { + return "udpforward,U"; + } + + static std::string GetParseName() { + return "udpforward"; + } + + static std::string GetParseDesc() { + return "Forward UDP local port on given target"; + } + + public: + 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_; + using boost::spirit::qi::rule; + typedef std::string::const_iterator str_it; + + 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); + + if (parsed) { + return std::shared_ptr( + new UdpPortForwading(listening_port, target_addr, target_port)); + } else { + ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + return std::shared_ptr(nullptr); + } + } + + static void RegisterToServiceOptionFactory() { + ServiceOptionFactory::RegisterUserServiceParser( + GetParseName(), GetFullParseName(), GetParseDesc(), + &UdpPortForwading::CreateServiceOptions); + } + + virtual std::string GetName() { + return "udpforward"; + } + + virtual std::vector> + GetRemoteServiceCreateVector() { + std::vector> result; + + services::admin::CreateServiceRequest r_forward( + services::fibers_to_datagrams::FibersToDatagrams::GetCreateRequest( + relay_fiber_port_, remote_addr_, remote_port_)); + + result.push_back(r_forward); + + return result; + } + + virtual std::vector> + GetRemoteServiceStopVector(Demux& demux) { + std::vector> result; + + auto id = GetRemoteServiceId(demux); + + if (id) { + result.push_back(admin::StopServiceRequest(id)); + } + + return result; + } + + virtual bool StartLocalServices(Demux& demux) { + services::admin::CreateServiceRequest l_forward( + services::datagrams_to_fibers::DatagramsToFibers::GetCreateRequest( + local_port_, relay_fiber_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); + 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_)); + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + auto status = p_service_factory->GetStatus( + r_socks.service_id(), r_socks.parameters(), GetRemoteServiceId(demux)); + + return status; + } + + virtual 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::fibers_to_datagrams::FibersToDatagrams::GetCreateRequest( + relay_fiber_port_, remote_addr_, 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; + } + } + + uint16_t local_port_; + std::string remote_addr_; + uint16_t remote_port_; + + local_port_type relay_fiber_port_; + + uint32_t remoteServiceId_; + uint32_t localServiceId_; +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_USER_SERVICES_UDP_PORT_FORWARDING_H_ diff --git a/ssf-1.1.0/src/services/user_services/udp_remote_port_forwarding.h b/ssf-1.1.0/src/services/user_services/udp_remote_port_forwarding.h new file mode 100644 index 000000000..b9551ef08 --- /dev/null +++ b/ssf-1.1.0/src/services/user_services/udp_remote_port_forwarding.h @@ -0,0 +1,196 @@ +#ifndef SSF_SERVICES_USER_SERVICES_UDP_REMOTE_PORT_FORWARDING_H_ +#define SSF_SERVICES_USER_SERVICES_UDP_REMOTE_PORT_FORWARDING_H_ + +#include + +#include +#include +#include +#include + +#ifndef BOOST_SPIRIT_USE_PHOENIX_V3 +#define BOOST_SPIRIT_USE_PHOENIX_V3 1 +#endif +#include +#include +#include + +#include + +#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 "core/factories/service_option_factory.h" + +#include "common/boost/fiber/detail/fiber_id.hpp" + +#include "services/datagrams_to_fibers/datagrams_to_fibers.h" +#include "services/fibers_to_datagrams/fibers_to_datagrams.h" + +namespace ssf { +namespace services { + +template +class UdpRemotePortForwading : 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) { + relay_fiber_port_ = remote_port_ + (1 << 16); + } + + public: + static std::string GetFullParseName() { + return "udpremote,V"; + } + + static std::string GetParseName() { + return "udpremote"; + } + + static std::string GetParseDesc() { + return "Forward remote udp port on given target"; + } + + public: + 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_; + using boost::spirit::qi::rule; + typedef std::string::const_iterator str_it; + + 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); + + if (parsed) { + return std::shared_ptr( + new UdpRemotePortForwading(listening_port, target_addr, target_port)); + } else { + ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); + return std::shared_ptr(nullptr); + } + } + + static void RegisterToServiceOptionFactory() { + ServiceOptionFactory::RegisterUserServiceParser( + GetParseName(), GetFullParseName(), GetParseDesc(), + &UdpRemotePortForwading::CreateServiceOptions); + } + + virtual std::string GetName() { + return "udpremote"; + } + + virtual std::vector> + GetRemoteServiceCreateVector() { + std::vector> result; + + services::admin::CreateServiceRequest r_forward( + services::datagrams_to_fibers::DatagramsToFibers:: + GetCreateRequest(remote_port_, relay_fiber_port_)); + + result.push_back(r_forward); + + return result; + } + + virtual std::vector> GetRemoteServiceStopVector( + Demux& demux) { + std::vector> result; + + auto id = GetRemoteServiceId(demux); + + if (id) { + result.push_back(admin::StopServiceRequest(id)); + } + + return result; + } + + virtual bool StartLocalServices(Demux& demux) { + services::admin::CreateServiceRequest l_forward( + services::fibers_to_datagrams::FibersToDatagrams::GetCreateRequest( + relay_fiber_port_, local_addr_, 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); + return !ec; + } + + virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { + services::admin::CreateServiceRequest r_forward( + services::datagrams_to_fibers::DatagramsToFibers::GetCreateRequest( + remote_port_, relay_fiber_port_)); + auto p_service_factory = + ServiceFactoryManager::GetServiceFactory(&demux); + auto status = p_service_factory->GetStatus(r_forward.service_id(), + r_forward.parameters(), + GetRemoteServiceId(demux)); + + return status; + } + + virtual 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::datagrams_to_fibers::DatagramsToFibers::GetCreateRequest( + remote_port_, relay_fiber_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; + } + } + + uint16_t local_port_; + std::string local_addr_; + uint16_t remote_port_; + + local_port_type relay_fiber_port_; + + uint32_t remoteServiceId_; + uint32_t localServiceId_; +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_USER_SERVICES_UDP_REMOTE_PORT_FORWARDING_H_ diff --git a/ssf-1.1.0/src/tests/CMakeLists.txt b/ssf-1.1.0/src/tests/CMakeLists.txt new file mode 100644 index 000000000..eca70c2cd --- /dev/null +++ b/ssf-1.1.0/src/tests/CMakeLists.txt @@ -0,0 +1,318 @@ +project(RUN_TESTS CXX) + +enable_testing() + +include_directories( + ${OpenSSL_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS}) + +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 diff --git a/ssf-1.1.0/src/tests/bouncing_tests.cpp b/ssf-1.1.0/src/tests/bouncing_tests.cpp new file mode 100644 index 000000000..72d3c792f --- /dev/null +++ b/ssf-1.1.0/src/tests/bouncing_tests.cpp @@ -0,0 +1,169 @@ +#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/ssf-1.1.0/src/tests/copy_file_test_fixture.h b/ssf-1.1.0/src/tests/copy_file_test_fixture.h new file mode 100644 index 000000000..7c5b740b3 --- /dev/null +++ b/ssf-1.1.0/src/tests/copy_file_test_fixture.h @@ -0,0 +1,283 @@ +#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/ssf-1.1.0/src/tests/fiber_asio_tests.cpp b/ssf-1.1.0/src/tests/fiber_asio_tests.cpp new file mode 100644 index 000000000..315c8a74e --- /dev/null +++ b/ssf-1.1.0/src/tests/fiber_asio_tests.cpp @@ -0,0 +1,1085 @@ +#include +#include +#include +#include +#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" +#include "common/boost/fiber/basic_endpoint.hpp" + +class FiberTest : public ::testing::Test { + protected: + FiberTest() + : clientThreads_(), + io_service_client_(), + p_client_worker(std::unique_ptr( + 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"), + iterator_client_(resolver_client_.resolve(query_client_)), + demux_client_(io_service_client_), + client_ready_(), + serverThreads_(), + io_service_server_(), + p_server_worker(std::unique_ptr( + new boost::asio::io_service::work(io_service_server_))), + 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"), + endpoint_server_(*resolver_server_.resolve(query_server_)), + demux_server_(io_service_server_), + server_ready_() {} + + ~FiberTest() {} + + typedef boost::asio::ip::tcp::socket socket; + typedef boost::asio::ip::tcp::resolver::iterator tcp_endpoint_it; + typedef boost::asio::fiber::stream_fiber stream_fiber; + typedef boost::asio::fiber::datagram_fiber datagram_fiber; + typedef boost::asio::fiber::basic_fiber_demux fiber_demux; + typedef stream_fiber::socket fiber; + typedef stream_fiber::acceptor fiber_acceptor; + typedef stream_fiber::endpoint fiber_endpoint; + typedef datagram_fiber::socket dgr_fiber; + typedef datagram_fiber::endpoint dgr_fiber_endpoint; + typedef std::function + accept_handler_type; + typedef std::function + connect_handler_type; + + virtual void SetUp() { + startServer(); + connectClient(); + } + + virtual void TearDown() { + clientStop(); + serverStop(); + } + + bool Wait() { + auto server_ready_future = server_ready_.get_future(); + auto client_ready_future = client_ready_.get_future(); + + server_ready_future.wait(); + client_ready_future.wait(); + + return server_ready_future.get() && client_ready_future.get(); + } + + private: + 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); + }; + + for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + 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_)); + } + + this->server_ready_.set_value(!ec); + accept_h(ec); + }; + + boost::system::error_code server_ec; + boost::asio::socket_base::reuse_address option(true); + acceptor_server_.open(endpoint_server_.protocol(), server_ec); + acceptor_server_.set_option(option, server_ec); + acceptor_server_.bind(endpoint_server_, server_ec); + acceptor_server_.listen(boost::asio::socket_base::max_connections, + server_ec); + acceptor_server_.async_accept(socket_server_, std::move(accepted_lambda)); + } + + void connectClient( + connect_handler_type connect_h = [](const boost::system::error_code&, + tcp_endpoint_it) {}) { + auto lambda = [this]() { + boost::system::error_code ec; + this->io_service_client_.run(ec); + }; + + for (uint8_t i = 1; i <= boost::thread::hardware_concurrency(); ++i) { + clientThreads_.create_thread(lambda); + } + + auto connected_lambda = [this, connect_h]( + const boost::system::error_code& ec, tcp_endpoint_it ep_it) { + if (!ec) { + this->demux_client_.fiberize(std::move(this->socket_client_)); + } + + this->client_ready_.set_value(!ec); + connect_h(ec, ep_it); + }; + + boost::asio::async_connect(socket_client_, iterator_client_, + std::move(connected_lambda)); + } + + void serverStop() { + demux_server_.close(); + socket_server_.close(); + acceptor_server_.close(); + p_server_worker.reset(); + + serverThreads_.join_all(); + io_service_server_.stop(); + } + + void clientStop() { + boost::system::error_code ec; + demux_client_.close(); + socket_client_.close(ec); + p_client_worker.reset(); + + clientThreads_.join_all(); + io_service_client_.stop(); + } + + protected: + boost::thread_group clientThreads_; + boost::asio::io_service io_service_client_; + std::unique_ptr p_client_worker; + boost::asio::ip::tcp::socket socket_client_; + boost::asio::ip::tcp::resolver resolver_client_; + boost::asio::ip::tcp::resolver::query query_client_; + boost::asio::ip::tcp::resolver::iterator iterator_client_; + fiber_demux demux_client_; + std::promise client_ready_; + + boost::thread_group serverThreads_; + boost::asio::io_service io_service_server_; + std::unique_ptr p_server_worker; + boost::asio::ip::tcp::socket socket_server_; + boost::asio::ip::tcp::acceptor acceptor_server_; + boost::asio::ip::tcp::resolver resolver_server_; + boost::asio::ip::tcp::resolver::query query_server_; + boost::asio::ip::tcp::endpoint endpoint_server_; + fiber_demux demux_server_; + std::promise server_ready_; +}; + +//----------------------------------------------------------------------------- +TEST_F(FiberTest, startStopServer) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::info); + Wait(); +} + +//---------------------------------------------------------------------------- +TEST_F(FiberTest, connectDisconnectFiberFromServer) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::info); + + Wait(); + + std::promise accept_closed; + std::promise connect_closed; + + fiber_acceptor fib_acceptor(io_service_server_); + 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"; + + boost::system::error_code close_fib_server_ec; + fib_server.close(close_fib_server_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 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)); + }; + + boost::system::error_code acceptor_ec; + fiber_endpoint fib_server_endpoint( + boost::asio::fiber::stream_fiber::v1(), demux_server_, 1); + fib_acceptor.open(fib_server_endpoint.protocol()); + fib_acceptor.bind(fib_server_endpoint, acceptor_ec); + fib_acceptor.listen(); + fib_acceptor.async_accept(fib_server, std::move(accepted_lambda)); + + fiber_endpoint fib_client_endpoint( + boost::asio::fiber::stream_fiber::v1(), demux_client_, 1); + fib_client.async_connect(fib_client_endpoint, connected_lambda); + + connect_closed.get_future().wait(); + accept_closed.get_future().wait(); + + boost::system::error_code close_fib_acceptor_ec; + 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; + std::promise connect_closed; + + fiber_acceptor fib_acceptor(io_service_server_); + 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) { + accept_closed_ref.set_value(!!ec); + }; + 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"; + + io_service_client_.post(boost::bind(wait_closing_lambda, ec)); + }; + + auto connected_lambda = [this, &fib_client, &connect_closed]( + const boost::system::error_code& ec) { + ASSERT_EQ(ec.value(), 0) << "Connect handler should not be in error"; + + if (!ec) { + boost::system::error_code close_fib_client_ec; + fib_client.close(close_fib_client_ec); + } + + connect_closed.set_value(!ec); + }; + + boost::system::error_code acceptor_ec; + fiber_endpoint fib_server_endpoint( + boost::asio::fiber::stream_fiber::v1(), demux_server_, 1); + fib_acceptor.open(fib_server_endpoint.protocol()); + fib_acceptor.bind(fib_server_endpoint, acceptor_ec); + fib_acceptor.listen(); + fib_acceptor.async_accept(fib_server, std::move(accepted_lambda)); + + fiber_endpoint fib_client_endpoint( + boost::asio::fiber::stream_fiber::v1(), demux_client_, 1); + fib_client.async_connect(fib_client_endpoint, connected_lambda); + + connect_closed.get_future().wait(); + accept_closed.get_future().wait(); + + boost::system::error_code close_fib_acceptor_ec; + fib_acceptor.close(close_fib_acceptor_ec); +} + +//----------------------------------------------------------------------------- +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; + std::atomic number_of_connected(0); + std::atomic number_of_accepted(0); + std::promise accepted_all; + std::promise connected_all; + + fiber_acceptor fib_acceptor(io_service_server_); + + std::function p_fiber, + const boost::system::error_code&)> async_accept_h1; + std::function async_accept_h2; + + async_accept_h2 = [this, &async_accept_h1, &fib_acceptor]() { + auto p_fiber = std::make_shared(io_service_server_); + fib_acceptor.async_accept(*p_fiber, + 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"; + } + }; + + boost::system::error_code ec_server; + fiber_endpoint fib_server_endpoint( + boost::asio::fiber::stream_fiber::v1(), demux_server_, 1); + fib_acceptor.open(fib_server_endpoint.protocol()); + fib_acceptor.bind(fib_server_endpoint, ec_server); + fib_acceptor.listen(); + async_accept_h2(); + + fiber_endpoint fib_client_endpoint( + boost::asio::fiber::stream_fiber::v1(), demux_client_, 1); + for (std::size_t i = 0; i < number_of_connections; ++i) { + auto p_fiber = std::make_shared(io_service_client_); + p_fiber->async_connect(fib_client_endpoint, + boost::bind(async_connect_h1, p_fiber, _1)); + } + + connected_all.get_future().wait(); + accepted_all.get_future().wait(); + + boost::system::error_code close_fib_acceptor_ec; + fib_acceptor.close(close_fib_acceptor_ec); +} + +//----------------------------------------------------------------------------- +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; + std::promise client_closed; + std::promise server_closed; + + fiber_acceptor fib_acceptor(io_service_server_); + fiber fib_server(io_service_server_); + fiber fib_client(io_service_client_); + + uint8_t buffer_client[5] = {0}; + uint8_t buffer_server[5] = {0}; + bool result_server = true; + int server_received = 0; + + std::function + async_receive_h1; + std::function + async_send_h1; + + async_receive_h1 = [&, this](const boost::system::error_code& ec, + std::size_t) { + ASSERT_EQ(ec.value(), 0); + + 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_server, boost::asio::buffer(buffer_server), + boost::bind(async_send_h1, _1, _2)); + } else { + fib_server.close(); + server_closed.set_value(true); + } + }; + + async_send_h1 = [&, this](const boost::system::error_code& ec, std::size_t) { + ASSERT_EQ(ec.value(), 0); + + boost::asio::async_read(fib_server, boost::asio::buffer(buffer_server), + boost::bind(async_receive_h1, _1, _2)); + }; + + auto async_accept_h = [&, this](const boost::system::error_code& ec) { + ASSERT_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_server, boost::asio::buffer(buffer_server), + boost::bind(async_receive_h1, _1, _2)); + }; + + std::function + async_receive_h2; + std::function + async_send_h2; + + async_receive_h2 = [&, this](const boost::system::error_code& ec, + std::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_client, boost::asio::buffer(buffer_client), + boost::bind(async_send_h2, _1, _2)); + } else { + fib_client.close(); + client_closed.set_value(true); + } + }; + + async_send_h2 = [&, this](const boost::system::error_code& ec, std::size_t) { + ASSERT_EQ(ec.value(), 0); + boost::asio::async_read(fib_client, boost::asio::buffer(buffer_client), + boost::bind(async_receive_h2, _1, _2)); + }; + + auto async_connect_h = [&, this](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_client, boost::asio::buffer(buffer_client), + boost::bind(async_send_h2, _1, _2)); + }; + + boost::system::error_code acceptor_ec; + fiber_endpoint server_endpoint(boost::asio::fiber::stream_fiber::v1(), + demux_server_, 1); + fib_acceptor.open(server_endpoint.protocol(), acceptor_ec); + fib_acceptor.bind(server_endpoint, acceptor_ec); + fib_acceptor.listen(boost::asio::socket_base::max_connections, acceptor_ec); + fib_acceptor.async_accept(fib_server, boost::bind(async_accept_h, _1)); + + fiber_endpoint client_endpoint(boost::asio::fiber::stream_fiber::v1(), + demux_client_, 1); + fib_client.async_connect(client_endpoint, + boost::bind(async_connect_h, _1)); + + client_closed.get_future().wait(); + server_closed.get_future().wait(); + + ASSERT_EQ(true, result_server); + ASSERT_EQ(packet_number, server_received); + boost::system::error_code close_fib_acceptor_ec; + fib_acceptor.close(close_fib_acceptor_ec); +} + +////----------------------------------------------------------------------------- +TEST_F(FiberTest, exchangePacketsFiveClients) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::info); + + Wait(); + + typedef std::array buffer_type; + + struct TestConnection { + TestConnection(boost::asio::io_service& io_service_server, + boost::asio::io_service& io_service_client) + : fib_server(io_service_server), fib_client(io_service_client) {} + + fiber fib_server; + fiber fib_client; + buffer_type buffer_server; + buffer_type buffer_client; + std::promise server_closed; + std::promise client_closed; + }; + + uint32_t connection_number = 5; + int32_t packet_number = 5 * 100; + + fiber_acceptor fib_acceptor(io_service_server_); + + std::list test_connections; + + boost::recursive_mutex server_received_mutex; + int32_t server_received = 0; + + std::atomic finished(false); + + std::function&, fiber&, buffer_type&)> + async_receive_h1; + 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))); + }; + + std::function&, fiber&, buffer_type&)> + async_receive_h2; + 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))); + }; + + boost::system::error_code acceptor_ec; + fiber_endpoint server_endpoint(boost::asio::fiber::stream_fiber::v1(), + demux_server_, 1); + fib_acceptor.open(server_endpoint.protocol(), acceptor_ec); + fib_acceptor.bind(server_endpoint, acceptor_ec); + fib_acceptor.listen(boost::asio::socket_base::max_connections, acceptor_ec); + + fiber_endpoint client_endpoint(boost::asio::fiber::stream_fiber::v1(), + demux_client_, 1); + + for (std::size_t i = 0; i < connection_number; ++i) { + test_connections.emplace_front(io_service_server_, io_service_client_); + auto& test_connection = test_connections.front(); + fib_acceptor.async_accept( + test_connection.fib_server, + boost::bind(async_accept_h, _1, + boost::ref(test_connection.server_closed), + boost::ref(test_connection.fib_server), + boost::ref(test_connection.buffer_server))); + test_connection.fib_client.async_connect( + client_endpoint, + boost::bind(async_connect_h, + boost::ref(test_connection.client_closed), + boost::ref(test_connection.fib_client), + boost::ref(test_connection.buffer_client), _1)); + } + + for (auto& test_connection : test_connections) { + test_connection.client_closed.get_future().wait(); + test_connection.server_closed.get_future().wait(); + } + + ASSERT_EQ(packet_number, server_received); + boost::system::error_code close_fib_acceptor_ec; + fib_acceptor.close(close_fib_acceptor_ec); + + ASSERT_EQ(finished, true) << "Test did not finish completely"; +} + +//----------------------------------------------------------------------------- +TEST_F(FiberTest, tooSmallReceiveBuffer) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::info); + + Wait(); + + 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; + + fiber_acceptor fib_acceptor(io_service_server_); + fiber fib_server(io_service_server_); + fiber fib_client(io_service_client_); + + 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; + + 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); + boost::asio::async_read(fib_server, boost::asio::buffer(buffer_server), + boost::bind(void_handler_receive, _1, _2)); + boost::asio::async_read(fib_server, boost::asio::buffer(buffer_server), + boost::bind(void_handler_receive, _1, _2)); + boost::asio::async_read(fib_server, boost::asio::buffer(buffer_server), + boost::bind(void_handler_receive, _1, _2)); + boost::asio::async_read(fib_server, boost::asio::buffer(buffer_server), + boost::bind(void_handler_receive, _1, _2)); + }; + + 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; + } + sent += s; + + fib_client.close(); + client_closed.set_value(true); + }; + + auto async_connect_h1 = [&, this](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_client, boost::asio::buffer(buffer_client), + boost::bind(void_handler_send, _1, _2)); + }; + + boost::system::error_code acceptor_ec; + fiber_endpoint server_endpoint(boost::asio::fiber::stream_fiber::v1(), + demux_server_, 1); + fib_acceptor.open(server_endpoint.protocol(), acceptor_ec); + fib_acceptor.bind(server_endpoint, acceptor_ec); + fib_acceptor.listen(boost::asio::socket_base::max_connections, acceptor_ec); + fib_acceptor.async_accept(fib_server, boost::bind(async_accept_h1, _1)); + + fiber_endpoint client_endpoint(boost::asio::fiber::stream_fiber::v1(), + demux_client_, 1); + fib_client.async_connect(client_endpoint, + boost::bind(async_connect_h1, _1)); + + client_closed.get_future().wait(); + server_closed.get_future().wait(); + + { + boost::recursive_mutex::scoped_lock lock(received_mutex); + } + + EXPECT_EQ(received, 5); + EXPECT_EQ(sent, 5); + boost::system::error_code close_fib_acceptor_ec; + fib_acceptor.close(close_fib_acceptor_ec); +} + +//----------------------------------------------------------------------------- +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; + + dgr_fiber_endpoint endpoint_server_local_port( + boost::asio::fiber::datagram_fiber::v1(), demux_server_, rem_p); + dgr_fiber_endpoint endpoint_client_remote_port( + boost::asio::fiber::datagram_fiber::v1(), demux_client_, rem_p); + dgr_fiber dgr_f_server(io_service_server_); + dgr_fiber dgr_f_client(io_service_client_); + std::array buffer_client; + + std::promise client_closed; + std::promise server_closed; + + boost::system::error_code ec; + + dgr_fiber_endpoint endpoint_server_from( + 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)); + }; + + dgr_f_server.open(endpoint_server_local_port.protocol(), ec); + dgr_f_server.bind(endpoint_server_local_port, ec); + ASSERT_EQ(ec.value(), 0); + + dgr_f_server.async_receive_from(boost::asio::buffer(buffer_server), + endpoint_server_from, + boost::bind(received_server, _1, _2)); + + buffer_client[0] = 1; + buffer_client[1] = 2; + buffer_client[2] = 3; + buffer_client[3] = 4; + buffer_client[4] = 5; + + 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)); + }; + + dgr_f_client.async_send_to(boost::asio::buffer(buffer_client), + endpoint_client_remote_port, + boost::bind(sent_client, _1, _2)); + + client_closed.get_future().wait(); + server_closed.get_future().wait(); +} + +//---------------------------------------------------------------------------- +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; + boost::asio::ssl::context ctx(boost::asio::ssl::context::tlsv12); + + 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"; + } + + X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256); + BOOST_LOG_TRIVIAL(debug) << "Verifying " << subject_name << "\n"; + + BOOST_LOG_TRIVIAL(debug) << "------------------------------" << 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/trusted/ca.crt", + NULL); + + // The certificate used by the local peer + ctx.use_certificate_chain_file("./certs/certificate.crt"); + + // The private key used by the local peer + ctx.use_private_key_file("./certs/private.key", + 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"); + + fiber_acceptor fib_acceptor(io_service_server_); + + ssl_fiber_t ssl_fiber_client(io_service_client_, ctx); + ssl_fiber_t ssl_fiber_server(io_service_server_, ctx); + + std::promise client_closed; + std::promise server_closed; + + uint32_t buffer_s = 1; + uint32_t buffer_c = 0; + + 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; + server_closed.set_value(true); + }; + + auto async_handshaked_s = [this, &ssl_fiber_server, &buffer_s, &sent]( + const boost::system::error_code& ec) { + EXPECT_EQ(ec.value(), 0) << "Handshaked handler should not be in error"; + buffer_s = 10; + boost::asio::async_write(ssl_fiber_server, + boost::asio::buffer(&buffer_s, sizeof(buffer_s)), + boost::bind(sent, _1, _2)); + }; + + auto async_accept_s = [this, &ssl_fiber_server, &async_handshaked_s]( + const boost::system::error_code& ec) { + EXPECT_EQ(ec.value(), 0) << "Accept handler should not be in error"; + ssl_fiber_server.async_handshake(boost::asio::ssl::stream_base::server, + 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 async_handshaked_c = [this, &ssl_fiber_client, &buffer_c, &received]( + const boost::system::error_code& ec) { + EXPECT_EQ(ec.value(), 0) << "Handshaked handler should not be in error"; + boost::asio::async_read(ssl_fiber_client, + boost::asio::buffer(&buffer_c, sizeof(buffer_c)), + boost::bind(received, _1, _2)); + }; + + auto async_connect_c = [this, &ssl_fiber_client, &async_handshaked_c]( + const boost::system::error_code& ec) { + EXPECT_EQ(ec.value(), 0) << "Connect handler should not be in error"; + ssl_fiber_client.async_handshake(boost::asio::ssl::stream_base::client, + boost::bind(async_handshaked_c, _1)); + }; + + boost::system::error_code acceptor_ec; + fiber_endpoint server_endpoint(boost::asio::fiber::stream_fiber::v1(), + demux_server_, 1); + fib_acceptor.open(server_endpoint.protocol(), acceptor_ec); + fib_acceptor.bind(server_endpoint, acceptor_ec); + fib_acceptor.listen(boost::asio::socket_base::max_connections, acceptor_ec); + fib_acceptor.async_accept(ssl_fiber_server.next_layer(), + boost::bind(async_accept_s, _1)); + + fiber_endpoint client_endpoint(boost::asio::fiber::stream_fiber::v1(), + demux_client_, 1); + ssl_fiber_client.next_layer().async_connect( + client_endpoint, boost::bind(async_connect_c, _1)); + + client_closed.get_future().wait(); + server_closed.get_future().wait(); + + boost::system::error_code ec; + ssl_fiber_server.next_layer().close(ec); + fib_acceptor.close(ec); +} diff --git a/ssf-1.1.0/src/tests/fiber_basic_tests.cpp b/ssf-1.1.0/src/tests/fiber_basic_tests.cpp new file mode 100644 index 000000000..e211ded87 --- /dev/null +++ b/ssf-1.1.0/src/tests/fiber_basic_tests.cpp @@ -0,0 +1,996 @@ +#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/ssf-1.1.0/src/tests/file_copy_from_client_tests.cpp b/ssf-1.1.0/src/tests/file_copy_from_client_tests.cpp new file mode 100644 index 000000000..a6408c23d --- /dev/null +++ b/ssf-1.1.0/src/tests/file_copy_from_client_tests.cpp @@ -0,0 +1,148 @@ +#include "tests/copy_file_test_fixture.h" + +#include +#include + +#include + +using Md5Digest = std::array; + +Md5Digest GetMd5Sum(const std::string& filepath, + boost::system::error_code& ec) { + using Buffer = std::array; + + Md5Digest md5 = {{0}}; + Buffer buf = {{0}}; + + 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()); + return md5; + } + + MD5_Init(&md5_context); + do { + file.read(buf.data(), buf.size()); + MD5_Update(&md5_context, buf.data(), + static_cast(file.gcount())); + } while (!file.eof()); + + MD5_Final(md5.data(), &md5_context); + + return md5; +} + +void FileExistsAndIdentical(const std::string& source_filepath, + const std::string& test_filepath) { + ASSERT_TRUE(boost::filesystem::is_regular_file(test_filepath)) + << "The file " << test_filepath << " should exist in files_copied"; + + // Test source_file is equal to test_file + // This test could fail due to the closing of fiber to file session after the + // execution of this test + boost::system::error_code ec; + Md5Digest source_digest = GetMd5Sum(source_filepath, ec); + ASSERT_EQ(ec.value(), 0) << "Source file could not be digested"; + Md5Digest test_digest = GetMd5Sum(test_filepath, ec); + ASSERT_EQ(ec.value(), 0) << "Test file could not be digested"; + + ASSERT_TRUE(std::equal(source_digest.begin(), source_digest.end(), + test_digest.begin())) + << "MD5 sum of file " << source_filepath << " and " << test_filepath + << " are different"; +} + +//----------------------------------------------------------------------------- +TEST_F(CopyNoFileFromClientToRemoteTest, CopyTest) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::info); + + ASSERT_TRUE(Wait()); + + ASSERT_TRUE(WaitClose()); + + // Check if file exists and content is equal + boost::filesystem::path copied_file("files_copied/test_filex.txt"); + ASSERT_FALSE(boost::filesystem::is_regular_file(copied_file)) + << "The file test_filex.txt should not exist in files_copied"; +} + +//----------------------------------------------------------------------------- +TEST_F(CopyUniqueFileFromClientToRemoteTest, CopyTest) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::info); + + ASSERT_TRUE(Wait()); + + ASSERT_TRUE(WaitClose()); + + FileExistsAndIdentical("files_to_copy/test_file1.txt", + "files_copied/test_file1.txt"); +} + +//----------------------------------------------------------------------------- +TEST_F(CopyGlobFileFromClientToRemoteTest, CopyTest) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::info); + + ASSERT_TRUE(Wait()); + + ASSERT_TRUE(WaitClose()); + + FileExistsAndIdentical("files_to_copy/test_file1.txt", + "files_copied/test_file1.txt"); + FileExistsAndIdentical("files_to_copy/test_file2.txt", + "files_copied/test_file2.txt"); +} + +//----------------------------------------------------------------------------- +TEST_F(CopyUniqueFileFromRemoteToClientTest, CopyTest) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::info); + + ASSERT_TRUE(Wait()); + + ASSERT_TRUE(WaitClose()); + + FileExistsAndIdentical("files_to_copy/test_file1.txt", + "files_copied/test_file1.txt"); +} + +//----------------------------------------------------------------------------- +TEST_F(CopyGlobFileFromRemoteToClientTest, CopyTest) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::info); + + ASSERT_TRUE(Wait()); + + ASSERT_TRUE(WaitClose()); + + FileExistsAndIdentical("files_to_copy/test_file1.txt", + "files_copied/test_file1.txt"); + FileExistsAndIdentical("files_to_copy/test_file2.txt", + "files_copied/test_file2.txt"); +} + +//----------------------------------------------------------------------------- +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(); + std::cin.rdbuf(in.rdbuf()); + + ASSERT_TRUE(Wait()); + + ASSERT_TRUE(WaitClose()); + + FileExistsAndIdentical("files_to_copy/test_file1.txt", + "files_copied/test_file1.txt"); + + // restore stdin + std::cin.rdbuf(cinbuf); +} \ No newline at end of file diff --git a/ssf-1.1.0/src/tests/files_to_copy/test_file1.txt b/ssf-1.1.0/src/tests/files_to_copy/test_file1.txt new file mode 100644 index 000000000..53ef9a9f3 --- /dev/null +++ b/ssf-1.1.0/src/tests/files_to_copy/test_file1.txt @@ -0,0 +1,3 @@ +This is test file 1 + +It contains dummy data \ No newline at end of file diff --git a/ssf-1.1.0/src/tests/files_to_copy/test_file2.txt b/ssf-1.1.0/src/tests/files_to_copy/test_file2.txt new file mode 100644 index 000000000..8a00e4d97 --- /dev/null +++ b/ssf-1.1.0/src/tests/files_to_copy/test_file2.txt @@ -0,0 +1,3 @@ +This is test file 2 + +It contains more dummy data \ No newline at end of file diff --git a/ssf-1.1.0/src/tests/load_config_tests.cpp b/ssf-1.1.0/src/tests/load_config_tests.cpp new file mode 100644 index 000000000..b5bf5ed26 --- /dev/null +++ b/ssf-1.1.0/src/tests/load_config_tests.cpp @@ -0,0 +1,107 @@ +#include +#include +#include + +#include +#include +#include + +#include "common/config/config.h" + +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_; +}; + +//----------------------------------------------------------------------------- +TEST_F(LoadConfigTest, loadNoFile) { + boost::system::error_code ec; + ssf::LoadConfig("", ec); + ASSERT_EQ(ec.value(), 0) << "Success if no file given"; +} + +//----------------------------------------------------------------------------- +TEST_F(LoadConfigTest, loadNonExistantFile) { + boost::system::error_code ec; + ssf::LoadConfig(filename_, ec); + ASSERT_NE(ec.value(), 0) << "No success if file not existant"; +} + +//----------------------------------------------------------------------------- +TEST_F(LoadConfigTest, loadEmptyFile) { + boost::system::error_code ec; + std::ofstream file; + file.open(filename_); + file.close(); + ssf::LoadConfig(filename_, ec); + ASSERT_NE(ec.value(), 0) << "No success if file empty"; +} + +//----------------------------------------------------------------------------- +TEST_F(LoadConfigTest, loadWrongFormatFile) { + 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()); + ASSERT_NE(ec.value(), 0) << "No success if wrong file format"; +} + +//----------------------------------------------------------------------------- +TEST_F(LoadConfigTest, loadPartialFile) { + 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); + 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, loadCompleteFile) { + 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); + 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"); +} diff --git a/ssf-1.1.0/src/tests/remote_socks_tests.cpp b/ssf-1.1.0/src/tests/remote_socks_tests.cpp new file mode 100644 index 000000000..92d01007b --- /dev/null +++ b/ssf-1.1.0/src/tests/remote_socks_tests.cpp @@ -0,0 +1,538 @@ +#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/ssf-1.1.0/src/tests/remote_stream_forwarding_tests.cpp b/ssf-1.1.0/src/tests/remote_stream_forwarding_tests.cpp new file mode 100644 index 000000000..0a032d46a --- /dev/null +++ b/ssf-1.1.0/src/tests/remote_stream_forwarding_tests.cpp @@ -0,0 +1,399 @@ +#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/ssf-1.1.0/src/tests/remote_udp_forwarding_tests.cpp b/ssf-1.1.0/src/tests/remote_udp_forwarding_tests.cpp new file mode 100644 index 000000000..4315c7fc4 --- /dev/null +++ b/ssf-1.1.0/src/tests/remote_udp_forwarding_tests.cpp @@ -0,0 +1,410 @@ +#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/ssf-1.1.0/src/tests/socks_tests.cpp b/ssf-1.1.0/src/tests/socks_tests.cpp new file mode 100644 index 000000000..510311a20 --- /dev/null +++ b/ssf-1.1.0/src/tests/socks_tests.cpp @@ -0,0 +1,536 @@ +#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/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(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_t)), + ec); + + if (ec) { + BOOST_LOG_TRIVIAL(error) << "dummy client : fail to write " << ec.value(); + Stop(); + } + + return !ec; + } + + bool ReceiveOneBuffer() { + 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(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::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 (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() { + boost::system::error_code ec; + sockets_.clear(); + 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_; + size_t one_buffer_size_; + std::array one_buffer_; + std::set> sockets_; + boost::thread_group threads_; +}; + +class SocksTest : 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: + SocksTest() + : 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) {} + + ~SocksTest() {} + + 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::Socks::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(&SocksTest::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() == "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(SocksTest, startStopTransmitSSFSocks) { + 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 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/ssf-1.1.0/src/tests/ssf_client_server_cipher_suites_tests.cpp b/ssf-1.1.0/src/tests/ssf_client_server_cipher_suites_tests.cpp new file mode 100644 index 000000000..806f93c40 --- /dev/null +++ b/ssf-1.1.0/src/tests/ssf_client_server_cipher_suites_tests.cpp @@ -0,0 +1,199 @@ +#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_port_forwarding.h" + +class SSFClientServerCipherSuitesTest : 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; + typedef boost::function + ClientCallback; + + public: + SSFClientServerCipherSuitesTest() + : 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) {} + + ~SSFClientServerCipherSuitesTest() {} + + virtual void TearDown() { + StopClientThreads(); + StopServerThreads(); + } + + void StartServer(const ssf::Config& config) { + p_ssf_server_.reset(new ssf::SSFServer< + ssf::SSLPolicy, ssf::NullLinkAuthenticationPolicy, + ssf::BounceProtocolPolicy, ssf::TransportProtocolPolicy>( + server_io_service_, config, 8000)); + + StartServerThreads(); + p_ssf_server_->run(); + } + + void StartClient(const ssf::Config& config, const ClientCallback& callback) { + typedef boost::asio::ip::tcp::socket socket; + typedef ssf::SSLWrapper<> ssl_socket; + typedef boost::asio::fiber::basic_fiber_demux demux; + + std::vector + client_options; + + std::map params( + {{"remote_addr", "127.0.0.1"}, {"remote_port", "8000"}}); + + p_ssf_client_.reset(new ssf::SSFClient< + ssf::SSLPolicy, ssf::NullLinkAuthenticationPolicy, + ssf::BounceProtocolPolicy, ssf::TransportProtocolPolicy>( + client_io_service_, "127.0.0.1", "8000", + config, client_options, callback)); + StartClientThreads(); + p_ssf_client_->run(params); + } + + 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(); + } + + 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_; +}; + +//----------------------------------------------------------------------------- +TEST_F(SSFClientServerCipherSuitesTest, connectDisconnectDifferentSuite) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::debug); + + + 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 client_config; + ssf::Config server_config; + server_config.tls.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) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::debug); + + + 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 client_config; + ssf::Config server_config; + client_config.tls.cipher_alg = "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256"; + server_config.tls.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/ssf-1.1.0/src/tests/ssf_client_server_tests.cpp b/ssf-1.1.0/src/tests/ssf_client_server_tests.cpp new file mode 100644 index 000000000..626498ad6 --- /dev/null +++ b/ssf-1.1.0/src/tests/ssf_client_server_tests.cpp @@ -0,0 +1,165 @@ +#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_port_forwarding.h" + +class SSFClientServerTest : 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: + SSFClientServerTest() + : 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) {} + + ~SSFClientServerTest() {} + + 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; + + 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(&SSFClientServerTest::SSFClientCallback, this, _1, _2, _3))); + StartClientThreads(); + p_ssf_client_->run(params); + } + + 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 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) { + transport_set_.set_value(false); + } + + return; + } + + if (type == ssf::services::initialisation::TRANSPORT) { + transport_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_; +}; + +//----------------------------------------------------------------------------- +TEST_F(SSFClientServerTest, connectDisconnect) { + boost::log::core::get()->set_filter(boost::log::trivial::severity >= + boost::log::trivial::debug); + + ASSERT_TRUE(Wait()); +} diff --git a/ssf-1.1.0/src/tests/stream_forwarding_tests.cpp b/ssf-1.1.0/src/tests/stream_forwarding_tests.cpp new file mode 100644 index 000000000..c5cbe15e2 --- /dev/null +++ b/ssf-1.1.0/src/tests/stream_forwarding_tests.cpp @@ -0,0 +1,397 @@ +#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/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 StreamForwardTest : 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: + StreamForwardTest() + : 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) {} + + ~StreamForwardTest() {} + + 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::PortForwading::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(&StreamForwardTest::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() == "forward") { + 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(StreamForwardTest, 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(); +} \ No newline at end of file diff --git a/ssf-1.1.0/src/tests/udp_forwarding_tests.cpp b/ssf-1.1.0/src/tests/udp_forwarding_tests.cpp new file mode 100644 index 000000000..d4b955a47 --- /dev/null +++ b/ssf-1.1.0/src/tests/udp_forwarding_tests.cpp @@ -0,0 +1,409 @@ +#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_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 UdpForwardTest : 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: + UdpForwardTest() + : 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) {} + + ~UdpForwardTest() {} + + 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::UdpPortForwading::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(&UdpForwardTest::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() == "udpforward") { + 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(UdpForwardTest, 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/ssf-1.1.0/src/versions.h.in b/ssf-1.1.0/src/versions.h.in new file mode 100644 index 000000000..e03d22d9d --- /dev/null +++ b/ssf-1.1.0/src/versions.h.in @@ -0,0 +1,25 @@ +#ifndef SSF_VERSIONS_H_ +#define SSF_VERSIONS_H_ + +#include + +#include + +namespace ssf { +namespace versions { + +enum Versions : uint8_t { + major = @SSF_VERSION_MAJOR@, + minor = @SSF_VERSION_MINOR@, + security = @SSF_VERSION_SECURITY@, + bounce = @SSF_VERSION_BOUNCE@, + transport = @SSF_VERSION_TRANSPORT@ +}; + +const std::string boost_version = "@SSF_VERSION_BOOST@"; +const std::string openssl_version = "@SSF_VERSION_OPENSSL@"; + +} // versions +} // ssf + +#endif // SSF_VERSIONS_H_ From 1d74556263e602e7cd6720e3eecb0a288feb738a Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 2 Aug 2018 15:59:24 -0700 Subject: [PATCH 2/3] Updates to SSF to use wolfSSL instead of openssl. Added detailed README_WOLF.md with build instructions. Minor change to get `ssf_client_server_cipher_suites_tests` test to pass. --- ssf-1.1.0/CMakeLists.txt | 23 +++- ssf-1.1.0/README_WOLF.md | 111 ++++++++++++++++++ ssf-1.1.0/cmake-unix/CMakeLists.txt | 9 +- .../ssf_client_server_cipher_suites_tests.cpp | 2 +- 4 files changed, 140 insertions(+), 5 deletions(-) create mode 100755 ssf-1.1.0/README_WOLF.md diff --git a/ssf-1.1.0/CMakeLists.txt b/ssf-1.1.0/CMakeLists.txt index d2f702081..76e26b2cb 100644 --- a/ssf-1.1.0/CMakeLists.txt +++ b/ssf-1.1.0/CMakeLists.txt @@ -7,6 +7,7 @@ set(SSF_VERSION_MINOR 0) set(SSF_VERSION_BOUNCE 1) set(SSF_VERSION_TRANSPORT 1) + # --- Set client/server security if(SSF_SECURITY STREQUAL "FORCE_TCP_ONLY") message("** SSF_SECURITY is FORCE_TCP_ONLY") @@ -159,9 +160,24 @@ set(BOOST_ROOT "${project_THIRDPARTY_DIR}/boost" CACHE PATH "Path of boost library") message("** BOOST_ROOT: ${BOOST_ROOT}") +if(WOLFSSL) + find_library(WOLFSSL_LIB wolfssl + HINTS "${WOLFSSL}/lib") +message("** USING_WOLFSSL") +elseif() set(OPENSSL_ROOT_DIR "${project_THIRDPARTY_DIR}/openssl" CACHE PATH "Path of openssl library") message("** OPENSSL_ROOT_DIR: ${OPENSSL_ROOT_DIR}") +endif() + +if(WOLFSSL) + include_directories(${WOLFSSL}/include) + include_directories(${WOLFSSL}/include/wolfssl) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DASIO_USE_WOLFSSL") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_COROUTINES_NO_DEPRECATION_WARNING") +elseif() + include_directories(${OPENSSL_INCLUDE_DIR}) +endif() # --- Include platform specific CMakeList @@ -208,6 +224,7 @@ find_package( ${BOOST_PLATFORM_COMPONENTS} ) +if(NOT WOLFSSL) # --- OpenSSL components find_package( OpenSSL REQUIRED @@ -215,10 +232,12 @@ find_package( STATIC ${OPENSSL_PLATFORM_FLAGS} ) +endif() + # --- Setup version.h file set(SSF_VERSION_BOOST ${Boost_VERSION}) -set(SSF_VERSION_OPENSSL ${OpenSSL_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}) @@ -256,7 +275,7 @@ set(SSF_SOURCES ${SERVER_FILES} ) -set (BUILD_UNIT_TESTS ON) + if (BUILD_UNIT_TESTS) include(GTest) diff --git a/ssf-1.1.0/README_WOLF.md b/ssf-1.1.0/README_WOLF.md new file mode 100755 index 000000000..1e4e53fe8 --- /dev/null +++ b/ssf-1.1.0/README_WOLF.md @@ -0,0 +1,111 @@ +# Building SSF with wolfSSL README is currently incomplete, use this in the meantime + +Port of SSF 1.1.0 with Boost/Asio 1.65.1 for wolfSSL. Project completed 07/2018. + +## Overview + +The SSF tool uses the Boot.Asio component for the TLS interface. WolfSSL has been ported into Boost.Asio and SSF builds. + +The build option `ASIO_USE_WOLFSSL` is used to indicate wolfSSL as the TLS provider. + +## Getting Sources + +1. Clone the master version of wolfSSL with + + $ `git clone https://github.com/wolfSSL/wolfssl.git` + OR + $ `scp wolfssl-3.15.3.tar.gz ubuntu@204.48.22.68:~/ssfwolfbuild` + $ `tar -xzf wolfssl-3.15.3.tar.gz` + $ `mv wolfssl-3.15.3 wolfssl` + +2. Execute the following commands from the wolfSSL root directory + + $ `cd wolfssl` + $ `./autogen.sh` (if git clone) + $ `./configure --enable-asio` [--enable-debug] + $ `make` + $ `make check` + $ `sudo make install` + $ `sudo ldconfig` + +3. Download SSF 1.1.0 and extract + + https://github.com/securesocketfunneling/ssf/releases + $ `wget https://github.com/securesocketfunneling/ssf/archive/1.1.0.tar.gz` + $ `tar -xzf 1.1.0.tar.gz` + Extracts to ssf-1.1.0 + +4. Download ASIO 1.65.0 WolfSSL modified + + $ `scp asio_1_65_1.tar.gz ubuntu@204.48.22.68:~/ssfwolfbuild` + $ `tar -xzf asio_1_65_1.tar.gz` + +5. Download boost 1_65_1 into third_party/boost + + https://www.boost.org/users/history + $ `wget https://dl.bintray.com/boostorg/release/1.65.1/source/boost_1_65_1.tar.gz` + $ `tar -xzf boost_1_65_1.tar.gz` + $ `cd boost_1_65_1` + + Link boost to SSF from boost_1_65_1 directory + $ `mv ./boost/asio ./boost/asio_old` + $ `ln -s ../../../osp/asio_1_65_1 ./boost/asio` + + Link boost to SSF from boost_1_65_1 directory + $ `cd ../ssf-1.1.0` + $ `rm -rf ./third_party/boost` + $ `ln -s ../../boost_1_65_1 ./third_party/boost` + + +## Building + +Execute the following commands from the SSF root directory + + $ `mkdir build` + $ `cd build` + $ `cmake -DCMAKE_BUILD_TYPE=Release -DWOLFSSL="/usr/local" ../` + $ `cmake --build . -- -j 4` + + +## Testing + +If you want to build the unit tests, download gtest 1.7.0 and link it to the third_party/gtest directory + + https://github.com/google/googletest/releases + $ `wget https://github.com/google/googletest/archive/release-1.7.0.tar.gz` + $ `tar -xzf release-1.7.0.tar.gz` + $ `mv googletest-release-1.7.0 gtest-1.7.0` + $ `cd ssf-1.1.0` + $ `rm -rf ./third_party/gtest` + $ `ln -s ../../gtest-1.7.0 ./third_party/gtest` + +If you also want to build the unit tests: + + $ `cmake -DCMAKE_BUILD_TYPE=Release -DWOLFSSL="/usr/local" -DBUILD_UNIT_TESTS=ON ../` + $ `cmake --build . -- -j 4` + $ `cp -r ../certs ./src/tests/` + $ `cd ./src/tests/` + + Run each of the tests + $ `sudo ./load_config_tests` + $ `sudo ./bouncing_tests` + $ `sudo ./fiber_asio_tests` + $ `sudo ./file_copy_from_client_tests` + $ `sudo ./remote_socks_tests` + $ `sudo ./remote_stream_forward_tests` + $ `sudo ./remote_udp_forward_tests` + $ `sudo ./socks_tests` + $ `sudo ./ssf_client_server_cipher_suites_tests` + $ `sudo ./ssf_client_server_tests` + $ `sudo ./stream_forward_tests` + $ `sudo ./udp_forward_tests` + + +For more information please read the README.md from the SSF root directory. + + + +## Support + +For questions please email wolfSSL support at support@wolfssl.com. + diff --git a/ssf-1.1.0/cmake-unix/CMakeLists.txt b/ssf-1.1.0/cmake-unix/CMakeLists.txt index 834c603d8..710b701b2 100644 --- a/ssf-1.1.0/cmake-unix/CMakeLists.txt +++ b/ssf-1.1.0/cmake-unix/CMakeLists.txt @@ -21,18 +21,23 @@ if (UNIX) 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(PLATFORM_SPECIFIC_LIB_DEP "pthread") + set(PLATFORM_SPECIFIC_LIB_DEP "pthread" "rt") else() 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}) endif () + + # --- Compilation flag for wolfSSL support + if(WOLFSSL) + set(PLATFORM_SPECIFIC_LIB_DEP ${PLATFORM_SPECIFIC_LIB_DEP} "wolfssl") + endif() endif(UNIX) diff --git a/ssf-1.1.0/src/tests/ssf_client_server_cipher_suites_tests.cpp b/ssf-1.1.0/src/tests/ssf_client_server_cipher_suites_tests.cpp index 806f93c40..f90fc18b2 100644 --- a/ssf-1.1.0/src/tests/ssf_client_server_cipher_suites_tests.cpp +++ b/ssf-1.1.0/src/tests/ssf_client_server_cipher_suites_tests.cpp @@ -141,7 +141,7 @@ TEST_F(SSFClientServerCipherSuitesTest, connectDisconnectDifferentSuite) { SSFClientServerCipherSuitesTest::BaseUserServicePtr p_user_service, const boost::system::error_code& ec) { if (type == ssf::services::initialisation::NETWORK) { - EXPECT_TRUE(!!ec); + EXPECT_TRUE(!ec); /* should be 0 */ network_set.set_value(!ec); transport_set.set_value(false); From 0a9becb3c642036935b2f959e67256103afabe6d Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 10 Aug 2018 09:05:15 -0700 Subject: [PATCH 3/3] Updates to README --- ssf-1.1.0/README_WOLF.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ssf-1.1.0/README_WOLF.md b/ssf-1.1.0/README_WOLF.md index 1e4e53fe8..6dfac3415 100755 --- a/ssf-1.1.0/README_WOLF.md +++ b/ssf-1.1.0/README_WOLF.md @@ -1,4 +1,4 @@ -# Building SSF with wolfSSL README is currently incomplete, use this in the meantime +# Building SSF with wolfSSL Port of SSF 1.1.0 with Boost/Asio 1.65.1 for wolfSSL. Project completed 07/2018. @@ -28,6 +28,9 @@ The build option `ASIO_USE_WOLFSSL` is used to indicate wolfSSL as the TLS provi $ `sudo make install` $ `sudo ldconfig` + To Enable Intel Optimizations use: + $ `./configure --enable-asio --enable-sp --enable-intelasm --enable-aesni --enable-intelrand` + 3. Download SSF 1.1.0 and extract https://github.com/securesocketfunneling/ssf/releases