diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..cbbcd1b7 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,62 @@ +# Change Log + +## 2.2.0 + +Features: +* Introducing [microservices](README.md#microservices), base components for client features +* #20 Enable/disable microservices in [configuration file](README.md#configuration-file) +* #24 Gateway ports. Allow client to specify network listening interface on client features: + `-g` command line option or `gateway_ports` microservice options (`stream_listener` and `datagram_listener`) +* Server as relay only: `-R` command line option + +Fixed bugs: +* #23 Delegate server hostname resolution to proxy +* #25 Fix server DOS on bogus connection +* #26 Fix a hang when the client stops during the connection stage + +File configuration: +* `http_proxy.credentials.reuse_ntlm` and `http_proxy.credentials.reuse_kerb`: boolean instead of string + +## 2.1.0 + +Features: +* TLS layer over circuit layer +* HTTP proxy support (CONNECT method), cf. configuration file +* HTTP proxy authentication support (Basic, Digest, NTLM [windows only], Negotiate), cf. configuration file +* Basic shell through socket (-X and -Y options) +* Server network interface option + +Fixed bugs: +* Linux static link to libstdc++ +* Linux dependency to GLIBC2.14 (memcpy) +* Stop behavior (signal instead of user input) +* Port forwarding listening side on localhost only + +## 2.0.0 + +/!\ BC break with version 1.\*.\* + +Features: +* New network layer based on SSF network framework + +## 1.1.0 +Features: +* ssfcp: file copy between client and server +* Rename executables: + * SSF_Client -> ssfc + * SSF_Server -> ssfs + +Fixed bugs: +* Crash issue due to exception when resolving endpoint +* Exception safety +* Windows compilation warnings (64bits) + +## 1.0.0 +Features: +* Local and remote TCP port forwarding (-L and -R options) +* Local and remote UDP port forwarding (-U and -V options) +* Local and remote SOCKS server (-D and -F options) +* Native relay protocol (-b option) +* Multi platform (Windows, Linux and OSX) +* TLS connection with the strongest cipher-suites +* Standalone executables diff --git a/CMakeLists.txt b/CMakeLists.txt index 8be45a1a..9273deb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,14 +3,16 @@ set(project_name "SSF") project(${project_name}) set(SSF_VERSION_MAJOR 2) -set(SSF_VERSION_MINOR 1) +set(SSF_VERSION_MINOR 2) set(SSF_VERSION_FIX 0) set(SSF_VERSION_BOUNCE 2) set(SSF_VERSION_TRANSPORT 1) set(SSF_VERSION "${SSF_VERSION_MAJOR}.${SSF_VERSION_MINOR}.${SSF_VERSION_FIX}") -message("** SSF_VERSION ${SSF_VERSION}") +message("** SSF_VERSION: ${SSF_VERSION}") + +message("** BUILD_TYPE: ${CMAKE_BUILD_TYPE}") # --- Options option(BUILD_UNIT_TESTS "Build SSF unit tests" ON) diff --git a/README.md b/README.md index 104740b3..036099db 100644 --- a/README.md +++ b/README.md @@ -1,154 +1,250 @@ -# Secure Socket Funneling (SSF) +# Secure Socket Funneling -## How to build - -### Requirements +Secure Socket Funneling (SSF) is a network tool and toolkit. - * Winrar >= 5.2.1 (Third party builds on windows) - * Boost >= 1.61.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.) - * libkrb5-dev or equivalent (gssapi on linux) - -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 +It provides simple and efficient ways to forward data from multiple sockets (TCP or UDP) through a single secure TLS link to a remote computer. -* Go in project directory +SSF is cross platform (Windows, Linux, OSX) and shipped as standalone executables. -```bash -cd PROJECT_PATH -``` +Features: +* Local and remote TCP port forwarding +* Local and remote UDP port forwarding +* Local and remote SOCKS server +* Local and remote shell through socket +* Native relay protocol +* TLS connection with strongest cipher-suites -* Copy [Boost archive](http://www.boost.org/users/download/) in ``third_party/boost`` +[Download prebuilt binaries](https://securesocketfunneling.github.io/ssf/#download) -```bash -cp boost_1_XX_Y.tar.bz2 PROJECT_PATH/third_party/boost -``` +[Documentation](https://securesocketfunneling.github.io/ssf/) -* Copy [OpenSSL archive](https://www.openssl.org/source/) in ``third_party/openssl`` +## How to use -```bash -cp openssl-1.0.XY.tar.gz PROJECT_PATH/third_party/openssl -``` +### Standard command line -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. +#### Client command line -* Copy [GTest archive](https://github.com/google/googletest/archive/release-1.7.0.zip) in ``third_party/gtest`` +```plaintext +ssfc[.exe] [options] host + +Basic options: + -h [ --help ] Produce help message + -v [ --verbosity ] level (=info) Verbosity: + critical|error|warning|info|debug|trace + -q [ --quiet ] Do not display log + +Local options: + -c [ --config ] config_file_path Set config file. If option empty, try to load 'config.json' file from working + directory + -b [ --circuit ] circuit_file_path Set circuit file. If option empty, try to load 'circuit.txt' file from working + directory + -p [ --port ] port (=8011) Set remote SSF server port + -g [ --gateway-ports ] Allow gateway ports. At connection, client will be allowed to specify + listening network interface on every services + -S [ --status ] Display microservices status (on/off) + +Supported service commands: + -Y [ --remote-shell ] [[rem_ip]:]rem_port + Open a port server side, each connection to that port launches a + shell client side with I/O forwarded from/to the socket (shell microservice + must be enabled client side prior to use) + -F [ --remote-socks ] [[rem_ip]:]rem_port + Run a SOCKS proxy on localhost accessible from server [[rem_ip]:]rem_port + -X [ --shell ] [[loc_ip]:]loc_port + Open a port on the client side, each connection to that port launches a + shell server side with I/O forwarded to/from the socket (shell microservice + must be enabled server side prior to use) + -D [ --socks ] [[loc_ip]:]loc_port + Run a SOCKS proxy on remote host accessible from client [[loc_ip]:]loc_port + -L [ --tcp-forward ] [[loc_ip]:]loc_port:dest_ip:dest_port + Forward TCP client [[loc_ip]:]port to dest_ip:dest_port from server + -R [ --tcp-remote-forward ] [[rem_ip]:]rem_port:dest_ip:dest_port + Forward TCP server [[rem_ip]:]rem_port to target dest_ip:dest_port from client + -U [ --udp-forward ] [[loc_ip]:]loc_port:dest_ip:dest_port + Forward UDP client [[loc_ip]:]loc_port to target dest_ip:dest_port from server + -V [ --udp-remote-forward ] [[rem_ip]:]rem_port:dest_ip:dest_port + Forward UDP server [[rem_ip]:]rem_port to dest_ip:dest_port from client +``` + +#### Server command line - ```bash - cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest - ``` +```plaintext +ssfs[.exe] [options] [host] -* Generate project +Basic options: + -h [ --help ] Produce help message + -v [ --verbosity ] level (=info) Verbosity: + critical|error|warning|info|debug|trace + -q [ --quiet ] Do not display log -```bash -git submodule update --init --recursive -mkdir PROJECT_PATH/build -cd PROJECT_PATH/build -cmake -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ +Local options: + -c [ --config ] config_file_path Set config file. If option empty, try to load 'config.json' file from working + directory + -p [ --port ] port (=8011) Set local SSF server port + -R [ --relay-only ] Server will only relay connections + -g [ --gateway-ports ] Allow gateway ports. At connection, client will be allowed to specify listening + network interface on every services + -S [ --status ] Display microservices status (on/off) ``` -* Build project +#### Client example -```bash -cd PROJECT_PATH/build -cmake --build . --config Debug|Release +Client will open port 9000 locally and wait SOCKS requests to be transferred to +server **192.168.0.1:8000** + +```plaintext +ssfc[.exe] -D 9000 -b bounce.txt -c config.json -p 8000 192.168.0.1 ``` -### Build SSF on Linux +#### Server example -* Go in project directory +Server will listen on all network interfaces on port **8011** -```bash -cd PROJECT_PATH +```plaintext +ssfs[.exe] ``` -* Copy [Boost archive](http://www.boost.org/users/download/) in ``third_party/boost`` +Server will listen on **192.168.0.1:9000** -```bash -cp boost_1_XX_Y.tar.bz2 PROJECT_PATH/third_party/boost +```plaintext +ssfs[.exe] -p 9000 192.168.0.1 ``` -* Copy [OpenSSL archive](https://www.openssl.org/source/) in ``third_party/openssl`` +### Copy command line -```bash -cp openssl-1.0.XY.tar.gz PROJECT_PATH/third_party/openssl -``` +Copy feature must be enabled both on client and server before usage. -* Copy [GTest archive](https://github.com/google/googletest/archive/release-1.7.0.zip) in ``third_party/gtest`` +Config file example: -```bash -cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest +``` +"ssf": { + "services": { + "file_copy": { "enable": true } + } +} ``` -* Generate project +#### Command line -```bash -git submodule update --init --recursive -mkdir PROJECT_PATH/build -cd PROJECT_PATH/build -cmake -DCMAKE_BUILD_TYPE=Release|Debug -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ -``` +```plaintext +ssfcp[.exe] [options] [host@]/absolute/path/file [[host@]/absolute/path/file] -* Build project +Basic options: + -h [ --help ] Produce help message + -v [ --verbosity ] level (=info) Verbosity: + critical|error|warning|info|debug|trace + -q [ --quiet ] Do not display log -```bash -cd PROJECT_PATH/build -cmake --build . -- -j +Local options: + -c [ --config ] config_file_path Set config file. If option empty, try to load 'config.json' file from working + directory + -b [ --circuit ] circuit_file_path Set circuit file. If option empty, try to load 'circuit.txt' file from working + directory + -p [ --port ] port (=8011) Set remote SSF server port + +Copy options: + -t [ --stdin ] Input will be stdin ``` -### Build SSF on Mac OS X +#### Copy from local to remote destination : -* Go in project directory +```plaintext +ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] path/to/file host@absolute/path/directory_destination +``` -```bash -cd PROJECT_PATH +```plaintext +ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] path/to/file* host@absolute/path/directory_destination ``` -* Copy [Boost archive](http://www.boost.org/users/download/) in ``third_party/boost`` +#### From stdin to remote destination -```bash -cp boost_1_XX_Y.tar.bz2 PROJECT_PATH/third_party/boost +```plaintext +data_in_stdin | ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] -t host@path/to/destination/file_destination ``` -* Copy [OpenSSL archive](https://www.openssl.org/source/) in ``third_party/openssl`` +#### Copy remote files to local destination : -```bash -cp openssl-1.0.XY.tar.gz PROJECT_PATH/third_party/openssl +```plaintext +ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] remote_host@path/to/file absolute/path/directory_destination ``` -* Copy [GTest archive](https://github.com/google/googletest/archive/release-1.7.0.zip) in ``third_party/gtest`` - -```bash -cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest +```plaintext +ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] remote_host@path/to/file* absolute/path/directory_destination ``` -* Generate project +### File example -```bash -git submodule update --init --recursive -mkdir PROJECT_PATH/build -cd PROJECT_PATH/build -cmake -DCMAKE_BUILD_TYPE=Release|Debug -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ +#### Bounce file (relay servers) + +```plaintext +127.0.0.1:8002 +127.0.0.1:8003 ``` -* Build project +#### Configuration file -```bash -cd PROJECT_PATH/build -cmake --build . +```plaintext +{ + "ssf": { + "tls" : { + "ca_cert_path": "./certs/trusted/ca.crt", + "cert_path": "./certs/certificate.crt", + "key_path": "./certs/private.key", + "key_password": "", + "dh_path": "./certs/dh4096.pem", + "cipher_alg": "DHE-RSA-AES256-GCM-SHA384" + }, + "http_proxy" : { + "host": "", + "port": "", + "credentials": { + "username": "", + "password": "", + "domain": "", + "reuse_ntlm": true, + "reuse_nego": true + } + }, + "services": { + "datagram_forwarder": { "enable": true }, + "datagram_listener": { + "enable": true, + "gateway_ports": false + }, + "stream_forwarder": { "enable": true }, + "stream_listener": { + "enable": true, + "gateway_ports": false + }, + "file_copy": { "enable": false }, + "shell": { + "enable": false, + "path": "/bin/bash|C:\\windows\\system32\\cmd.exe", + "args": "" + }, + "socks": { "enable": true } + } + } +} ``` +* _tls.ca_cert_path_ : relative or absolute path to the CA certificate file +* _tls.cert_path_ : relative or absolute path to the instance certificate file +* _tls.key_path_ : relative or absolute path to the private key file +* _tls.dh_path_ : relative or absolute path to the Diffie-Hellman file +* _tls.cipher_alg_ : cipher algorithm +* _http_proxy.host_ : HTTP proxy host +* _http_proxy.port_ : HTTP proxy port +* _http_proxy.credentials.username_ : proxy username credentials (all platform: Basic or Digest, Windows: NTLM and Negotiate if reuse = false) +* _http_proxy.credentials.password_ : proxy password credentials (all platform: Basic or Digest, Windows: NTLM and Negotiate if reuse = false) +* _http_proxy.credentials.domain_ : user domain (NTLM and Negotiate auth on Windows only) +* _http_proxy.credentials.reuse_ntlm_ : reuse current computer user credentials to authenticate with proxy NTLM auth (SSO) +* _http_proxy.credentials.reuse_kerb_ : reuse current computer user credentials (Kerberos ticket) to authenticate with proxy Negotiate auth (SSO) +* _services.*.enable_ : [enable/disable microservice](#microservices) +* _services.*.gateway_ports_ : enable/disable gateway ports +* _services.shell.path_ : binary path used for shell creation (optional) +* _services.shell.args_ : binary arguments used for shell creation (optional) + ## How to configure ### Generating certificates for TLS connections @@ -200,12 +296,14 @@ openssl x509 -extfile extfile.txt -extensions v3_req_p -req -sha1 -days 3650 -CA ### Configuration file +#### TLS + 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 +* `./certs/dh4096.pem` +* `./certs/certificate.crt` +* `./certs/private.key` +* `./certs/trusted/ca.crt` Where: @@ -217,6 +315,57 @@ However, if you want those files at different paths, it is possible to customize An example is given in the file example section. +#### Microservices + +SSF is using microservices to build its features (TCP forwarding, remote SOCKS, ...) + +There are 7 microservices: +* stream_forwarder +* stream_listener +* datagram_forwarder +* datagram_listener +* file_copy +* socks +* shell + +Each feature is the combination of at least one client side microservice and one server side microservice. + +This table sums up how each feature is assembled: + +| ssfc feature | microservice client side | microservice server side | +|:----------------------------|:-------------------------|:-------------------------| +| `-L`: TCP forwarding | stream_listener | stream_forwarder | +| `-R`: remote TCP forwarding | stream_forwarder | stream_listener | +| `-U`: UDP forwarding | datagram_listener | datagram_forwarder | +| `-V`: remote UDP forwarding | datagram_forwarder | datagram_listener | +| `-D`: SOCKS | stream_listener | socks | +| `-F`: remote SOCKS | socks | stream_listener | +| `-X`: shell | stream_listener | shell | +| `-Y`: remote shell | shell | stream_listener | + +This architecture makes it easier to build remote features: they use the same microservices but on the opposite side. + +`ssfc` and `ssfs` come with pre-enabled microservices. +Here is the default microservices configuration: + +``` +"ssf": { + "services": { + "datagram_forwarder": { "enable": true }, + "datagram_listener": { "enable": true }, + "stream_forwarder": { "enable": true }, + "stream_listener": { "enable": true }, + "socks": { "enable": true }, + "file_copy": { "enable": false }, + "shell": { "enable": false } + } +} +``` + +To enable or disable a microservice, set its `enable` option to `true` or `false`. + +Trying to use a feature requiring a disabled microservice will result in an error message. + ### Relay chain file This file will contain the bounce servers and ports which will be used to establish the connection. @@ -230,141 +379,151 @@ SERVER3:PORT3 The chain will be CLIENT -> SERVER1:PORT1 -> SERVER2:PORT2 -> SERVER3:PORT3 -> TARGET -## How to use +## How to build -### Standard command line +### Requirements -```plaintext -ssf[.exe] [-h] [-v verb_level] [-q] [-L loc:ip:dest] [-R rem:ip:dest] [-D port] [-F port] [-U loc:ip:dest] [-V rem:ip:dest] [-X port] [-Y port] [-b bounce_file] [-c config_file] [-p port] [host] -``` - -* -v : Verbosity level (critical, error, warning, info, debug, trace), default is info -* -q : Quiet mode (no log) -* -L : TCP port forwarding with *loc* as the local TCP port, *ip* and *dest* as destination toward which the forward should be done from the server. -* -R : TCP remote port forwarding with *rem* as the TCP port to forward from the remote host, *ip* and *dest* as destination toward which the forward should be done from the client. -* -D : open a port (*port*) on the client to connect to a SOCKS server on the server from the client. -* -F : open a port (*port*) on the server to connect to a SOCKS server on the client from the server. -* -U : UDP port forwarding with *loc* as the UDP port to forward from the client, *ip* and *dest* as destination toward which the forward should be done from the server. -* -V : UDP remote port forwarding with *rem* as the UDP port to forward from the server, *ip* and *dest* as destination toward which the forward should be done from the client. -* -X : open a port (*port*) on the client side, each connection to that port creates a process with I/O forwarded to/from the server side (the binary used can be set with the config file) -* -Y : open a port (*port*) on the server side, each connection to that port creates a process with I/O forwarded to/from the client side (the binary used can be set with the config file) -* -b : *bounce_file* is the file containing the list of relays to use. -* -c : *config_file* is the config file containing configuration for SSF (TLS configuration). -* -p : *port* is the port on which to listen (for the server) or to connect (for the client). The default value is 8011. -* host : the IP address or the name of the remote server to connect to. + * Winrar >= 5.2.1 (Third party builds on windows) + * Boost >= 1.61.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.) + * libkrb5-dev or equivalent (gssapi on linux) -#### Server example +SSF_SECURITY: -Server will listen on all network interfaces on port **8011** +* **STANDARD**: the project will be build with standard security features +* **FORCE_TCP_ONLY**: the project will be built without security features to facilitate debugging -```plaintext -ssfs[.exe] +### Build SSF on Windows + +* Go in project directory + +```bash +cd PROJECT_PATH ``` -Server will listen on **192.168.0.1:9000** +* Copy [Boost archive](http://www.boost.org/users/download/) in ``third_party/boost`` -```plaintext -ssfs[.exe] -p 9000 192.168.0.1 +```bash +cp boost_1_XX_Y.tar.bz2 PROJECT_PATH/third_party/boost ``` -#### Client example +* Copy [OpenSSL archive](https://www.openssl.org/source/) in ``third_party/openssl`` -Client will open port 9000 locally and wait SOCKS requests to be transferred to -server **192.168.0.1:8000** +```bash +cp openssl-1.0.XY.tar.gz PROJECT_PATH/third_party/openssl +``` -```plaintext -ssfc[.exe] -D 9000 -b bounce.txt -c config.json -p 8000 192.168.0.1 +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](https://github.com/google/googletest/archive/release-1.7.0.zip) in ``third_party/gtest`` + + ```bash + cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest + ``` + +* Generate project + +```bash +git submodule update --init --recursive +mkdir PROJECT_PATH/build +cd PROJECT_PATH/build +cmake -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ ``` -### Copy command line +* Build project -```plaintext -ssfcp[.exe] [-h] [-b bounce_file] [-c config_file] [-p port] [-t] [host@]path [[host@]path] +```bash +cd PROJECT_PATH/build +cmake --build . --config Debug|Release ``` -* -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 +### Build SSF on Linux -#### Copy from local to remote destination : +* Go in project directory -```plaintext -ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] path/to/file host@absolute/path/directory_destination +```bash +cd PROJECT_PATH ``` -```plaintext -ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] path/to/file* host@absolute/path/directory_destination +* 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 ``` -#### From stdin to remote destination +* Copy [OpenSSL archive](https://www.openssl.org/source/) in ``third_party/openssl`` -```plaintext -data_in_stdin | ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] -t host@path/to/destination/file_destination +```bash +cp openssl-1.0.XY.tar.gz PROJECT_PATH/third_party/openssl ``` -#### Copy remote files to local destination : +* Copy [GTest archive](https://github.com/google/googletest/archive/release-1.7.0.zip) in ``third_party/gtest`` -```plaintext -ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] remote_host@path/to/file absolute/path/directory_destination +```bash +cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest ``` -```plaintext -ssfcp[.exe] [-b bounce_file] [-c config_file] [-p port] remote_host@path/to/file* absolute/path/directory_destination +* Generate project + +```bash +git submodule update --init --recursive +mkdir PROJECT_PATH/build +cd PROJECT_PATH/build +cmake -DCMAKE_BUILD_TYPE=Release|Debug -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ ``` -### File example +* Build project -#### Bounce file (relay servers) +```bash +cd PROJECT_PATH/build +cmake --build . -- -j +``` -```plaintext -127.0.0.1:8002 -127.0.0.1:8003 +### Build SSF on Mac OS X + +* Go in project directory + +```bash +cd PROJECT_PATH ``` -#### Config file +* Copy [Boost archive](http://www.boost.org/users/download/) in ``third_party/boost`` -```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" - }, - "http_proxy": { - "host": "proxy.example.com", - "port": "3128", - "credentials": { - "username": "user", - "password": "password", - "domain": "EXAMPLE.COM", - "reuse_ntlm": "true", - "reuse_kerb": "true" - } - }, - "services": { - "shell": { - "path": "/bin/bash", - "args": "" - } - } - } -} +```bash +cp boost_1_XX_Y.tar.bz2 PROJECT_PATH/third_party/boost ``` -* *tls.ca_cert_path* : relative or absolute path to the CA certificate file -* *tls.cert_path* : relative or absolute path to the instance certificate file -* *tls.key_path* : relative or absolute path to the private key file -* *tls.dh_path* : relative or absolute path to the Diffie-Hellman file -* *tls.cipher_alg* : cipher algorithm -* *http_proxy.host* : HTTP proxy host -* *http_proxy.port* : HTTP proxy port -* *http_proxy.credentials.username* : proxy username credentials (all platform: Basic or Digest, Windows: NTLM and Negotiate if reuse = false) -* *http_proxy.credentials.password* : proxy password credentials (all platform: Basic or Digest, Windows: NTLM and Negotiate if reuse = false) -* *http_proxy.credentials.domain* : user domain (NTLM and Negotiate auth on Windows only) -* *http_proxy.credentials.reuse_ntlm* : reuse current computer user credentials to authenticate with proxy NTLM auth (SSO) -* *http_proxy.credentials.reuse_kerb* : reuse current computer user credentials (Kerberos ticket) to authenticate with proxy Negotiate auth (SSO) -* *services.shell.path* : binary path used for shell creation (optional) -* *services.shell.args* : binary arguments used for shell creation (optional) +* 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](https://github.com/google/googletest/archive/release-1.7.0.zip) in ``third_party/gtest`` + +```bash +cp gtest-1.X.Y.zip PROJECT_PATH/third_party/gtest +``` + +* Generate project + +```bash +git submodule update --init --recursive +mkdir PROJECT_PATH/build +cd PROJECT_PATH/build +cmake -DCMAKE_BUILD_TYPE=Release|Debug -DSSF_SECURITY:STRING="STANDARD|FORCE_TCP_ONLY" ../ +``` + +* Build project + +```bash +cd PROJECT_PATH/build +cmake --build . +``` diff --git a/src/common/config/config.cpp b/src/common/config/config.cpp index 12b61542..1237efad 100644 --- a/src/common/config/config.cpp +++ b/src/common/config/config.cpp @@ -1,6 +1,8 @@ +#include +#include #include -#include +#include #include #include @@ -9,96 +11,25 @@ #include "common/config/config.h" #include "common/error/error.h" -namespace ssf { -namespace config { - -Tls::Tls() - : ca_cert_path_("./certs/trusted/ca.crt"), - cert_path_("./certs/certificate.crt"), - key_path_("./certs/private.key"), - key_password_(""), - dh_path_("./certs/dh4096.pem"), - cipher_alg_("DHE-RSA-AES256-GCM-SHA384") {} - -void Tls::Log() const { -#ifdef TLS_OVER_TCP_LINK - SSF_LOG(kLogInfo) << "config[tls]: CA cert path: <" << ca_cert_path_ << ">"; - SSF_LOG(kLogInfo) << "config[tls]: cert path: <" << cert_path_ << ">"; - SSF_LOG(kLogInfo) << "config[tls]: key path: <" << key_path_ << ">"; - SSF_LOG(kLogInfo) << "config[tls]: key password: <" << key_password_ << ">"; - SSF_LOG(kLogInfo) << "config[tls]: dh path: <" << dh_path_ << ">"; - SSF_LOG(kLogInfo) << "config[tls]: cipher suite: <" << cipher_alg_ << ">"; +#if defined(BOOST_ASIO_WINDOWS) +#define SSF_PROCESS_SERVICE_BINARY_PATH \ + "\"C:\\\\windows\\\\system32\\\\cmd.exe\"" +#else +#define SSF_PROCESS_SERVICE_BINARY_PATH "\"/bin/bash\"" #endif -} - -Proxy::Proxy() - : host_(""), - port_(""), - username_(""), - domain_(""), - password_(""), - reuse_ntlm_(true), - reuse_kerb_(true) {} - -void Proxy::Log() const { - if (IsSet()) { - SSF_LOG(kLogInfo) << "config[HTTP proxy]: <" << host_ << ":" << port_ - << ">"; - if (!username_.empty()) { - SSF_LOG(kLogInfo) << "config[HTTP proxy]: username: <" << username_ - << ">"; - } - SSF_LOG(kLogInfo) << "config[HTTP proxy]: reuse NTLM credentials <" - << (reuse_ntlm_ ? "true" : "false") << ">"; - SSF_LOG(kLogInfo) << "config[HTTP proxy]: reuse Kerberos credentials <" - << (reuse_kerb_ ? "true" : "false") << ">"; - } else { - SSF_LOG(kLogInfo) << "config[HTTP proxy]: "; - } -} - -ProcessService::ProcessService() - : path_(SSF_PROCESS_SERVICE_BINARY_PATH), args_("") {} - -ProcessService::ProcessService(const ProcessService& process_service) - : path_(process_service.path_), args_(process_service.args_) {} - -Services::Services() : process_() {} -Services::Services(const Services& services) : process_(services.process_) {} - -void Services::UpdateProcessService(const boost::property_tree::ptree& pt) { - auto shell_optional = pt.get_child_optional("shell"); - if (!shell_optional) { - SSF_LOG(kLogDebug) - << "config[update]: shell service configuration not found"; - return; - } +namespace ssf { +namespace config { - auto& shell_prop = shell_optional.get(); - auto path = shell_prop.get_child_optional("path"); - if (path) { - process_.set_path(path.get().data()); - } - auto args = shell_prop.get_child_optional("args"); - if (args) { - process_.set_args(args.get().data()); - } -} +Config::Config() : tls_(), http_proxy_(), services_() {} -void Services::Log() const { - SSF_LOG(kLogInfo) << "config[services][shell]: path: <" - << process_service().path() << ">"; - std::string args(process_service().args()); - if (!args.empty()) { - SSF_LOG(kLogInfo) << "config[services][shell]: args: <" << args << ">"; - } +void Config::Init() { + boost::system::error_code ec; + UpdateFromString(default_config_, ec); } -Config::Config() : tls_(), http_proxy_(), services_() {} - -void Config::Update(const std::string& filepath, - boost::system::error_code& ec) { +void Config::UpdateFromFile(const std::string& filepath, + boost::system::error_code& ec) { std::string conf_file("config.json"); ec.assign(::error::success, ::error::get_ssf_category()); if (filepath.empty()) { @@ -116,9 +47,7 @@ void Config::Update(const std::string& filepath, boost::property_tree::ptree pt; boost::property_tree::read_json(conf_file, pt); - UpdateTls(pt); - UpdateHttpProxy(pt); - UpdateServices(pt); + UpdateFromPTree(pt); } catch (const std::exception& e) { SSF_LOG(kLogError) << "config[ssf]: error reading SSF config file: " << e.what(); @@ -126,111 +55,113 @@ void Config::Update(const std::string& filepath, } } +void Config::UpdateFromString(const std::string& config_string, + boost::system::error_code& ec) { + std::stringstream ss_config; + ss_config << config_string; + + SSF_LOG(kLogDebug) << "config[ssf]: update config from string:" + << default_config_; + + try { + boost::property_tree::ptree pt; + boost::property_tree::read_json(ss_config, pt); + + UpdateFromPTree(pt); + } catch (const std::exception& e) { + SSF_LOG(kLogError) << "config[ssf]: error reading config string: " + << e.what(); + } +} + void Config::Log() const { tls_.Log(); http_proxy_.Log(); services_.Log(); } -void Config::UpdateTls(const boost::property_tree::ptree& pt) { +void Config::LogStatus() const { services_.LogServiceStatus(); } + +void Config::UpdateFromPTree(const PTree& pt) { + UpdateTls(pt); + UpdateHttpProxy(pt); + UpdateServices(pt); +} + +void Config::UpdateTls(const PTree& pt) { auto tls_optional = pt.get_child_optional("ssf.tls"); if (!tls_optional) { SSF_LOG(kLogDebug) << "config[update]: TLS configuration not found"; return; } - auto& tls_prop = tls_optional.get(); - - auto ca_cert_path_optional = tls_prop.get_child_optional("ca_cert_path"); - if (ca_cert_path_optional) { - tls_.set_ca_cert_path(ca_cert_path_optional.get().data()); - } - - auto cert_path_optional = tls_prop.get_child_optional("cert_path"); - if (cert_path_optional) { - tls_.set_cert_path(cert_path_optional.get().data()); - } - - auto key_path_optional = tls_prop.get_child_optional("key_path"); - if (key_path_optional) { - tls_.set_key_path(key_path_optional.get().data()); - } - - auto key_password_optional = tls_prop.get_child_optional("key_password"); - if (key_password_optional) { - tls_.set_key_password(key_password_optional.get().data()); - } - - auto dh_path_optional = tls_prop.get_child_optional("dh_path"); - if (dh_path_optional) { - tls_.set_dh_path(dh_path_optional.get().data()); - } - - auto cipher_alg_optional = tls_prop.get_child_optional("cipher_alg"); - if (cipher_alg_optional) { - tls_.set_cipher_alg(cipher_alg_optional.get().data()); - } + tls_.Update(tls_optional.get()); } -void Config::UpdateHttpProxy(const boost::property_tree::ptree& pt) { +void Config::UpdateHttpProxy(const PTree& pt) { auto proxy_optional = pt.get_child_optional("ssf.http_proxy"); if (!proxy_optional) { SSF_LOG(kLogDebug) << "config[update]: proxy configuration not found"; return; } - auto& proxy_prop = proxy_optional.get(); - - auto host_optional = proxy_prop.get_child_optional("host"); - if (host_optional) { - http_proxy_.set_host(host_optional.get().data()); - } - - auto port_optional = proxy_prop.get_child_optional("port"); - if (port_optional) { - http_proxy_.set_port(port_optional.get().data()); - } - - auto cred_username_optional = - proxy_prop.get_child_optional("credentials.username"); - if (cred_username_optional) { - http_proxy_.set_username(cred_username_optional.get().data()); - } - - auto cred_domain_optional = - proxy_prop.get_child_optional("credentials.domain"); - if (cred_domain_optional) { - http_proxy_.set_domain(cred_domain_optional.get().data()); - } - - auto cred_password_optional = - proxy_prop.get_child_optional("credentials.password"); - if (cred_password_optional) { - http_proxy_.set_password(cred_password_optional.get().data()); - } - - auto cred_reuse_ntlm_optional = - proxy_prop.get_child_optional("credentials.reuse_ntlm"); - if (cred_reuse_ntlm_optional) { - http_proxy_.set_reuse_ntlm(cred_reuse_ntlm_optional.get().data() == "true"); - } - - auto cred_reuse_kerb_optional = - proxy_prop.get_child_optional("credentials.reuse_kerb"); - if (cred_reuse_kerb_optional) { - http_proxy_.set_reuse_kerb(cred_reuse_kerb_optional.get().data() == "true"); - } + http_proxy_.Update(proxy_optional.get()); } -void Config::UpdateServices(const boost::property_tree::ptree& pt) { +void Config::UpdateServices(const PTree& pt) { auto services_optional = pt.get_child_optional("ssf.services"); if (!services_optional) { SSF_LOG(kLogDebug) << "config[update]: services configuration not found"; return; } - services_.UpdateProcessService(services_optional.get()); + services_.Update(services_optional.get()); +} + +const char* Config::default_config_ = R"RAWSTRING( +{ + "ssf": { + "tls" : { + "ca_cert_path": "./certs/trusted/ca.crt", + "cert_path": "./certs/certificate.crt", + "key_path": "./certs/private.key", + "key_password": "", + "dh_path": "./certs/dh4096.pem", + "cipher_alg": "DHE-RSA-AES256-GCM-SHA384" + }, + "http_proxy" : { + "host": "", + "port": "", + "credentials": { + "username": "", + "password": "", + "domain": "", + "reuse_ntlm": true, + "reuse_nego": true + } + }, + "services": { + "datagram_forwarder": { "enable": true }, + "datagram_listener": { + "enable": true, + "gateway_ports": false + }, + "stream_forwarder": { "enable": true }, + "stream_listener": { + "enable": true, + "gateway_ports": false + }, + "file_copy": { "enable": false }, + "shell": { + "enable": false, + "path": )RAWSTRING" SSF_PROCESS_SERVICE_BINARY_PATH R"RAWSTRING(, + "args": "" + }, + "socks": { "enable": true } + } + } } +)RAWSTRING"; } // config } // ssf diff --git a/src/common/config/config.h b/src/common/config/config.h index 956da9d3..1a11f99b 100644 --- a/src/common/config/config.h +++ b/src/common/config/config.h @@ -3,188 +3,26 @@ #include -#include - #include #include -#if defined(BOOST_ASIO_WINDOWS) -#define SSF_PROCESS_SERVICE_BINARY_PATH "C:\\windows\\system32\\cmd.exe" -#else -#define SSF_PROCESS_SERVICE_BINARY_PATH "/bin/bash" -#endif +#include "common/config/tls.h" +#include "common/config/proxy.h" +#include "common/config/services.h" namespace ssf { namespace config { -class Tls { - public: - Tls(); - - public: - void Log() const; - - inline std::string ca_cert_path() const { return ca_cert_path_; } - - inline void set_ca_cert_path(const std::string& ca_cert_path) { - ca_cert_path_ = ca_cert_path; - } - - inline std::string cert_path() const { return cert_path_; } - - inline void set_cert_path(const std::string& cert_path) { - cert_path_ = cert_path; - } - - inline std::string key_path() const { return key_path_; } - - inline void set_key_path(const std::string& key_path) { - key_path_ = key_path; - } - - inline std::string key_password() const { return key_password_; } - - inline void set_key_password(const std::string& key_password) { - key_password_ = key_password; - } - - inline std::string dh_path() const { return dh_path_; } - - inline void set_dh_path(const std::string& dh_path) { dh_path_ = dh_path; } - - inline std::string cipher_alg() const { return cipher_alg_; } - - inline void set_cipher_alg(const std::string& cipher_alg) { - cipher_alg_ = cipher_alg; - } - - private: - // CA certificate filepath - std::string ca_cert_path_; - // Client certificate filepath - std::string cert_path_; - // Client key filepath - std::string key_path_; - // Client key password - std::string key_password_; - // Diffie-Hellman ephemeral parameters filepath - std::string dh_path_; - // Allowed cipher suite algorithms - std::string cipher_alg_; -}; - -struct Proxy { - public: - Proxy(); - - public: - void Log() const; - - inline bool IsSet() const { - return !host_.empty() && !port_.empty(); - } - - inline std::string host() const { return host_; } - - inline void set_host(const std::string& host) { - host_ = host; - } - - inline std::string port() const { return port_; } - - inline void set_port(const std::string& port) { - port_ = port; - } - - inline std::string username() const { return username_; } - - inline void set_username(const std::string& username) { - username_ = username; - } - - inline std::string domain() const { return domain_; } - - inline void set_domain(const std::string& domain) { - domain_ = domain; - } - - inline std::string password() const { return password_; } - - inline void set_password(const std::string& password) { - password_ = password; - } - - inline bool reuse_ntlm() const { return reuse_ntlm_; } - - inline void set_reuse_ntlm(bool reuse_ntlm) { - reuse_ntlm_ = reuse_ntlm; - } - - inline bool reuse_kerb() const { return reuse_kerb_; } - - inline void set_reuse_kerb(bool reuse_kerb) { - reuse_kerb_ = reuse_kerb; - } - - private: - // Proxy host - std::string host_; - // Proxy port - std::string port_; - // Credentials username - std::string username_; - // Credentials user's domain - std::string domain_; - // Credentials password - std::string password_; - // Reuse default NTLM credentials - bool reuse_ntlm_; - // Reuse default Kerberos/Negotiate credentials - bool reuse_kerb_; -}; - -class ProcessService { - public: - ProcessService(); - ProcessService(const ProcessService& process_service); - - inline std::string path() const { return path_; } - inline void set_path(const std::string& path) { path_ = path; } - - inline std::string args() const { return args_; } - inline void set_args(const std::string& args) { args_ = args; } - - private: - std::string path_; - std::string args_; -}; - -class Services { +class Config { public: - Services(); - Services(const Services& services); - - inline const ProcessService& process_service() const { return process_; } - inline ProcessService& process() { return process_; } - - void UpdateProcessService(const boost::property_tree::ptree& pt); - void Log() const; + using PTree = boost::property_tree::ptree; - private: - ProcessService process_; -}; - -class Config { public: Config(); public: /** - * Update configuration with JSON file - * If no file provided, try to load config from "config.json" file - * @param filepath config filepath (relative or absolute) - * @param ec error code set if update failed - * + * Initialize config with default values * Format example (default values): * { * "ssf": { @@ -196,33 +34,68 @@ class Config { * "dh_path": "./certs/dh4096.pem", * "cipher_alg": "DHE-RSA-AES256-GCM-SHA384" * }, - * "proxy" : { + * "http_proxy" : { * "host": "", * "port": "", * "credentials": { * "username": "", * "password": "", * "domain": "", - * "reuse_ntlm": "true", - * "reuse_nego": "true" + * "reuse_ntlm": true, + * "reuse_nego": true * } * }, * "services": { - * "process": { - * "path": "/bin/bash", + * "datagram_forwarder": { "enable": true }, + * "datagram_listener": { + * "enable": true, + * "gateway_ports": false + * }, + * "stream_forwarder": { "enable": true }, + * "stream_listener": { + * "enable": true, + * "gateway_ports": false + * }, + * "file_copy": { "enable": false }, + * "shell": { + * "enable": false, + * "path": "/bin/bash|C:\\windows\\system32\\cmd.exe", * "args": "" - * } + * }, + * "socks": { "enable": true } * } * } * } */ - void Update(const std::string& filepath, boost::system::error_code& ec); + void Init(); + + /** + * Update configuration with JSON file + * If no file provided, try to load config from "config.json" file + * @param filepath config filepath (relative or absolute) + * @param ec error code set if update failed + */ + void UpdateFromFile(const std::string& filepath, + boost::system::error_code& ec); + + /** + * Update configuration from ptree + * @param pt + * @param ec error code set if update failed + */ + void UpdateFromString(const std::string& config_string, + boost::system::error_code& ec); /** * Log configuration */ void Log() const; + /** + * Log status + */ + void LogStatus() const; + inline const Tls& tls() const { return tls_; } inline Tls& tls() { return tls_; } @@ -233,11 +106,13 @@ class Config { inline Services& services() { return services_; } private: - void UpdateTls(const boost::property_tree::ptree& pt); - void UpdateHttpProxy(const boost::property_tree::ptree& pt); - void UpdateServices(const boost::property_tree::ptree& pt); + void UpdateFromPTree(const PTree& pt); + void UpdateTls(const PTree& pt); + void UpdateHttpProxy(const PTree& pt); + void UpdateServices(const PTree& pt); private: + static const char* default_config_; Tls tls_; Proxy http_proxy_; Services services_; diff --git a/src/common/config/proxy.cpp b/src/common/config/proxy.cpp new file mode 100644 index 00000000..0e53ecd3 --- /dev/null +++ b/src/common/config/proxy.cpp @@ -0,0 +1,78 @@ +#include + +#include + +#include "common/config/proxy.h" +namespace ssf { +namespace config { + +Proxy::Proxy() + : host_(""), + port_(""), + username_(""), + domain_(""), + password_(""), + reuse_ntlm_(true), + reuse_kerb_(true) {} + +void Proxy::Update(const PTree& proxy_prop) { + auto host_optional = proxy_prop.get_child_optional("host"); + if (host_optional) { + host_ = host_optional.get().data(); + } + + auto port_optional = proxy_prop.get_child_optional("port"); + if (port_optional) { + port_ = port_optional.get().data(); + } + + auto cred_username_optional = + proxy_prop.get_child_optional("credentials.username"); + if (cred_username_optional) { + username_ = cred_username_optional.get().data(); + } + + auto cred_domain_optional = + proxy_prop.get_child_optional("credentials.domain"); + if (cred_domain_optional) { + domain_ = cred_domain_optional.get().data(); + } + + auto cred_password_optional = + proxy_prop.get_child_optional("credentials.password"); + if (cred_password_optional) { + password_ = cred_password_optional.get().data(); + } + + auto cred_reuse_ntlm_optional = + proxy_prop.get_child_optional("credentials.reuse_ntlm"); + if (cred_reuse_ntlm_optional) { + reuse_ntlm_ = cred_reuse_ntlm_optional.get().get_value(); + } + + auto cred_reuse_kerb_optional = + proxy_prop.get_child_optional("credentials.reuse_kerb"); + if (cred_reuse_kerb_optional) { + reuse_kerb_ = cred_reuse_kerb_optional.get().get_value(); + } +} + +void Proxy::Log() const { + if (IsSet()) { + SSF_LOG(kLogInfo) << "config[HTTP proxy]: <" << host_ << ":" << port_ + << ">"; + if (!username_.empty()) { + SSF_LOG(kLogInfo) << "config[HTTP proxy]: username: <" << username_ + << ">"; + } + SSF_LOG(kLogInfo) << "config[HTTP proxy]: reuse NTLM credentials <" + << (reuse_ntlm_ ? "true" : "false") << ">"; + SSF_LOG(kLogInfo) << "config[HTTP proxy]: reuse Kerberos credentials <" + << (reuse_kerb_ ? "true" : "false") << ">"; + } else { + SSF_LOG(kLogInfo) << "config[HTTP proxy]: "; + } +} + +} // config +} // ssf \ No newline at end of file diff --git a/src/common/config/proxy.h b/src/common/config/proxy.h new file mode 100644 index 00000000..96c604d7 --- /dev/null +++ b/src/common/config/proxy.h @@ -0,0 +1,59 @@ +#ifndef SSF_COMMON_CONFIG_PROXY_H_ +#define SSF_COMMON_CONFIG_PROXY_H_ + +#include + +#include + +namespace ssf { +namespace config { + +struct Proxy { + public: + using PTree = boost::property_tree::ptree; + + public: + Proxy(); + + public: + void Update(const PTree& pt); + + void Log() const; + + inline bool IsSet() const { return !host_.empty() && !port_.empty(); } + + inline std::string host() const { return host_; } + + inline std::string port() const { return port_; } + + inline std::string username() const { return username_; } + + inline std::string domain() const { return domain_; } + + inline std::string password() const { return password_; } + + inline bool reuse_ntlm() const { return reuse_ntlm_; } + + inline bool reuse_kerb() const { return reuse_kerb_; } + + private: + // Proxy host + std::string host_; + // Proxy port + std::string port_; + // Credentials username + std::string username_; + // Credentials user's domain + std::string domain_; + // Credentials password + std::string password_; + // Reuse default NTLM credentials + bool reuse_ntlm_; + // Reuse default Kerberos/Negotiate credentials + bool reuse_kerb_; +}; + +} // config +} // ssf + +#endif // SSF_COMMON_CONFIG_PROXY_H_ \ No newline at end of file diff --git a/src/common/config/services.cpp b/src/common/config/services.cpp new file mode 100644 index 00000000..07b2e3a6 --- /dev/null +++ b/src/common/config/services.cpp @@ -0,0 +1,200 @@ +#include + +#include "common/config/services.h" + +namespace ssf { +namespace config { + +Services::Services() + : datagram_forwarder_(), + datagram_listener_(), + file_copy_(), + shell_(), + socks_(), + stream_forwarder_(), + stream_listener_() {} + +Services::Services(const Services& services) + : datagram_forwarder_(services.datagram_forwarder_), + datagram_listener_(services.datagram_listener_), + file_copy_(services.file_copy_), + shell_(services.shell_), + socks_(services.socks_), + stream_forwarder_(services.stream_forwarder_), + stream_listener_(services.stream_listener_) {} + +void Services::Update(const PTree& pt) { + UpdateDatagramForwarder(pt); + UpdateDatagramListener(pt); + UpdateStreamForwarder(pt); + UpdateStreamListener(pt); + + UpdateShell(pt); + UpdateSocks(pt); + UpdateFileCopy(pt); +} + +void Services::SetGatewayPorts(bool gateway_ports) { + datagram_listener_.set_gateway_ports(gateway_ports); + stream_listener_.set_gateway_ports(gateway_ports); +} + +void Services::Log() const { + if (datagram_listener_.enabled()) { + if (datagram_listener_.gateway_ports()) { + SSF_LOG(kLogWarning) << "config[microservices][datagram_listener]: " + "gateway ports allowed"; + } + } + if (stream_listener_.enabled()) { + if (stream_listener_.gateway_ports()) { + SSF_LOG(kLogWarning) + << "config[microservices][stream_listener]: gateway ports allowed"; + } + } + if (shell_.enabled()) { + SSF_LOG(kLogInfo) << "config[microservices][shell]: path: <" + << process().path() << ">"; + std::string args(process().args()); + if (!args.empty()) { + SSF_LOG(kLogInfo) << "config[microservices][shell]: args: <" << args + << ">"; + } + } +} + +void Services::LogServiceStatus() const { + SSF_LOG(kLogInfo) << "status[microservices] datagram_forwarder: " + << (datagram_forwarder_.enabled() ? "On" : "Off"); + SSF_LOG(kLogInfo) << "status[microservices] datagram_listener: " + << (datagram_listener_.enabled() ? "On" : "Off"); + SSF_LOG(kLogInfo) << "status[microservices] stream_forwarder: " + << (stream_forwarder_.enabled() ? "On" : "Off"); + SSF_LOG(kLogInfo) << "status[microservices] stream_listener: " + << (stream_listener_.enabled() ? "On" : "Off"); + SSF_LOG(kLogInfo) << "status[microservices] file_copy: " + << (file_copy_.enabled() ? "On" : "Off"); + SSF_LOG(kLogInfo) << "status[microservices] shell: " + << (shell_.enabled() ? "On" : "Off"); + SSF_LOG(kLogInfo) << "status[microservices] socks: " + << (socks_.enabled() ? "On" : "Off"); +} + +void Services::UpdateDatagramForwarder(const PTree& pt) { + auto optional = pt.get_child_optional("datagram_forwarder"); + if (!optional) { + SSF_LOG(kLogDebug) + << "config[update]: datagram_forwarder service configuration not found"; + return; + } + + datagram_forwarder_.set_enabled( + ServiceEnabled(optional.get(), datagram_forwarder_.enabled())); +} + +void Services::UpdateDatagramListener(const PTree& pt) { + auto optional = pt.get_child_optional("datagram_listener"); + if (!optional) { + SSF_LOG(kLogDebug) + << "config[update]: datagram_listener service configuration not found"; + return; + } + + auto& datagram_listener_prop = optional.get(); + + datagram_listener_.set_enabled( + ServiceEnabled(datagram_listener_prop, datagram_listener_.enabled())); + + auto gateway_ports = + datagram_listener_prop.get_child_optional("gateway_ports"); + if (gateway_ports) { + datagram_listener_.set_gateway_ports(gateway_ports.get().get_value()); + } +} + +void Services::UpdateFileCopy(const PTree& pt) { + auto file_copy_optional = pt.get_child_optional("file_copy"); + if (!file_copy_optional) { + SSF_LOG(kLogDebug) + << "config[update]: file_copy service configuration not found"; + return; + } + + file_copy_.set_enabled( + ServiceEnabled(file_copy_optional.get(), file_copy_.enabled())); +} + +void Services::UpdateShell(const PTree& pt) { + auto shell_optional = pt.get_child_optional("shell"); + if (!shell_optional) { + SSF_LOG(kLogDebug) + << "config[update]: shell service configuration not found"; + return; + } + + auto& shell_prop = shell_optional.get(); + shell_.set_enabled(ServiceEnabled(shell_prop, shell_.enabled())); + + auto path = shell_prop.get_child_optional("path"); + if (path) { + shell_.set_path(path.get().data()); + } + auto args = shell_prop.get_child_optional("args"); + if (args) { + shell_.set_args(args.get().data()); + } +} + +void Services::UpdateSocks(const PTree& pt) { + auto socks_optional = pt.get_child_optional("socks"); + if (!socks_optional) { + SSF_LOG(kLogDebug) + << "config[update]: socks service configuration not found"; + return; + } + + socks_.set_enabled(ServiceEnabled(socks_optional.get(), socks_.enabled())); +} + +void Services::UpdateStreamForwarder(const PTree& pt) { + auto optional = pt.get_child_optional("stream_forwarder"); + if (!optional) { + SSF_LOG(kLogDebug) + << "config[update]: stream_forwarder service configuration not found"; + return; + } + + stream_forwarder_.set_enabled( + ServiceEnabled(optional.get(), stream_forwarder_.enabled())); +} + +void Services::UpdateStreamListener(const PTree& pt) { + auto optional = pt.get_child_optional("stream_listener"); + if (!optional) { + SSF_LOG(kLogDebug) + << "config[update]: stream_listener service configuration not found"; + return; + } + + auto& stream_listener_prop = optional.get(); + + stream_listener_.set_enabled( + ServiceEnabled(stream_listener_prop, stream_listener_.enabled())); + + auto gateway_ports = stream_listener_prop.get_child_optional("gateway_ports"); + if (gateway_ports) { + stream_listener_.set_gateway_ports(gateway_ports.get().get_value()); + } +} + +bool Services::ServiceEnabled(const PTree& service_ptree, bool default_value) { + auto enable_prop = service_ptree.get_child_optional("enable"); + if (enable_prop) { + return enable_prop.get().get_value(); + } else { + return default_value; + } +} + +} // config +} // ssf \ No newline at end of file diff --git a/src/common/config/services.h b/src/common/config/services.h new file mode 100644 index 00000000..22092366 --- /dev/null +++ b/src/common/config/services.h @@ -0,0 +1,89 @@ +#ifndef SSF_COMMON_CONFIG_SERVICES_H_ +#define SSF_COMMON_CONFIG_SERVICES_H_ + +#include +#include + +#include "services/copy_file/config.h" +#include "services/datagrams_to_fibers/config.h" +#include "services/fibers_to_sockets/config.h" +#include "services/fibers_to_datagrams/config.h" +#include "services/process/config.h" +#include "services/sockets_to_fibers/config.h" +#include "services/socks/config.h" + +namespace ssf { +namespace config { + +class Services { + public: + using PTree = boost::property_tree::ptree; + + using DatagramForwarderConfig = ssf::services::fibers_to_datagrams::Config; + using DatagramListenerConfig = ssf::services::datagrams_to_fibers::Config; + using FileCopyConfig = ssf::services::copy_file::Config; + using ShellConfig = ssf::services::process::Config; + using SocksConfig = ssf::services::socks::Config; + using StreamForwarderConfig = ssf::services::fibers_to_sockets::Config; + using StreamListenerConfig = ssf::services::sockets_to_fibers::Config; + + public: + Services(); + Services(const Services& services); + + inline const DatagramForwarderConfig datagram_forwarder() const { + return datagram_forwarder_; + } + + inline const DatagramListenerConfig datagram_listener() const { + return datagram_listener_; + } + + inline const ShellConfig& process() const { return shell_; } + + inline const SocksConfig& socks() const { return socks_; } + + inline const FileCopyConfig& file_copy() const { return file_copy_; } + + inline const StreamForwarderConfig& stream_forwarder() const { + return stream_forwarder_; + } + + inline const StreamListenerConfig& stream_listener() const { + return stream_listener_; + } + + void Update(const PTree& pt); + + // Set gateway ports on listener microservices + void SetGatewayPorts(bool gateway_ports); + + void Log() const; + + void LogServiceStatus() const; + + private: + void UpdateDatagramForwarder(const PTree& pt); + void UpdateDatagramListener(const PTree& pt); + void UpdateFileCopy(const PTree& pt); + void UpdateShell(const PTree& pt); + void UpdateSocks(const PTree& pt); + void UpdateStreamForwarder(const PTree& pt); + void UpdateStreamListener(const PTree& pt); + + static bool ServiceEnabled(const PTree& service, bool default_value); + + private: + DatagramForwarderConfig datagram_forwarder_; + DatagramListenerConfig datagram_listener_; + FileCopyConfig file_copy_; + ShellConfig shell_; + SocksConfig socks_; + StreamForwarderConfig stream_forwarder_; + StreamListenerConfig stream_listener_; +}; + +} // config +} // ssf + +#endif // SSF_COMMON_CONFIG_SERVICES_H_ \ No newline at end of file diff --git a/src/common/config/tls.cpp b/src/common/config/tls.cpp new file mode 100644 index 00000000..02f91f8b --- /dev/null +++ b/src/common/config/tls.cpp @@ -0,0 +1,60 @@ +#include + +#include "common/config/tls.h" + +namespace ssf { +namespace config { + +Tls::Tls() + : ca_cert_path_("./certs/trusted/ca.crt"), + cert_path_("./certs/certificate.crt"), + key_path_("./certs/private.key"), + key_password_(""), + dh_path_("./certs/dh4096.pem"), + cipher_alg_("DHE-RSA-AES256-GCM-SHA384") {} + +void Tls::Update(const PTree& tls_prop) { + auto ca_cert_path_optional = tls_prop.get_child_optional("ca_cert_path"); + if (ca_cert_path_optional) { + ca_cert_path_ = ca_cert_path_optional.get().data(); + } + + auto cert_path_optional = tls_prop.get_child_optional("cert_path"); + if (cert_path_optional) { + cert_path_ = cert_path_optional.get().data(); + } + + auto key_path_optional = tls_prop.get_child_optional("key_path"); + if (key_path_optional) { + key_path_ = key_path_optional.get().data(); + } + + auto key_password_optional = tls_prop.get_child_optional("key_password"); + if (key_password_optional) { + key_password_ = key_password_optional.get().data(); + } + + auto dh_path_optional = tls_prop.get_child_optional("dh_path"); + if (dh_path_optional) { + dh_path_ = dh_path_optional.get().data(); + } + + auto cipher_alg_optional = tls_prop.get_child_optional("cipher_alg"); + if (cipher_alg_optional) { + cipher_alg_ = cipher_alg_optional.get().data(); + } +} + +void Tls::Log() const { +#ifdef TLS_OVER_TCP_LINK + SSF_LOG(kLogInfo) << "config[tls]: CA cert path: <" << ca_cert_path_ << ">"; + SSF_LOG(kLogInfo) << "config[tls]: cert path: <" << cert_path_ << ">"; + SSF_LOG(kLogInfo) << "config[tls]: key path: <" << key_path_ << ">"; + SSF_LOG(kLogInfo) << "config[tls]: key password: <" << key_password_ << ">"; + SSF_LOG(kLogInfo) << "config[tls]: dh path: <" << dh_path_ << ">"; + SSF_LOG(kLogInfo) << "config[tls]: cipher suite: <" << cipher_alg_ << ">"; +#endif +} + +} // config +} // ssf diff --git a/src/common/config/tls.h b/src/common/config/tls.h new file mode 100644 index 00000000..c444038f --- /dev/null +++ b/src/common/config/tls.h @@ -0,0 +1,53 @@ +#ifndef SSF_COMMON_CONFIG_TLS_H_ +#define SSF_COMMON_CONFIG_TLS_H_ + +#include + +#include + +namespace ssf { +namespace config { + +class Tls { + public: + using PTree = boost::property_tree::ptree; + + public: + Tls(); + + public: + void Update(const PTree& pt); + + void Log() const; + + inline std::string ca_cert_path() const { return ca_cert_path_; } + + inline std::string cert_path() const { return cert_path_; } + + inline std::string key_path() const { return key_path_; } + + inline std::string key_password() const { return key_password_; } + + inline std::string dh_path() const { return dh_path_; } + + inline std::string cipher_alg() const { return cipher_alg_; } + + private: + // CA certificate filepath + std::string ca_cert_path_; + // Client certificate filepath + std::string cert_path_; + // Client key filepath + std::string key_path_; + // Client key password + std::string key_password_; + // Diffie-Hellman ephemeral parameters filepath + std::string dh_path_; + // Allowed cipher suite algorithms + std::string cipher_alg_; +}; + +} // config +} // ssf + +#endif // SSF_COMMON_CONFIG_TLS_H_ \ No newline at end of file diff --git a/src/core/client/client.h b/src/core/client/client.h index f0d08330..5e29a0c8 100644 --- a/src/core/client/client.h +++ b/src/core/client/client.h @@ -53,8 +53,7 @@ class SSFClient boost::asio::io_service& get_io_service(); private: - void NetworkToTransport(const boost::system::error_code& ec, - NetworkSocketPtr p_socket); + void NetworkToTransport(const boost::system::error_code& ec); void DoSSFStart(NetworkSocketPtr p_socket, const boost::system::error_code& ec); @@ -69,6 +68,8 @@ class SSFClient private: AsyncEngine async_engine_; + NetworkSocketPtr p_socket_; + Demux fiber_demux_; std::vector user_services_; diff --git a/src/core/client/client.ipp b/src/core/client/client.ipp index e208d320..5c30ddf1 100644 --- a/src/core/client/client.ipp +++ b/src/core/client/client.ipp @@ -15,11 +15,10 @@ #include "services/copy_file/file_to_fiber/file_to_fiber.h" #include "services/datagrams_to_fibers/datagrams_to_fibers.h" #include "services/fibers_to_datagrams/fibers_to_datagrams.h" -#include "services/fibers_to_datagrams/fibers_to_datagrams.h" #include "services/fibers_to_sockets/fibers_to_sockets.h" +#include "services/process/server.h" #include "services/sockets_to_fibers/sockets_to_fibers.h" #include "services/socks/socks_server.h" -#include "services/process/server.h" #include "ssf/log/log.h" @@ -32,6 +31,7 @@ SSFClient::SSFClient(std::vector user_services, : T( boost::bind(&SSFClient::DoSSFStart, this, _1, _2)), async_engine_(), + p_socket_(nullptr), fiber_demux_(async_engine_.get_io_service()), user_services_(user_services), services_config_(services_config), @@ -52,8 +52,7 @@ void SSFClient::Run(const NetworkQuery& query, } // Create network socket - NetworkSocketPtr p_socket = - std::make_shared(async_engine_.get_io_service()); + p_socket_ = std::make_shared(async_engine_.get_io_service()); // resolve remote endpoint with query NetworkResolver resolver(async_engine_.get_io_service()); @@ -67,16 +66,27 @@ void SSFClient::Run(const NetworkQuery& query, async_engine_.Start(); // async connect client to given endpoint - p_socket->async_connect( + p_socket_->async_connect( *endpoint_it, - boost::bind(&SSFClient::NetworkToTransport, this, _1, p_socket)); + boost::bind(&SSFClient::NetworkToTransport, this, _1)); } template class T> void SSFClient::Stop() { + if (!async_engine_.IsStarted()) { + return; + } + fiber_demux_.close(); + if (p_socket_.get() != nullptr) { + boost::system::error_code close_ec; + p_socket_->shutdown(boost::asio::socket_base::shutdown_both, close_ec); + p_socket_->close(close_ec); + } + async_engine_.Stop(); + p_socket_.reset(); } template class T> @@ -85,21 +95,15 @@ boost::asio::io_service& SSFClient::get_io_service() { } template class T> -void SSFClient::NetworkToTransport(const boost::system::error_code& ec, - NetworkSocketPtr p_socket) { +void SSFClient::NetworkToTransport(const boost::system::error_code& ec) { if (!ec) { - this->DoSSFInitiate(p_socket); + this->DoSSFInitiate(p_socket_); return; } - SSF_LOG(kLogError) << "client: error when connecting to server " + SSF_LOG(kLogError) << "client: error when connecting to server: " << ec.message(); - if (p_socket) { - boost::system::error_code close_ec; - p_socket->close(close_ec); - } - Notify(ssf::services::initialisation::NETWORK, nullptr, ec); } @@ -115,7 +119,7 @@ void SSFClient::DoSSFStart(NetworkSocketPtr p_socket, Notify(ssf::services::initialisation::TRANSPORT, nullptr, ec); } else { - SSF_LOG(kLogError) << "client: SSF protocol error " << ec.message(); + SSF_LOG(kLogError) << "client: SSF protocol error (" << ec.message() << ")"; } } @@ -139,21 +143,26 @@ void SSFClient::DoFiberize(NetworkSocketPtr p_socket, // Register supported micro services services::socks::SocksServer::RegisterToServiceFactory( - p_service_factory); + p_service_factory, services_config_.socks()); services::fibers_to_sockets::FibersToSockets::RegisterToServiceFactory( - p_service_factory); + p_service_factory, services_config_.stream_forwarder()); services::sockets_to_fibers::SocketsToFibers::RegisterToServiceFactory( - p_service_factory); + p_service_factory, services_config_.stream_listener()); services::fibers_to_datagrams::FibersToDatagrams< - Demux>::RegisterToServiceFactory(p_service_factory); + Demux>::RegisterToServiceFactory(p_service_factory, + services_config_.datagram_forwarder()); services::datagrams_to_fibers::DatagramsToFibers< - Demux>::RegisterToServiceFactory(p_service_factory); + Demux>::RegisterToServiceFactory(p_service_factory, + services_config_.datagram_listener()); services::copy_file::file_to_fiber::FileToFiber< - Demux>::RegisterToServiceFactory(p_service_factory); + Demux>::RegisterToServiceFactory(p_service_factory, + services_config_.file_copy()); services::copy_file::fiber_to_file::FiberToFile< - Demux>::RegisterToServiceFactory(p_service_factory); + Demux>::RegisterToServiceFactory(p_service_factory, + services_config_.file_copy()); services::copy_file::file_enquirer::FileEnquirer< - Demux>::RegisterToServiceFactory(p_service_factory); + Demux>::RegisterToServiceFactory(p_service_factory, + services_config_.file_copy()); services::process::Server::RegisterToServiceFactory( p_service_factory, services_config_.process()); diff --git a/src/core/client/main.cpp b/src/core/client/main.cpp index 43d570f0..39a11ee0 100644 --- a/src/core/client/main.cpp +++ b/src/core/client/main.cpp @@ -98,8 +98,11 @@ int main(int argc, char** argv) { // Load SSF config if any ssf::config::Config ssf_config; + ssf_config.Init(); - ssf_config.Update(cmd.config_file(), ec); + ssf_config.services().SetGatewayPorts(cmd.gateway_ports()); + + ssf_config.UpdateFromFile(cmd.config_file(), ec); if (ec) { SSF_LOG(kLogError) << "client: invalid config file format -- Exiting"; @@ -108,6 +111,10 @@ int main(int argc, char** argv) { ssf_config.Log(); + if (cmd.show_status()) { + ssf_config.LogStatus(); + } + CircuitConfig circuit_config; circuit_config.Update(cmd.circuit_file(), ec); @@ -146,7 +153,7 @@ int main(int argc, char** argv) { if (p_service.get() != nullptr) { if (ec) { SSF_LOG(kLogError) << "client: service <" << p_service->GetName() - << "> NOK"; + << "> KO"; } else { SSF_LOG(kLogInfo) << "client: service <" << p_service->GetName() << "> OK"; @@ -173,7 +180,7 @@ int main(int argc, char** argv) { client.Run(endpoint_query, ec); if (ec) { - SSF_LOG(kLogError) << "client: error happened when running client : " + SSF_LOG(kLogError) << "client: error happened when running client: " << ec.message(); return 1; } @@ -226,16 +233,17 @@ void InitializeClientServices(ClientServices* p_client_services, // Initialize requested user services (socks, port forwarding) for (const auto& parameter : parameters) { for (const auto& single_parameter : parameter.second) { + boost::system::error_code parse_ec; auto p_service_options = ssf::ServiceOptionFactory::ParseServiceLine( - parameter.first, single_parameter, ec); - - if (!ec) { + parameter.first, single_parameter, parse_ec); + if (!parse_ec) { p_client_services->push_back(p_service_options); } else { - SSF_LOG(kLogError) << "client: wrong parameter " << parameter.first - << ": " << single_parameter << ": " << ec.message(); - return; + SSF_LOG(kLogError) << "client: invalid option value for service<" + << parameter.first << ">: " << single_parameter + << " (" << parse_ec.message() << ")"; + ec.assign(parse_ec.value(), parse_ec.category()); } } } diff --git a/src/core/client/main_cp.cpp b/src/core/client/main_cp.cpp index f1bca8c5..ff1a05a5 100644 --- a/src/core/client/main_cp.cpp +++ b/src/core/client/main_cp.cpp @@ -50,7 +50,7 @@ int main(int argc, char** argv) { SSF_LOG(kLogError) << "client: wrong command line arguments"; return 1; } - + ssf::log::Configure(cmd.log_level()); // Create and initialize copy user service @@ -79,7 +79,9 @@ int main(int argc, char** argv) { boost::system::error_code ec_config; ssf::config::Config ssf_config; - ssf_config.Update(cmd.config_file(), ec_config); + ssf_config.Init(); + + ssf_config.UpdateFromFile(cmd.config_file(), ec_config); if (ec_config) { SSF_LOG(kLogError) << "client: invalid config file format"; diff --git a/src/core/command_line/base.cpp b/src/core/command_line/base.cpp index be989927..54189072 100644 --- a/src/core/command_line/base.cpp +++ b/src/core/command_line/base.cpp @@ -1,21 +1,14 @@ -#include - #include -#include #include -#include -#include -#include -#include -#include +#include + +#include #include "common/error/error.h" #include "core/command_line/base.h" -#include - #include "versions.h" namespace ssf { @@ -23,7 +16,7 @@ namespace command_line { BaseCommandLine::BaseCommandLine() : host_(""), - port_(), + port_(0), config_file_(""), circuit_file_(""), log_level_(ssf::log::kLogInfo), @@ -39,6 +32,12 @@ BaseCommandLine::ParsedParameters BaseCommandLine::Parse( BaseCommandLine::ParsedParameters BaseCommandLine::Parse( int ac, char* av[], const OptionDescription& services, boost::system::error_code& ec) { + // Set executable name + if (ac > 0) { + boost::filesystem::path path(av[0]); + exec_name_ = path.filename().string(); + } + // Basic options OptionDescription basic_opts("Basic options"); InitBasicOptions(basic_opts); @@ -108,7 +107,8 @@ bool BaseCommandLine::DisplayHelp(const VariableMap& vm, } std::cout << "SSF " << ssf::versions::major << "." << ssf::versions::minor - << "." << ssf::versions::fix << std::endl << std::endl; + << "." << ssf::versions::fix << std::endl + << std::endl; std::cout << "Usage: " << GetUsageDesc() << std::endl; @@ -197,14 +197,16 @@ void BaseCommandLine::InitLocalOptions(OptionDescription& local_opts) { ("config,c", boost::program_options::value() ->value_name("config_file_path"), - "Set config file"); + "Set config file. If option empty, try to load 'config.json' file from" + " working directory"); if (!IsServerCli()) { local_opts.add_options() ("circuit,b", boost::program_options::value() ->value_name("circuit_file_path"), - "Set circuit file"); + "Set circuit file. If option empty, try to load 'circuit.txt' file " + "from working directory"); local_opts.add_options() ("port,p", diff --git a/src/core/command_line/base.h b/src/core/command_line/base.h index 67087e8a..50c6a277 100644 --- a/src/core/command_line/base.h +++ b/src/core/command_line/base.h @@ -3,17 +3,13 @@ #include +#include #include -#include #include -#include -#include #include #include -#include - namespace ssf { namespace command_line { @@ -83,6 +79,7 @@ class BaseCommandLine { void set_log_level(const std::string& level); protected: + std::string exec_name_; std::string host_; uint16_t port_; std::string config_file_; diff --git a/src/core/command_line/copy/command_line.cpp b/src/core/command_line/copy/command_line.cpp index 64b2ca04..63fa5427 100644 --- a/src/core/command_line/copy/command_line.cpp +++ b/src/core/command_line/copy/command_line.cpp @@ -1,19 +1,14 @@ -#include "core/command_line/copy/command_line.h" - #include #include -#include -#include -#include -#include #include -#include -#include +#include #include "common/error/error.h" +#include "core/command_line/copy/command_line.h" + #include "versions.h" namespace ssf { @@ -22,6 +17,8 @@ namespace copy { CommandLine::CommandLine() : BaseCommandLine(), from_stdin_(false) {} +CommandLine::~CommandLine() {} + void CommandLine::PopulateBasicOptions(OptionDescription& basic_opts) {} void CommandLine::PopulateLocalOptions(OptionDescription& local_opts) {} @@ -82,8 +79,10 @@ void CommandLine::ParseOptions(const VariableMap& vm, } std::string CommandLine::GetUsageDesc() { - return "ssfcp [options] [host@]/absolute/path/file " - "[[host@]/absolute/path/file]"; + std::stringstream ss_desc; + ss_desc << exec_name_ << " [options] [host@]/absolute/path/file" + " [[host@]/absolute/path/file]"; + return ss_desc.str(); } void CommandLine::ParseFirstArgument(const std::string& first_arg, diff --git a/src/core/command_line/copy/command_line.h b/src/core/command_line/copy/command_line.h index f368fb25..6b0c9794 100644 --- a/src/core/command_line/copy/command_line.h +++ b/src/core/command_line/copy/command_line.h @@ -1,18 +1,7 @@ #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 - -#include #include "core/command_line/base.h" @@ -24,7 +13,7 @@ class CommandLine : public BaseCommandLine { public: CommandLine(); - virtual ~CommandLine() {} + ~CommandLine(); bool from_stdin() const; diff --git a/src/core/command_line/standard/command_line.cpp b/src/core/command_line/standard/command_line.cpp index e07fb75a..315ec1cd 100644 --- a/src/core/command_line/standard/command_line.cpp +++ b/src/core/command_line/standard/command_line.cpp @@ -1,3 +1,5 @@ +#include + #include "core/command_line/standard/command_line.h" #include "common/error/error.h" @@ -7,7 +9,13 @@ namespace command_line { namespace standard { CommandLine::CommandLine(bool is_server) - : BaseCommandLine(), is_server_(is_server) {} + : BaseCommandLine(), + is_server_(is_server), + show_status_(false), + relay_only_(false), + gateway_ports_(false) {} + +CommandLine::~CommandLine() {} void CommandLine::PopulateBasicOptions(OptionDescription& basic_opts) {} @@ -17,11 +25,28 @@ void CommandLine::PopulateLocalOptions(OptionDescription& local_opts) { if (!IsServerCli()) { p_host_option->required(); } + + if (IsServerCli()) { + local_opts.add_options()( + "relay-only,R", + boost::program_options::bool_switch()->default_value(false), + "Server will only relay connections"); + } + // clang-format off local_opts.add_options() ("host,H", p_host_option, "Set host"); + local_opts.add_options() + ("gateway-ports,g", + boost::program_options::bool_switch()->default_value(false), + "Allow gateway ports. At connection, client will be allowed to " + "specify listening network interface on every services"); + local_opts.add_options() + ("status,S", + boost::program_options::bool_switch()->default_value(false), + "Display microservices status (on/off)"); // clang-format on } @@ -40,13 +65,23 @@ void CommandLine::ParseOptions(const VariableMap& vm, host_ = vm["host"].as(); host_set_ = true; } + if (vm.count("status")) { + show_status_ = vm["status"].as(); + } + if (vm.count("relay-only")) { + relay_only_ = vm["relay-only"].as(); + } + if (vm.count("gateway-ports")) { + gateway_ports_ = vm["gateway-ports"].as(); + } } std::string CommandLine::GetUsageDesc() { - return "ssf[c|s] [options] [host]"; + std::stringstream ss_desc; + ss_desc << exec_name_ << " [options] [host]"; + return ss_desc.str(); } - } // standard } // command_line } // ssf diff --git a/src/core/command_line/standard/command_line.h b/src/core/command_line/standard/command_line.h index 5cd49fe2..07f6f35b 100644 --- a/src/core/command_line/standard/command_line.h +++ b/src/core/command_line/standard/command_line.h @@ -1,17 +1,6 @@ #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 - #include #include "core/command_line/base.h" @@ -21,13 +10,16 @@ namespace command_line { namespace standard { class CommandLine : public BaseCommandLine { - public: - using ParsedParameters = std::map>; - public: CommandLine(bool is_server = false); - virtual ~CommandLine() {} + ~CommandLine(); + + inline bool show_status() const { return show_status_; } + + inline bool relay_only() const { return relay_only_; } + + inline bool gateway_ports() const { return gateway_ports_; } protected: void PopulateBasicOptions(OptionDescription& desc) override; @@ -41,6 +33,9 @@ class CommandLine : public BaseCommandLine { private: bool is_server_; + bool show_status_; + bool relay_only_; + bool gateway_ports_; }; } // standard diff --git a/src/core/factories/service_option_factory.h b/src/core/factories/service_option_factory.h index 4dea36b9..052fa116 100644 --- a/src/core/factories/service_option_factory.h +++ b/src/core/factories/service_option_factory.h @@ -4,12 +4,11 @@ #include #include #include - -#include +#include #include - #include +#include #include "common/error/error.h" #include "services/user_services/base_user_service.h" @@ -33,22 +32,21 @@ class ServiceOptionFactory { using ServiceParserMap = std::map; public: - static bool RegisterUserServiceParser(std::string index, - std::string full_name, - std::string value_name, - std::string description, + static bool RegisterUserServiceParser(const std::string& index, + const std::string& full_name, + const std::string& value_name, + const 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(value_name), std::move(description)}; + service_options_[index] = {parser, full_name, value_name, description}; return true; } } - static bool UnregisterUserServiceParser(std::string index) { + static bool UnregisterUserServiceParser(const std::string& index) { boost::recursive_mutex::scoped_lock lock(service_options_mutex_); auto it = service_options_.find(index); @@ -77,7 +75,7 @@ class ServiceOptionFactory { } static std::shared_ptr> - ParseServiceLine(std::string option, std::string parameters, + ParseServiceLine(const std::string& option, const std::string& parameters, boost::system::error_code& ec) { boost::recursive_mutex::scoped_lock lock(service_options_mutex_); auto it = service_options_.find(option); diff --git a/src/core/network_protocol.cpp b/src/core/network_protocol.cpp index 7c273197..f4944076 100644 --- a/src/core/network_protocol.cpp +++ b/src/core/network_protocol.cpp @@ -1,3 +1,5 @@ +#include "common/config/config.h" + #include "core/network_protocol.h" namespace ssf { @@ -30,7 +32,7 @@ NetworkProtocol::Query NetworkProtocol::GenerateClientTCPQuery( const ssf::config::Config& ssf_config, const ssf::circuit::NodeList& circuit_nodes) { ssf::layer::LayerParameters proxy_param_layer = - ProxyConfigToLayerParameters(ssf_config); + ProxyConfigToLayerParameters(ssf_config, false); ssf::layer::LayerParameters default_param_layer = {{"default", "true"}}; @@ -59,7 +61,7 @@ NetworkProtocol::Query NetworkProtocol::GenerateClientTLSQuery( TlsConfigToLayerParameters(ssf_config); ssf::layer::LayerParameters proxy_param_layer = - ProxyConfigToLayerParameters(ssf_config); + ProxyConfigToLayerParameters(ssf_config, false); ssf::layer::LayerParameters default_param_layer = {{"default", "true"}}; @@ -97,12 +99,16 @@ NetworkProtocol::Query NetworkProtocol::GenerateServerTCPQuery( } ssf::layer::LayerParameters proxy_param_layer = - ProxyConfigToLayerParameters(ssf_config); + ProxyConfigToLayerParameters(ssf_config, true); + + ssf::layer::LayerParameters default_proxy_param_layer = + ProxyConfigToLayerParameters(ssf_config, false); ssf::layer::ParameterStack layer_parameters; layer_parameters.push_front(physical_parameters); - ssf::layer::ParameterStack default_parameters = {proxy_param_layer, {}}; + ssf::layer::ParameterStack default_parameters = {default_proxy_param_layer, + {}}; return ssf::layer::data_link::make_forwarding_acceptor_parameter_stack( "server", default_parameters, layer_parameters); @@ -121,7 +127,10 @@ NetworkProtocol::Query NetworkProtocol::GenerateServerTLSQuery( } ssf::layer::LayerParameters proxy_param_layer = - ProxyConfigToLayerParameters(ssf_config); + ProxyConfigToLayerParameters(ssf_config, true); + + ssf::layer::LayerParameters default_proxy_param_layer = + ProxyConfigToLayerParameters(ssf_config, false); ssf::layer::ParameterStack layer_parameters; layer_parameters.push_front(physical_parameters); @@ -129,7 +138,7 @@ NetworkProtocol::Query NetworkProtocol::GenerateServerTLSQuery( layer_parameters.push_front(tls_param_layer); ssf::layer::ParameterStack default_parameters = { - {}, tls_param_layer, proxy_param_layer, {}}; + {}, tls_param_layer, default_proxy_param_layer, {}}; Query query = ssf::layer::data_link::make_forwarding_acceptor_parameter_stack( "server", default_parameters, layer_parameters); @@ -154,16 +163,19 @@ ssf::layer::LayerParameters NetworkProtocol::TlsConfigToLayerParameters( } ssf::layer::LayerParameters NetworkProtocol::ProxyConfigToLayerParameters( - const ssf::config::Config& ssf_config) { - return {{"http_host", ssf_config.http_proxy().host()}, - {"http_port", ssf_config.http_proxy().port()}, - {"http_username", ssf_config.http_proxy().username()}, - {"http_domain", ssf_config.http_proxy().domain()}, - {"http_password", ssf_config.http_proxy().password()}, - {"http_reuse_ntlm", - ssf_config.http_proxy().reuse_ntlm() ? "true" : "false"}, - {"http_reuse_kerb", - ssf_config.http_proxy().reuse_kerb() ? "true" : "false"}}; + const ssf::config::Config& ssf_config, bool acceptor_endpoint) { + return { + {"acceptor_endpoint", acceptor_endpoint ? "true" : "false"}, + {"http_host", ssf_config.http_proxy().host()}, + {"http_port", ssf_config.http_proxy().port()}, + {"http_username", ssf_config.http_proxy().username()}, + {"http_domain", ssf_config.http_proxy().domain()}, + {"http_password", ssf_config.http_proxy().password()}, + {"http_reuse_ntlm", + ssf_config.http_proxy().reuse_ntlm() ? "true" : "false"}, + {"http_reuse_kerb", + ssf_config.http_proxy().reuse_kerb() ? "true" : "false"}, + }; } } // network diff --git a/src/core/network_protocol.h b/src/core/network_protocol.h index 2231aa12..f113f3b7 100644 --- a/src/core/network_protocol.h +++ b/src/core/network_protocol.h @@ -12,10 +12,14 @@ #include #include -#include "common/config/config.h" #include "core/circuit/config.h" namespace ssf { + +namespace config { +class Config; +} // config + namespace network { class NetworkProtocol { @@ -80,7 +84,7 @@ class NetworkProtocol { const ssf::config::Config& ssf_config); static ssf::layer::LayerParameters ProxyConfigToLayerParameters( - const ssf::config::Config& ssf_config); + const ssf::config::Config& ssf_config, bool acceptor_endpoint); }; } // network diff --git a/src/core/server/main.cpp b/src/core/server/main.cpp index 4fd489b8..3e8bd872 100644 --- a/src/core/server/main.cpp +++ b/src/core/server/main.cpp @@ -27,7 +27,7 @@ int main(int argc, char** argv) { // Parse the command line boost::system::error_code ec; cmd.Parse(argc, argv, ec); - + if (ec.value() == ::error::operation_canceled) { return 0; } @@ -36,12 +36,16 @@ int main(int argc, char** argv) { SSF_LOG(kLogError) << "server: wrong arguments -- Exiting"; return 1; } - + ssf::log::Configure(cmd.log_level()); // Load SSF config if any ssf::config::Config ssf_config; - ssf_config.Update(cmd.config_file(), ec); + ssf_config.Init(); + + ssf_config.services().SetGatewayPorts(cmd.gateway_ports()); + + ssf_config.UpdateFromFile(cmd.config_file(), ec); if (ec) { SSF_LOG(kLogError) << "server: invalid config file format -- Exiting"; @@ -50,8 +54,12 @@ int main(int argc, char** argv) { ssf_config.Log(); + if (cmd.show_status()) { + ssf_config.LogStatus(); + } + // Initiate and start the server - Server server(ssf_config.services()); + Server server(ssf_config.services(), cmd.relay_only()); // construct endpoint parameter stack auto endpoint_query = NetworkProtocol::GenerateServerQuery( diff --git a/src/core/server/server.h b/src/core/server/server.h index 2a3ac09d..ae951a6e 100644 --- a/src/core/server/server.h +++ b/src/core/server/server.h @@ -4,13 +4,12 @@ #include #include -#include +#include #include #include "common/boost/fiber/basic_fiber_demux.hpp" #include "core/async_engine.h" -#include "core/factories/service_factory.h" #include "core/service_manager/service_manager.h" namespace ssf { @@ -37,7 +36,8 @@ class SSFServer using ServiceManagerPtrMap = std::map; public: - SSFServer(const ssf::config::Services& services_config); + SSFServer(const ssf::config::Services& services_config, + bool relay_only = false); ~SSFServer(); @@ -62,6 +62,7 @@ class SSFServer AsyncEngine async_engine_; NetworkAcceptor network_acceptor_; ssf::config::Services services_config_; + bool relay_only_; DemuxPtrSet p_fiber_demuxes_; ServiceManagerPtrMap p_service_managers_; diff --git a/src/core/server/server.ipp b/src/core/server/server.ipp index 4a1ed9fd..a74fef1e 100644 --- a/src/core/server/server.ipp +++ b/src/core/server/server.ipp @@ -25,12 +25,14 @@ namespace ssf { template class T> -SSFServer::SSFServer(const ssf::config::Services& services_config) +SSFServer::SSFServer(const ssf::config::Services& services_config, + bool relay_only) : T( boost::bind(&SSFServer::DoSSFStart, this, _1, _2)), async_engine_(), network_acceptor_(async_engine_.get_io_service()), - services_config_(services_config) {} + services_config_(services_config), + relay_only_(relay_only) {} template class T> SSFServer::~SSFServer() { @@ -47,6 +49,10 @@ void SSFServer::Run(const NetworkQuery& query, return; } + if (relay_only_) { + SSF_LOG(kLogWarning) << "server: relay only"; + } + // resolve remote endpoint with query NetworkResolver resolver(async_engine_.get_io_service()); auto endpoint_it = resolver.resolve(query, ec); @@ -58,8 +64,13 @@ void SSFServer::Run(const NetworkQuery& query, // set acceptor network_acceptor_.open(); + + /* + // TODO: reuse address ? network_acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec); + */ + network_acceptor_.bind(*endpoint_it, ec); if (ec) { SSF_LOG(kLogError) << "server: could not bind acceptor to network endpoint"; @@ -110,14 +121,24 @@ void SSFServer::AsyncAcceptConnection() { template class T> void SSFServer::NetworkToTransport(const boost::system::error_code& ec, NetworkSocketPtr p_socket) { + AsyncAcceptConnection(); + if (!ec) { - this->DoSSFInitiateReceive(p_socket); - AsyncAcceptConnection(); - } else { - boost::system::error_code close_ec; - p_socket->shutdown(boost::asio::socket_base::shutdown_both, close_ec); - p_socket->close(close_ec); + if (!relay_only_) { + this->DoSSFInitiateReceive(p_socket); + return; + } + } + + // Close connection + boost::system::error_code close_ec; + p_socket->shutdown(boost::asio::socket_base::shutdown_both, close_ec); + p_socket->close(close_ec); + if (ec) { SSF_LOG(kLogError) << "server: network error: " << ec.message(); + } else if (relay_only_) { + SSF_LOG(kLogWarning) + << "server: direct connection attempt with relay-only option"; } } @@ -162,25 +183,29 @@ void SSFServer::DoFiberize(NetworkSocketPtr p_socket, auto p_service_factory = ServiceFactory::Create( async_engine_.get_io_service(), *p_fiber_demux, p_service_manager); - // Register supported micro services + // Register supported microservices services::socks::SocksServer::RegisterToServiceFactory( - p_service_factory); + p_service_factory, services_config_.socks()); services::fibers_to_sockets::FibersToSockets::RegisterToServiceFactory( - p_service_factory); + p_service_factory, services_config_.stream_forwarder()); services::sockets_to_fibers::SocketsToFibers::RegisterToServiceFactory( - p_service_factory); + p_service_factory, services_config_.stream_listener()); services::fibers_to_datagrams::FibersToDatagrams< - demux>::RegisterToServiceFactory(p_service_factory); + demux>::RegisterToServiceFactory(p_service_factory, + services_config_.datagram_forwarder()); services::datagrams_to_fibers::DatagramsToFibers< - demux>::RegisterToServiceFactory(p_service_factory); + demux>::RegisterToServiceFactory(p_service_factory, + services_config_.datagram_listener()); services::copy_file::file_to_fiber::FileToFiber< - demux>::RegisterToServiceFactory(p_service_factory); + demux>::RegisterToServiceFactory(p_service_factory, + services_config_.file_copy()); services::copy_file::fiber_to_file::FiberToFile< - demux>::RegisterToServiceFactory(p_service_factory); + demux>::RegisterToServiceFactory(p_service_factory, + services_config_.file_copy()); services::process::Server::RegisterToServiceFactory( p_service_factory, services_config_.process()); - // Start the admin micro service + // Start the admin microservice std::map empty_map; auto p_admin_service = services::admin::Admin::Create( diff --git a/src/core/transport_virtual_layer_policies/transport_protocol_policy.h b/src/core/transport_virtual_layer_policies/transport_protocol_policy.h index 51256653..274c8782 100644 --- a/src/core/transport_virtual_layer_policies/transport_protocol_policy.h +++ b/src/core/transport_virtual_layer_policies/transport_protocol_policy.h @@ -32,8 +32,10 @@ class TransportProtocolPolicy { public: TransportProtocolPolicy(Callback callback) : callback_(callback) {} + virtual ~TransportProtocolPolicy() {} + void DoSSFInitiate(SocketPtr p_socket) { - SSF_LOG(kLogInfo) << "transport: starting SSF protocol"; + SSF_LOG(kLogDebug) << "transport: starting SSF protocol"; uint32_t version = GetVersion(); auto p_ssf_request = std::make_shared(version); @@ -81,7 +83,7 @@ class TransportProtocolPolicy { void DoSSFValidReceive(SSFRequestPtr p_ssf_request, SocketPtr p_socket, const boost::system::error_code& ec, size_t length) { if (!ec) { - SSF_LOG(kLogInfo) << "transport: SSF request sent"; + SSF_LOG(kLogDebug) << "transport: SSF request sent"; auto p_ssf_reply = std::make_shared(); @@ -101,7 +103,7 @@ class TransportProtocolPolicy { size_t length) { if (!ec) { if (p_ssf_reply->result()) { - SSF_LOG(kLogInfo) << "transport: SSF reply OK"; + SSF_LOG(kLogDebug) << "transport: SSF reply OK"; callback_(p_socket, ec); } else { boost::system::error_code result_ec(::error::wrong_protocol_type, diff --git a/src/framework/ssf/layer/data_link/basic_circuit_acceptor_service.h b/src/framework/ssf/layer/data_link/basic_circuit_acceptor_service.h index 52621544..a3926035 100644 --- a/src/framework/ssf/layer/data_link/basic_circuit_acceptor_service.h +++ b/src/framework/ssf/layer/data_link/basic_circuit_acceptor_service.h @@ -139,6 +139,10 @@ class basic_CircuitAcceptor_service boost::system::error_code close(implementation_type& impl, boost::system::error_code& ec) { + if (impl.p_next_layer_acceptor) { + impl.p_next_layer_acceptor->close(ec); + } + { boost::recursive_mutex::scoped_lock lock(bind_mutex_); @@ -212,7 +216,7 @@ class basic_CircuitAcceptor_service native_handle_type native_handle(implementation_type& impl) { return impl; } - /// Set a socket option. + // Set a socket option. template boost::system::error_code set_option(implementation_type& impl, const SettableSocketOption& option, @@ -223,10 +227,10 @@ class basic_CircuitAcceptor_service return ec; } - /// Register given endpoint if not already provided - /// Create connection queue if not a forward endpoint - /// Bind the acceptor of the next layer to the next layer endpoint - /// Link the acceptor of the next layer to the current impl + // Register given endpoint if not already provided + // Create connection queue if not a forward endpoint + // Bind the acceptor of the next layer to the next layer endpoint + // Link the acceptor of the next layer to the current impl boost::system::error_code bind(implementation_type& impl, const endpoint_type& endpoint, boost::system::error_code& ec) { @@ -284,7 +288,7 @@ class basic_CircuitAcceptor_service impl.p_local_endpoint->next_layer_endpoint(), ec); } - /// Wait for new connections from next layer if not already listening + // Wait for new connections from next layer if not already listening boost::system::error_code listen(implementation_type& impl, int backlog, boost::system::error_code& ec) { boost::recursive_mutex::scoped_lock lock(bind_mutex_); @@ -325,8 +329,8 @@ class basic_CircuitAcceptor_service p_queue = &queue_it->second; } - /// Waiting for new connections from next layer (p_queue is populated - /// asynchronously) + // Waiting for new connections from next layer (p_queue is populated + // asynchronously) while (true) { boost::this_thread::sleep(boost::posix_time::milliseconds(100)); boost::recursive_mutex::scoped_lock lock(accept_mutex_); @@ -404,7 +408,7 @@ class basic_CircuitAcceptor_service } private: - /// Start async accepting new connection on the next layer + // Start async accepting new connection on the next layer void start_accepting(p_next_acceptor_type p_next_layer_acceptor, p_next_socket_type p_next_layer_socket = nullptr, p_next_endpoint_type p_next_layer_endpoint = nullptr) { @@ -433,8 +437,12 @@ class basic_CircuitAcceptor_service p_next_socket_type p_next_layer_socket, p_next_endpoint_type p_next_layer_endpoint, const boost::system::error_code& ec) { + // Do not break accept loop + start_accepting(p_next_layer_acceptor); + if (ec) { - // TODO : log error + SSF_LOG(kLogDebug) << "network[data_link]: could not accept connection (" + << ec.message() << ")"; return; } @@ -469,8 +477,6 @@ class basic_CircuitAcceptor_service this, p_next_layer_socket, std::move(p_remote_endpoint), p_received_endpoint, next_local_endpoint, _1)); } - - start_accepting(p_next_layer_acceptor); } void connection_initiated_handler(p_next_socket_type p_next_layer_socket, @@ -479,7 +485,8 @@ class basic_CircuitAcceptor_service next_endpoint_type next_local_endpoint, const boost::system::error_code& ec) { if (ec) { - // TODO : log error + SSF_LOG(kLogDebug) << "network[data_link]: connection not initialized (" + << ec.message() << ")"; return; } @@ -522,7 +529,8 @@ class basic_CircuitAcceptor_service p_endpoint_type p_remote_endpoint, const boost::system::error_code& ec) { if (ec) { - // TODO : log error + SSF_LOG(kLogDebug) << "network[data_link]: connection not valid (" + << ec.message() << ")"; return; } diff --git a/src/framework/ssf/layer/data_link/basic_circuit_socket_service.h b/src/framework/ssf/layer/data_link/basic_circuit_socket_service.h index 53af3965..e5e3e021 100644 --- a/src/framework/ssf/layer/data_link/basic_circuit_socket_service.h +++ b/src/framework/ssf/layer/data_link/basic_circuit_socket_service.h @@ -73,6 +73,12 @@ class basic_CircuitSocket_service boost::system::error_code open(implementation_type& impl, const protocol_type& protocol, boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return ec; + } + return impl.p_next_layer_socket->open(typename protocol_type::next_layer_protocol(), ec); } @@ -87,6 +93,10 @@ class basic_CircuitSocket_service } bool is_open(const implementation_type& impl) const { + if (!impl.p_next_layer_socket) { + return false; + } + return impl.p_next_layer_socket->is_open(); } @@ -114,6 +124,12 @@ class basic_CircuitSocket_service boost::system::error_code close(implementation_type& impl, boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return ec; + } + return impl.p_next_layer_socket->close(ec); } @@ -123,6 +139,11 @@ class basic_CircuitSocket_service bool at_mark(const implementation_type& impl, boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return false; + } return impl.p_next_layer_socket->at_mark(ec); } @@ -140,6 +161,8 @@ class basic_CircuitSocket_service boost::system::error_code cancel(implementation_type& impl, boost::system::error_code& ec) { if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); return ec; } diff --git a/src/framework/ssf/layer/physical/host.cpp b/src/framework/ssf/layer/physical/host.cpp new file mode 100644 index 00000000..e3e84bef --- /dev/null +++ b/src/framework/ssf/layer/physical/host.cpp @@ -0,0 +1,17 @@ +#include "ssf/layer/physical/host.h" + +#include "ssf/utils/map_helpers.h" + +namespace ssf { +namespace layer { +namespace physical { + +Host::Host() : addr_(""), port_("") {} + +Host::Host(const LayerParameters& parameters) + : addr_(ssf::helpers::GetField("addr", parameters)), + port_(ssf::helpers::GetField("port", parameters)) {} + +} // physical +} // layer +} // ssf \ No newline at end of file diff --git a/src/framework/ssf/layer/physical/host.h b/src/framework/ssf/layer/physical/host.h new file mode 100644 index 00000000..0c73a3c2 --- /dev/null +++ b/src/framework/ssf/layer/physical/host.h @@ -0,0 +1,30 @@ +#ifndef SSF_LAYER_PHYSICAL_HOST_H_ +#define SSF_LAYER_PHYSICAL_HOST_H_ + +#include + +#include "ssf/layer/parameters.h" + +namespace ssf { +namespace layer { +namespace physical { + +class Host { + public: + Host(); + Host(const LayerParameters& parameters); + + inline const std::string& addr() const { return addr_; } + + inline const std::string& port() const { return port_; } + + private: + std::string addr_; + std::string port_; +}; + +} // physical +} // layer +} // ssf + +#endif // SSF_LAYER_PHYSICAL_HOST_H_ diff --git a/src/framework/ssf/layer/physical/tcp.h b/src/framework/ssf/layer/physical/tcp.h index cf5df41d..0c61444b 100644 --- a/src/framework/ssf/layer/physical/tcp.h +++ b/src/framework/ssf/layer/physical/tcp.h @@ -15,6 +15,7 @@ #include "ssf/layer/parameters.h" #include "ssf/layer/physical/tcp_helpers.h" #include "ssf/layer/protocol_attributes.h" +#include "ssf/layer/physical/host.h" namespace ssf { namespace layer { @@ -32,12 +33,12 @@ class tcp { static const char* NAME; - typedef int socket_context; - typedef int acceptor_context; - typedef boost::asio::ip::tcp::socket socket; - typedef boost::asio::ip::tcp::acceptor acceptor; - typedef boost::asio::ip::tcp::resolver resolver; - typedef boost::asio::ip::tcp::endpoint endpoint; + using socket_context = int; + using acceptor_context = int; + using acceptor = boost::asio::ip::tcp::acceptor; + using endpoint = boost::asio::ip::tcp::endpoint; + using resolver = boost::asio::ip::tcp::resolver; + using socket = boost::asio::ip::tcp::socket; private: using query = ParameterStack; @@ -46,29 +47,27 @@ class tcp { public: operator boost::asio::ip::tcp() { return boost::asio::ip::tcp::v4(); } - static std::string get_name() { - return NAME; - } + static std::string get_name() { return NAME; } static endpoint make_endpoint(boost::asio::io_service& io_service, query::const_iterator parameters_it, uint32_t, boost::system::error_code& ec) { - return ssf::layer::physical::detail::make_tcp_endpoint( - io_service, *parameters_it, ec); + return ssf::layer::physical::detail::make_tcp_endpoint(io_service, + *parameters_it, ec); } - static std::string get_address(const endpoint& endpoint) { - return endpoint.address().to_string(); + return endpoint.address().to_string(); } static unsigned short get_port(const endpoint& endpoint) { return endpoint.port(); } - static void add_params_from_property_tree( - query* p_query, const boost::property_tree::ptree& property_tree, - bool connect, boost::system::error_code& ec) { + static void add_params_from_property_tree(query* p_query, + const ptree& property_tree, + bool connect, + boost::system::error_code& ec) { auto layer_name = property_tree.get_child_optional("layer"); if (!layer_name || layer_name.get().data() != NAME) { ec.assign(ssf::error::invalid_argument, ssf::error::get_ssf_category()); diff --git a/src/framework/ssf/layer/physical/tcp_helpers.cpp b/src/framework/ssf/layer/physical/tcp_helpers.cpp index 2dd76aa2..2847165e 100644 --- a/src/framework/ssf/layer/physical/tcp_helpers.cpp +++ b/src/framework/ssf/layer/physical/tcp_helpers.cpp @@ -1,14 +1,7 @@ -#include "ssf/layer/physical/tcp_helpers.h" - -#include - -#include -#include - #include "ssf/error/error.h" -#include "ssf/utils/map_helpers.h" -#include "ssf/layer/parameters.h" +#include "ssf/layer/physical/host.h" +#include "ssf/layer/physical/tcp_helpers.h" namespace ssf { namespace layer { @@ -18,13 +11,12 @@ namespace detail { boost::asio::ip::tcp::endpoint make_tcp_endpoint( boost::asio::io_service& io_service, const LayerParameters& parameters, boost::system::error_code& ec) { - auto addr = ssf::helpers::GetField("addr", parameters); - auto port = ssf::helpers::GetField("port", parameters); + Host host(parameters); - if (!port.empty()) { - if (!addr.empty()) { + if (!host.port().empty()) { + if (!host.addr().empty()) { boost::asio::ip::tcp::resolver resolver(io_service); - boost::asio::ip::tcp::resolver::query query(addr, port); + boost::asio::ip::tcp::resolver::query query(host.addr(), host.port()); boost::asio::ip::tcp::resolver::iterator iterator( resolver.resolve(query, ec)); @@ -33,8 +25,8 @@ boost::asio::ip::tcp::endpoint make_tcp_endpoint( } } else { try { - return boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), - (uint16_t)std::stoul(port)); + return boost::asio::ip::tcp::endpoint( + boost::asio::ip::tcp::v4(), (uint16_t)std::stoul(host.port())); } catch (const std::exception&) { ec.assign(ssf::error::bad_address, ssf::error::get_ssf_category()); return boost::asio::ip::tcp::endpoint(); diff --git a/src/framework/ssf/layer/physical/udp.h b/src/framework/ssf/layer/physical/udp.h index 4fde712a..7518035f 100644 --- a/src/framework/ssf/layer/physical/udp.h +++ b/src/framework/ssf/layer/physical/udp.h @@ -3,8 +3,6 @@ #include -#include - #include #include @@ -25,16 +23,17 @@ class udp { facilities = ssf::layer::facilities::datagram, mtu = 1500 - overhead }; + enum { endpoint_stack_size = 1 }; - typedef int socket_context; - typedef int acceptor_context; - typedef boost::asio::ip::udp::socket socket; - typedef boost::asio::ip::udp::resolver resolver; - typedef boost::asio::ip::udp::endpoint endpoint; + using socket_context = int; + using acceptor_context = int; + using endpoint = boost::asio::ip::udp::endpoint; + using resolver = boost::asio::ip::udp::resolver; + using socket = boost::asio::ip::udp::socket; -private: - using query = ParameterStack; + private: + using query = ParameterStack; public: operator boost::asio::ip::udp() { return boost::asio::ip::udp::v4(); } @@ -42,8 +41,8 @@ class udp { static endpoint make_endpoint(boost::asio::io_service& io_service, query::const_iterator parameters_it, uint32_t, boost::system::error_code& ec) { - return endpoint(ssf::layer::physical::detail::make_udp_endpoint( - io_service, *parameters_it, ec)); + return ssf::layer::physical::detail::make_udp_endpoint(io_service, + *parameters_it, ec); } static std::string get_address(const endpoint& endpoint) { diff --git a/src/framework/ssf/layer/physical/udp_helpers.cpp b/src/framework/ssf/layer/physical/udp_helpers.cpp index 9d2bf3db..dc59d2bd 100644 --- a/src/framework/ssf/layer/physical/udp_helpers.cpp +++ b/src/framework/ssf/layer/physical/udp_helpers.cpp @@ -1,16 +1,7 @@ -#include "ssf/layer/physical/udp_helpers.h" - -#include - -#include -#include - -#include - #include "ssf/error/error.h" -#include "ssf/utils/map_helpers.h" -#include "ssf/layer/parameters.h" +#include "ssf/layer/physical/host.h" +#include "ssf/layer/physical/udp_helpers.h" namespace ssf { namespace layer { @@ -20,13 +11,12 @@ namespace detail { boost::asio::ip::udp::endpoint make_udp_endpoint( boost::asio::io_service& io_service, const LayerParameters& parameters, boost::system::error_code& ec) { - auto addr = helpers::GetField("addr", parameters); - auto port = helpers::GetField("port", parameters); + Host host(parameters); - if (!port.empty()) { - if (!addr.empty()) { + if (!host.port().empty()) { + if (!host.addr().empty()) { boost::asio::ip::udp::resolver resolver(io_service); - boost::asio::ip::udp::resolver::query query(addr, port); + boost::asio::ip::udp::resolver::query query(host.addr(), host.port()); boost::asio::ip::udp::resolver::iterator iterator( resolver.resolve(query, ec)); @@ -35,8 +25,8 @@ boost::asio::ip::udp::endpoint make_udp_endpoint( } } else { try { - return boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), - (uint16_t)std::stoul(port)); + return boost::asio::ip::udp::endpoint( + boost::asio::ip::udp::v4(), (uint16_t)std::stoul(host.port())); } catch (const std::exception&) { ec.assign(ssf::error::bad_address, ssf::error::get_ssf_category()); return boost::asio::ip::udp::endpoint(); diff --git a/src/framework/ssf/layer/proxy/basic_proxy_acceptor_service.h b/src/framework/ssf/layer/proxy/basic_proxy_acceptor_service.h index 390e374a..57b152ed 100644 --- a/src/framework/ssf/layer/proxy/basic_proxy_acceptor_service.h +++ b/src/framework/ssf/layer/proxy/basic_proxy_acceptor_service.h @@ -97,6 +97,10 @@ class basic_ProxyAcceptor_service : public boost::asio::detail::service_base< } bool is_open(const implementation_type& impl) const { + if (!impl.p_next_layer_acceptor) { + return false; + } + return impl.p_next_layer_acceptor->is_open(); } @@ -113,6 +117,12 @@ class basic_ProxyAcceptor_service : public boost::asio::detail::service_base< boost::system::error_code close(implementation_type& impl, boost::system::error_code& ec) { + if (!impl.p_next_layer_acceptor) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return ec; + } + return impl.p_next_layer_acceptor->close(ec); } @@ -124,22 +134,38 @@ class basic_ProxyAcceptor_service : public boost::asio::detail::service_base< template boost::system::error_code set_option(implementation_type& impl, const SettableSocketOption& option, - boost::system::error_code& ec) { - if (impl.p_next_layer_acceptor) { - return impl.p_next_layer_acceptor->set_option(option, ec); + boost::system::error_code& ec) { + if (!impl.p_next_layer_acceptor) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return ec; } - return ec; + + return impl.p_next_layer_acceptor->set_option(option, ec); + } boost::system::error_code bind(implementation_type& impl, const endpoint_type& endpoint, boost::system::error_code& ec) { + if (!impl.p_next_layer_acceptor) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return ec; + } + impl.p_local_endpoint = std::make_shared(endpoint); return impl.p_next_layer_acceptor->bind(endpoint.next_layer_endpoint(), ec); } boost::system::error_code listen(implementation_type& impl, int backlog, - boost::system::error_code& ec) { + boost::system::error_code& ec) { + if (!impl.p_next_layer_acceptor) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return ec; + } + return impl.p_next_layer_acceptor->listen(backlog, ec); } @@ -149,7 +175,13 @@ class basic_ProxyAcceptor_service : public boost::asio::detail::service_base< boost::asio::basic_socket& peer, endpoint_type* p_peer_endpoint, boost::system::error_code& ec, typename std::enable_if::value>::type* = 0) { + protocol_type, Protocol1>::value>::type* = 0) { + if (!impl.p_next_layer_acceptor) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return ec; + } + auto& peer_impl = peer.native_handle(); peer_impl.p_remote_endpoint = std::make_shared(); @@ -183,6 +215,14 @@ class basic_ProxyAcceptor_service : public boost::asio::detail::service_base< void(boost::system::error_code)> init(std::forward(handler)); + if (!impl.p_next_layer_acceptor) { + io::PostHandler( + this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category())); + return init.result.get(); + } + auto& peer_impl = peer.native_handle(); peer_impl.p_local_endpoint = std::make_shared(); diff --git a/src/framework/ssf/layer/proxy/basic_proxy_protocol.h b/src/framework/ssf/layer/proxy/basic_proxy_protocol.h index 1c79ed15..28af6c48 100644 --- a/src/framework/ssf/layer/proxy/basic_proxy_protocol.h +++ b/src/framework/ssf/layer/proxy/basic_proxy_protocol.h @@ -4,8 +4,6 @@ #include #include -#include "ssf/error/error.h" - #include "ssf/layer/basic_endpoint.h" #include "ssf/layer/basic_impl.h" #include "ssf/layer/basic_resolver.h" @@ -65,12 +63,22 @@ class basic_ProxyProtocol { typename query::const_iterator parameters_it, uint32_t, boost::system::error_code& ec) { auto context = MakeProxyContext(io_service, *parameters_it, ec); + if (ec) { return endpoint(); } - return endpoint(context, next_layer_protocol::make_endpoint( - io_service, ++parameters_it, id, ec)); + // Get next layer parameters + ++parameters_it; + + if (!context.acceptor_endpoint() && + context.UpdateRemoteHost(*parameters_it)) { + // Proxy context contains remote host data + return endpoint(context); + } else { + return endpoint(context, next_layer_protocol::make_endpoint( + io_service, parameters_it, id, ec)); + } } static void add_params_from_property_tree( diff --git a/src/framework/ssf/layer/proxy/basic_proxy_socket_service.h b/src/framework/ssf/layer/proxy/basic_proxy_socket_service.h index 46eddb46..4daa23ef 100644 --- a/src/framework/ssf/layer/proxy/basic_proxy_socket_service.h +++ b/src/framework/ssf/layer/proxy/basic_proxy_socket_service.h @@ -86,6 +86,10 @@ class basic_ProxySocket_service : public boost::asio::detail::service_base< } bool is_open(const implementation_type& impl) const { + if (!impl.p_next_layer_socket) { + return false; + } + return impl.p_next_layer_socket->is_open(); } @@ -113,6 +117,11 @@ class basic_ProxySocket_service : public boost::asio::detail::service_base< boost::system::error_code close(implementation_type& impl, boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return ec; + } + return impl.p_next_layer_socket->close(ec); } @@ -122,11 +131,22 @@ class basic_ProxySocket_service : public boost::asio::detail::service_base< boost::system::error_code cancel(implementation_type& impl, boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return ec; + } + return impl.p_next_layer_socket->cancel(ec); } bool at_mark(const implementation_type& impl, boost::system::error_code& ec) const { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return false; + } return impl.p_next_layer_socket->at_mark(ec); } @@ -187,6 +207,12 @@ class basic_ProxySocket_service : public boost::asio::detail::service_base< const ConstBufferSequence& buffers, boost::asio::socket_base::message_flags flags, boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::bad_file_descriptor, + ssf::error::get_ssf_category()); + return 0; + } + return impl.p_next_layer_socket->send(buffers, flags, ec); } @@ -196,8 +222,21 @@ class basic_ProxySocket_service : public boost::asio::detail::service_base< async_send(implementation_type& impl, const ConstBufferSequence& buffers, boost::asio::socket_base::message_flags flags, WriteHandler&& handler) { - return impl.p_next_layer_socket->async_send( - buffers, std::forward(handler)); + boost::asio::detail::async_result_init< + WriteHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_next_layer_socket) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::broken_pipe, + ssf::error::get_ssf_category()), + 0); + return init.result.get(); + } + + impl.p_next_layer_socket->async_send(buffers, init.handler); + + return init.result.get(); } template @@ -205,6 +244,11 @@ class basic_ProxySocket_service : public boost::asio::detail::service_base< const MutableBufferSequence& buffers, boost::asio::socket_base::message_flags flags, boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return 0; + } + return impl.p_next_layer_socket->receive(buffers, flags, ec); } @@ -215,13 +259,31 @@ class basic_ProxySocket_service : public boost::asio::detail::service_base< const MutableBufferSequence& buffers, boost::asio::socket_base::message_flags flags, ReadHandler&& handler) { - return impl.p_next_layer_socket->async_receive( - buffers, std::forward(handler)); + boost::asio::detail::async_result_init< + ReadHandler, void(boost::system::error_code, std::size_t)> + init(std::forward(handler)); + + if (!impl.p_next_layer_socket) { + io::PostHandler(this->get_io_service(), init.handler, + boost::system::error_code(ssf::error::broken_pipe, + ssf::error::get_ssf_category()), + 0); + return init.result.get(); + } + + impl.p_next_layer_socket->async_receive(buffers, init.handler); + + return init.result.get(); } boost::system::error_code shutdown( implementation_type& impl, boost::asio::socket_base::shutdown_type what, boost::system::error_code& ec) { + if (!impl.p_next_layer_socket) { + ec.assign(ssf::error::broken_pipe, ssf::error::get_ssf_category()); + return ec; + } + impl.p_next_layer_socket->shutdown(what, ec); return ec; diff --git a/src/framework/ssf/layer/proxy/connect_op.h b/src/framework/ssf/layer/proxy/connect_op.h index 9a7331c8..61ee61bf 100644 --- a/src/framework/ssf/layer/proxy/connect_op.h +++ b/src/framework/ssf/layer/proxy/connect_op.h @@ -22,9 +22,9 @@ class ConnectOp { void operator()(boost::system::error_code& ec) { auto& context = peer_endpoint_.endpoint_context(); - if (!context.IsProxyEnabled()) { + if (!context.proxy_enabled()) { ssf::layer::detail::ConnectOp( - stream_, p_local_endpoint_, std::move(peer_endpoint_))(ec); + stream_, p_local_endpoint_, std::move(peer_endpoint_))(ec); return; } @@ -54,7 +54,7 @@ class AsyncConnectOp { void operator()() { auto& context = peer_endpoint_.endpoint_context(); - if (!context.IsProxyEnabled()) { + if (!context.proxy_enabled()) { ssf::layer::detail::AsyncConnectOp< Protocol, Stream, Endpoint, typename boost::asio::handler_type< diff --git a/src/framework/ssf/layer/proxy/digest_auth_strategy.cpp b/src/framework/ssf/layer/proxy/digest_auth_strategy.cpp index 16167e08..c9cd5d36 100644 --- a/src/framework/ssf/layer/proxy/digest_auth_strategy.cpp +++ b/src/framework/ssf/layer/proxy/digest_auth_strategy.cpp @@ -1,12 +1,12 @@ #include + #include +#include #include #include #include #include -#include -#include #include @@ -257,11 +257,12 @@ std::string DigestAuthStrategy::GenerateRandomString(std::size_t strlen) { "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "1234567890"); random_string.reserve(strlen); - boost::random::mt19937 rng; + // TODO improve randomness - rng.seed(static_cast(std::time(0))); - boost::random::uniform_int_distribution<> index_dist(0, - characters.size() - 1); + std::random_device rd; + std::mt19937 rng(rd()); + std::uniform_int_distribution<> index_dist( + 0, static_cast(characters.size() - 1)); for (std::size_t i = 0; i < strlen; ++i) { random_string[i] = characters[index_dist(rng)]; } diff --git a/src/framework/ssf/layer/proxy/http_connect_op.h b/src/framework/ssf/layer/proxy/http_connect_op.h index 0cd3a891..c37678b3 100644 --- a/src/framework/ssf/layer/proxy/http_connect_op.h +++ b/src/framework/ssf/layer/proxy/http_connect_op.h @@ -38,7 +38,7 @@ class HttpConnectOp { auto& endpoint_context = peer_endpoint_.endpoint_context(); stream_.connect( - endpoint_context.http_proxy.ToTcpEndpoint(stream_.get_io_service()), + endpoint_context.http_proxy().ToTcpEndpoint(stream_.get_io_service()), ec); if (ec) { @@ -55,11 +55,10 @@ class HttpConnectOp { HttpResponseBuilder response_builder; Buffer buffer; std::size_t bytes_read; - auto& next_layer_remote_endpoint = peer_endpoint_.next_layer_endpoint(); - session_initializer.Reset( - next_layer_remote_endpoint.address().to_string(), - std::to_string(next_layer_remote_endpoint.port()), endpoint_context); + session_initializer.Reset(endpoint_context.remote_host().addr(), + endpoint_context.remote_host().port(), + endpoint_context); HttpRequest http_request; @@ -72,7 +71,7 @@ class HttpConnectOp { stream_.shutdown(boost::asio::socket_base::shutdown_both, ec); stream_.close(ec); } - stream_.connect(endpoint_context.http_proxy.ToTcpEndpoint( + stream_.connect(endpoint_context.http_proxy().ToTcpEndpoint( stream_.get_io_service())); } @@ -182,18 +181,17 @@ class AsyncHttpConnectOp { } boost::system::error_code connect_ec; - auto& next_layer_remote_endpoint = peer_endpoint_.next_layer_endpoint(); auto& endpoint_context = peer_endpoint_.endpoint_context(); HttpRequest http_request; reenter(coro_) { - p_session_initializer_->Reset( - next_layer_remote_endpoint.address().to_string(), - std::to_string(next_layer_remote_endpoint.port()), endpoint_context); + p_session_initializer_->Reset(endpoint_context.remote_host().addr(), + endpoint_context.remote_host().port(), + endpoint_context); yield stream_.async_connect( - endpoint_context.http_proxy.ToTcpEndpoint(stream_.get_io_service()), + endpoint_context.http_proxy().ToTcpEndpoint(stream_.get_io_service()), std::move(*this)); p_local_endpoint_->set(); @@ -208,9 +206,10 @@ class AsyncHttpConnectOp { close_ec_); stream_.close(close_ec_); } - yield stream_.async_connect(endpoint_context.http_proxy.ToTcpEndpoint( - stream_.get_io_service()), - std::move(*this)); + yield stream_.async_connect( + endpoint_context.http_proxy().ToTcpEndpoint( + stream_.get_io_service()), + std::move(*this)); } // send request diff --git a/src/framework/ssf/layer/proxy/http_session_initializer.cpp b/src/framework/ssf/layer/proxy/http_session_initializer.cpp index 343e3c09..82bfd8d7 100644 --- a/src/framework/ssf/layer/proxy/http_session_initializer.cpp +++ b/src/framework/ssf/layer/proxy/http_session_initializer.cpp @@ -34,12 +34,13 @@ void HttpSessionInitializer::Reset(const std::string& target_host, p_current_auth_strategy_ = nullptr; auth_strategies_.clear(); auth_strategies_.emplace_back( - new NegotiateAuthStrategy(proxy_ep_ctx_.http_proxy)); - auth_strategies_.emplace_back(new NtlmAuthStrategy(proxy_ep_ctx_.http_proxy)); + new NegotiateAuthStrategy(proxy_ep_ctx_.http_proxy())); auth_strategies_.emplace_back( - new DigestAuthStrategy(proxy_ep_ctx_.http_proxy)); + new NtlmAuthStrategy(proxy_ep_ctx_.http_proxy())); auth_strategies_.emplace_back( - new BasicAuthStrategy(proxy_ep_ctx_.http_proxy)); + new DigestAuthStrategy(proxy_ep_ctx_.http_proxy())); + auth_strategies_.emplace_back( + new BasicAuthStrategy(proxy_ep_ctx_.http_proxy())); } void HttpSessionInitializer::PopulateRequest(HttpRequest* p_request, diff --git a/src/framework/ssf/layer/proxy/proxy_endpoint_context.cpp b/src/framework/ssf/layer/proxy/proxy_endpoint_context.cpp index 7acccd96..e9f1f087 100644 --- a/src/framework/ssf/layer/proxy/proxy_endpoint_context.cpp +++ b/src/framework/ssf/layer/proxy/proxy_endpoint_context.cpp @@ -1,4 +1,8 @@ +#include "ssf/log/log.h" +#include "ssf/utils/map_helpers.h" + #include "ssf/layer/proxy/proxy_endpoint_context.h" +#include "ssf/layer/proxy/proxy_helpers.h" namespace ssf { namespace layer { @@ -7,25 +11,70 @@ namespace proxy { Proxy::Proxy() : host(""), port("") {} boost::asio::ip::tcp::endpoint Proxy::ToTcpEndpoint( - boost::asio::io_service& io_service) { + boost::asio::io_service& io_service) const { + boost::system::error_code ec; boost::asio::ip::tcp::resolver resolver(io_service); - auto endpoint_it = resolver.resolve({host, port}); + auto endpoint_it = resolver.resolve({host, port}, ec); + if (ec.value() != 0) { + return boost::asio::ip::tcp::endpoint(); + } + return *endpoint_it; } ProxyEndpointContext::ProxyEndpointContext() - : proxy_enabled(false), http_proxy() {} + : proxy_enabled_(false), + acceptor_endpoint_(false), + http_proxy_(), + remote_host_() {} + +void ProxyEndpointContext::Init(const LayerParameters& proxy_parameters) { + proxy_enabled_ = false; + + acceptor_endpoint_ = (ssf::helpers::GetField( + "acceptor_endpoint", proxy_parameters) == "true"); + + auto http_host = + ssf::helpers::GetField("http_host", proxy_parameters); + auto http_port = + ssf::helpers::GetField("http_port", proxy_parameters); + if (http_port.empty() || http_host.empty()) { + return; + } -bool ProxyEndpointContext::IsProxyEnabled() const { return proxy_enabled; } + proxy_enabled_ = true; + + http_proxy_.host = http_host; + http_proxy_.port = http_port; + http_proxy_.username = + ssf::helpers::GetField("http_username", proxy_parameters); + http_proxy_.domain = + ssf::helpers::GetField("http_domain", proxy_parameters); + http_proxy_.password = + ssf::helpers::GetField("http_password", proxy_parameters); + http_proxy_.reuse_ntlm = (ssf::helpers::GetField( + "http_reuse_ntlm", proxy_parameters) == "true"); + http_proxy_.reuse_kerb = (ssf::helpers::GetField( + "http_reuse_kerb", proxy_parameters) == "true"); +} bool ProxyEndpointContext::HttpProxyEnabled() const { - return proxy_enabled && !http_proxy.host.empty() && !http_proxy.port.empty(); + return proxy_enabled_ && !http_proxy_.host.empty() && + !http_proxy_.port.empty(); +} + +bool ProxyEndpointContext::UpdateRemoteHost(const LayerParameters& parameters) { + if (proxy_enabled_) { + remote_host_ = ProxyEndpointContext::Host(parameters); + return true; + } + return false; } bool ProxyEndpointContext::operator==(const ProxyEndpointContext& rhs) const { - return proxy_enabled == rhs.proxy_enabled && - http_proxy.host == rhs.http_proxy.host && - http_proxy.port == rhs.http_proxy.port; + return proxy_enabled_ == rhs.proxy_enabled_ && + http_proxy_.host == rhs.http_proxy_.host && + http_proxy_.port == rhs.http_proxy_.port; } bool ProxyEndpointContext::operator!=(const ProxyEndpointContext& rhs) const { @@ -33,7 +82,7 @@ bool ProxyEndpointContext::operator!=(const ProxyEndpointContext& rhs) const { } bool ProxyEndpointContext::operator<(const ProxyEndpointContext& rhs) const { - return http_proxy.host < rhs.http_proxy.host; + return http_proxy_.host < rhs.http_proxy_.host; } } // proxy diff --git a/src/framework/ssf/layer/proxy/proxy_endpoint_context.h b/src/framework/ssf/layer/proxy/proxy_endpoint_context.h index 7edb7fa5..96a799c7 100644 --- a/src/framework/ssf/layer/proxy/proxy_endpoint_context.h +++ b/src/framework/ssf/layer/proxy/proxy_endpoint_context.h @@ -5,6 +5,8 @@ #include +#include "ssf/layer/physical/host.h" + namespace ssf { namespace layer { namespace proxy { @@ -13,7 +15,7 @@ struct Proxy { Proxy(); boost::asio::ip::tcp::endpoint ToTcpEndpoint( - boost::asio::io_service& io_service); + boost::asio::io_service& io_service) const; std::string host; std::string port; @@ -24,10 +26,19 @@ struct Proxy { bool reuse_kerb; }; -struct ProxyEndpointContext { +class ProxyEndpointContext { + public: + using Host = ssf::layer::physical::Host; + + public: ProxyEndpointContext(); - bool IsProxyEnabled() const; + // Init context from proxy layer params + void Init(const LayerParameters& proxy_parameters); + + // Update remote host component with TCP layer parameters + // @returns true if remote host was updated + bool UpdateRemoteHost(const LayerParameters& tcp_parameters); bool HttpProxyEnabled() const; @@ -37,8 +48,19 @@ struct ProxyEndpointContext { bool operator<(const ProxyEndpointContext& rhs) const; - bool proxy_enabled; - Proxy http_proxy; + inline bool proxy_enabled() const { return proxy_enabled_; } + + inline bool acceptor_endpoint() const { return acceptor_endpoint_; } + + inline const Proxy& http_proxy() const { return http_proxy_; } + + inline const Host& remote_host() const { return remote_host_; } + + private: + bool proxy_enabled_; + bool acceptor_endpoint_; + Proxy http_proxy_; + Host remote_host_; }; } // proxy diff --git a/src/framework/ssf/layer/proxy/proxy_helpers.cpp b/src/framework/ssf/layer/proxy/proxy_helpers.cpp index 0199c4af..cf69060a 100644 --- a/src/framework/ssf/layer/proxy/proxy_helpers.cpp +++ b/src/framework/ssf/layer/proxy/proxy_helpers.cpp @@ -24,35 +24,18 @@ ProxyEndpointContext MakeProxyContext(boost::asio::io_service& io_service, const LayerParameters& parameters, boost::system::error_code& ec) { ProxyEndpointContext context; - context.proxy_enabled = false; - auto http_host = ssf::helpers::GetField("http_host", parameters); - auto http_port = ssf::helpers::GetField("http_port", parameters); - if (http_port.empty() || http_host.empty()) { - return context; - } - if (!ValidateIPTarget(io_service, http_host, http_port)) { + context.Init(parameters); + + if (context.proxy_enabled() && + !ValidateIPTarget(io_service, context.http_proxy().host, + context.http_proxy().port)) { ec.assign(ssf::error::bad_address, ssf::error::get_ssf_category()); SSF_LOG(kLogError) << "network[proxy]: could not resolve target address <" - << http_host << ":" << http_port << ">"; - return context; + << context.http_proxy().host << ":" + << context.http_proxy().port << ">"; } - context.proxy_enabled = true; - context.http_proxy.host = http_host; - context.http_proxy.port = http_port; - context.http_proxy.username = - ssf::helpers::GetField("http_username", parameters); - context.http_proxy.domain = - ssf::helpers::GetField("http_domain", parameters); - context.http_proxy.password = - ssf::helpers::GetField("http_password", parameters); - context.http_proxy.reuse_ntlm = - (ssf::helpers::GetField("http_reuse_ntlm", parameters) == - "true"); - context.http_proxy.reuse_kerb = - (ssf::helpers::GetField("http_reuse_kerb", parameters) == - "true"); return context; } diff --git a/src/framework/ssf/layer/proxy/windows/sspi_auth_impl.cpp b/src/framework/ssf/layer/proxy/windows/sspi_auth_impl.cpp index fd9573ae..64d09b67 100644 --- a/src/framework/ssf/layer/proxy/windows/sspi_auth_impl.cpp +++ b/src/framework/ssf/layer/proxy/windows/sspi_auth_impl.cpp @@ -62,13 +62,16 @@ bool SSPIAuthImpl::Init() { use_identity = true; identity.Domain = reinterpret_cast( const_cast(proxy_ctx_.domain.c_str())); - identity.DomainLength = proxy_ctx_.domain.size(); + identity.DomainLength = + static_cast(proxy_ctx_.domain.size()); identity.User = reinterpret_cast( const_cast(proxy_ctx_.username.c_str())); - identity.UserLength = proxy_ctx_.username.size(); + identity.UserLength = + static_cast(proxy_ctx_.username.size()); identity.Password = reinterpret_cast( const_cast(proxy_ctx_.password.c_str())); - identity.PasswordLength = proxy_ctx_.password.size(); + identity.PasswordLength = + static_cast(proxy_ctx_.password.size()); identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI; } @@ -102,7 +105,7 @@ bool SSPIAuthImpl::ProcessServerToken(const Token& server_token) { out_sec_buff_desc.pBuffers = &out_sec_buff; out_sec_buff.BufferType = SECBUFFER_TOKEN; out_sec_buff.pvBuffer = output_token_.data(); - out_sec_buff.cbBuffer = output_token_.size(); + out_sec_buff.cbBuffer = static_cast(output_token_.size()); // input token in_sec_buf_desc.ulVersion = SECBUFFER_VERSION; @@ -110,7 +113,7 @@ bool SSPIAuthImpl::ProcessServerToken(const Token& server_token) { in_sec_buf_desc.pBuffers = &in_sec_buff; in_sec_buff.BufferType = SECBUFFER_TOKEN; in_sec_buff.pvBuffer = const_cast(server_token.data()); - in_sec_buff.cbBuffer = server_token.size(); + in_sec_buff.cbBuffer = static_cast(server_token.size()); // update security context unsigned long attrs; diff --git a/src/framework/tests/proxy/README.md b/src/framework/tests/proxy/README.md index 0265bc75..4d7aae35 100644 --- a/src/framework/tests/proxy/README.md +++ b/src/framework/tests/proxy/README.md @@ -2,7 +2,7 @@ * Copy file 'proxy.json.dist' into 'proxy.json' * Fill options with correct values: - - target_host: address of your machine reachable from proxy + - target_host: FQDN of your machine reachable from proxy only - target_port: port of test server (9000 by default) - proxy_host: address of http proxy - proxy_port: port of http proxy @@ -10,6 +10,6 @@ - password: password for proxy authentication - domain: user domain (NTLM and Negotiate auth on Windows only) - reuse_ntlm: reuse current computer user credentials to authenticate with proxy NTLM auth (SSO) - "reuse_kerb": reuse current computer user credentials (Kerberos ticket) to authenticate with proxy Negotiate auth (SSO) + - reuse_kerb: reuse current computer user credentials (Kerberos ticket) to authenticate with proxy Negotiate auth (SSO) * Run `cmake ..` at project build directory * Build and run `proxy_layer_tests` diff --git a/src/framework/tests/proxy/proxy.json.dist b/src/framework/tests/proxy/proxy.json.dist index 1a7f210c..a0911f48 100644 --- a/src/framework/tests/proxy/proxy.json.dist +++ b/src/framework/tests/proxy/proxy.json.dist @@ -1,5 +1,5 @@ { - "target_host": "public.localhost.name", + "target_host": "public.fqdn.name", "target_port": "9000", "proxy_host": "proxy.example.com", "proxy_port": "3128", diff --git a/src/framework/tests/proxy_layer_tests.cpp b/src/framework/tests/proxy_layer_tests.cpp index e85ed7e2..6dfc029f 100644 --- a/src/framework/tests/proxy_layer_tests.cpp +++ b/src/framework/tests/proxy_layer_tests.cpp @@ -37,10 +37,10 @@ TEST_F(ProxyTestFixture, ProxySetTest) { ssf::layer::ParameterStack client_parameters; client_parameters.push_back(GetProxyParam()); - client_parameters.push_back(GetTcpParam()); + client_parameters.push_back(GetProxyTcpParam()); ssf::layer::ParameterStack client_error_connection_parameters; - client_error_connection_parameters.push_back(GetTcpParam()); + client_error_connection_parameters.push_back(GetProxyTcpParam()); client_error_connection_parameters.push_back( client_error_tcp_addr.ToTCPParam()); @@ -48,7 +48,7 @@ TEST_F(ProxyTestFixture, ProxySetTest) { ssf::layer::ParameterStack client_invalid_proxy_parameters; client_invalid_proxy_parameters.push_back( client_error_proxy_addr.ToProxyParam()); - client_invalid_proxy_parameters.push_back(GetTcpParam()); + client_invalid_proxy_parameters.push_back(GetProxyTcpParam()); TestStreamErrorConnectionProtocol( client_error_connection_parameters); @@ -105,17 +105,17 @@ TEST_F(ProxyTestFixture, TLSOverProxyTCPTest) { client_parameters.push_back( tests::virtual_network_helpers::tls_client_parameters); client_parameters.push_back(GetProxyParam()); - client_parameters.push_back(GetTcpParam()); + client_parameters.push_back(GetProxyTcpParam()); ssf::layer::ParameterStack client_error_parameters; client_error_parameters.push_back( tests::virtual_network_helpers::tls_client_parameters); - client_error_parameters.push_back(GetTcpParam()); + client_error_parameters.push_back(GetProxyTcpParam()); client_error_parameters.push_back(client_error_tcp_addr.ToTCPParam()); ssf::layer::ParameterStack client_wrong_number_parameters; client_wrong_number_parameters.push_back(empty_layer); - client_wrong_number_parameters.push_back(GetTcpParam()); + client_wrong_number_parameters.push_back(GetProxyTcpParam()); TestEndpointResolverError(client_wrong_number_parameters); @@ -159,7 +159,7 @@ TEST_F(ProxyTestFixture, ProxyNotSetTest) { ssf::layer::ParameterStack client_parameters; client_parameters.push_back(empty_layer); - client_parameters.push_back(GetTcpParam()); + client_parameters.push_back(GetLocalTcpParam()); ssf::layer::ParameterStack client_error_connection_parameters; client_error_connection_parameters.push_back(empty_layer); diff --git a/src/framework/tests/proxy_test_fixture.cpp b/src/framework/tests/proxy_test_fixture.cpp index 54f903e0..14152f03 100644 --- a/src/framework/tests/proxy_test_fixture.cpp +++ b/src/framework/tests/proxy_test_fixture.cpp @@ -57,7 +57,7 @@ std::string ProxyTestFixture::GetOption(const std::string& name) const { return opt_it != config_options_.end() ? opt_it->second : ""; } -ssf::layer::LayerParameters ProxyTestFixture::GetTcpParam() const { +ssf::layer::LayerParameters ProxyTestFixture::GetProxyTcpParam() const { ssf::layer::LayerParameters tcp_params; tcp_params["addr"] = GetOption("target_host"); tcp_params["port"] = GetOption("target_port"); @@ -65,6 +65,14 @@ ssf::layer::LayerParameters ProxyTestFixture::GetTcpParam() const { return tcp_params; } +ssf::layer::LayerParameters ProxyTestFixture::GetLocalTcpParam() const { + ssf::layer::LayerParameters tcp_params; + tcp_params["addr"] = "127.0.0.1"; + tcp_params["port"] = GetOption("target_port"); + + return tcp_params; +} + ssf::layer::LayerParameters ProxyTestFixture::GetProxyParam() const { ssf::layer::LayerParameters proxy_params; proxy_params["http_host"] = GetOption("proxy_host"); diff --git a/src/framework/tests/proxy_test_fixture.h b/src/framework/tests/proxy_test_fixture.h index 4cf7a909..c0b894a1 100644 --- a/src/framework/tests/proxy_test_fixture.h +++ b/src/framework/tests/proxy_test_fixture.h @@ -43,7 +43,9 @@ class ProxyTestFixture : public ::testing::Test { bool Initialized(); - ssf::layer::LayerParameters GetTcpParam() const; + ssf::layer::LayerParameters GetProxyTcpParam() const; + + ssf::layer::LayerParameters GetLocalTcpParam() const; ssf::layer::LayerParameters GetProxyParam() const; diff --git a/src/services/admin/admin.h b/src/services/admin/admin.h index 9c003407..5a70b443 100644 --- a/src/services/admin/admin.h +++ b/src/services/admin/admin.h @@ -161,7 +161,6 @@ class Admin : public BaseService { void ReceiveInstructionHeader(); void ReceiveInstructionParameters(); void TreatInstructionId(); - void CreateNewService(); void ShutdownServices(); template diff --git a/src/services/admin/admin.ipp b/src/services/admin/admin.ipp index fb327689..1a293663 100644 --- a/src/services/admin/admin.ipp +++ b/src/services/admin/admin.ipp @@ -157,7 +157,9 @@ void Admin::InitializeRemoteServices( // If something went wrong remote_all_started_ > 0 if (remote_all_started_) { - SSF_LOG(kLogWarning) << "service[admin]: remote could not start"; + SSF_LOG(kLogError) << "service[admin]: could not start remote " + "microservice for service[" + << user_services_[i_]->GetName() << "]"; Notify(ssf::services::initialisation::SERVICE, user_services_[i_], boost::system::error_code(::error::operation_canceled, @@ -173,8 +175,8 @@ void Admin::InitializeRemoteServices( boost::bind(&Admin::InitializeRemoteServices, this->SelfFromThis(), _1)); } - - return; + // Try next user service + continue; } // Start local associated services @@ -183,7 +185,9 @@ void Admin::InitializeRemoteServices( // If something went wrong local_all_started_ == false if (!local_all_started_) { - SSF_LOG(kLogWarning) << "service[admin]: local could not start"; + SSF_LOG(kLogError) << "service[admin]: could not start local " + "microservice for service[" + << user_services_[i_]->GetName() << "]"; Notify(ssf::services::initialisation::SERVICE, user_services_[i_], boost::system::error_code(::error::operation_canceled, @@ -201,8 +205,8 @@ void Admin::InitializeRemoteServices( } // Stop local services user_services_[i_]->StopLocalServices(this->get_demux()); - - return; + // Try next user service + continue; } Notify(ssf::services::initialisation::SERVICE, user_services_[i_], diff --git a/src/services/admin/requests/service_status.h b/src/services/admin/requests/service_status.h index d723784d..37d748ba 100644 --- a/src/services/admin/requests/service_status.h +++ b/src/services/admin/requests/service_status.h @@ -101,7 +101,7 @@ class ServiceStatus { uint32_t error_code_value() { return error_code_value_; } - void add_parameter(std::string key, std::string value) { + void add_parameter(const std::string& key, const std::string& value) { parameters_[key] = value; } diff --git a/src/services/base_service.h b/src/services/base_service.h index 323e6b04..e3d2b550 100644 --- a/src/services/base_service.h +++ b/src/services/base_service.h @@ -14,6 +14,7 @@ #include "common/boost/fiber/datagram_fiber.hpp" namespace ssf { + //---------------------------------------------------------------------------- /// Base class for services template @@ -36,12 +37,12 @@ class BaseService : public std::enable_shared_from_this> { typedef std::map Parameters; public: + virtual ~BaseService() {} + 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; } diff --git a/src/services/base_service_config.cpp b/src/services/base_service_config.cpp new file mode 100644 index 00000000..c03c9944 --- /dev/null +++ b/src/services/base_service_config.cpp @@ -0,0 +1,9 @@ +#include "services/base_service_config.h" + +namespace ssf { + +BaseServiceConfig::BaseServiceConfig(bool enabled) : enabled_(enabled) {} + +BaseServiceConfig::~BaseServiceConfig() {} + +} // ssf diff --git a/src/services/base_service_config.h b/src/services/base_service_config.h new file mode 100644 index 00000000..676d8f04 --- /dev/null +++ b/src/services/base_service_config.h @@ -0,0 +1,23 @@ +#ifndef SSF_SERVICES_BASE_SERVICE_CONFIG_H_ +#define SSF_SERVICES_BASE_SERVICE_CONFIG_H_ + +namespace ssf { + +class BaseServiceConfig { + public: + virtual ~BaseServiceConfig(); + + inline bool enabled() const { return enabled_; } + + inline void set_enabled(bool enabled) { enabled_ = enabled; } + + protected: + BaseServiceConfig(bool enabled); + + private: + bool enabled_; +}; + +} // ssf + +#endif // SSF_SERVICES_BASE_SERVICE_CONFIG_H_ \ No newline at end of file diff --git a/src/services/copy_file/config.cpp b/src/services/copy_file/config.cpp new file mode 100644 index 00000000..0935885e --- /dev/null +++ b/src/services/copy_file/config.cpp @@ -0,0 +1,14 @@ +#include "services/copy_file/config.h" + +namespace ssf { +namespace services { +namespace copy_file { + +Config::Config() : BaseServiceConfig(false) {} + +Config::Config(const Config& process_service) + : BaseServiceConfig(process_service.enabled()) {} + +} // copy_file +} // services +} // ssf \ No newline at end of file diff --git a/src/services/copy_file/config.h b/src/services/copy_file/config.h new file mode 100644 index 00000000..284bb384 --- /dev/null +++ b/src/services/copy_file/config.h @@ -0,0 +1,20 @@ +#ifndef SSF_SERVICES_COPY_FILE_CONFIG_H_ +#define SSF_SERVICES_COPY_FILE_CONFIG_H_ + +#include "services/base_service_config.h" + +namespace ssf { +namespace services { +namespace copy_file { + +class Config : public BaseServiceConfig { + public: + Config(); + Config(const Config& process_service); +}; + +} // copy_file +} // services +} // ssf + +#endif // SSF_SERVICES_COPY_FILE_CONFIG_H_ \ No newline at end of file diff --git a/src/services/copy_file/fiber_to_file/fiber_to_file.h b/src/services/copy_file/fiber_to_file/fiber_to_file.h index cfe9be3d..b0fd3dbd 100644 --- a/src/services/copy_file/fiber_to_file/fiber_to_file.h +++ b/src/services/copy_file/fiber_to_file/fiber_to_file.h @@ -11,6 +11,7 @@ #include "core/factories/service_factory.h" #include "services/admin/requests/create_service_request.h" +#include "services/copy_file/config.h" #include "services/copy_file/fiber_to_file/fiber_to_ofstream_session.h" namespace ssf { @@ -40,13 +41,29 @@ class FiberToFile : public BaseService { return FiberToFilePtr(new FiberToFile(io_service, fiber_demux)); } - virtual ~FiberToFile() {} + static void RegisterToServiceFactory( + std::shared_ptr> p_factory, const Config& config) { + if (!config.enabled()) { + // service factory is not enabled + return; + } + + p_factory->RegisterServiceCreator(factory_id, &FiberToFile::Create); + } + + static ssf::services::admin::CreateServiceRequest GetCreateRequest() { + ssf::services::admin::CreateServiceRequest create(factory_id); + + return create; + } + public: // Start service and listen new fiber on demux port kServicePort - virtual void start(boost::system::error_code& ec) { + void start(boost::system::error_code& ec) override { endpoint ep(this->get_demux(), kServicePort); - SSF_LOG(kLogInfo) << "service[fiber to file]: start accept on fiber port " - << kServicePort; + SSF_LOG(kLogInfo) + << "microservice[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) { @@ -56,25 +73,14 @@ class FiberToFile : public BaseService { } // Stop service - virtual void stop(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "service[fiber to file]: stopping"; + void stop(boost::system::error_code& ec) override { + SSF_LOG(kLogInfo) << "microservice[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; - } + uint32_t service_type_id() override { return factory_id; } private: FiberToFile(boost::asio::io_service& io_service, demux& fiber_demux) @@ -93,7 +99,7 @@ class FiberToFile : public BaseService { // Create a session to transmit files for the new connection void StartDataForwarderSessionHandler(const boost::system::error_code& ec) { if (ec) { - SSF_LOG(kLogInfo) << "service[fiber to file]: fail accept fiber"; + SSF_LOG(kLogInfo) << "microservice[fiber to file]: fail accept fiber"; return; } diff --git a/src/services/copy_file/file_enquirer/file_enquirer.h b/src/services/copy_file/file_enquirer/file_enquirer.h index f8685bab..8a7f4420 100644 --- a/src/services/copy_file/file_enquirer/file_enquirer.h +++ b/src/services/copy_file/file_enquirer/file_enquirer.h @@ -7,6 +7,7 @@ #include "core/factories/service_factory.h" #include "services/admin/requests/create_service_request.h" +#include "services/copy_file/config.h" #include "services/copy_file/file_to_fiber/file_to_fiber.h" namespace ssf { @@ -40,18 +41,13 @@ class FileEnquirer : public BaseService { 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) { + std::shared_ptr> p_factory, const Config& config) { + if (!config.enabled()) { + // service factory is not enabled + return; + } + p_factory->RegisterServiceCreator(factory_id, &FileEnquirer::Create); } @@ -64,6 +60,15 @@ class FileEnquirer : public BaseService { return create; } + public: + void start(boost::system::error_code& ec) override { + SendRequest(boost::system::error_code(), 0); + } + + void stop(boost::system::error_code& ec) override { fiber_.close(ec); } + + uint32_t service_type_id() override { return factory_id; } + private: FileEnquirer(boost::asio::io_service& io_service, Demux& fiber_demux, const std::string& input_pattern, @@ -86,9 +91,8 @@ class FileEnquirer : public BaseService { } reenter(coroutine_) { - SSF_LOG(kLogDebug) - << "service[file enquirer]: connect to remote fiber acceptor port " - << remote_endpoint_.port(); + SSF_LOG(kLogDebug) << "microservice[file enquirer]: connect to remote " + "fiber acceptor port " << remote_endpoint_.port(); yield fiber_.async_connect( remote_endpoint_, diff --git a/src/services/copy_file/file_to_fiber/file_to_fiber.h b/src/services/copy_file/file_to_fiber/file_to_fiber.h index a7814709..a451cc1a 100644 --- a/src/services/copy_file/file_to_fiber/file_to_fiber.h +++ b/src/services/copy_file/file_to_fiber/file_to_fiber.h @@ -25,6 +25,7 @@ #include "services/admin/requests/create_service_request.h" #include "services/copy_file/file_to_fiber/istream_to_fiber_session.h" +#include "services/copy_file/config.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" @@ -75,10 +76,34 @@ class FileToFiber : public BaseService { return nullptr; } - virtual ~FileToFiber() {} + static void RegisterToServiceFactory( + std::shared_ptr> p_factory, const Config& config) { + if (!config.enabled()) { + // service factory is not enabled + return; + } + + 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; + } + + public: // Start service - virtual void start(boost::system::error_code& ec) { + void start(boost::system::error_code& ec) override { // set stdin in binary mode #ifdef WIN32 if (_setmode(_fileno(stdin), _O_BINARY) == -1) { @@ -88,8 +113,9 @@ class FileToFiber : public BaseService { if (accept_) { endpoint ep(this->get_demux(), kServicePort); - SSF_LOG(kLogInfo) << "service[file to fiber]: start accept on fiber port " - << kServicePort; + SSF_LOG(kLogInfo) + << "microservice[file to fiber]: start accept on fiber port " + << kServicePort; fiber_acceptor_.bind(ep, ec); fiber_acceptor_.listen(boost::asio::socket_base::max_connections, ec); if (ec) { @@ -102,8 +128,8 @@ class FileToFiber : public BaseService { } // Stop service - virtual void stop(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "service[file to fiber]: stopping"; + void stop(boost::system::error_code& ec) override { + SSF_LOG(kLogInfo) << "microservice[file to fiber]: stopping"; manager_.stop_all(); boost::system::error_code close_ec; fiber_acceptor_.close(close_ec); @@ -116,27 +142,7 @@ class FileToFiber : public BaseService { #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; - } + uint32_t service_type_id() override { return factory_id; } private: FileToFiber(boost::asio::io_service& io_service, demux& fiber_demux) @@ -276,7 +282,7 @@ class FileToFiber : public BaseService { std::make_shared(this->get_demux().get_io_service()); SSF_LOG(kLogDebug) - << "service[file to fiber]: connect to remote fiber acceptor port " + << "microservice[file to fiber]: connect to remote fiber acceptor port " << ssf::services::copy_file::fiber_to_file::FiberToFile< Demux>::kServicePort; @@ -304,11 +310,12 @@ class FileToFiber : public BaseService { if (from_stdin) { SSF_LOG(kLogInfo) - << "service[file to fiber]: start forward data from stdin to " + << "microservice[file to fiber]: start forward data from stdin to " << output_file; } else { - SSF_LOG(kLogInfo) << "service[file to fiber]: start forward data from " - << input_file << " to " << output_file; + SSF_LOG(kLogInfo) + << "microservice[file to fiber]: start forward data from " + << input_file << " to " << output_file; } if (!from_stdin) { diff --git a/src/services/datagrams_to_fibers/config.cpp b/src/services/datagrams_to_fibers/config.cpp new file mode 100644 index 00000000..2d26436f --- /dev/null +++ b/src/services/datagrams_to_fibers/config.cpp @@ -0,0 +1,15 @@ +#include "services/datagrams_to_fibers/config.h" + +namespace ssf { +namespace services { +namespace datagrams_to_fibers { + +Config::Config() : BaseServiceConfig(true), gateway_ports_(false) {} + +Config::Config(const Config& datagram_listener) + : BaseServiceConfig(datagram_listener.enabled()), + gateway_ports_(datagram_listener.gateway_ports_) {} + +} // datagrams_to_fibers +} // services +} // ssf \ No newline at end of file diff --git a/src/services/datagrams_to_fibers/config.h b/src/services/datagrams_to_fibers/config.h new file mode 100644 index 00000000..5dd0c618 --- /dev/null +++ b/src/services/datagrams_to_fibers/config.h @@ -0,0 +1,28 @@ +#ifndef SSF_SERVICES_DATAGRAMS_TO_FIBERS_CONFIG_H_ +#define SSF_SERVICES_DATAGRAMS_TO_FIBERS_CONFIG_H_ + +#include "services/base_service_config.h" + +namespace ssf { +namespace services { +namespace datagrams_to_fibers { + +class Config : public BaseServiceConfig { + public: + Config(); + Config(const Config& datagram_listener); + + inline bool gateway_ports() const { return gateway_ports_; } + inline void set_gateway_ports(bool gateway_ports) { + gateway_ports_ = gateway_ports; + } + + private: + bool gateway_ports_; +}; + +} // datagrams_to_fibers +} // services +} // ssf + +#endif // SSF_SERVICES_DATAGRAMS_TO_FIBERS_CONFIG_H_ \ No newline at end of file diff --git a/src/services/datagrams_to_fibers/datagrams_to_fibers.h b/src/services/datagrams_to_fibers/datagrams_to_fibers.h index d61d5246..f6f0529a 100644 --- a/src/services/datagrams_to_fibers/datagrams_to_fibers.h +++ b/src/services/datagrams_to_fibers/datagrams_to_fibers.h @@ -18,11 +18,16 @@ #include "core/factories/service_factory.h" #include "services/admin/requests/create_service_request.h" +#include "services/datagrams_to_fibers/config.h" namespace ssf { namespace services { namespace datagrams_to_fibers { +// datagram_listener microservice +// Listen to UDP endpoint (local_addr, local_port). Each received datagrams +// will be forwarded to the fiber remote_port. Datagrams from fiber will be +// forwarded to sending UDP socket. template class DatagramsToFibers : public BaseService { public: @@ -45,44 +50,83 @@ class DatagramsToFibers : public BaseService { typedef std::array WorkingBufferType; public: + enum { factory_id = 6 }; + + public: + DatagramsToFibers() = delete; + DatagramsToFibers(const DatagramsToFibers&) = delete; + + public: + // Factory method for datagram_listener microservice + // @param io_service + // @param fiber_demux + // @param parameters microservice configuration parameters + // @param gateway_ports true to interpret local_addr parameters. Default + // behavior will set local_addr to 127.0.0.1 + // @returns Microservice or nullptr if an error occured + // + // parameters format: + // { + // "local_addr": IP_ADDR|*|"" + // "local_port": TCP_PORT + // "remote_port": FIBER_PORT + // } static DatagramsToFibersPtr Create(boost::asio::io_service& io_service, - Demux& fiber_demux, - Parameters parameters) { - if (!parameters.count("local_port") || !parameters.count("remote_port")) { + Demux& fiber_demux, Parameters parameters, + bool gateway_ports) { + if (!parameters.count("local_addr") || !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 }; + std::string local_addr("127.0.0.1"); + if (gateway_ports && parameters.count("local_addr") && + !parameters["local_addr"].empty()) { + if (parameters["local_addr"] == "*") { + local_addr = "0.0.0.0"; + } else { + local_addr = parameters["local_addr"]; + } + } + return std::shared_ptr( + new DatagramsToFibers(io_service, fiber_demux, local_addr, + (uint16_t)std::stoul(parameters["local_port"]), + std::stoul(parameters["remote_port"]))); + } static void RegisterToServiceFactory( - std::shared_ptr> p_factory) { - p_factory->RegisterServiceCreator(factory_id, &DatagramsToFibers::Create); - } + std::shared_ptr> p_factory, const Config& config) { + if (!config.enabled()) { + // service factory is not enabled + return; + } - virtual void start(boost::system::error_code& ec); - virtual void stop(boost::system::error_code& ec); - virtual uint32_t service_type_id(); + p_factory->RegisterServiceCreator( + factory_id, boost::bind(&DatagramsToFibers::Create, _1, _2, _3, + config.gateway_ports())); + } static ssf::services::admin::CreateServiceRequest GetCreateRequest( - uint16_t local_port, remote_port_type remote_port) { + const std::string& local_addr, uint16_t local_port, + remote_port_type remote_port) { ssf::services::admin::CreateServiceRequest create(factory_id); + create.add_parameter("local_addr", local_addr); create.add_parameter("local_port", std::to_string(local_port)); create.add_parameter("remote_port", std::to_string(remote_port)); return create; } + public: + void start(boost::system::error_code& ec) override; + void stop(boost::system::error_code& ec) override; + uint32_t service_type_id() override; + private: DatagramsToFibers(boost::asio::io_service& io_service, Demux& fiber_demux, - uint16_t local, remote_port_type remote_port); + const std::string& local_addr, uint16_t local_port, + remote_port_type remote_port); - private: void StartReceivingDatagrams(); void SocketReceiveHandler(const boost::system::error_code& ec, size_t length); @@ -97,6 +141,8 @@ class DatagramsToFibers : public BaseService { this->shared_from_this()); } + private: + std::string local_addr_; uint16_t local_port_; remote_port_type remote_port_; socket socket_; diff --git a/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp b/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp index 7f2cfb57..152bba67 100644 --- a/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp +++ b/src/services/datagrams_to_fibers/datagrams_to_fibers.ipp @@ -8,12 +8,15 @@ namespace ssf { namespace services { namespace datagrams_to_fibers { + template DatagramsToFibers::DatagramsToFibers(boost::asio::io_service& io_service, Demux& fiber_demux, + const std::string& local_addr, uint16_t local_port, remote_port_type remote_port) : ssf::BaseService::BaseService(io_service, fiber_demux), + local_addr_(local_addr), local_port_(local_port), remote_port_(remote_port), socket_(io_service), @@ -24,18 +27,19 @@ DatagramsToFibers::DatagramsToFibers(boost::asio::io_service& io_service, template void DatagramsToFibers::start(boost::system::error_code& ec) { SSF_LOG(kLogInfo) - << "service[datagrams to fibers]: starting relay on local port udp " - << local_port_; + << "microservice[datagram_listener]: start forwarding local UDP port <" + << local_addr_ << ":" << local_port_ << "> to fiber port " + << remote_port_; boost::asio::ip::udp::resolver resolver(socket_.get_io_service()); - boost::asio::ip::udp::resolver::query query( - boost::asio::ip::udp::v4(), "localhost", std::to_string(local_port_)); + boost::asio::ip::udp::resolver::query query(local_addr_, + std::to_string(local_port_)); auto ep_it = resolver.resolve(query, ec); if (ec) { - SSF_LOG(kLogError) - << "service[datagrams to fibers]: could not resolve query "; + SSF_LOG(kLogError) << "microservice[datagram_listener]: could not " + "resolve query <" << local_addr_ << ":" << local_port_ + << ">"; return; } @@ -44,22 +48,27 @@ void DatagramsToFibers::start(boost::system::error_code& ec) { boost::system::error_code close_ec; socket_.open(boost::asio::ip::udp::v4(), ec); if (ec) { - SSF_LOG(kLogError) << "service[datagrams to fibers]: could not open socket"; + SSF_LOG(kLogError) + << "microservice[datagram_listener]: could not open socket"; socket_.close(close_ec); return; } + + /** + // TODO: reuse_address = legit socket option? boost::asio::socket_base::reuse_address reuse_address_option(true); socket_.set_option(reuse_address_option, ec); if (ec) { - SSF_LOG(kLogError) - << "service[datagrams to fibers]: could not set reuse address option"; + SSF_LOG(kLogError) << "microservice[datagram_listener]: could not set " + "reuse address option"; socket_.close(close_ec); return; - } + }*/ socket_.bind(endpoint_, ec); if (ec) { - SSF_LOG(kLogError) << "service[datagrams to fibers]: could not bind socket"; + SSF_LOG(kLogError) + << "microservice[datagram_listener]: could not bind socket"; socket_.close(close_ec); return; } @@ -71,11 +80,11 @@ void DatagramsToFibers::start(boost::system::error_code& ec) { template void DatagramsToFibers::stop(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "service[datagrams to fibers]: stopping"; + SSF_LOG(kLogInfo) << "microservice[datagram_listener]: stopping"; socket_.close(ec); if (ec) { - SSF_LOG(kLogDebug) << "service[datagrams to fibers]: error on stop " + SSF_LOG(kLogDebug) << "microservice[datagram_listener]: error on stop " << ec.message(); } @@ -89,7 +98,8 @@ uint32_t DatagramsToFibers::service_type_id() { template void DatagramsToFibers::StartReceivingDatagrams() { - SSF_LOG(kLogTrace) << "service[datagrams to fibers]: receiving new datagrams"; + SSF_LOG(kLogTrace) + << "microservice[datagram_listener]: receiving new datagrams"; socket_.async_receive_from( boost::asio::buffer(working_buffer_), endpoint_, diff --git a/src/services/fibers_to_datagrams/config.cpp b/src/services/fibers_to_datagrams/config.cpp new file mode 100644 index 00000000..38ffba23 --- /dev/null +++ b/src/services/fibers_to_datagrams/config.cpp @@ -0,0 +1,14 @@ +#include "services/fibers_to_datagrams/config.h" + +namespace ssf { +namespace services { +namespace fibers_to_datagrams { + +Config::Config() : BaseServiceConfig(true) {} + +Config::Config(const Config& datagram_forwarder) + : BaseServiceConfig(datagram_forwarder.enabled()) {} + +} // fibers_to_datagrams +} // services +} // ssf \ No newline at end of file diff --git a/src/services/fibers_to_datagrams/config.h b/src/services/fibers_to_datagrams/config.h new file mode 100644 index 00000000..53c1f2a7 --- /dev/null +++ b/src/services/fibers_to_datagrams/config.h @@ -0,0 +1,20 @@ +#ifndef SSF_SERVICES_FIBERS_TO_DATAGRAMS_CONFIG_H_ +#define SSF_SERVICES_FIBERS_TO_DATAGRAMS_CONFIG_H_ + +#include "services/base_service_config.h" + +namespace ssf { +namespace services { +namespace fibers_to_datagrams { + +class Config : public BaseServiceConfig { + public: + Config(); + Config(const Config& datagram_forwarder); +}; + +} // fibers_to_datagrams +} // services +} // ssf + +#endif // SSF_SERVICES_FIBERS_TO_DATAGRAMS_CONFIG_H_ \ No newline at end of file diff --git a/src/services/fibers_to_datagrams/fibers_to_datagrams.h b/src/services/fibers_to_datagrams/fibers_to_datagrams.h index ee5db75c..acc81645 100644 --- a/src/services/fibers_to_datagrams/fibers_to_datagrams.h +++ b/src/services/fibers_to_datagrams/fibers_to_datagrams.h @@ -18,6 +18,7 @@ #include "core/factories/service_factory.h" #include "services/admin/requests/create_service_request.h" +#include "services/fibers_to_datagrams/config.h" namespace ssf { namespace services { @@ -44,6 +45,13 @@ class FibersToDatagrams : public BaseService { typedef std::array WorkingBufferType; + public: + enum { factory_id = 5 }; + + public: + FibersToDatagrams() = delete; + FibersToDatagrams(const FibersToDatagrams&) = delete; + public: static FibersToDatagramsPtr create(boost::asio::io_service& io_service, Demux& fiber_demux, @@ -59,17 +67,16 @@ class FibersToDatagrams : public BaseService { } } - enum { factory_id = 5 }; - static void RegisterToServiceFactory( - std::shared_ptr> p_factory) { + std::shared_ptr> p_factory, const Config& config) { + if (!config.enabled()) { + // service factory is not enabled + return; + } + 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) { @@ -81,12 +88,16 @@ class FibersToDatagrams : public BaseService { return create; } + public: + void start(boost::system::error_code& ec) override; + void stop(boost::system::error_code& ec) override; + uint32_t service_type_id() override; + 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); diff --git a/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp b/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp index 7f6b9cba..d64d2810 100644 --- a/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp +++ b/src/services/fibers_to_datagrams/fibers_to_datagrams.ipp @@ -24,9 +24,9 @@ FibersToDatagrams::FibersToDatagrams(boost::asio::io_service& io_service, template void FibersToDatagrams::start(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) - << "service[fibers to datagrams]: starting relay on local port udp " - << local_port_; + SSF_LOG(kLogInfo) << "microservice[datagram_forwarder]: start forwarding " + "fiber datagrams from fiber port " << local_port_ + << " to <" << ip_ << ":" << remote_port_ << ">"; // fiber.open() fiber_.bind(datagram_endpoint(this->get_demux(), local_port_), ec); @@ -46,7 +46,7 @@ void FibersToDatagrams::start(boost::system::error_code& ec) { template void FibersToDatagrams::stop(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "service[fibers to datagrams]: stopping"; + SSF_LOG(kLogInfo) << "microservice[datagram_forwarder]: stopping"; ec.assign(::error::success, ::error::get_ssf_category()); fiber_.close(); @@ -60,7 +60,8 @@ uint32_t FibersToDatagrams::service_type_id() { template void FibersToDatagrams::StartReceivingDatagrams() { - SSF_LOG(kLogTrace) << "service[fibers to datagrams]: receiving new datagrams"; + SSF_LOG(kLogTrace) + << "microservice[datagram_forwarder]: receiving new datagrams"; fiber_.async_receive_from( boost::asio::buffer(working_buffer_), received_from_, diff --git a/src/services/fibers_to_sockets/config.cpp b/src/services/fibers_to_sockets/config.cpp new file mode 100644 index 00000000..d0d28cba --- /dev/null +++ b/src/services/fibers_to_sockets/config.cpp @@ -0,0 +1,14 @@ +#include "services/fibers_to_sockets/config.h" + +namespace ssf { +namespace services { +namespace fibers_to_sockets { + +Config::Config() : BaseServiceConfig(true) {} + +Config::Config(const Config& stream_forwarder) + : BaseServiceConfig(stream_forwarder.enabled()) {} + +} // fibers_to_sockets +} // services +} // ssf \ No newline at end of file diff --git a/src/services/fibers_to_sockets/config.h b/src/services/fibers_to_sockets/config.h new file mode 100644 index 00000000..a22edbaf --- /dev/null +++ b/src/services/fibers_to_sockets/config.h @@ -0,0 +1,20 @@ +#ifndef SSF_SERVICES_FIBERS_TO_SOCKETS_CONFIG_H_ +#define SSF_SERVICES_FIBERS_TO_SOCKETS_CONFIG_H_ + +#include "services/base_service_config.h" + +namespace ssf { +namespace services { +namespace fibers_to_sockets { + +class Config : public BaseServiceConfig { + public: + Config(); + Config(const Config& stream_forwarder); +}; + +} // fibers_to_sockets +} // services +} // ssf + +#endif // SSF_SERVICES_FIBERS_TO_SOCKETS_CONFIG_H_ \ No newline at end of file diff --git a/src/services/fibers_to_sockets/fibers_to_sockets.h b/src/services/fibers_to_sockets/fibers_to_sockets.h index 9fb631d8..6eae0101 100644 --- a/src/services/fibers_to_sockets/fibers_to_sockets.h +++ b/src/services/fibers_to_sockets/fibers_to_sockets.h @@ -17,6 +17,7 @@ #include "core/factories/service_factory.h" #include "services/admin/requests/create_service_request.h" +#include "services/fibers_to_sockets/config.h" namespace ssf { namespace services { @@ -37,6 +38,13 @@ class FibersToSockets : public BaseService { typedef boost::asio::ip::tcp::socket socket; + public: + enum { factory_id = 3 }; + + public: + FibersToSockets() = delete; + FibersToSockets(const FibersToSockets&) = delete; + public: static FibersToSocketsPtr create(boost::asio::io_service& io_service, demux& fiber_demux, Parameters parameters) { @@ -51,17 +59,16 @@ class FibersToSockets : public BaseService { } } - enum { factory_id = 3 }; - static void RegisterToServiceFactory( - std::shared_ptr> p_factory) { + std::shared_ptr> p_factory, const Config& config) { + if (!config.enabled()) { + // service factory is not enabled + return; + } + 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) { @@ -73,12 +80,16 @@ class FibersToSockets : public BaseService { return create; } + public: + void start(boost::system::error_code& ec) override; + void stop(boost::system::error_code& ec) override; + uint32_t service_type_id() override; + 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(); @@ -96,6 +107,7 @@ class FibersToSockets : public BaseService { return std::static_pointer_cast(this->shared_from_this()); } + private: uint16_t remote_port_; std::string ip_; local_port_type local_port_; diff --git a/src/services/fibers_to_sockets/fibers_to_sockets.ipp b/src/services/fibers_to_sockets/fibers_to_sockets.ipp index 10c6d527..0e3a3b4b 100644 --- a/src/services/fibers_to_sockets/fibers_to_sockets.ipp +++ b/src/services/fibers_to_sockets/fibers_to_sockets.ipp @@ -26,9 +26,9 @@ FibersToSockets::FibersToSockets(boost::asio::io_service& io_service, template void FibersToSockets::start(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) - << "service[fibers to sockets]: starting relay on local port tcp " - << local_port_; + SSF_LOG(kLogInfo) << "microservice[stream_forwarder]: start " + "forwarding stream fiber from fiber port " << local_port_ + << " to TCP <" << ip_ << ":" << remote_port_ << ">"; endpoint ep(this->get_demux(), local_port_); fiber_acceptor_.bind(ep, ec); @@ -49,7 +49,7 @@ void FibersToSockets::start(boost::system::error_code& ec) { template void FibersToSockets::stop(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "service[fibers to sockets]: stopping"; + SSF_LOG(kLogInfo) << "microservice[stream_forwarder]: stopping"; ec.assign(::error::success, ::error::get_ssf_category()); fiber_acceptor_.close(); @@ -63,7 +63,7 @@ uint32_t FibersToSockets::service_type_id() { template void FibersToSockets::StartAcceptFibers() { - SSF_LOG(kLogTrace) << "service[fibers to sockets]: accepting new clients"; + SSF_LOG(kLogTrace) << "microservice[stream_forwarder]: accepting new clients"; fiber_acceptor_.async_accept( fiber_, Then(&FibersToSockets::FiberAcceptHandler, this->SelfFromThis())); @@ -72,7 +72,7 @@ void FibersToSockets::StartAcceptFibers() { template void FibersToSockets::FiberAcceptHandler( const boost::system::error_code& ec) { - SSF_LOG(kLogTrace) << "service[fibers to sockets]: accept handler"; + SSF_LOG(kLogTrace) << "microservice[stream_forwarder]: accept handler"; if (!fiber_acceptor_.is_open()) { return; @@ -88,7 +88,7 @@ void FibersToSockets::FiberAcceptHandler( template void FibersToSockets::SocketConnectHandler( const boost::system::error_code& ec) { - SSF_LOG(kLogTrace) << "service[fibers to sockets]: connect handler"; + SSF_LOG(kLogTrace) << "microservice[stream_forwarder]: connect handler"; if (!ec) { auto session = SessionForwarder::create( diff --git a/src/services/process/config.cpp b/src/services/process/config.cpp new file mode 100644 index 00000000..77cb5f26 --- /dev/null +++ b/src/services/process/config.cpp @@ -0,0 +1,16 @@ +#include "services/process/config.h" + +namespace ssf { +namespace services { +namespace process { + +Config::Config() : BaseServiceConfig(false), path_(""), args_("") {} + +Config::Config(const Config& process_service) + : BaseServiceConfig(process_service.enabled()), + path_(process_service.path_), + args_(process_service.args_) {} + +} // process +} // services +} // ssf \ No newline at end of file diff --git a/src/services/process/config.h b/src/services/process/config.h new file mode 100644 index 00000000..c1945cc8 --- /dev/null +++ b/src/services/process/config.h @@ -0,0 +1,32 @@ +#ifndef SSF_SERVICES_PROCESS_CONFIG_H_ +#define SSF_SERVICES_PROCESS_CONFIG_H_ + +#include + +#include "services/base_service_config.h" + +namespace ssf { +namespace services { +namespace process { + +class Config : public BaseServiceConfig { + public: + Config(); + Config(const Config& process_service); + + inline std::string path() const { return path_; } + inline void set_path(const std::string& path) { path_ = path; } + + inline std::string args() const { return args_; } + inline void set_args(const std::string& args) { args_ = args; } + + private: + std::string path_; + std::string args_; +}; + +} // process +} // services +} // ssf + +#endif // SSF_SERVICES_PROCESS_CONFIG_H_ \ No newline at end of file diff --git a/src/services/process/posix/session.ipp b/src/services/process/posix/session.ipp index 82307a5b..8830aee9 100644 --- a/src/services/process/posix/session.ipp +++ b/src/services/process/posix/session.ipp @@ -42,14 +42,14 @@ Session::Session(SessionManager* p_session_manager, fiber client, template void Session::start(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "session[process]: start"; + SSF_LOG(kLogInfo) << "session[shell]: start"; int master_tty; int slave_tty; InitMasterSlaveTty(&master_tty, &slave_tty, ec); if (ec) { - SSF_LOG(kLogError) << "session[process]: init tty failed"; + SSF_LOG(kLogError) << "session[shell]: init tty failed"; stop(ec); return; } @@ -57,14 +57,14 @@ void Session::start(boost::system::error_code& ec) { signal_.add(SIGCHLD, ec); if (ec) { SSF_LOG(kLogError) - << "session[process]: init signal handler on SIGCHLD failed"; + << "session[shell]: init signal handler on SIGCHLD failed"; stop(ec); return; } child_pid_ = fork(); if (child_pid_ < 0) { - SSF_LOG(kLogError) << "session[process]: fork failed"; + SSF_LOG(kLogError) << "session[shell]: fork failed"; ec.assign(::error::process_not_created, ::error::get_ssf_category()); stop(ec); return; @@ -78,7 +78,7 @@ void Session::start(boost::system::error_code& ec) { tcgetattr(slave_tty, &new_term_settings); // IGNCR: ignore carriage return on input - new_term_settings.c_iflag |= ( IGNCR ); + new_term_settings.c_iflag |= (IGNCR); tcsetattr(slave_tty, TCSANOW, &new_term_settings); // new process as session leader @@ -138,11 +138,11 @@ void Session::start(boost::system::error_code& ec) { template void Session::stop(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "session[process]: stop"; + SSF_LOG(kLogInfo) << "session[shell]: stop"; client_.close(); if (ec) { - SSF_LOG(kLogError) << "session[process]: stop error " << ec.message(); + SSF_LOG(kLogError) << "session[shell]: stop error " << ec.message(); } if (child_pid_ > 0) { @@ -253,7 +253,7 @@ void Session::InitMasterSlaveTty(int* p_master_tty, int* p_slave_tty, // open an available pseudo terminal device (master/slave pair) *p_master_tty = posix_openpt(O_RDWR | O_NOCTTY); if (*p_master_tty < 0) { - SSF_LOG(kLogError) << "session[process]: could not open master tty"; + SSF_LOG(kLogError) << "session[shell]: could not open master tty"; ec.assign(::error::broken_pipe, ::error::get_ssf_category()); return; } @@ -273,7 +273,7 @@ void Session::InitMasterSlaveTty(int* p_master_tty, int* p_slave_tty, // open slave side *p_slave_tty = open(ptsname(*p_master_tty), O_RDWR | O_NOCTTY); if (*p_slave_tty < 0) { - SSF_LOG(kLogError) << "session[process]: could not open slave tty"; + SSF_LOG(kLogError) << "session[shell]: could not open slave tty"; ec.assign(::error::broken_pipe, ::error::get_ssf_category()); return; } @@ -283,8 +283,7 @@ template void Session::StartForwarding(boost::system::error_code& ec) { sd_.assign(master_tty_, ec); if (ec) { - SSF_LOG(kLogError) - << "session[process]: could not initialize stream handle"; + SSF_LOG(kLogError) << "session[shell]: could not initialize stream handle"; return; } diff --git a/src/services/process/server.h b/src/services/process/server.h index a2870375..68f26cbe 100644 --- a/src/services/process/server.h +++ b/src/services/process/server.h @@ -11,13 +11,13 @@ #include "services/base_service.h" -#include "common/config/config.h" #include "common/boost/fiber/stream_fiber.hpp" #include "common/boost/fiber/basic_fiber_demux.hpp" #include "core/factories/service_factory.h" #include "services/admin/requests/create_service_request.h" +#include "services/process/config.h" #if defined(BOOST_ASIO_WINDOWS) #include "services/process/windows/session.h" @@ -73,8 +73,12 @@ class Server : public BaseService { // Function used to register the micro service to the given factory static void RegisterToServiceFactory( - std::shared_ptr> p_factory, - const ssf::config::ProcessService& config) { + std::shared_ptr> p_factory, const Config& config) { + if (!config.enabled()) { + // service factory is not enabled + return; + } + p_factory->RegisterServiceCreator( factory_id, boost::bind(&Server::Create, _1, _2, _3, config.path(), config.args())); diff --git a/src/services/process/server.ipp b/src/services/process/server.ipp index 341393b2..4015d624 100644 --- a/src/services/process/server.ipp +++ b/src/services/process/server.ipp @@ -34,24 +34,25 @@ void Server::start(boost::system::error_code& ec) { if (ec) { SSF_LOG(kLogError) - << "service[process]: fiber acceptor could not bind on port " + << "microservice[shell]: fiber acceptor could not bind on port " << local_port_; return; } fiber_acceptor_.listen(boost::asio::socket_base::max_connections, ec); if (ec) { - SSF_LOG(kLogError) << "service[process]: fiber acceptor could not listen"; + SSF_LOG(kLogError) + << "microservice[shell]: fiber acceptor could not listen"; return; } if (!CheckBinaryPath()) { - SSF_LOG(kLogError) << "service[process]: binary not found"; + SSF_LOG(kLogError) << "microservice[shell]: binary not found"; ec.assign(::error::file_not_found, ::error::get_ssf_category()); return; } - SSF_LOG(kLogInfo) << "service[process]: starting server on port " + SSF_LOG(kLogInfo) << "microservice[shell]: start server on fiber port " << local_port_; this->StartAccept(); @@ -61,7 +62,7 @@ template void Server::stop(boost::system::error_code& ec) { ec.assign(boost::system::errc::success, boost::system::system_category()); - SSF_LOG(kLogInfo) << "service[process]: stopping server"; + SSF_LOG(kLogInfo) << "microservice[shell]: stop server"; this->HandleStop(); } @@ -72,7 +73,7 @@ uint32_t Server::service_type_id() { template void Server::StartAccept() { - SSF_LOG(kLogTrace) << "service[process]: accept new session"; + SSF_LOG(kLogTrace) << "microservice[shell]: accepting new session"; fiber_acceptor_.async_accept( new_connection_, boost::bind(&Server::HandleAccept, this->SelfFromThis(), _1)); @@ -80,19 +81,20 @@ void Server::StartAccept() { template void Server::HandleAccept(const boost::system::error_code& ec) { - SSF_LOG(kLogTrace) << "service[process]: HandleAccept"; + SSF_LOG(kLogTrace) << "microservice[shell]: HandleAccept"; if (!fiber_acceptor_.is_open()) { return; } if (ec) { - SSF_LOG(kLogError) << "service[process]: error accepting new connection: " - << ec.message() << " " << ec.value(); + SSF_LOG(kLogError) + << "microservice[shell]: error accepting new connections: " + << ec.message() << " " << ec.value(); this->StartAccept(); } - SSF_LOG(kLogInfo) << "service[process]: start session"; + SSF_LOG(kLogInfo) << "microservice[shell]: start session"; ssf::BaseSessionPtr new_process_session = std::make_shared( &(this->session_manager_), std::move(this->new_connection_), binary_path_, binary_args_); diff --git a/src/services/process/windows/session.ipp b/src/services/process/windows/session.ipp index b9d50bc4..d3a9eb92 100644 --- a/src/services/process/windows/session.ipp +++ b/src/services/process/windows/session.ipp @@ -45,18 +45,18 @@ Session::Session(SessionManager* p_session_manager, fiber client, template void Session::start(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "session[process]: start"; + SSF_LOG(kLogInfo) << "session[shell]: start"; InitPipes(ec); if (ec) { - SSF_LOG(kLogError) << "session[process]: pipes initialization failed"; + SSF_LOG(kLogError) << "session[shell]: pipes initialization failed"; stop(ec); return; } StartProcess(ec); if (ec) { - SSF_LOG(kLogError) << "session[process]: start process failed"; + SSF_LOG(kLogError) << "session[shell]: start process failed"; stop(ec); return; } @@ -64,7 +64,7 @@ void Session::start(boost::system::error_code& ec) { StartForwarding(ec); if (ec) { SSF_LOG(kLogError) - << "session[process]: forwarding data from process to client failed"; + << "session[shell]: forwarding data from process to client failed"; stop(ec); return; } @@ -72,7 +72,7 @@ void Session::start(boost::system::error_code& ec) { template void Session::stop(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "session[process]: stop"; + SSF_LOG(kLogInfo) << "session[shell]: stop"; if (process_info_.hProcess != INVALID_HANDLE_VALUE) { ::TerminateProcess(process_info_.hProcess, 0); @@ -111,7 +111,7 @@ void Session::stop(boost::system::error_code& ec) { client_.close(); if (ec) { - SSF_LOG(kLogError) << "session[process]: stop error " << ec.message(); + SSF_LOG(kLogError) << "session[shell]: stop error " << ec.message(); } } @@ -131,21 +131,21 @@ void Session::StartForwarding(boost::system::error_code& ec) { h_out_.assign(data_out_, ec); if (ec) { SSF_LOG(kLogError) - << "session[process]: could not initialize out stream handle"; + << "session[shell]: could not initialize out stream handle"; return; } data_out_ = INVALID_HANDLE_VALUE; h_err_.assign(data_err_, ec); if (ec) { SSF_LOG(kLogError) - << "session[process]: could not initialize err stream handle"; + << "session[shell]: could not initialize err stream handle"; return; } data_err_ = INVALID_HANDLE_VALUE; h_in_.assign(data_in_, ec); if (ec) { SSF_LOG(kLogError) - << "session[process]: could not initialize in stream handle"; + << "session[shell]: could not initialize in stream handle"; return; } data_in_ = INVALID_HANDLE_VALUE; @@ -188,7 +188,7 @@ void Session::StartProcess(boost::system::error_code& ec) { NULL, TRUE, CREATE_NEW_CONSOLE, NULL, (home_dir_set ? home_dir : NULL), &startup_info, &process_info_)) { - SSF_LOG(kLogError) << "session[process]: create process <" << command_line + SSF_LOG(kLogError) << "session[shell]: create process <" << command_line << "> failed"; ec.assign(::error::process_not_created, ::error::get_ssf_category()); } @@ -206,8 +206,7 @@ template void Session::InitPipes(boost::system::error_code& ec) { auto local_fib_ep = client_.remote_endpoint(ec); if (ec) { - SSF_LOG(kLogError) - << "session[process]: could not get fiber local endpoint"; + SSF_LOG(kLogError) << "session[shell]: could not get fiber local endpoint"; return; } @@ -225,21 +224,21 @@ void Session::InitPipes(boost::system::error_code& ec) { InitOutNamedPipe(out_pipe_name_, &data_out_, &proc_out_, &sec_attr, pipe_size, ec); if (ec) { - SSF_LOG(kLogError) << "session[process]: init out pipe failed"; + SSF_LOG(kLogError) << "session[shell]: init out pipe failed"; return; } InitOutNamedPipe(err_pipe_name_, &data_err_, &proc_err_, &sec_attr, pipe_size, ec); if (ec) { - SSF_LOG(kLogError) << "session[process]: init err pipe failed"; + SSF_LOG(kLogError) << "session[shell]: init err pipe failed"; return; } InitInNamedPipe(in_pipe_name_, &proc_in_, &data_in_, &sec_attr, pipe_size, ec); if (ec) { - SSF_LOG(kLogError) << "session[process]: init in pipe failed"; + SSF_LOG(kLogError) << "session[shell]: init in pipe failed"; return; } } @@ -256,7 +255,7 @@ void Session::InitOutNamedPipe(const std::string& pipe_name, 0, p_pipe_attributes); if (read_pipe_tmp == INVALID_HANDLE_VALUE) { - SSF_LOG(kLogError) << "session[process]: create read side of named pipe <" + SSF_LOG(kLogError) << "session[shell]: create read side of named pipe <" << pipe_name << "> failed"; ec.assign(::error::broken_pipe, ::error::get_ssf_category()); goto cleanup; @@ -268,7 +267,7 @@ void Session::InitOutNamedPipe(const std::string& pipe_name, if (*p_write_pipe == INVALID_HANDLE_VALUE) { ec.assign(::error::broken_pipe, ::error::get_ssf_category()); - SSF_LOG(kLogError) << "session[process]: create write side of named pipe <" + SSF_LOG(kLogError) << "session[shell]: create write side of named pipe <" << pipe_name << "> failed"; goto cleanup; } @@ -276,9 +275,8 @@ void Session::InitOutNamedPipe(const std::string& pipe_name, if (!::DuplicateHandle(GetCurrentProcess(), read_pipe_tmp, GetCurrentProcess(), p_read_pipe, 0, FALSE, DUPLICATE_SAME_ACCESS)) { - SSF_LOG(kLogError) - << "session[process]: duplicate read side of named pipe <" << pipe_name - << "> failed"; + SSF_LOG(kLogError) << "session[shell]: duplicate read side of named pipe <" + << pipe_name << "> failed"; ec.assign(::error::broken_pipe, ::error::get_ssf_category()); goto cleanup; } @@ -301,7 +299,7 @@ void Session::InitInNamedPipe(const std::string& pipe_name, 0, p_pipe_attributes); if (write_pipe_tmp == INVALID_HANDLE_VALUE) { - SSF_LOG(kLogError) << "session[process]: create write side of named pipe <" + SSF_LOG(kLogError) << "session[shell]: create write side of named pipe <" << pipe_name << "> failed"; ec.assign(::error::broken_pipe, ::error::get_ssf_category()); goto cleanup; @@ -312,7 +310,7 @@ void Session::InitInNamedPipe(const std::string& pipe_name, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); if (*p_read_pipe == INVALID_HANDLE_VALUE) { - SSF_LOG(kLogError) << "session[process]: create read side of named pipe <" + SSF_LOG(kLogError) << "session[shell]: create read side of named pipe <" << pipe_name << "> failed"; ec.assign(::error::broken_pipe, ::error::get_ssf_category()); goto cleanup; @@ -321,9 +319,8 @@ void Session::InitInNamedPipe(const std::string& pipe_name, if (!::DuplicateHandle(::GetCurrentProcess(), write_pipe_tmp, ::GetCurrentProcess(), p_write_pipe, 0, FALSE, DUPLICATE_SAME_ACCESS)) { - SSF_LOG(kLogError) - << "session[process]: duplicate write side of named pipe <" << pipe_name - << "> failed"; + SSF_LOG(kLogError) << "session[shell]: duplicate write side of named pipe <" + << pipe_name << "> failed"; ec.assign(::error::broken_pipe, ::error::get_ssf_category()); goto cleanup; } diff --git a/src/services/sockets_to_fibers/config.cpp b/src/services/sockets_to_fibers/config.cpp new file mode 100644 index 00000000..02a22b54 --- /dev/null +++ b/src/services/sockets_to_fibers/config.cpp @@ -0,0 +1,15 @@ +#include "services/sockets_to_fibers/config.h" + +namespace ssf { +namespace services { +namespace sockets_to_fibers { + +Config::Config() : BaseServiceConfig(true), gateway_ports_(false) {} + +Config::Config(const Config& stream_listener) + : BaseServiceConfig(stream_listener.enabled()), + gateway_ports_(stream_listener.gateway_ports_) {} + +} // sockets_to_fibers +} // services +} // ssf \ No newline at end of file diff --git a/src/services/sockets_to_fibers/config.h b/src/services/sockets_to_fibers/config.h new file mode 100644 index 00000000..fadc8dc0 --- /dev/null +++ b/src/services/sockets_to_fibers/config.h @@ -0,0 +1,28 @@ +#ifndef SSF_SERVICES_SOCKETS_TO_FIBERS_CONFIG_H_ +#define SSF_SERVICES_SOCKETS_TO_FIBERS_CONFIG_H_ + +#include "services/base_service_config.h" + +namespace ssf { +namespace services { +namespace sockets_to_fibers { + +class Config : public BaseServiceConfig { + public: + Config(); + Config(const Config& stream_listener); + + inline bool gateway_ports() const { return gateway_ports_; } + inline void set_gateway_ports(bool gateway_ports) { + gateway_ports_ = gateway_ports; + } + + private: + bool gateway_ports_; +}; + +} // sockets_to_fibers +} // services +} // ssf + +#endif // SSF_SERVICES_SOCKETS_TO_FIBERS_CONFIG_H_ \ No newline at end of file diff --git a/src/services/sockets_to_fibers/sockets_to_fibers.h b/src/services/sockets_to_fibers/sockets_to_fibers.h index f567c0d7..f0fa7c5d 100644 --- a/src/services/sockets_to_fibers/sockets_to_fibers.h +++ b/src/services/sockets_to_fibers/sockets_to_fibers.h @@ -15,11 +15,16 @@ #include "core/factories/service_factory.h" #include "services/admin/requests/create_service_request.h" +#include "services/sockets_to_fibers/config.h" namespace ssf { namespace services { namespace sockets_to_fibers { +// stream_listener microservice +// Listen to new connection on TCP endpoint (local_addr, local_port). Each +// connection will create a new fiber connected to the remote_port and forward +// I/O from/to TCP socket template class SocketsToFibers : public BaseService { public: @@ -36,43 +41,83 @@ class SocketsToFibers : public BaseService { typedef boost::asio::ip::tcp::acceptor socket_acceptor; public: + enum { factory_id = 4 }; + + public: + SocketsToFibers() = delete; + SocketsToFibers(const SocketsToFibers&) = delete; + + public: + // Factory method for stream_listener microservice + // @param io_service + // @param fiber_demux + // @param parameters microservice configuration parameters + // @param gateway_ports true to interpret local_addr parameters. Default + // behavior will set local_addr to 127.0.0.1 + // @returns Microservice or nullptr if an error occured + // + // parameters format: + // { + // "local_addr": IP_ADDR|*|"" + // "local_port": TCP_PORT + // "remote_port": FIBER_PORT + // } static SocketsToFibersPtr Create(boost::asio::io_service& io_service, - demux& fiber_demux, Parameters parameters) { - if (!parameters.count("local_port") || !parameters.count("remote_port")) { + demux& fiber_demux, Parameters parameters, + bool gateway_ports) { + if (!parameters.count("local_addr") || !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 }; + std::string local_addr("127.0.0.1"); + if (gateway_ports && parameters.count("local_addr") && + !parameters["local_addr"].empty()) { + if (parameters["local_addr"] == "*") { + local_addr = "0.0.0.0"; + } else { + local_addr = parameters["local_addr"]; + } + } + return std::shared_ptr( + new SocketsToFibers(io_service, fiber_demux, local_addr, + (uint16_t)std::stoul(parameters["local_port"]), + std::stoul(parameters["remote_port"]))); + } static void RegisterToServiceFactory( - std::shared_ptr> p_factory) { - p_factory->RegisterServiceCreator(factory_id, &SocketsToFibers::Create); - } + std::shared_ptr> p_factory, const Config& config) { + if (!config.enabled()) { + // service factory is not enabled + return; + } - virtual void start(boost::system::error_code& ec); - virtual void stop(boost::system::error_code& ec); - virtual uint32_t service_type_id(); + p_factory->RegisterServiceCreator( + factory_id, boost::bind(&SocketsToFibers::Create, _1, _2, _3, + config.gateway_ports())); + } static ssf::services::admin::CreateServiceRequest GetCreateRequest( - uint16_t local_port, remote_port_type remote_port) { + const std::string& local_addr, uint16_t local_port, + remote_port_type remote_port) { ssf::services::admin::CreateServiceRequest create(factory_id); + create.add_parameter("local_addr", local_addr); create.add_parameter("local_port", std::to_string(local_port)); create.add_parameter("remote_port", std::to_string(remote_port)); return create; } + public: + void start(boost::system::error_code& ec) override; + void stop(boost::system::error_code& ec) override; + uint32_t service_type_id() override; + private: SocketsToFibers(boost::asio::io_service& io_service, demux& fiber_demux, - uint16_t local, remote_port_type remote_port); + const std::string& local_addr, uint16_t local_port, + remote_port_type remote_port); - private: void StartAcceptSockets(); void SocketAcceptHandler(const boost::system::error_code& ec); @@ -89,6 +134,8 @@ class SocketsToFibers : public BaseService { return std::static_pointer_cast(this->shared_from_this()); } + private: + std::string local_addr_; uint16_t local_port_; remote_port_type remote_port_; socket_acceptor socket_acceptor_; diff --git a/src/services/sockets_to_fibers/sockets_to_fibers.ipp b/src/services/sockets_to_fibers/sockets_to_fibers.ipp index a2b9f375..095037e5 100644 --- a/src/services/sockets_to_fibers/sockets_to_fibers.ipp +++ b/src/services/sockets_to_fibers/sockets_to_fibers.ipp @@ -10,9 +10,12 @@ namespace sockets_to_fibers { template SocketsToFibers::SocketsToFibers(boost::asio::io_service& io_service, - demux& fiber_demux, uint16_t local_port, + demux& fiber_demux, + const std::string& local_addr, + uint16_t local_port, remote_port_type remote_port) : ssf::BaseService::BaseService(io_service, fiber_demux), + local_addr_(local_addr), local_port_(local_port), remote_port_(remote_port), socket_acceptor_(io_service), @@ -21,19 +24,18 @@ SocketsToFibers::SocketsToFibers(boost::asio::io_service& io_service, template void SocketsToFibers::start(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) - << "service[sockets to fibers]: starting relay on local port tcp " - << local_port_; + SSF_LOG(kLogInfo) << "microservice[stream_listener]: start forwarding TCP <" + << local_addr_ << ":" << local_port_ << "> to fiber port " + << remote_port_; boost::asio::ip::tcp::resolver resolver(socket_.get_io_service()); - boost::asio::ip::tcp::resolver::query query( - boost::asio::ip::tcp::v4(), "localhost", std::to_string(local_port_)); + boost::asio::ip::tcp::resolver::query query(local_addr_, + std::to_string(local_port_)); auto ep_it = resolver.resolve(query, ec); if (ec) { - SSF_LOG(kLogError) - << "service[sockets to fibers]: could not resolve query "; + SSF_LOG(kLogError) << "microservice[stream_listener]: could not resolve " + "query <" << local_addr_ << ":" << local_port_ << ">"; return; } @@ -46,25 +48,30 @@ void SocketsToFibers::start(boost::system::error_code& ec) { return; } + /** + // TODO: reuse_address = legit socket option? boost::asio::socket_base::reuse_address option(true); socket_acceptor_.set_option(option, ec); if (ec) { - SSF_LOG(kLogError) - << "service[sockets to fibers]: could not set reuse address option"; + SSF_LOG(kLogError) << "microservice[stream_listener]: could not set " + "reuse address option"; socket_acceptor_.close(close_ec); return; } + */ socket_acceptor_.bind(endpoint, ec); if (ec) { - SSF_LOG(kLogError) << "service[sockets to fibers]: could not bind acceptor"; + SSF_LOG(kLogError) + << "microservice[stream_listener]: could not bind acceptor"; socket_acceptor_.close(close_ec); return; } socket_acceptor_.listen(boost::asio::socket_base::max_connections, ec); if (ec) { - SSF_LOG(kLogError) << "service[sockets to fibers]: could not listen"; + SSF_LOG(kLogError) + << "microservice[stream_listener]: could not listen new connections"; socket_acceptor_.close(close_ec); return; } @@ -74,10 +81,10 @@ void SocketsToFibers::start(boost::system::error_code& ec) { template void SocketsToFibers::stop(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "service[sockets to fibers]: stopping"; + SSF_LOG(kLogInfo) << "microservice[stream_listener]: stopping"; socket_acceptor_.close(ec); if (ec) { - SSF_LOG(kLogDebug) << "service[sockets to fibers]: " << ec.message(); + SSF_LOG(kLogDebug) << "microservice[stream_listener]: " << ec.message(); } manager_.stop_all(); } @@ -89,7 +96,7 @@ uint32_t SocketsToFibers::service_type_id() { template void SocketsToFibers::StartAcceptSockets() { - SSF_LOG(kLogTrace) << "service[sockets to fibers]: accepting new clients"; + SSF_LOG(kLogTrace) << "microservice[stream_listener]: accepting new clients"; if (!socket_acceptor_.is_open()) { return; @@ -103,7 +110,7 @@ void SocketsToFibers::StartAcceptSockets() { template void SocketsToFibers::SocketAcceptHandler( const boost::system::error_code& ec) { - SSF_LOG(kLogTrace) << "service[sockets to fibers]: accept handler"; + SSF_LOG(kLogTrace) << "microservice[stream_listener]: accept handler"; if (!socket_acceptor_.is_open()) { return; @@ -119,7 +126,7 @@ void SocketsToFibers::SocketAcceptHandler( template void SocketsToFibers::FiberConnectHandler( const boost::system::error_code& ec) { - SSF_LOG(kLogTrace) << "service[sockets to fibers]: connect handler"; + SSF_LOG(kLogTrace) << "microservice[stream_listener]: connect handler"; if (!ec) { auto session = SessionForwarder::create( diff --git a/src/services/socks/config.cpp b/src/services/socks/config.cpp new file mode 100644 index 00000000..d7878458 --- /dev/null +++ b/src/services/socks/config.cpp @@ -0,0 +1,14 @@ +#include "services/socks/config.h" + +namespace ssf { +namespace services { +namespace socks { + +Config::Config() : BaseServiceConfig(true) {} + +Config::Config(const Config& process_service) + : BaseServiceConfig(process_service.enabled()) {} + +} // socks +} // services +} // ssf \ No newline at end of file diff --git a/src/services/socks/config.h b/src/services/socks/config.h new file mode 100644 index 00000000..971dff6a --- /dev/null +++ b/src/services/socks/config.h @@ -0,0 +1,22 @@ +#ifndef SSF_SERVICES_SOCKS_CONFIG_H_ +#define SSF_SERVICES_SOCKS_CONFIG_H_ + +#include + +#include "services/base_service_config.h" + +namespace ssf { +namespace services { +namespace socks { + +class Config : public BaseServiceConfig { + public: + Config(); + Config(const Config& process_service); +}; + +} // socks +} // services +} // ssf + +#endif // SSF_SERVICES_SOCKS_CONFIG_H_ \ No newline at end of file diff --git a/src/services/socks/socks_server.h b/src/services/socks/socks_server.h index 387d02cb..f8a728bb 100644 --- a/src/services/socks/socks_server.h +++ b/src/services/socks/socks_server.h @@ -19,6 +19,8 @@ #include "services/admin/requests/create_service_request.h" +#include "services/socks/config.h" + namespace ssf { namespace services { namespace socks { @@ -36,6 +38,10 @@ class SocksServer : public BaseService { typedef typename ssf::BaseService::fiber_acceptor fiber_acceptor; typedef typename ssf::BaseService::endpoint endpoint; + public: + // SSF service ID for identification in the service factory + enum { factory_id = 2 }; + public: SocksServer(const SocksServer&) = delete; SocksServer& operator=(const SocksServer&) = delete; @@ -51,24 +57,17 @@ class SocksServer : public BaseService { } } - // 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) { + std::shared_ptr> p_factory, const Config& config) { + if (!config.enabled()) { + // service factory is not enabled + return; + } + 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) { @@ -78,11 +77,20 @@ class SocksServer : public BaseService { return std::move(create); } + public: + /// Start the service instance + void start(boost::system::error_code& ec) override; + + /// Stop the service instance + void stop(boost::system::error_code& ec) override; + + /// Return the type of the service + uint32_t service_type_id() override; + private: 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(); @@ -99,8 +107,6 @@ class SocksServer : public BaseService { private: fiber_acceptor fiber_acceptor_; - - private: SessionManager session_manager_; fiber new_connection_; boost::system::error_code init_ec_; diff --git a/src/services/socks/socks_server.ipp b/src/services/socks/socks_server.ipp index 1ad2588d..c00ca18f 100644 --- a/src/services/socks/socks_server.ipp +++ b/src/services/socks/socks_server.ipp @@ -34,7 +34,7 @@ SocksServer::SocksServer(boost::asio::io_service& io_service, template void SocksServer::start(boost::system::error_code& ec) { - SSF_LOG(kLogInfo) << "service[socks]: starting server on port " + SSF_LOG(kLogInfo) << "microservice[socks]: start server on fiber port " << local_port_; ec = init_ec_; @@ -47,7 +47,7 @@ template void SocksServer::stop(boost::system::error_code& ec) { ec.assign(boost::system::errc::success, boost::system::system_category()); - SSF_LOG(kLogInfo) << "service[socks]: stopping server"; + SSF_LOG(kLogInfo) << "microservice[socks]: stopping server"; this->HandleStop(); } @@ -64,52 +64,55 @@ void SocksServer::StartAccept() { template void SocksServer::HandleAccept(const boost::system::error_code& ec) { - SSF_LOG(kLogTrace) << "service[socks]: HandleAccept"; + SSF_LOG(kLogTrace) << "microservice[socks]: HandleAccept"; if (!fiber_acceptor_.is_open()) { return; } if (ec) { - SSF_LOG(kLogError) << "service[socks]: error accepting new connection: " - << ec.message() << " " << ec.value(); + SSF_LOG(kLogError) + << "microservice[socks]: error accepting new connection: " + << ec.message() << " " << 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) { - SSF_LOG(kLogError) << "service[socks]: error reading protocol version: " - << ec.message() << " " << ec.value(); - fiber fib = std::move(this->new_connection_); - this->StartAccept(); - } else if (p_version->Number() == 4) { - SSF_LOG(kLogTrace) << "service[socks]: 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) { - SSF_LOG(kLogTrace) << "service[socks]: 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 { - SSF_LOG(kLogError) << "service[socks]: protocol not supported yet: " - << p_version->Number(); - this->new_connection_.close(); - fiber fib = std::move(this->new_connection_); - this->StartAccept(); - } - }; + auto start_handler = + [this, self, p_version](boost::system::error_code ec, std::size_t) { + if (ec) { + SSF_LOG(kLogError) + << "microservice[socks]: error reading protocol version: " + << ec.message() << " " << ec.value(); + fiber fib = std::move(this->new_connection_); + this->StartAccept(); + } else if (p_version->Number() == 4) { + SSF_LOG(kLogTrace) << "microservice[socks]: 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) { + SSF_LOG(kLogTrace) << "microservice[socks]: 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 { + SSF_LOG(kLogError) + << "microservice[socks]: protocol not supported yet: " + << 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); diff --git a/src/services/user_services/option_parser.cpp b/src/services/user_services/option_parser.cpp new file mode 100644 index 00000000..9171430f --- /dev/null +++ b/src/services/user_services/option_parser.cpp @@ -0,0 +1,113 @@ +#ifndef BOOST_SPIRIT_USE_PHOENIX_V3 +#define BOOST_SPIRIT_USE_PHOENIX_V3 1 +#endif +#include +#include +#include +#include +#include + +#include "common/error/error.h" + +#include "services/user_services/option_parser.h" + +namespace ssf { +namespace services { + +Endpoint::Endpoint() : addr(""), port(0) {} + +ForwardOptions::ForwardOptions() : from(), to() {} + +ForwardOptions OptionParser::ParseForwardOptions( + const std::string& option, boost::system::error_code& ec) { + using boost::spirit::qi::ushort_; + using boost::spirit::qi::alnum; + using boost::spirit::qi::char_; + using boost::spirit::qi::lit; + using boost::spirit::qi::rule; + typedef std::string::const_iterator str_it; + + ForwardOptions forward_options; + + rule local_addr_pattern = *char_("0-9a-zA-Z.-"); + rule target_pattern = +char_("0-9a-zA-Z.-"); + + str_it begin = option.begin(), end = option.end(); + + // basic format + if (boost::spirit::qi::parse( + begin, end, ushort_ >> ":" >> target_pattern >> ":" >> ushort_, + forward_options.from.port, forward_options.to.addr, + forward_options.to.port) && + begin == end) { + ec.assign(::error::success, ::error::get_ssf_category()); + return forward_options; + } + + begin = option.begin(), end = option.end(); + // extended format + if (boost::spirit::qi::parse( + begin, end, local_addr_pattern >> ":" >> ushort_ >> ":" >> + target_pattern >> ":" >> ushort_, + forward_options.from.addr, forward_options.from.port, + forward_options.to.addr, forward_options.to.port) && + begin == end) { + if (forward_options.from.addr.empty()) { + // empty value means all network interfaces + forward_options.from.addr = "*"; + } + + // not found + ec.assign(::error::success, ::error::get_ssf_category()); + return forward_options; + } + + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + + return forward_options; +} + +Endpoint OptionParser::ParseListeningOption(const std::string& option, + boost::system::error_code& ec) { + using boost::spirit::qi::ushort_; + using boost::spirit::qi::alnum; + using boost::spirit::qi::char_; + using boost::spirit::qi::lit; + using boost::spirit::qi::rule; + typedef std::string::const_iterator str_it; + + Endpoint listening_option; + + rule local_addr_pattern = *char_("0-9a-zA-Z.-"); + + str_it begin = option.begin(), end = option.end(); + + // basic format + if (boost::spirit::qi::parse(begin, end, ushort_, listening_option.port) && + begin == end) { + listening_option.addr = ""; + ec.assign(::error::success, ::error::get_ssf_category()); + return listening_option; + } + + begin = option.begin(), end = option.end(); + // extended format + if (boost::spirit::qi::parse(begin, end, local_addr_pattern >> ":" >> ushort_, + listening_option.addr, listening_option.port) && + begin == end) { + if (listening_option.addr.empty()) { + // empty value means all network interfaces + listening_option.addr = "*"; + } + ec.assign(::error::success, ::error::get_ssf_category()); + return listening_option; + } + + // not found + ec.assign(::error::invalid_argument, ::error::get_ssf_category()); + + return listening_option; +} + +} // services +} // ssf diff --git a/src/services/user_services/option_parser.h b/src/services/user_services/option_parser.h new file mode 100644 index 00000000..ea75d2ac --- /dev/null +++ b/src/services/user_services/option_parser.h @@ -0,0 +1,46 @@ +#ifndef SSF_SERVICES_OPTION_PARSER_H_ +#define SSF_SERVICES_OPTION_PARSER_H_ + +#include + +#include + +#include + +namespace ssf { +namespace services { + +struct Endpoint { + Endpoint(); + + std::string addr; + uint16_t port; +}; + +struct ForwardOptions { + ForwardOptions(); + Endpoint from; + Endpoint to; +}; + +class OptionParser { + public: + // Parse forward option + // Expected format: [[loc_addr]:]loc_port:rem_addr:rem_port + // If loc_addr is empty on purpose, set its value as "*" + // Set ec if parsing failed + static ForwardOptions ParseForwardOptions(const std::string& option, + boost::system::error_code& ec); + + // Parse listening option + // Expected format: [[loc_addr]:]loc_port + // If loc_addr is empty on purpose, set its value as "*" + // Set ec if parsing failed + static Endpoint ParseListeningOption(const std::string& option, + boost::system::error_code& ec); +}; + +} // services +} // ssf + +#endif // SSF_SERVICES_OPTION_PARSER_H_ \ No newline at end of file diff --git a/src/services/user_services/port_forwarding.h b/src/services/user_services/port_forwarding.h index 1f438791..1c64df9c 100644 --- a/src/services/user_services/port_forwarding.h +++ b/src/services/user_services/port_forwarding.h @@ -8,17 +8,12 @@ #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/option_parser.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" @@ -39,9 +34,10 @@ class PortForwarding : public BaseUserService { typedef boost::asio::fiber::detail::fiber_id::local_port_type local_port_type; private: - PortForwarding(uint16_t local_port, std::string remote_addr, - uint16_t remote_port) - : local_port_(local_port), + PortForwarding(const std::string& local_addr, uint16_t local_port, + const std::string& remote_addr, uint16_t remote_port) + : local_addr_(local_addr), + local_port_(local_port), remote_addr_(remote_addr), remote_port_(remote_port), remoteServiceId_(0), @@ -50,40 +46,31 @@ class PortForwarding : public BaseUserService { } public: - static std::string GetFullParseName() { return "forward,L"; } + static std::string GetFullParseName() { return "tcp-forward,L"; } - static std::string GetParseName() { return "forward"; } + static std::string GetParseName() { return "tcp-forward"; } - static std::string GetValueName() { return "loc_port:dest_ip:dest_port"; } + static std::string GetValueName() { + return "[[loc_ip]:]loc_port:dest_ip:dest_port"; + } static std::string GetParseDesc() { - return "Forward local port on given target from server"; + return "Forward TCP client [[loc_ip]:]port to dest_ip:dest_port from " + "server"; } - 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 PortForwarding(listening_port, target_addr, target_port)); - } else { + auto forward_options = OptionParser::ParseForwardOptions(line, ec); + + if (ec) { ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return std::shared_ptr(nullptr); } + + return std::shared_ptr( + new PortForwarding(forward_options.from.addr, forward_options.from.port, + forward_options.to.addr, forward_options.to.port)); } static void RegisterToServiceOptionFactory() { @@ -92,23 +79,26 @@ class PortForwarding : public BaseUserService { &PortForwarding::CreateServiceOptions); } - virtual std::string GetName() { return "forward"; } + public: + virtual ~PortForwarding() {} - virtual std::vector> - GetRemoteServiceCreateVector() { + std::string GetName() override { return "tcp-forward"; } + + std::vector> GetRemoteServiceCreateVector() + override { std::vector> result; - services::admin::CreateServiceRequest r_socks( + services::admin::CreateServiceRequest r_port_forwarding( services::fibers_to_sockets::FibersToSockets::GetCreateRequest( relay_fiber_port_, remote_addr_, remote_port_)); - result.push_back(r_socks); + result.push_back(r_port_forwarding); return result; } - virtual std::vector> - GetRemoteServiceStopVector(Demux& demux) { + std::vector> GetRemoteServiceStopVector( + Demux& demux) override { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -120,10 +110,10 @@ class PortForwarding : public BaseUserService { return result; } - virtual bool StartLocalServices(Demux& demux) { + bool StartLocalServices(Demux& demux) override { services::admin::CreateServiceRequest l_forward( services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - local_port_, relay_fiber_port_)); + local_addr_, local_port_, relay_fiber_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); @@ -131,14 +121,14 @@ class PortForwarding : public BaseUserService { localServiceId_ = p_service_factory->CreateRunNewService( l_forward.service_id(), l_forward.parameters(), ec); if (ec) { - SSF_LOG(kLogError) << "user_service[forward]: " + SSF_LOG(kLogError) << "user_service[tcp-forward]: " << "local_service[sockets to fibers]: start failed: " << ec.message(); } return !ec; } - virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { + uint32_t CheckRemoteServiceStatus(Demux& demux) override { services::admin::CreateServiceRequest r_socks( services::fibers_to_sockets::FibersToSockets::GetCreateRequest( relay_fiber_port_, remote_addr_, remote_port_)); @@ -150,7 +140,7 @@ class PortForwarding : public BaseUserService { return status; } - virtual void StopLocalServices(Demux& demux) { + void StopLocalServices(Demux& demux) override { auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); @@ -174,6 +164,7 @@ class PortForwarding : public BaseUserService { } } + std::string local_addr_; uint16_t local_port_; std::string remote_addr_; uint16_t remote_port_; diff --git a/src/services/user_services/process.h b/src/services/user_services/process.h index d3ec07f7..525ec742 100644 --- a/src/services/user_services/process.h +++ b/src/services/user_services/process.h @@ -9,6 +9,8 @@ #include "core/factories/service_option_factory.h" +#include "services/user_services/option_parser.h" + #include "services/admin/requests/create_service_request.h" #include "services/admin/requests/stop_service_request.h" #include "services/process/server.h" @@ -20,34 +22,29 @@ namespace services { template class Process : public BaseUserService { - private: - Process(uint16_t local_port) - : local_port_(local_port), remoteServiceId_(0), localServiceId_(0) {} - public: - static std::string GetFullParseName() { return "process,X"; } + static std::string GetFullParseName() { return "shell,X"; } - static std::string GetParseName() { return "process"; } + static std::string GetParseName() { return "shell"; } - static std::string GetValueName() { return "local_port"; } + static std::string GetValueName() { return "[[loc_ip]:]loc_port"; } static std::string GetParseDesc() { return "Open a port on the client side, each connection to that port " - "creates a process with I/O forwarded to/from the server side"; + "launches a shell server side with I/O forwarded to/from the socket" + " (shell microservice must be enabled server side prior to use)"; } static std::shared_ptr CreateServiceOptions( std::string line, boost::system::error_code& ec) { - try { - uint16_t port = (uint16_t)std::stoul(line); - return std::shared_ptr(new Process(port)); - } catch (const std::invalid_argument&) { + auto listener = OptionParser::ParseListeningOption(line, ec); + + if (ec) { ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return std::shared_ptr(nullptr); - } catch (const std::out_of_range&) { - ec.assign(::error::out_of_range, ::error::get_ssf_category()); - return std::shared_ptr(nullptr); } + + return std::shared_ptr(new Process(listener.addr, listener.port)); } static void RegisterToServiceOptionFactory() { @@ -57,8 +54,12 @@ class Process : public BaseUserService { } public: - std::vector> - GetRemoteServiceCreateVector() { + virtual ~Process() {} + + std::string GetName() override { return "shell"; }; + + std::vector> GetRemoteServiceCreateVector() + override { std::vector> result; services::admin::CreateServiceRequest r_process_server( @@ -70,7 +71,7 @@ class Process : public BaseUserService { }; std::vector> GetRemoteServiceStopVector( - Demux& demux) { + Demux& demux) override { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -82,7 +83,7 @@ class Process : public BaseUserService { return result; }; - uint32_t CheckRemoteServiceStatus(Demux& demux) { + uint32_t CheckRemoteServiceStatus(Demux& demux) override { services::admin::CreateServiceRequest r_process_server( services::process::Server::GetCreateRequest(local_port_)); @@ -93,12 +94,10 @@ class Process : public BaseUserService { GetRemoteServiceId(demux)); }; - std::string GetName() { return "process"; }; - - bool StartLocalServices(Demux& demux) { + bool StartLocalServices(Demux& demux) override { services::admin::CreateServiceRequest l_forward( services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - local_port_, local_port_)); + local_addr_, local_port_, local_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); @@ -106,20 +105,27 @@ class Process : public BaseUserService { localServiceId_ = p_service_factory->CreateRunNewService( l_forward.service_id(), l_forward.parameters(), ec); if (ec) { - SSF_LOG(kLogError) << "user_service[process]: " - << "local_service[sockets to fibers]: start failed: " - << ec.message(); + SSF_LOG(kLogError) + << "user_service[shell]: " + << "local microservice[stream_listener]: start failed: " + << ec.message(); } return !ec; }; - void StopLocalServices(Demux& demux) { + void StopLocalServices(Demux& demux) override { auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); }; private: + Process(const std::string& local_addr, uint16_t local_port) + : local_addr_(local_addr), + local_port_(local_port), + remoteServiceId_(0), + localServiceId_(0) {} + uint32_t GetRemoteServiceId(Demux& demux) { if (remoteServiceId_) { return remoteServiceId_; @@ -139,6 +145,7 @@ class Process : public BaseUserService { } private: + std::string local_addr_; uint16_t local_port_; uint32_t remoteServiceId_; uint32_t localServiceId_; diff --git a/src/services/user_services/remote_port_forwarding.h b/src/services/user_services/remote_port_forwarding.h index 2149e046..286d006f 100644 --- a/src/services/user_services/remote_port_forwarding.h +++ b/src/services/user_services/remote_port_forwarding.h @@ -6,19 +6,13 @@ #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/option_parser.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" @@ -38,52 +32,32 @@ class RemotePortForwarding : public BaseUserService { private: typedef boost::asio::fiber::detail::fiber_id::local_port_type local_port_type; - private: - RemotePortForwarding(uint16_t remote_port, std::string local_addr, - uint16_t local_port) - : local_port_(local_port), - local_addr_(local_addr), - remote_port_(remote_port), - remoteServiceId_(0), - localServiceId_(0) { - relay_fiber_port_ = remote_port_; - } - public: - static std::string GetFullParseName() { return "remote,R"; } + static std::string GetFullParseName() { return "tcp-remote-forward,R"; } - static std::string GetParseName() { return "remote"; } + static std::string GetParseName() { return "tcp-remote-forward"; } - static std::string GetValueName() { return "rem_port:dest_ip:dest_port"; } + static std::string GetValueName() { + return "[[rem_ip]:]rem_port:dest_ip:dest_port"; + } static std::string GetParseDesc() { - return "Forward remote port on given target from client"; + return "Forward TCP server [[rem_ip]:]rem_port to target dest_ip:dest_port " + "from client"; } - 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 RemotePortForwarding(listening_port, target_addr, target_port)); - } else { + auto forward_options = OptionParser::ParseForwardOptions(line, ec); + + if (ec) { ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return std::shared_ptr(nullptr); } + + return std::shared_ptr(new RemotePortForwarding( + forward_options.from.addr, forward_options.from.port, + forward_options.to.addr, forward_options.to.port)); } static void RegisterToServiceOptionFactory() { @@ -92,23 +66,26 @@ class RemotePortForwarding : public BaseUserService { &RemotePortForwarding::CreateServiceOptions); } - virtual std::string GetName() { return "remote"; } + public: + virtual ~RemotePortForwarding() {} - virtual std::vector> - GetRemoteServiceCreateVector() { + std::string GetName() override { return "tcp-remote-forward"; } + + std::vector> GetRemoteServiceCreateVector() + override { std::vector> result; services::admin::CreateServiceRequest r_forward( services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - remote_port_, relay_fiber_port_)); + remote_addr_, remote_port_, relay_fiber_port_)); result.push_back(r_forward); return result; } - virtual std::vector> - GetRemoteServiceStopVector(Demux& demux) { + std::vector> GetRemoteServiceStopVector( + Demux& demux) override { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -120,7 +97,7 @@ class RemotePortForwarding : public BaseUserService { return result; } - virtual bool StartLocalServices(Demux& demux) { + bool StartLocalServices(Demux& demux) override { services::admin::CreateServiceRequest l_forward( services::fibers_to_sockets::FibersToSockets::GetCreateRequest( relay_fiber_port_, local_addr_, local_port_)); @@ -131,17 +108,17 @@ class RemotePortForwarding : public BaseUserService { localServiceId_ = p_service_factory->CreateRunNewService( l_forward.service_id(), l_forward.parameters(), ec); if (ec) { - SSF_LOG(kLogError) << "user_service[forward]: " + SSF_LOG(kLogError) << "user_service[tcp-remote-forward]: " << "local_service[fibers to sockets]: start failed: " << ec.message(); } return !ec; } - virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { + uint32_t CheckRemoteServiceStatus(Demux& demux) override { services::admin::CreateServiceRequest r_forward( services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - remote_port_, relay_fiber_port_)); + remote_addr_, remote_port_, relay_fiber_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); auto status = p_service_factory->GetStatus(r_forward.service_id(), @@ -151,20 +128,31 @@ class RemotePortForwarding : public BaseUserService { return status; } - virtual void StopLocalServices(Demux& demux) { + void StopLocalServices(Demux& demux) override { auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); } private: + RemotePortForwarding(const std::string& remote_addr, uint16_t remote_port, + const std::string& local_addr, uint16_t local_port) + : local_port_(local_port), + local_addr_(local_addr), + remote_port_(remote_port), + remote_addr_(remote_addr), + remoteServiceId_(0), + localServiceId_(0) { + relay_fiber_port_ = remote_port_; + } + 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_)); + remote_addr_, remote_port_, relay_fiber_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); @@ -178,6 +166,7 @@ class RemotePortForwarding : public BaseUserService { uint16_t local_port_; std::string local_addr_; uint16_t remote_port_; + std::string remote_addr_; local_port_type relay_fiber_port_; diff --git a/src/services/user_services/remote_process.h b/src/services/user_services/remote_process.h index 77c00bd9..f4d3c8d3 100644 --- a/src/services/user_services/remote_process.h +++ b/src/services/user_services/remote_process.h @@ -9,6 +9,8 @@ #include "core/factories/service_option_factory.h" +#include "services/user_services/option_parser.h" + #include "services/admin/requests/create_service_request.h" #include "services/admin/requests/stop_service_request.h" #include "services/process/server.h" @@ -20,34 +22,30 @@ namespace services { template class RemoteProcess : public BaseUserService { - private: - RemoteProcess(uint16_t remote_port) - : remote_port_(remote_port), remoteServiceId_(0), localServiceId_(0) {} - public: - static std::string GetFullParseName() { return "remote_process,Y"; } + static std::string GetFullParseName() { return "remote-shell,Y"; } - static std::string GetParseName() { return "remote_process"; } + static std::string GetParseName() { return "remote-shell"; } - static std::string GetValueName() { return "remote_port"; } + static std::string GetValueName() { return "[[rem_ip]:]rem_port"; } static std::string GetParseDesc() { - return "Open a port on the server side, each connection to that port " - "creates a process with I/O forwarded to/from the client side"; + return "Open a port server side, each connection to that port " + "launches a shell client side with I/O forwarded from/to the socket" + " (shell microservice must be enabled client side prior to use)"; } static std::shared_ptr CreateServiceOptions( std::string line, boost::system::error_code& ec) { - try { - uint16_t port = (uint16_t)std::stoul(line); - return std::shared_ptr(new RemoteProcess(port)); - } catch (const std::invalid_argument&) { + auto listener = OptionParser::ParseListeningOption(line, ec); + + if (ec) { ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return std::shared_ptr(nullptr); - } catch (const std::out_of_range&) { - ec.assign(::error::out_of_range, ::error::get_ssf_category()); - return std::shared_ptr(nullptr); } + + return std::shared_ptr( + new RemoteProcess(listener.addr, listener.port)); } static void RegisterToServiceOptionFactory() { @@ -57,13 +55,17 @@ class RemoteProcess : public BaseUserService { } public: - std::vector> - GetRemoteServiceCreateVector() { + virtual ~RemoteProcess() {} + + std::string GetName() override { return "remote-shell"; }; + + std::vector> GetRemoteServiceCreateVector() + override { std::vector> result; services::admin::CreateServiceRequest r_forward( services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - remote_port_, remote_port_)); + remote_addr_, remote_port_, remote_port_)); result.push_back(r_forward); @@ -71,7 +73,7 @@ class RemoteProcess : public BaseUserService { }; std::vector> GetRemoteServiceStopVector( - Demux& demux) { + Demux& demux) override { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -83,10 +85,10 @@ class RemoteProcess : public BaseUserService { return result; }; - uint32_t CheckRemoteServiceStatus(Demux& demux) { + uint32_t CheckRemoteServiceStatus(Demux& demux) override { services::admin::CreateServiceRequest r_forward( services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - remote_port_, remote_port_)); + remote_addr_, remote_port_, remote_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); @@ -95,9 +97,7 @@ class RemoteProcess : public BaseUserService { GetRemoteServiceId(demux)); }; - std::string GetName() { return "remote_process"; }; - - bool StartLocalServices(Demux& demux) { + bool StartLocalServices(Demux& demux) override { services::admin::CreateServiceRequest l_process_server( services::process::Server::GetCreateRequest(remote_port_)); @@ -107,27 +107,33 @@ class RemoteProcess : public BaseUserService { localServiceId_ = p_service_factory->CreateRunNewService( l_process_server.service_id(), l_process_server.parameters(), ec); if (ec) { - SSF_LOG(kLogError) << "user_service[remote process]: " - << "local_service[process]: start failed: " + SSF_LOG(kLogError) << "user_service[remote-shell]: " + << "local microservice[process]: start failed: " << ec.message(); } return !ec; }; - void StopLocalServices(Demux& demux) { + void StopLocalServices(Demux& demux) override { auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); }; private: + RemoteProcess(const std::string& remote_addr, uint16_t remote_port) + : remote_addr_(remote_addr), + remote_port_(remote_port), + remoteServiceId_(0), + localServiceId_(0) {} + uint32_t GetRemoteServiceId(Demux& demux) { if (remoteServiceId_) { return remoteServiceId_; } else { services::admin::CreateServiceRequest r_forward( services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - remote_port_, remote_port_)); + remote_addr_, remote_port_, remote_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); @@ -141,6 +147,7 @@ class RemoteProcess : public BaseUserService { } private: + std::string remote_addr_; uint16_t remote_port_; uint32_t remoteServiceId_; uint32_t localServiceId_; diff --git a/src/services/user_services/remote_socks.h b/src/services/user_services/remote_socks.h index 6ad759a0..b3704235 100644 --- a/src/services/user_services/remote_socks.h +++ b/src/services/user_services/remote_socks.h @@ -11,6 +11,8 @@ #include "common/error/error.h" +#include "services/user_services/option_parser.h" + #include "services/admin/requests/create_service_request.h" #include "services/admin/requests/stop_service_request.h" #include "services/sockets_to_fibers/sockets_to_fibers.h" @@ -24,36 +26,29 @@ 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 GetFullParseName() { return "remote-socks,F"; } - static std::string GetParseName() { return "remote_socks"; } + static std::string GetParseName() { return "remote-socks"; } - static std::string GetValueName() { return "remote_port"; } + static std::string GetValueName() { return "[[rem_ip]:]rem_port"; } static std::string GetParseDesc() { - return "Run a proxy socks on local host"; + return "Run a SOCKS proxy on localhost accessible from server " + "[[rem_ip]:]rem_port"; } - 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&) { + auto listener = OptionParser::ParseListeningOption(line, ec); + + if (ec) { ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return std::shared_ptr(nullptr); - } catch (const std::out_of_range&) { - ec.assign(::error::out_of_range, ::error::get_ssf_category()); - return std::shared_ptr(nullptr); } + + return std::shared_ptr( + new RemoteSocks(listener.addr, listener.port)); } static void RegisterToServiceOptionFactory() { @@ -62,23 +57,26 @@ class RemoteSocks : public BaseUserService { &RemoteSocks::CreateServiceOptions); } - virtual std::string GetName() { return "remote_socks"; } + public: + virtual ~RemoteSocks() {} + + std::string GetName() override { return "remote-socks"; } - virtual std::vector> - GetRemoteServiceCreateVector() { + std::vector> GetRemoteServiceCreateVector() + override { std::vector> result; services::admin::CreateServiceRequest r_forward( services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - remote_port_, remote_port_)); + remote_addr_, remote_port_, remote_port_)); result.push_back(r_forward); return result; } - virtual std::vector> - GetRemoteServiceStopVector(Demux& demux) { + std::vector> GetRemoteServiceStopVector( + Demux& demux) override { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -90,7 +88,7 @@ class RemoteSocks : public BaseUserService { return result; } - virtual bool StartLocalServices(Demux& demux) { + bool StartLocalServices(Demux& demux) override { services::admin::CreateServiceRequest l_socks( services::socks::SocksServer::GetCreateRequest(remote_port_)); @@ -101,17 +99,17 @@ class RemoteSocks : public BaseUserService { l_socks.service_id(), l_socks.parameters(), ec); if (ec) { - SSF_LOG(kLogError) << "user_service[remote socks]: " - << "local_service[socks server]: start failed: " + SSF_LOG(kLogError) << "user_service[remote-socks]: " + << "local_service[socks]: start failed: " << ec.message(); } return !ec; } - virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { + uint32_t CheckRemoteServiceStatus(Demux& demux) override { services::admin::CreateServiceRequest r_forward( services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - remote_port_, remote_port_)); + remote_addr_, remote_port_, remote_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); auto status = p_service_factory->GetStatus(r_forward.service_id(), @@ -121,20 +119,26 @@ class RemoteSocks : public BaseUserService { return status; } - virtual void StopLocalServices(Demux& demux) { + void StopLocalServices(Demux& demux) override { auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(local_service_id_); } private: + RemoteSocks(const std::string& remote_addr, uint16_t remote_port) + : remote_addr_(remote_addr), + remote_port_(remote_port), + remote_service_id_(0), + local_service_id_(0) {} + 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_)); + remote_addr_, remote_port_, remote_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); @@ -145,6 +149,7 @@ class RemoteSocks : public BaseUserService { } } + std::string remote_addr_; uint16_t remote_port_; uint32_t remote_service_id_; uint32_t local_service_id_; diff --git a/src/services/user_services/socks.h b/src/services/user_services/socks.h index 7e6216bb..5252579d 100644 --- a/src/services/user_services/socks.h +++ b/src/services/user_services/socks.h @@ -16,6 +16,7 @@ #include "services/sockets_to_fibers/sockets_to_fibers.h" #include "services/socks/socks_server.h" #include "services/user_services/base_user_service.h" +#include "services/user_services/option_parser.h" #include "core/factories/service_option_factory.h" @@ -24,34 +25,28 @@ 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 GetValueName() { return "local_port"; } + static std::string GetValueName() { return "[[loc_ip]:]loc_port"; } static std::string GetParseDesc() { - return "Run a proxy socks on remote host"; + return "Run a SOCKS proxy on remote host accessible from client " + "[[loc_ip]:]loc_port"; } - 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&) { + auto listener = OptionParser::ParseListeningOption(line, ec); + + if (ec) { ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return std::shared_ptr(nullptr); - } catch (const std::out_of_range&) { - ec.assign(::error::out_of_range, ::error::get_ssf_category()); - return std::shared_ptr(nullptr); } + + return std::shared_ptr(new Socks(listener.addr, listener.port)); } static void RegisterToServiceOptionFactory() { @@ -60,10 +55,13 @@ class Socks : public BaseUserService { &Socks::CreateServiceOptions); } - virtual std::string GetName() { return "socks"; } + public: + virtual ~Socks() {} - virtual std::vector> - GetRemoteServiceCreateVector() { + std::string GetName() override { return "socks"; } + + std::vector> GetRemoteServiceCreateVector() + override { std::vector> result; services::admin::CreateServiceRequest r_socks( @@ -74,8 +72,8 @@ class Socks : public BaseUserService { return result; } - virtual std::vector> - GetRemoteServiceStopVector(Demux& demux) { + std::vector> GetRemoteServiceStopVector( + Demux& demux) override { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -87,10 +85,10 @@ class Socks : public BaseUserService { return result; } - virtual bool StartLocalServices(Demux& demux) { + bool StartLocalServices(Demux& demux) override { services::admin::CreateServiceRequest l_forward( services::sockets_to_fibers::SocketsToFibers::GetCreateRequest( - local_port_, local_port_)); + local_addr_, local_port_, local_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); @@ -106,7 +104,7 @@ class Socks : public BaseUserService { return !ec; } - virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { + uint32_t CheckRemoteServiceStatus(Demux& demux) override { services::admin::CreateServiceRequest r_socks( services::socks::SocksServer::GetCreateRequest(local_port_)); auto p_service_factory = @@ -117,13 +115,19 @@ class Socks : public BaseUserService { return status; } - virtual void StopLocalServices(Demux& demux) { + void StopLocalServices(Demux& demux) override { auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); } private: + Socks(const std::string& local_addr, uint16_t local_port) + : local_addr_(local_addr), + local_port_(local_port), + remoteServiceId_(0), + localServiceId_(0) {} + uint32_t GetRemoteServiceId(Demux& demux) { if (remoteServiceId_) { return remoteServiceId_; @@ -142,6 +146,7 @@ class Socks : public BaseUserService { } } + std::string local_addr_; uint16_t local_port_; uint32_t remoteServiceId_; uint32_t localServiceId_; diff --git a/src/services/user_services/udp_port_forwarding.h b/src/services/user_services/udp_port_forwarding.h index 51a6daa1..84423b85 100644 --- a/src/services/user_services/udp_port_forwarding.h +++ b/src/services/user_services/udp_port_forwarding.h @@ -6,19 +6,13 @@ #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/option_parser.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" @@ -38,52 +32,32 @@ class UdpPortForwarding : public BaseUserService { private: typedef boost::asio::fiber::detail::fiber_id::local_port_type local_port_type; - private: - UdpPortForwarding(uint16_t local_port, std::string remote_addr, - uint16_t remote_port) - : local_port_(local_port), - remote_addr_(remote_addr), - remote_port_(remote_port), - remoteServiceId_(0), - localServiceId_(0) { - relay_fiber_port_ = remote_port_ + (1 << 16); - } - public: - static std::string GetFullParseName() { return "udpforward,U"; } + static std::string GetFullParseName() { return "udp-forward,U"; } - static std::string GetParseName() { return "udpforward"; } + static std::string GetParseName() { return "udp-forward"; } - static std::string GetValueName() { return "local_port:dest_ip:dest_port"; } + static std::string GetValueName() { + return "[[loc_ip]:]loc_port:dest_ip:dest_port"; + } static std::string GetParseDesc() { - return "Forward UDP local port on given target from server"; + return "Forward UDP client [[loc_ip]:]loc_port to target dest_ip:dest_port " + "from server"; } - 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 UdpPortForwarding(listening_port, target_addr, target_port)); - } else { + auto forward_options = OptionParser::ParseForwardOptions(line, ec); + + if (ec) { ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return std::shared_ptr(nullptr); } + + return std::shared_ptr(new UdpPortForwarding( + forward_options.from.addr, forward_options.from.port, + forward_options.to.addr, forward_options.to.port)); } static void RegisterToServiceOptionFactory() { @@ -92,10 +66,13 @@ class UdpPortForwarding : public BaseUserService { &UdpPortForwarding::CreateServiceOptions); } - virtual std::string GetName() { return "udpforward"; } + public: + virtual ~UdpPortForwarding() {} + + std::string GetName() override { return "udp-forward"; } - virtual std::vector> - GetRemoteServiceCreateVector() { + std::vector> GetRemoteServiceCreateVector() + override { std::vector> result; services::admin::CreateServiceRequest r_forward( @@ -108,8 +85,8 @@ class UdpPortForwarding : public BaseUserService { return result; } - virtual std::vector> - GetRemoteServiceStopVector(Demux& demux) { + std::vector> GetRemoteServiceStopVector( + Demux& demux) override { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -121,10 +98,11 @@ class UdpPortForwarding : public BaseUserService { return result; } - virtual bool StartLocalServices(Demux& demux) { + bool StartLocalServices(Demux& demux) override { services::admin::CreateServiceRequest l_forward( services::datagrams_to_fibers::DatagramsToFibers< - Demux>::GetCreateRequest(local_port_, relay_fiber_port_)); + Demux>::GetCreateRequest(local_addr_, local_port_, + relay_fiber_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); @@ -141,7 +119,7 @@ class UdpPortForwarding : public BaseUserService { return !ec; } - virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { + uint32_t CheckRemoteServiceStatus(Demux& demux) override { services::admin::CreateServiceRequest r_socks( services::fibers_to_datagrams::FibersToDatagrams< Demux>::GetCreateRequest(relay_fiber_port_, remote_addr_, @@ -154,13 +132,24 @@ class UdpPortForwarding : public BaseUserService { return status; } - virtual void StopLocalServices(Demux& demux) { + void StopLocalServices(Demux& demux) override { auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); } private: + UdpPortForwarding(const std::string& local_addr, uint16_t local_port, + const std::string& remote_addr, uint16_t remote_port) + : local_addr_(local_addr), + local_port_(local_port), + remote_addr_(remote_addr), + remote_port_(remote_port), + remoteServiceId_(0), + localServiceId_(0) { + relay_fiber_port_ = remote_port_ + (1 << 16); + } + uint32_t GetRemoteServiceId(Demux& demux) { if (remoteServiceId_) { return remoteServiceId_; @@ -179,6 +168,7 @@ class UdpPortForwarding : public BaseUserService { } } + std::string local_addr_; uint16_t local_port_; std::string remote_addr_; uint16_t remote_port_; diff --git a/src/services/user_services/udp_remote_port_forwarding.h b/src/services/user_services/udp_remote_port_forwarding.h index 4a4bf747..408de28a 100644 --- a/src/services/user_services/udp_remote_port_forwarding.h +++ b/src/services/user_services/udp_remote_port_forwarding.h @@ -6,19 +6,13 @@ #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/option_parser.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" @@ -38,52 +32,32 @@ class UdpRemotePortForwarding : public BaseUserService { private: typedef boost::asio::fiber::detail::fiber_id::local_port_type local_port_type; - private: - UdpRemotePortForwarding(uint16_t remote_port, std::string local_addr, - uint16_t local_port) - : local_port_(local_port), - local_addr_(local_addr), - remote_port_(remote_port), - remoteServiceId_(0), - localServiceId_(0) { - relay_fiber_port_ = remote_port_ + (1 << 16); - } - public: - static std::string GetFullParseName() { return "udpremote,V"; } + static std::string GetFullParseName() { return "udp-remote-forward,V"; } - static std::string GetParseName() { return "udpremote"; } + static std::string GetParseName() { return "udp-remote-forward"; } - static std::string GetValueName() { return "rem_port:dest_ip:dest_port"; } + static std::string GetValueName() { + return "[[rem_ip]:]rem_port:dest_ip:dest_port"; + } static std::string GetParseDesc() { - return "Forward remote udp port on given target from client"; + return "Forward UDP server [[rem_ip]:]rem_port to dest_ip:dest_port from " + "client"; } - 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 UdpRemotePortForwarding(listening_port, target_addr, target_port)); - } else { + auto forward_options = OptionParser::ParseForwardOptions(line, ec); + + if (ec) { ec.assign(::error::invalid_argument, ::error::get_ssf_category()); return std::shared_ptr(nullptr); } + + return std::shared_ptr(new UdpRemotePortForwarding( + forward_options.from.addr, forward_options.from.port, + forward_options.to.addr, forward_options.to.port)); } static void RegisterToServiceOptionFactory() { @@ -92,23 +66,27 @@ class UdpRemotePortForwarding : public BaseUserService { &UdpRemotePortForwarding::CreateServiceOptions); } - virtual std::string GetName() { return "udpremote"; } + public: + virtual ~UdpRemotePortForwarding() {} - virtual std::vector> - GetRemoteServiceCreateVector() { + std::string GetName() override { return "udp-remote-forward"; } + + std::vector> GetRemoteServiceCreateVector() + override { std::vector> result; services::admin::CreateServiceRequest r_forward( services::datagrams_to_fibers::DatagramsToFibers< - Demux>::GetCreateRequest(remote_port_, relay_fiber_port_)); + Demux>::GetCreateRequest(remote_addr_, remote_port_, + relay_fiber_port_)); result.push_back(r_forward); return result; } - virtual std::vector> - GetRemoteServiceStopVector(Demux& demux) { + std::vector> GetRemoteServiceStopVector( + Demux& demux) override { std::vector> result; auto id = GetRemoteServiceId(demux); @@ -120,7 +98,7 @@ class UdpRemotePortForwarding : public BaseUserService { return result; } - virtual bool StartLocalServices(Demux& demux) { + bool StartLocalServices(Demux& demux) override { services::admin::CreateServiceRequest l_forward( services::fibers_to_datagrams::FibersToDatagrams< Demux>::GetCreateRequest(relay_fiber_port_, local_addr_, @@ -141,10 +119,11 @@ class UdpRemotePortForwarding : public BaseUserService { return !ec; } - virtual uint32_t CheckRemoteServiceStatus(Demux& demux) { + uint32_t CheckRemoteServiceStatus(Demux& demux) override { services::admin::CreateServiceRequest r_forward( services::datagrams_to_fibers::DatagramsToFibers< - Demux>::GetCreateRequest(remote_port_, relay_fiber_port_)); + Demux>::GetCreateRequest(remote_addr_, remote_port_, + relay_fiber_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); auto status = p_service_factory->GetStatus(r_forward.service_id(), @@ -154,20 +133,32 @@ class UdpRemotePortForwarding : public BaseUserService { return status; } - virtual void StopLocalServices(Demux& demux) { + void StopLocalServices(Demux& demux) override { auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); p_service_factory->StopService(localServiceId_); } private: + UdpRemotePortForwarding(const std::string& remote_addr, uint16_t remote_port, + const std::string& local_addr, uint16_t local_port) + : local_port_(local_port), + local_addr_(local_addr), + remote_port_(remote_port), + remote_addr_(remote_addr), + remoteServiceId_(0), + localServiceId_(0) { + relay_fiber_port_ = remote_port_ + (1 << 16); + } + uint32_t GetRemoteServiceId(Demux& demux) { if (remoteServiceId_) { return remoteServiceId_; } else { services::admin::CreateServiceRequest r_forward( services::datagrams_to_fibers::DatagramsToFibers< - Demux>::GetCreateRequest(remote_port_, relay_fiber_port_)); + Demux>::GetCreateRequest(remote_addr_, remote_port_, + relay_fiber_port_)); auto p_service_factory = ServiceFactoryManager::GetServiceFactory(&demux); @@ -178,9 +169,11 @@ class UdpRemotePortForwarding : public BaseUserService { } } + private: uint16_t local_port_; std::string local_addr_; uint16_t remote_port_; + std::string remote_addr_; local_port_type relay_fiber_port_; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index ce82efe7..3bf62506 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -10,6 +10,7 @@ include_directories( ${GTEST_ROOT_DIR}/include ) +add_subdirectory(commandline) add_subdirectory(config) add_subdirectory(network) -add_subdirectory(services) \ No newline at end of file +add_subdirectory(services) diff --git a/src/tests/commandline/CMakeLists.txt b/src/tests/commandline/CMakeLists.txt new file mode 100644 index 00000000..347089bd --- /dev/null +++ b/src/tests/commandline/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 2.8) + +set(commandline_tests_group_name "Unit Tests/CommandLine") + +# --- Fiber asio tests +set(commandline_tests_source_files + "commandline_tests.cpp" + ${SSF_SOURCES} + ${CORE_COMMAND_LINE_STANDARD_FILES} + ${CORE_COMMAND_LINE_COPY_FILES} +) + +add_target("commandline_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${commandline_tests_source_files} +) +project_group(${commandline_tests_group_name} commandline_tests) diff --git a/src/tests/commandline/commandline_tests.cpp b/src/tests/commandline/commandline_tests.cpp new file mode 100644 index 00000000..cf656a26 --- /dev/null +++ b/src/tests/commandline/commandline_tests.cpp @@ -0,0 +1,195 @@ +#include + +#include + +#include + +#include "core/command_line/standard/command_line.h" +#include "core/command_line/copy/command_line.h" + +TEST(StandardCommandLineTests, ServerTest) { + ssf::command_line::standard::CommandLine cmd(true); + + ASSERT_FALSE(cmd.host_set()); + ASSERT_EQ("", cmd.host()); + ASSERT_FALSE(cmd.port_set()); + ASSERT_EQ(0, cmd.port()); + ASSERT_TRUE(cmd.config_file().empty()); + ASSERT_TRUE(cmd.circuit_file().empty()); + ASSERT_EQ(ssf::log::kLogInfo, cmd.log_level()); + + ASSERT_FALSE(cmd.show_status()); + ASSERT_FALSE(cmd.relay_only()); + ASSERT_FALSE(cmd.gateway_ports()); + + boost::system::error_code ec; + + std::vector argv = {"test_exec", "-p", "8012", "-c", + "config_file.json", "-v", "critical", "-S", "-R", + "-g", "127.0.0.1"}; + + cmd.Parse(static_cast(argv.size()), argv.data(), ec); + + ASSERT_EQ(0, ec.value()) << "Parsing failed"; + ASSERT_TRUE(cmd.host_set()); + ASSERT_EQ("127.0.0.1", cmd.host()); + ASSERT_TRUE(cmd.port_set()); + ASSERT_EQ(8012, cmd.port()); + ASSERT_FALSE(cmd.config_file().empty()); + ASSERT_EQ(cmd.config_file(), "config_file.json"); + ASSERT_TRUE(cmd.circuit_file().empty()); + ASSERT_EQ(ssf::log::kLogCritical, cmd.log_level()); + + ASSERT_TRUE(cmd.show_status()); + ASSERT_TRUE(cmd.relay_only()); + ASSERT_TRUE(cmd.gateway_ports()); +} + +TEST(StandardCommandLineTests, ClientTest) { + ssf::command_line::standard::CommandLine cmd(false); + + ASSERT_FALSE(cmd.host_set()); + ASSERT_EQ("", cmd.host()); + ASSERT_FALSE(cmd.port_set()); + ASSERT_EQ(0, cmd.port()); + ASSERT_TRUE(cmd.config_file().empty()); + ASSERT_TRUE(cmd.circuit_file().empty()); + ASSERT_EQ(ssf::log::kLogInfo, cmd.log_level()); + + ASSERT_FALSE(cmd.show_status()); + ASSERT_FALSE(cmd.relay_only()); + ASSERT_FALSE(cmd.gateway_ports()); + + boost::system::error_code ec; + + std::vector argv = {"test_exec", "-p", "8012", "-c", + "config_file.json", "-b", "circuit_file.txt", "-v", + "critical", "-S", "-g", "127.0.0.1"}; + + cmd.Parse(static_cast(argv.size()), argv.data(), ec); + + ASSERT_EQ(0, ec.value()) << "Parsing failed"; + ASSERT_TRUE(cmd.host_set()); + ASSERT_EQ("127.0.0.1", cmd.host()); + ASSERT_TRUE(cmd.port_set()); + ASSERT_EQ(8012, cmd.port()); + ASSERT_FALSE(cmd.config_file().empty()); + ASSERT_EQ(cmd.config_file(), "config_file.json"); + ASSERT_FALSE(cmd.circuit_file().empty()); + ASSERT_EQ("circuit_file.txt", cmd.circuit_file()); + ASSERT_EQ(ssf::log::kLogCritical, cmd.log_level()); + + ASSERT_TRUE(cmd.show_status()); + ASSERT_FALSE(cmd.relay_only()); + ASSERT_TRUE(cmd.gateway_ports()); +} + +TEST(CopyCommandLineTests, FromStdinToServerTest) { + ssf::command_line::copy::CommandLine cmd; + + ASSERT_FALSE(cmd.host_set()); + ASSERT_EQ("", cmd.host()); + ASSERT_FALSE(cmd.port_set()); + ASSERT_EQ(0, cmd.port()); + ASSERT_TRUE(cmd.config_file().empty()); + ASSERT_TRUE(cmd.circuit_file().empty()); + ASSERT_EQ(ssf::log::kLogInfo, cmd.log_level()); + + boost::system::error_code ec; + + std::vector argv = {"test_exec", "-p", "8012", "-c", + "config_file.json", "-b", "circuit_file.txt", "-v", + "critical", "-t", + "127.0.0.1@/tmp/test_out/output"}; + + cmd.Parse(static_cast(argv.size()), argv.data(), ec); + + ASSERT_EQ(0, ec.value()) << "Parsing failed"; + ASSERT_TRUE(cmd.host_set()); + ASSERT_EQ("127.0.0.1", cmd.host()); + ASSERT_TRUE(cmd.port_set()); + ASSERT_EQ(8012, cmd.port()); + ASSERT_FALSE(cmd.config_file().empty()); + ASSERT_EQ(cmd.config_file(), "config_file.json"); + ASSERT_FALSE(cmd.circuit_file().empty()); + ASSERT_EQ("circuit_file.txt", cmd.circuit_file()); + ASSERT_EQ(ssf::log::kLogCritical, cmd.log_level()); + + ASSERT_TRUE(cmd.from_stdin()); + ASSERT_TRUE(cmd.from_local_to_remote()); + ASSERT_EQ("/tmp/test_out/output", cmd.output_pattern()); +} + +TEST(CopyCommandLineTests, ClientToServerTest) { + ssf::command_line::copy::CommandLine cmd; + + ASSERT_FALSE(cmd.host_set()); + ASSERT_EQ("", cmd.host()); + ASSERT_FALSE(cmd.port_set()); + ASSERT_EQ(0, cmd.port()); + ASSERT_TRUE(cmd.config_file().empty()); + ASSERT_TRUE(cmd.circuit_file().empty()); + ASSERT_EQ(ssf::log::kLogInfo, cmd.log_level()); + + boost::system::error_code ec; + + std::vector argv = {"test_exec", "-p", "8012", "-c", + "config_file.json", "-b", "circuit_file.txt", "-v", + "critical", "/tmp/test_in", + "127.0.0.1@/tmp/test_out"}; + + cmd.Parse(static_cast(argv.size()), argv.data(), ec); + + ASSERT_EQ(0, ec.value()) << "Parsing failed"; + ASSERT_TRUE(cmd.host_set()); + ASSERT_EQ("127.0.0.1", cmd.host()); + ASSERT_TRUE(cmd.port_set()); + ASSERT_EQ(8012, cmd.port()); + ASSERT_FALSE(cmd.config_file().empty()); + ASSERT_EQ(cmd.config_file(), "config_file.json"); + ASSERT_FALSE(cmd.circuit_file().empty()); + ASSERT_EQ("circuit_file.txt", cmd.circuit_file()); + ASSERT_EQ(ssf::log::kLogCritical, cmd.log_level()); + + ASSERT_FALSE(cmd.from_stdin()); + ASSERT_TRUE(cmd.from_local_to_remote()); + ASSERT_EQ("/tmp/test_in", cmd.input_pattern()); + ASSERT_EQ("/tmp/test_out/", cmd.output_pattern()); +} + +TEST(CopyCommandLineTests, ServerToClientTest) { + ssf::command_line::copy::CommandLine cmd; + + ASSERT_FALSE(cmd.host_set()); + ASSERT_EQ(cmd.host(), ""); + ASSERT_FALSE(cmd.port_set()); + ASSERT_EQ(cmd.port(), 0); + ASSERT_TRUE(cmd.config_file().empty()); + ASSERT_TRUE(cmd.circuit_file().empty()); + ASSERT_EQ(cmd.log_level(), ssf::log::kLogInfo); + + boost::system::error_code ec; + + std::vector argv = {"test_exec", "-p", "8012", "-c", + "config_file.json", "-b", "circuit_file.txt", "-v", + "critical", "127.0.0.1@/tmp/test_in", + "/tmp/test_out"}; + + cmd.Parse(static_cast(argv.size()), argv.data(), ec); + + ASSERT_EQ(ec.value(), 0) << "Parsing failed"; + ASSERT_TRUE(cmd.host_set()); + ASSERT_EQ(cmd.host(), "127.0.0.1"); + ASSERT_TRUE(cmd.port_set()); + ASSERT_EQ(cmd.port(), 8012); + ASSERT_FALSE(cmd.config_file().empty()); + ASSERT_EQ(cmd.config_file(), "config_file.json"); + ASSERT_FALSE(cmd.circuit_file().empty()); + ASSERT_EQ(cmd.circuit_file(), "circuit_file.txt"); + ASSERT_EQ(cmd.log_level(), ssf::log::kLogCritical); + + ASSERT_FALSE(cmd.from_stdin()); + ASSERT_FALSE(cmd.from_local_to_remote()); + ASSERT_EQ("/tmp/test_in", cmd.input_pattern()); + ASSERT_EQ("/tmp/test_out/", cmd.output_pattern()); +} diff --git a/src/tests/config/CMakeLists.txt b/src/tests/config/CMakeLists.txt index ad293c2b..a5ea9bc5 100644 --- a/src/tests/config/CMakeLists.txt +++ b/src/tests/config/CMakeLists.txt @@ -1,15 +1,15 @@ cmake_minimum_required(VERSION 2.8) -set(config_tests_group_name "Unit Tests/Config") +set(config_tests_group_name "Unit Tests/Config") set(project_BINARY_CONFIG_TESTS_DIR "${project_BINARY_DIR}/src/tests/config") - + # --- copy test config files set(project_BINARY_CONFIG_TESTS_CONF_DIR ${project_BINARY_CONFIG_TESTS_DIR}/config_files) file(GLOB_RECURSE CONFIG_TEST_FILES "${CMAKE_CURRENT_SOURCE_DIR}/config_files/*.json") - + file(MAKE_DIRECTORY ${project_BINARY_CONFIG_TESTS_CONF_DIR}) file(COPY ${CONFIG_TEST_FILES} DESTINATION ${project_BINARY_CONFIG_TESTS_CONF_DIR}) @@ -21,6 +21,7 @@ set(load_config_test_source_files "load_config_tests.cpp" ${COMMON_CONFIG_FILES} ${COMMON_ERROR_FILES} + ${SERVICES_FILES} ) add_target("load_config_tests" @@ -36,4 +37,4 @@ add_target("load_config_tests" FILES ${load_config_test_source_files} ) -project_group(${config_tests_group_name} load_config_tests) \ No newline at end of file +project_group(${config_tests_group_name} load_config_tests) diff --git a/src/tests/config/config_files/complete.json b/src/tests/config/config_files/complete.json index 67c9773a..efcfe1c6 100644 --- a/src/tests/config/config_files/complete.json +++ b/src/tests/config/config_files/complete.json @@ -15,8 +15,8 @@ "username": "test_user", "password": "test_password", "domain": "test_domain", - "reuse_ntlm": "false", - "reuse_kerb": "false" + "reuse_ntlm": false, + "reuse_kerb": false } }, "services" : { diff --git a/src/tests/config/config_files/proxy.json b/src/tests/config/config_files/proxy.json index 18f81e80..b400f9cf 100644 --- a/src/tests/config/config_files/proxy.json +++ b/src/tests/config/config_files/proxy.json @@ -7,8 +7,8 @@ "username": "test_user", "password": "test_password", "domain": "test_domain", - "reuse_ntlm": "false", - "reuse_kerb": "false" + "reuse_ntlm": false, + "reuse_kerb": false } } } diff --git a/src/tests/config/config_files/services.json b/src/tests/config/config_files/services.json index 560c8fd3..3275a067 100644 --- a/src/tests/config/config_files/services.json +++ b/src/tests/config/config_files/services.json @@ -1,10 +1,23 @@ { "ssf": { "services" : { + "datagram_forwarder": { "enable": false }, + "datagram_listener": { + "enable": false, + "gateway_ports": true + }, + "stream_forwarder": { "enable": false }, + "stream_listener": { + "enable": false, + "gateway_ports": true + }, + "file_copy": { "enable": true }, "shell": { + "enable": true, "path": "/bin/custom_path", "args": "-custom args" - } + }, + "socks": { "enable": false } } } } diff --git a/src/tests/config/load_config_tests.cpp b/src/tests/config/load_config_tests.cpp index daa05d3c..a3049208 100644 --- a/src/tests/config/load_config_tests.cpp +++ b/src/tests/config/load_config_tests.cpp @@ -9,6 +9,9 @@ #include "common/config/config.h" class LoadConfigTest : public ::testing::Test { + public: + virtual void SetUp() { config_.Init(); } + protected: ssf::config::Config config_; }; @@ -16,14 +19,15 @@ class LoadConfigTest : public ::testing::Test { TEST_F(LoadConfigTest, LoadNoFileTest) { boost::system::error_code ec; - config_.Update("", ec); + config_.UpdateFromFile("", ec); ASSERT_EQ(ec.value(), 0) << "Success if no file given"; } TEST_F(LoadConfigTest, LoadNonExistantFileTest) { boost::system::error_code ec; - config_.Update("./config_files/unknown.json", ec); + + config_.UpdateFromFile("./config_files/unknown.json", ec); ASSERT_NE(ec.value(), 0) << "No success if file not existant"; } @@ -31,7 +35,7 @@ TEST_F(LoadConfigTest, LoadNonExistantFileTest) { TEST_F(LoadConfigTest, LoadEmptyFileTest) { boost::system::error_code ec; - config_.Update("./config_files/empty.json", ec); + config_.UpdateFromFile("./config_files/empty.json", ec); ASSERT_NE(ec.value(), 0) << "No success if file empty"; } @@ -39,15 +43,45 @@ TEST_F(LoadConfigTest, LoadEmptyFileTest) { TEST_F(LoadConfigTest, LoadWrongFormatFileTest) { boost::system::error_code ec; - config_.Update("./config_files/wrong_format.json", ec); + config_.UpdateFromFile("./config_files/wrong_format.json", ec); ASSERT_NE(ec.value(), 0) << "No success if wrong file format"; } +TEST_F(LoadConfigTest, DefaultValueTest) { + ASSERT_EQ(config_.tls().ca_cert_path(), "./certs/trusted/ca.crt"); + ASSERT_EQ(config_.tls().cert_path(), "./certs/certificate.crt"); + ASSERT_EQ(config_.tls().key_path(), "./certs/private.key"); + ASSERT_EQ(config_.tls().key_password(), ""); + ASSERT_EQ(config_.tls().dh_path(), "./certs/dh4096.pem"); + ASSERT_EQ(config_.tls().cipher_alg(), "DHE-RSA-AES256-GCM-SHA384"); + ASSERT_EQ(config_.http_proxy().host(), ""); + ASSERT_EQ(config_.http_proxy().port(), ""); + ASSERT_EQ(config_.http_proxy().username(), ""); + ASSERT_EQ(config_.http_proxy().domain(), ""); + ASSERT_EQ(config_.http_proxy().password(), ""); + ASSERT_EQ(config_.http_proxy().reuse_kerb(), true); + ASSERT_EQ(config_.http_proxy().reuse_ntlm(), true); + + ASSERT_TRUE(config_.services().datagram_forwarder().enabled()); + ASSERT_TRUE(config_.services().datagram_listener().enabled()); + ASSERT_FALSE(config_.services().datagram_listener().gateway_ports()); + ASSERT_FALSE(config_.services().file_copy().enabled()); + ASSERT_TRUE(config_.services().socks().enabled()); + ASSERT_TRUE(config_.services().stream_forwarder().enabled()); + ASSERT_TRUE(config_.services().stream_listener().enabled()); + ASSERT_FALSE(config_.services().stream_listener().gateway_ports()); + ASSERT_FALSE(config_.services().process().enabled()); + + ASSERT_GT(config_.services().process().path().length(), + static_cast(0)); + ASSERT_EQ(config_.services().process().args(), ""); +} + TEST_F(LoadConfigTest, LoadTlsPartialFileTest) { boost::system::error_code ec; - config_.Update("./config_files/tls_partial.json", ec); + config_.UpdateFromFile("./config_files/tls_partial.json", ec); ASSERT_EQ(ec.value(), 0) << "Success if partial file format"; ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); @@ -61,7 +95,7 @@ TEST_F(LoadConfigTest, LoadTlsPartialFileTest) { TEST_F(LoadConfigTest, LoadTlsCompleteFileTest) { boost::system::error_code ec; - config_.Update("./config_files/tls_complete.json", ec); + config_.UpdateFromFile("./config_files/tls_complete.json", ec); ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); @@ -77,15 +111,26 @@ TEST_F(LoadConfigTest, LoadTlsCompleteFileTest) { ASSERT_EQ(config_.http_proxy().password(), ""); ASSERT_EQ(config_.http_proxy().reuse_kerb(), true); ASSERT_EQ(config_.http_proxy().reuse_ntlm(), true); - ASSERT_EQ(config_.services().process().path(), - SSF_PROCESS_SERVICE_BINARY_PATH); + + ASSERT_TRUE(config_.services().datagram_forwarder().enabled()); + ASSERT_TRUE(config_.services().datagram_listener().enabled()); + ASSERT_FALSE(config_.services().datagram_listener().gateway_ports()); + ASSERT_FALSE(config_.services().file_copy().enabled()); + ASSERT_TRUE(config_.services().socks().enabled()); + ASSERT_TRUE(config_.services().stream_forwarder().enabled()); + ASSERT_TRUE(config_.services().stream_listener().enabled()); + ASSERT_FALSE(config_.services().stream_listener().gateway_ports()); + ASSERT_FALSE(config_.services().process().enabled()); + + ASSERT_GT(config_.services().process().path().length(), + static_cast(0)); ASSERT_EQ(config_.services().process().args(), ""); } TEST_F(LoadConfigTest, LoadProxyFileTest) { boost::system::error_code ec; - config_.Update("./config_files/proxy.json", ec); + config_.UpdateFromFile("./config_files/proxy.json", ec); ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; ASSERT_EQ(config_.http_proxy().host(), "127.0.0.1"); ASSERT_EQ(config_.http_proxy().port(), "8080"); @@ -99,9 +144,19 @@ TEST_F(LoadConfigTest, LoadProxyFileTest) { TEST_F(LoadConfigTest, LoadServicesFileTest) { boost::system::error_code ec; - config_.Update("./config_files/services.json", ec); + config_.UpdateFromFile("./config_files/services.json", ec); ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; + ASSERT_FALSE(config_.services().datagram_forwarder().enabled()); + ASSERT_FALSE(config_.services().datagram_listener().enabled()); + ASSERT_TRUE(config_.services().datagram_listener().gateway_ports()); + ASSERT_TRUE(config_.services().file_copy().enabled()); + ASSERT_FALSE(config_.services().socks().enabled()); + ASSERT_FALSE(config_.services().stream_forwarder().enabled()); + ASSERT_FALSE(config_.services().stream_listener().enabled()); + ASSERT_TRUE(config_.services().stream_listener().gateway_ports()); + ASSERT_TRUE(config_.services().process().enabled()); + ASSERT_EQ(config_.services().process().path(), "/bin/custom_path"); ASSERT_EQ(config_.services().process().args(), "-custom args"); } @@ -109,7 +164,7 @@ TEST_F(LoadConfigTest, LoadServicesFileTest) { TEST_F(LoadConfigTest, LoadCompleteFileTest) { boost::system::error_code ec; - config_.Update("./config_files/complete.json", ec); + config_.UpdateFromFile("./config_files/complete.json", ec); ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); diff --git a/src/tests/load_config_tests.cpp b/src/tests/load_config_tests.cpp deleted file mode 100644 index e9faf17d..00000000 --- a/src/tests/load_config_tests.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include -#include -#include - -#include -#include -#include - -#include "common/config/config.h" - -class LoadConfigTest : public ::testing::Test { - protected: - ssf::config::Config config_; -}; - -TEST_F(LoadConfigTest, LoadNoFileTest) { - boost::system::error_code ec; - - config_.Update("", ec); - - ASSERT_EQ(ec.value(), 0) << "Success if no file given"; -} - -TEST_F(LoadConfigTest, LoadNonExistantFileTest) { - boost::system::error_code ec; - config_.Update("./config_files/unknown.json", ec); - - ASSERT_NE(ec.value(), 0) << "No success if file not existant"; -} - -TEST_F(LoadConfigTest, LoadEmptyFileTest) { - boost::system::error_code ec; - - config_.Update("./config_files/empty.json", ec); - - ASSERT_NE(ec.value(), 0) << "No success if file empty"; -} - -TEST_F(LoadConfigTest, LoadWrongFormatFileTest) { - boost::system::error_code ec; - - config_.Update("./config_files/wrong_format.json", ec); - - ASSERT_NE(ec.value(), 0) << "No success if wrong file format"; -} - -TEST_F(LoadConfigTest, LoadTlsPartialFileTest) { - boost::system::error_code ec; - - config_.Update("./config_files/tls_partial.json", ec); - - ASSERT_EQ(ec.value(), 0) << "Success if partial file format"; - ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); - ASSERT_EQ(config_.tls().cert_path(), "./certs/certificate.crt"); - ASSERT_EQ(config_.tls().key_path(), "test_key_path"); - ASSERT_EQ(config_.tls().key_password(), ""); - ASSERT_EQ(config_.tls().dh_path(), "test_dh_path"); - ASSERT_EQ(config_.tls().cipher_alg(), "DHE-RSA-AES256-GCM-SHA384"); -} - -TEST_F(LoadConfigTest, LoadTlsCompleteFileTest) { - boost::system::error_code ec; - - config_.Update("./config_files/tls_complete.json", ec); - - ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; - ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); - ASSERT_EQ(config_.tls().cert_path(), "test_cert_path"); - ASSERT_EQ(config_.tls().key_path(), "test_key_path"); - ASSERT_EQ(config_.tls().key_password(), "test_key_password"); - ASSERT_EQ(config_.tls().dh_path(), "test_dh_path"); - ASSERT_EQ(config_.tls().cipher_alg(), "test_cipher_alg"); - ASSERT_EQ(config_.proxy().http_addr(), ""); - ASSERT_EQ(config_.proxy().http_port(), ""); -} - -TEST_F(LoadConfigTest, LoadProxyFileTest) { - boost::system::error_code ec; - - config_.Update("./config_files/proxy.json", ec); - ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; - ASSERT_EQ(config_.proxy().http_addr(), "127.0.0.1"); - ASSERT_EQ(config_.proxy().http_port(), "8080"); -} - -TEST_F(LoadConfigTest, LoadCompleteFileTest) { - boost::system::error_code ec; - - config_.Update("./config_files/complete.json", ec); - - ASSERT_EQ(ec.value(), 0) << "Success if complete file format"; - ASSERT_EQ(config_.tls().ca_cert_path(), "test_ca_path"); - ASSERT_EQ(config_.tls().cert_path(), "test_cert_path"); - ASSERT_EQ(config_.tls().key_path(), "test_key_path"); - ASSERT_EQ(config_.tls().key_password(), "test_key_password"); - ASSERT_EQ(config_.tls().dh_path(), "test_dh_path"); - ASSERT_EQ(config_.tls().cipher_alg(), "test_cipher_alg"); - ASSERT_EQ(config_.proxy().http_addr(), "127.0.0.1"); - ASSERT_EQ(config_.proxy().http_port(), "8080"); -} diff --git a/src/tests/network/CMakeLists.txt b/src/tests/network/CMakeLists.txt index 9f550c24..de7aa16e 100644 --- a/src/tests/network/CMakeLists.txt +++ b/src/tests/network/CMakeLists.txt @@ -17,6 +17,11 @@ file(COPY ${SSF_CERT_TEST_TRUSTED_FILES} # --- Network tests +set(ssf_fixture_test_file + "ssf_fixture_test.h" + "ssf_fixture_test.cpp" +) + # --- Fiber asio tests set(fiber_asio_test_source_files "fiber_asio_tests.cpp" @@ -80,10 +85,33 @@ add_target("ssf_client_server_cipher_suites_tests" ) project_group(${network_tests_group_name} ssf_client_server_cipher_suites_tests) +# --- SSF Client tests +set(ssf_client_source_files + "ssf_client_tests.cpp" + ${SSF_SOURCES} + ${ssf_fixture_test_file} +) + +add_target("ssf_client_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${OpenSSL_LIBRARIES} + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + ${ssf_client_source_files} +) +project_group(${network_tests_group_name} ssf_client_tests) + # --- SSF Server tests set(ssf_server_source_files "ssf_server_tests.cpp" ${SSF_SOURCES} + ${ssf_fixture_test_file} ) add_target("ssf_server_tests" diff --git a/src/tests/network/bouncing_tests.cpp b/src/tests/network/bouncing_tests.cpp index ba7eb277..45b35f63 100644 --- a/src/tests/network/bouncing_tests.cpp +++ b/src/tests/network/bouncing_tests.cpp @@ -40,9 +40,11 @@ TEST(BouncingTests, BouncingChain) { std::list servers; ssf::circuit::NodeList node_list; - uint16_t initial_server_port = 10000; + uint16_t initial_server_port = 11000; uint8_t nb_of_servers = 5; ssf::config::Config ssf_config; + + ssf_config.Init(); boost::system::error_code client_ec; boost::system::error_code circuit_ec; diff --git a/src/tests/network/ssf_client_server_cipher_suites_tests.cpp b/src/tests/network/ssf_client_server_cipher_suites_tests.cpp index 64e5af65..045e015e 100644 --- a/src/tests/network/ssf_client_server_cipher_suites_tests.cpp +++ b/src/tests/network/ssf_client_server_cipher_suites_tests.cpp @@ -44,9 +44,10 @@ class SSFClientServerCipherSuitesTest : public ::testing::Test { p_ssf_server_->Stop(); } - void StartServer(const ssf::config::Config& config) { + void StartServer(const std::string& server_port, + const ssf::config::Config& config) { auto endpoint_query = - NetworkProtocol::GenerateServerTLSQuery("", "8000", config); + NetworkProtocol::GenerateServerTLSQuery("", server_port, config); p_ssf_server_.reset(new Server(config.services())); @@ -54,14 +55,16 @@ class SSFClientServerCipherSuitesTest : public ::testing::Test { p_ssf_server_->Run(endpoint_query, run_ec); } - void StartClient(const ssf::config::Config& config, + void StartClient(const std::string& server_port, + const ssf::config::Config& config, const ClientCallback& callback) { std::vector client_options; auto endpoint_query = NetworkProtocol::GenerateClientTLSQuery( - "127.0.0.1", "8000", config, {}); + "127.0.0.1", server_port, config, {}); - p_ssf_client_.reset(new Client(client_options, config.services(), callback)); + p_ssf_client_.reset( + new Client(client_options, config.services(), callback)); boost::system::error_code run_ec; p_ssf_client_->Run(endpoint_query, run_ec); @@ -91,11 +94,28 @@ TEST_F(SSFClientServerCipherSuitesTest, connectDisconnectDifferentSuite) { } }; ssf::config::Config client_config; + client_config.Init(); ssf::config::Config server_config; - server_config.tls().set_cipher_alg("DHE-RSA-AES256-GCM-SHA256"); + server_config.Init(); + boost::system::error_code ec; + + const char* new_config = R"RAWSTRING( +{ + "ssf": { + "tls" : { + "cipher_alg": "DHE-RSA-AES256-GCM-SHA256" + } + } +} +)RAWSTRING"; + + server_config.UpdateFromString(new_config, ec); + ASSERT_EQ(ec.value(), 0) << "Could not update server config from string " + << new_config; - StartServer(server_config); - StartClient(client_config, callback); + std::string server_port("8600"); + StartServer(server_port, server_config); + StartClient(server_port, client_config, callback); network_set_future.wait(); transport_set_future.wait(); @@ -125,15 +145,40 @@ TEST_F(SSFClientServerCipherSuitesTest, connectDisconnectTwoSuites) { } }; ssf::config::Config client_config; + client_config.Init(); ssf::config::Config server_config; - - client_config.tls().set_cipher_alg( - "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256^"); - server_config.tls().set_cipher_alg( - "ECDH-ECDSA-AES128-SHA256:DHE-RSA-AES128-SHA256"); - - StartServer(server_config); - StartClient(client_config, callback); + server_config.Init(); + boost::system::error_code ec; + const char* new_client_config = R"RAWSTRING( +{ + "ssf": { + "tls" : { + "cipher_alg": "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA256" + } + } +} +)RAWSTRING"; + + const char* new_server_config = R"RAWSTRING( +{ + "ssf": { + "tls" : { + "cipher_alg": "ECDH-ECDSA-AES128-SHA256:DHE-RSA-AES128-SHA256" + } + } +} +)RAWSTRING"; + + client_config.UpdateFromString(new_client_config, ec); + ASSERT_EQ(ec.value(), 0) << "Could not update client config from string " + << new_client_config; + server_config.UpdateFromString(new_server_config, ec); + ASSERT_EQ(ec.value(), 0) << "Could not update server config from string " + << new_server_config; + + std::string server_port("8700"); + StartServer(server_port, server_config); + StartClient(server_port, client_config, callback); network_set_future.wait(); transport_set_future.wait(); diff --git a/src/tests/network/ssf_client_server_tests.cpp b/src/tests/network/ssf_client_server_tests.cpp index b3e344a4..a317d906 100644 --- a/src/tests/network/ssf_client_server_tests.cpp +++ b/src/tests/network/ssf_client_server_tests.cpp @@ -38,8 +38,9 @@ class SSFClientServerTest : public ::testing::Test { ~SSFClientServerTest() {} virtual void SetUp() { - StartServer(); - StartClient(); + std::string server_port("8100"); + StartServer(server_port); + StartClient(server_port); } virtual void TearDown() { @@ -47,24 +48,28 @@ class SSFClientServerTest : public ::testing::Test { p_ssf_server_->Stop(); } - void StartServer() { + void StartServer(const std::string& server_port) { ssf::config::Config ssf_config; + + ssf_config.Init(); auto endpoint_query = - NetworkProtocol::GenerateServerQuery("", "8000", ssf_config); + NetworkProtocol::GenerateServerQuery("", server_port, ssf_config); p_ssf_server_.reset(new Server(ssf_config.services())); boost::system::error_code run_ec; p_ssf_server_->Run(endpoint_query, run_ec); } - void StartClient() { + void StartClient(const std::string& server_port) { std::vector client_options; ssf::config::Config ssf_config; + + ssf_config.Init(); auto endpoint_query = NetworkProtocol::GenerateClientQuery( - "127.0.0.1", "8000", ssf_config, {}); + "127.0.0.1", server_port, ssf_config, {}); p_ssf_client_.reset( new Client(client_options, ssf_config.services(), diff --git a/src/tests/network/ssf_client_tests.cpp b/src/tests/network/ssf_client_tests.cpp new file mode 100644 index 00000000..d16de84b --- /dev/null +++ b/src/tests/network/ssf_client_tests.cpp @@ -0,0 +1,101 @@ +#include +#include + +#include +#include + +#include "tests/network/ssf_fixture_test.h" + +void InitTCPServer(boost::asio::ip::tcp::acceptor& server, int server_port); + +TEST_F(SSFFixtureTest, ConnectToUnknownHost) { + // Init timer (if client hangs) + boost::system::error_code timer_ec; + auto timer_callback = [this](const boost::system::error_code& ec) { + EXPECT_NE(0, ec.value()) << "Timer should be canceled. Client is hanging"; + if (!ec) { + SendNotification(false); + } + }; + StartTimer(std::chrono::seconds(5), timer_callback, timer_ec); + + auto client_callback = [this](ssf::services::initialisation::type type, + BaseUserServicePtr p_user_service, + const boost::system::error_code& ec) { + EXPECT_EQ(ssf::services::initialisation::NETWORK, type); + EXPECT_NE(0, ec.value()); + SendNotification(ec.value() != 0); + }; + + boost::system::error_code run_ec; + StartClient("16000", client_callback, run_ec); + ASSERT_EQ(0, run_ec.value()) << "Could not start client"; + + // Wait client action + WaitNotification(); + + EXPECT_TRUE(IsNotificationSuccess()) << "Stop failed"; + + StopClient(); + StopTimer(); +} + +TEST_F(SSFFixtureTest, CloseWhileConnecting) { + // Init server + int server_port = 15000; + boost::asio::ip::tcp::acceptor server(get_io_service()); + InitTCPServer(server, server_port); + + // Init timer (if client hangs) + boost::system::error_code timer_ec; + auto timer_callback = [this](const boost::system::error_code& ec) { + EXPECT_NE(0, ec.value()) << "Timer should be canceled. Client is hanging"; + if (!ec) { + SendNotification(false); + } + }; + StartTimer(std::chrono::seconds(5), timer_callback, timer_ec); + ASSERT_EQ(0, timer_ec.value()) << "Could not start timer"; + + // Init client + auto client_callback = [this](ssf::services::initialisation::type type, + BaseUserServicePtr p_user_service, + const boost::system::error_code& ec) { + EXPECT_EQ(ssf::services::initialisation::NETWORK, type); + EXPECT_NE(0, ec.value()); + SendNotification(ec.value() != 0); + }; + boost::system::error_code run_ec; + StartClient(std::to_string(server_port), client_callback, run_ec); + ASSERT_EQ(0, run_ec.value()); + + // Wait new server connection + boost::asio::ip::tcp::socket socket(get_io_service()); + server.async_accept(socket, [this](const boost::system::error_code& ec) { + EXPECT_EQ(0, ec.value()) << "Accept connection in error"; + // Stop client while connecting + StopClient(); + }); + + // Wait client action + WaitNotification(); + + EXPECT_TRUE(IsNotificationSuccess()) << "Stop failed"; + + StopTimer(); + boost::system::error_code close_ec; + socket.close(close_ec); + server.close(close_ec); +} + +void InitTCPServer(boost::asio::ip::tcp::acceptor& server, int server_port) { + boost::system::error_code server_ec; + boost::asio::ip::tcp::endpoint server_ep(boost::asio::ip::tcp::v4(), + server_port); + server.open(boost::asio::ip::tcp::v4(), server_ec); + ASSERT_EQ(0, server_ec.value()) << "Could not open server acceptor"; + server.bind(server_ep, server_ec); + ASSERT_EQ(0, server_ec.value()) << "Could not bind server acceptor"; + server.listen(boost::asio::socket_base::max_connections, server_ec); + ASSERT_EQ(0, server_ec.value()) << "Server acceptor could not listen"; +} diff --git a/src/tests/network/ssf_fixture_test.cpp b/src/tests/network/ssf_fixture_test.cpp new file mode 100644 index 00000000..319d729e --- /dev/null +++ b/src/tests/network/ssf_fixture_test.cpp @@ -0,0 +1,94 @@ +#include "tests/network/ssf_fixture_test.h" + +SSFFixtureTest::SSFFixtureTest() : success_(false), stopped_(false) {} + +void SSFFixtureTest::SetUp() { StartAsyncEngine(); } + +void SSFFixtureTest::TearDown() { StopAsyncEngine(); } + +boost::asio::io_service& SSFFixtureTest::get_io_service() { + return async_engine_.get_io_service(); +} + +void SSFFixtureTest::StartAsyncEngine() { async_engine_.Start(); } + +void SSFFixtureTest::StopAsyncEngine() { async_engine_.Stop(); } + +void SSFFixtureTest::StartClient(const std::string& server_port, + ClientCallback callback, + boost::system::error_code& ec) { + std::vector client_options; + + ssf::config::Config ssf_config; + + ssf_config.Init(); + + auto endpoint_query = NetworkProtocol::GenerateClientQuery( + "127.0.0.1", server_port, ssf_config, {}); + + p_ssf_client_.reset( + new Client(client_options, ssf_config.services(), callback)); + + p_ssf_client_->Run(endpoint_query, ec); +} + +void SSFFixtureTest::StopClient() { + if (!p_ssf_client_.get()) { + return; + } + p_ssf_client_->Stop(); +} + +void SSFFixtureTest::StartServer(const std::string& addr, + const std::string& server_port, + boost::system::error_code& ec) { + ssf::config::Config ssf_config; + + ssf_config.Init(); + + auto endpoint_query = + NetworkProtocol::GenerateServerQuery(addr, server_port, ssf_config); + p_ssf_server_.reset(new Server(ssf_config.services())); + + p_ssf_server_->Run(endpoint_query, ec); +} + +void SSFFixtureTest::StopServer() { + if (!p_ssf_server_.get()) { + return; + } + p_ssf_server_->Stop(); +} + +void SSFFixtureTest::StartTimer(const std::chrono::seconds& wait_duration, + TimerCallback callback, + boost::system::error_code& ec) { + p_timer_.reset(new boost::asio::steady_timer(async_engine_.get_io_service())); + p_timer_->expires_from_now(wait_duration, ec); + p_timer_->async_wait(callback); +} + +void SSFFixtureTest::StopTimer() { + if (!p_timer_.get()) { + return; + } + boost::system::error_code ec; + p_timer_->cancel(ec); +} + +void SSFFixtureTest::WaitNotification() { + std::unique_lock lock(mutex_); + wait_stop_cv_.wait(lock, [this]() { return stopped_; }); + lock.unlock(); +} + +void SSFFixtureTest::SendNotification(bool success) { + { + boost::lock_guard lock(mutex_); + stopped_ = true; + success_ = success; + } + wait_stop_cv_.notify_all(); +} + +bool SSFFixtureTest::IsNotificationSuccess() { return success_; } \ No newline at end of file diff --git a/src/tests/network/ssf_fixture_test.h b/src/tests/network/ssf_fixture_test.h new file mode 100644 index 00000000..b816be88 --- /dev/null +++ b/src/tests/network/ssf_fixture_test.h @@ -0,0 +1,78 @@ +#ifndef TESTS_NETWORK_SSF_FIXTURE_TEST_H_ +#define TESTS_NETWORK_SSF_FIXTURE_TEST_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "common/config/config.h" + +#include "core/network_protocol.h" +#include "core/client/client.h" +#include "core/server/server.h" + +#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" + +using NetworkProtocol = ssf::network::NetworkProtocol; + +class SSFFixtureTest : public ::testing::Test { + public: + using Client = + ssf::SSFClient; + using Server = + ssf::SSFServer; + using ClientCallback = Client::ClientCallback; + using BaseUserServicePtr = Client::BaseUserServicePtr; + using TimerCallback = std::function; + + public: + void SetUp() override; + void TearDown() override; + + boost::asio::io_service& get_io_service(); + + void StartAsyncEngine(); + void StopAsyncEngine(); + + void StartClient(const std::string& server_port, ClientCallback callback, + boost::system::error_code& ec); + void StopClient(); + + void StartServer(const std::string& addr, const std::string& server_port, + boost::system::error_code& ec); + void StopServer(); + + void StartTimer(const std::chrono::seconds& wait_duration, + TimerCallback callback, boost::system::error_code&); + + void StopTimer(); + + void WaitNotification(); + + void SendNotification(bool success); + + bool IsNotificationSuccess(); + + protected: + SSFFixtureTest(); + + protected: + std::unique_ptr p_ssf_client_; + std::unique_ptr p_ssf_server_; + std::unique_ptr p_timer_; + + private: + ssf::AsyncEngine async_engine_; + std::condition_variable wait_stop_cv_; + std::mutex mutex_; + bool success_; + bool stopped_; +}; + +#endif // TESTS_NETWORK_SSF_FIXTURE_TEST_H_ \ No newline at end of file diff --git a/src/tests/network/ssf_server_tests.cpp b/src/tests/network/ssf_server_tests.cpp index 523b4971..23969608 100644 --- a/src/tests/network/ssf_server_tests.cpp +++ b/src/tests/network/ssf_server_tests.cpp @@ -1,61 +1,84 @@ -#include +#include +#include -#include "common/config/config.h" +#include +#include -#include "core/network_protocol.h" -#include "core/transport_virtual_layer_policies/transport_protocol_policy.h" -#include "core/server/server.h" +#include "tests/network/ssf_fixture_test.h" -using NetworkProtocol = ssf::network::NetworkProtocol; +TEST_F(SSFFixtureTest, failListeningWrongInterface) { + boost::system::error_code server_ec; + StartServer("1.1.1.1", "8200", server_ec); -TEST(SSFServerTest, failListeningWrongInterface) { - using Server = - ssf::SSFServer; + ASSERT_NE(0, server_ec.value()); - ssf::config::Config ssf_config; - - auto endpoint_query = - NetworkProtocol::GenerateServerQuery("1.1.1.1", "8000", ssf_config); - Server server(ssf_config.services()); - - boost::system::error_code run_ec; - server.Run(endpoint_query, run_ec); - - ASSERT_NE(0, run_ec.value()); + StopServer(); } -TEST(SSFServerTest, listeningAllInterfaces) { - using Server = - ssf::SSFServer; - - ssf::config::Config ssf_config; - - auto endpoint_query = - NetworkProtocol::GenerateServerQuery("", "8000", ssf_config); - Server server(ssf_config.services()); +TEST_F(SSFFixtureTest, listeningAllInterfaces) { + boost::system::error_code server_ec; + StartServer("", "8300", server_ec); - boost::system::error_code run_ec; - server.Run(endpoint_query, run_ec); + ASSERT_EQ(0, server_ec.value()); - ASSERT_EQ(0, run_ec.value()); - - server.Stop(); + StopServer(); } -TEST(SSFServerTest, listeningLocalhostInterface) { - using Server = - ssf::SSFServer; - - ssf::config::Config ssf_config; +TEST_F(SSFFixtureTest, listeningLocalhostInterface) { + boost::system::error_code server_ec; + StartServer("127.0.0.1", "8300", server_ec); - auto endpoint_query = - NetworkProtocol::GenerateServerQuery("127.0.0.1", "8000", ssf_config); - Server server(ssf_config.services()); + ASSERT_EQ(0, server_ec.value()); - boost::system::error_code run_ec; - server.Run(endpoint_query, run_ec); - - ASSERT_EQ(0, run_ec.value()); + StopServer(); +} - server.Stop(); +TEST_F(SSFFixtureTest, buggyConnectionTest) { + boost::system::error_code timer_ec; + auto timer_callback = [this](const boost::system::error_code& ec) { + EXPECT_NE(0, ec.value()) << "Timer should be canceled. Client is hanging"; + if (!ec) { + SendNotification(false); + } + }; + StartTimer(std::chrono::seconds(10), timer_callback, timer_ec); + ASSERT_EQ(0, timer_ec.value()) << "Could not start timer"; + + int server_port = 8400; + boost::system::error_code server_ec; + StartServer("127.0.0.1", std::to_string(server_port), server_ec); + ASSERT_EQ(0, server_ec.value()); + + auto client_callback = [this](ssf::services::initialisation::type type, + BaseUserServicePtr p_user_service, + const boost::system::error_code& ec) { + if (type != ssf::services::initialisation::NETWORK) { + return; + } + EXPECT_EQ(0, ec.value()); + SendNotification(ec.value() == 0); + }; + + boost::asio::ip::tcp::socket buggy_client(get_io_service()); + boost::asio::ip::tcp::endpoint server_ep( + boost::asio::ip::address::from_string("127.0.0.1"), server_port); + + auto connect_callback = [client_callback, &buggy_client, &server_port, this]( + const boost::system::error_code& ec) { + boost::system::error_code run_ec; + StartClient(std::to_string(server_port), client_callback, run_ec); + EXPECT_EQ(0, run_ec.value()); + buggy_client.close(run_ec); + }; + + buggy_client.async_connect(server_ep, connect_callback); + + // Wait client action + WaitNotification(); + + EXPECT_TRUE(IsNotificationSuccess()) << "Client connection failed"; + + StopTimer(); + StopServer(); + StopClient(); } diff --git a/src/tests/services/CMakeLists.txt b/src/tests/services/CMakeLists.txt index 66ecf8f7..211e3163 100644 --- a/src/tests/services/CMakeLists.txt +++ b/src/tests/services/CMakeLists.txt @@ -28,12 +28,33 @@ set(SOCKS_HELPERS_FILES "socks_helpers.cpp") set(SERVICE_TESTS_FILES + "datagram_fixture_test.h" "service_fixture_test.h" "process_fixture_test.h" + "socks_fixture_test.h" + "stream_fixture_test.h" ) # --- Service tests +# --- Option parser + +add_target("option_parser_tests" + TYPE + executable ${EXEC_FLAG} TEST + LINKS + ${Boost_LIBRARIES} + ${PLATFORM_SPECIFIC_LIB_DEP} + lib_ssf_network + PREFIX_SKIP .*/src + HEADER_FILTER "\\.h(h|m|pp|xx|\\+\\+)?" + FILES + "option_parser_tests.cpp" + ${USER_SERVICE_FILES} + ${COMMON_ERROR_FILES} +) +project_group(${services_tests_group_name} option_parser_tests) + # --- Socks test set(socks_test_source_files "socks_tests.cpp" diff --git a/src/tests/services/datagram_fixture_test.h b/src/tests/services/datagram_fixture_test.h new file mode 100644 index 00000000..08456b6a --- /dev/null +++ b/src/tests/services/datagram_fixture_test.h @@ -0,0 +1,62 @@ +#ifndef TESTS_SERVICES_DATAGRAM_FIXTURE_TEST_H_ +#define TESTS_SERVICES_DATAGRAM_FIXTURE_TEST_H_ + +#include + +#include +#include + +#include "tests/services/service_fixture_test.h" +#include "tests/services/udp_helpers.h" + +template