diff --git a/.gitignore b/.gitignore index 8745738..d431297 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /build/ -/.vscode/ \ No newline at end of file +/.vscode/ +*.pem \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index c86e64f..28c9db1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(webfuse_static STATIC src/webfuse/ws/client.cpp src/webfuse/ws/messagewriter.cpp src/webfuse/ws/messagereader.cpp + src/webfuse/ws/url.cpp ) target_include_directories(webfuse_static PUBLIC src) diff --git a/script/create_cert.sh b/script/create_cert.sh new file mode 100755 index 0000000..26115bb --- /dev/null +++ b/script/create_cert.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +openssl req -x509 -newkey rsa:4096 \ + -keyout server-key.pem \ + -out server-cert.pem \ + -days 365 -nodes -batch \ + -subj /CN=localhost diff --git a/src/provider_main.cpp b/src/provider_main.cpp index fa0d22f..c992ae2 100644 --- a/src/provider_main.cpp +++ b/src/provider_main.cpp @@ -33,6 +33,7 @@ public: { {"path" , required_argument, nullptr, 'p'}, {"url" , required_argument, nullptr, 'u'}, + {"ca-path", required_argument, nullptr, 'a'}, {"version", no_argument , nullptr, 'v'}, {"help" , no_argument , nullptr, 'h'}, {nullptr , 0 , nullptr, 0 } @@ -44,7 +45,7 @@ public: while (!finished) { int option_index = 0; - const int c = getopt_long(argc, argv, "p:u:vh", long_options, &option_index); + const int c = getopt_long(argc, argv, "p:u:a:vh", long_options, &option_index); switch (c) { case -1: @@ -56,6 +57,9 @@ public: case 'u': url = optarg; break; + case 'a': + ca_path = optarg; + break; case 'h': cmd = command::show_help; break; @@ -81,6 +85,7 @@ public: std::string base_path; std::string url; + std::string ca_path; command cmd; int exit_code; }; @@ -91,11 +96,12 @@ void print_usage() expose a local directory via webfuse2 Usage: - webfuse-provider -u [-p ] + webfuse-provider -u [-p ] [-a ] Options: --url, -u set url of webfuse2 service --path, -p set path of directory to expose (default: .) + --ca-path, -a set path of ca file (default: not set) --version, -v print version and quit --help, -h print this message and quit @@ -439,7 +445,7 @@ int main(int argc, char* argv[]) signal(SIGTERM, &on_signal); filesystem fs(ctx.base_path); - webfuse::provider provider(fs); + webfuse::provider provider(fs, ctx.ca_path); provider.set_connection_listener([](bool connected) { if (!connected) { diff --git a/src/webfuse/provider.cpp b/src/webfuse/provider.cpp index 05cad76..9ca6fee 100644 --- a/src/webfuse/provider.cpp +++ b/src/webfuse/provider.cpp @@ -13,9 +13,9 @@ namespace webfuse class provider::detail { public: - detail(filesystem_i & fs) + detail(filesystem_i & fs, std::string const & ca_path) : fs_(fs) - , client([this](auto& reader) { return this->on_message(reader); }) + , client(ca_path, [this](auto& reader) { return this->on_message(reader); }) { } @@ -375,8 +375,8 @@ private: ws_client client; }; -provider::provider(filesystem_i & fs) -: d(new detail(fs)) +provider::provider(filesystem_i & fs, std::string const & ca_path) +: d(new detail(fs, ca_path)) { } diff --git a/src/webfuse/provider.hpp b/src/webfuse/provider.hpp index 7c10a8a..4748d87 100644 --- a/src/webfuse/provider.hpp +++ b/src/webfuse/provider.hpp @@ -13,7 +13,7 @@ class provider provider(provider const &) = delete; provider& operator=(provider const &) = delete; public: - provider(filesystem_i & fs); + provider(filesystem_i & fs, std::string const & ca_path); ~provider(); provider(provider && other); provider& operator=(provider && other); diff --git a/src/webfuse/ws/client.cpp b/src/webfuse/ws/client.cpp index e164362..87dae9d 100644 --- a/src/webfuse/ws/client.cpp +++ b/src/webfuse/ws/client.cpp @@ -1,4 +1,6 @@ #include "webfuse/ws/client.hpp" +#include "webfuse/ws/url.hpp" + #include #include #include @@ -106,8 +108,10 @@ class ws_client::detail detail(detail &&) = delete; detail& operator=(detail &&) = delete; public: - detail(ws_client_handler handler) + detail(std::string const & ca_path, ws_client_handler handler) { + lws_set_log_level(0, nullptr); + memset(reinterpret_cast(protocols), 0, sizeof(lws_protocols) * 2); protocols[0].callback = &webfuse_client_callback; protocols[0].name = "webfuse2-client"; @@ -119,12 +123,22 @@ public: info.protocols = protocols; info.uid = -1; info.gid = -1; + info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS; data.handler = handler; data.connection_listener = [](bool){ }; data.connection = nullptr; context = lws_create_context(&info); + + struct lws_vhost * vhost = lws_create_vhost(context, &info); + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + if (!ca_path.empty()) + { + info.client_ssl_ca_filepath = ca_path.c_str(); + } + + lws_init_vhost_client_ssl(&info, vhost); } ~detail() @@ -134,15 +148,17 @@ public: void connect(std::string const & url) { + ws_url parsed_url(url); + lws_client_connect_info info; memset(reinterpret_cast(&info), 0, sizeof(lws_client_connect_info)); info.context = context; - info.port = 8081; //NOLINT(readability-magic-numbers) - info.address = "localhost"; - info.host = "localhost"; - info.path = "/"; - info.origin = "localhost"; - info.ssl_connection = 0; + info.port = parsed_url.port; + info.address = parsed_url.hostname.c_str(); + info.host = info.address; + info.path = parsed_url.path.c_str(); + info.origin = info.address; + info.ssl_connection = (parsed_url.use_tls) ? LCCSCF_USE_SSL : 0; info.protocol = "webfuse2"; info.local_protocol_name = "webfuse2-client"; info.pwsi = &data.connection; @@ -172,8 +188,8 @@ private: user_data data; }; -ws_client::ws_client(ws_client_handler handler) -: d(new detail(handler)) +ws_client::ws_client(std::string const & ca_path, ws_client_handler handler) +: d(new detail(ca_path, handler)) { } diff --git a/src/webfuse/ws/client.hpp b/src/webfuse/ws/client.hpp index 17fb475..0fa3161 100644 --- a/src/webfuse/ws/client.hpp +++ b/src/webfuse/ws/client.hpp @@ -16,7 +16,7 @@ class ws_client ws_client(ws_client const &) = delete; ws_client& operator=(ws_client const &) = delete; public: - ws_client(ws_client_handler handler); + ws_client(std::string const & ca_path, ws_client_handler handler); ~ws_client(); ws_client(ws_client && other); ws_client& operator=(ws_client && other); diff --git a/src/webfuse/ws/server.cpp b/src/webfuse/ws/server.cpp index 651a62c..199e7ab 100644 --- a/src/webfuse/ws/server.cpp +++ b/src/webfuse/ws/server.cpp @@ -164,6 +164,8 @@ public: detail(ws_config const & config) : shutdown_requested(false) { + lws_set_log_level(0, nullptr); + memset(reinterpret_cast(protocols), 0, sizeof(protocols)); protocols[0].name = "webfuse2"; protocols[0].callback = &ws_server_callback; @@ -173,15 +175,21 @@ public: memset(reinterpret_cast(&info), 0, sizeof(info)); info.port = config.port; info.protocols = protocols; - info.vhost_name = "localhost"; + info.vhost_name = config.vhost_name.c_str(); info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE | LWS_SERVER_OPTION_EXPLICIT_VHOSTS; + if (config.use_tls) + { + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.ssl_cert_filepath = config.cert_path.c_str(); + info.ssl_private_key_filepath = config.key_path.c_str(); + } + context = lws_create_context(&info); lws_create_vhost(context, &info); // lws_vhost * const vhost = lws_create_vhost(context, &info); - // port = lws_get_vhost_port(vhost); - + // int port = lws_get_vhost_port(vhost); thread = std::thread([this]() { while (!shutdown_requested) diff --git a/src/webfuse/ws/url.cpp b/src/webfuse/ws/url.cpp new file mode 100644 index 0000000..f024265 --- /dev/null +++ b/src/webfuse/ws/url.cpp @@ -0,0 +1,70 @@ +#include "webfuse/ws/url.hpp" + +#include +#include + +namespace +{ + +bool starts_with(std::string const & value, std::string const & prefix) +{ + return (0 == value.find(prefix)); +} + +std::string parse_protocol(std::string const &url, bool &use_tls) +{ + if (starts_with(url, "ws://")) + { + use_tls = false; + return url.substr(strlen("ws://")); + } + + if (starts_with(url, "wss://")) + { + use_tls = true; + return url.substr(strlen("wss://")); + } + + throw std::runtime_error("unknown protocol"); +} + + +} + +namespace webfuse +{ + +constexpr uint16_t const ws_port = 80; +constexpr uint16_t const wss_port = 443; + +ws_url::ws_url(std::string const & url) +{ + auto remainder = parse_protocol(url, use_tls); + + auto const path_start = remainder.find('/'); + if (path_start != std::string::npos) + { + path = remainder.substr(path_start); + remainder = remainder.substr(0, path_start); + } + else + { + path = "/"; + } + + auto const port_start = remainder.find(':'); + if (port_start != std::string::npos) + { + auto const port_str = remainder.substr(port_start + 1); + port = static_cast(std::stoi(port_str)); + hostname = remainder.substr(0, port_start); + } + else + { + port = (use_tls) ? wss_port : ws_port; + hostname = remainder; + } +} + + +} \ No newline at end of file diff --git a/src/webfuse/ws/url.hpp b/src/webfuse/ws/url.hpp new file mode 100644 index 0000000..d7b07c7 --- /dev/null +++ b/src/webfuse/ws/url.hpp @@ -0,0 +1,24 @@ +#ifndef WEBFUSE_URL_HPP +#define WEBFUSE_URL_HPP + +#include +#include + +namespace webfuse +{ + +class ws_url +{ +public: + ws_url(std::string const & url); + ~ws_url() = default; + + bool use_tls; + std::string hostname; + uint16_t port; + std::string path; +}; + +} + +#endif diff --git a/test-src/integration/webfuse/test/fixture.cpp b/test-src/integration/webfuse/test/fixture.cpp index 58ad574..1e286fc 100644 --- a/test-src/integration/webfuse/test/fixture.cpp +++ b/test-src/integration/webfuse/test/fixture.cpp @@ -10,7 +10,7 @@ namespace webfuse fixture::fixture(filesystem_i & fs) : shutdown_requested(false) , provider_running(false) -, fs_provider(fs) +, fs_provider(fs, "") , app(working_dir.name()) { fs_provider.set_connection_listener([this](bool is_connected) {