diff options
author | Claudius "keldu" Holeksa <mail@keldu.de> | 2023-12-04 12:18:14 +0100 |
---|---|---|
committer | Claudius "keldu" Holeksa <mail@keldu.de> | 2023-12-04 12:18:14 +0100 |
commit | a14896f9ed209dd3f9597722e5a5697bd7dbf531 (patch) | |
tree | 089ca5cbbd206d1921f8f6b53292f5bc1902ca5c /modules/io-tls | |
parent | 84ecdcbca9e55b1f57fbb832e12ff4fdbb86e7c9 (diff) |
meta: Renamed folder containing source
Diffstat (limited to 'modules/io-tls')
-rw-r--r-- | modules/io-tls/.nix/derivation.nix | 32 | ||||
-rw-r--r-- | modules/io-tls/SConscript | 38 | ||||
-rw-r--r-- | modules/io-tls/SConstruct | 66 | ||||
-rw-r--r-- | modules/io-tls/tls.cpp | 252 | ||||
-rw-r--r-- | modules/io-tls/tls.h | 68 |
5 files changed, 456 insertions, 0 deletions
diff --git a/modules/io-tls/.nix/derivation.nix b/modules/io-tls/.nix/derivation.nix new file mode 100644 index 0000000..23d8ba6 --- /dev/null +++ b/modules/io-tls/.nix/derivation.nix @@ -0,0 +1,32 @@ +{ lib +, stdenv +, scons +, clang-tools +, version +, forstio +, gnutls +}: + +let + +in stdenv.mkDerivation { + pname = "forstio-io-tls"; + inherit version; + src = ./..; + + enableParallelBuilding = true; + + nativeBuildInputs = [ + scons + clang-tools + ]; + + buildInputs = [ + forstio.core + forstio.async + forstio.io + gnutls + ]; + + outputs = ["out" "dev"]; +} diff --git a/modules/io-tls/SConscript b/modules/io-tls/SConscript new file mode 100644 index 0000000..4f88f37 --- /dev/null +++ b/modules/io-tls/SConscript @@ -0,0 +1,38 @@ +#!/bin/false + +import os +import os.path +import glob + + +Import('env') + +dir_path = Dir('.').abspath + +# Environment for base library +io_tls_env = env.Clone(); + +io_tls_env.sources = sorted(glob.glob(dir_path + "/*.cpp")) +io_tls_env.headers = sorted(glob.glob(dir_path + "/*.h")) + +env.sources += io_tls_env.sources; +env.headers += io_tls_env.headers; + +## Shared lib +objects_shared = [] +io_tls_env.add_source_files(objects_shared, io_tls_env.sources, shared=True); +io_tls_env.library_shared = io_tls_env.SharedLibrary('#build/forstio-io-tls', [objects_shared]); + +## Static lib +objects_static = [] +io_tls_env.add_source_files(objects_static, io_tls_env.sources, shared=False); +io_tls_env.library_static = io_tls_env.StaticLibrary('#build/forstio-io-tls', [objects_static]); + +# Set Alias +env.Alias('library_io_tls', [io_tls_env.library_shared, io_tls_env.library_static]); + +env.targets += ['library_io_tls']; + +# Install +env.Install('$prefix/lib/', [io_tls_env.library_shared, io_tls_env.library_static]); +env.Install('$prefix/include/forstio/io/tls/', [io_tls_env.headers]); diff --git a/modules/io-tls/SConstruct b/modules/io-tls/SConstruct new file mode 100644 index 0000000..fbd8657 --- /dev/null +++ b/modules/io-tls/SConstruct @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +import glob +import re + + +if sys.version_info < (3,): + def isbasestring(s): + return isinstance(s,basestring) +else: + def isbasestring(s): + return isinstance(s, (str,bytes)) + +def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): + + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = sorted(glob.glob(dir_path+"/"+filetype)) + + for path in filetype: + target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) + if shared: + target_name+='.os' + sources.append( self.SharedObject( target=target_name, source=path ) ) + else: + target_name+='.o' + sources.append( self.StaticObject( target=target_name, source=path ) ) + pass + +def isAbsolutePath(key, dirname, env): + assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) + +env_vars = Variables( + args=ARGUMENTS +) + +env_vars.Add('prefix', + help='Installation target location of build results and headers', + default='/usr/local/', + validator=isAbsolutePath +) + +env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], + CPPDEFINES=['SAW_UNIX'], + CXXFLAGS=['-std=c++20','-g','-Wall','-Wextra'], + LIBS=['gnutls','forstio-io']) +env.__class__.add_source_files = add_kel_source_files +env.Tool('compilation_db'); +env.cdb = env.CompilationDatabase('compile_commands.json'); + +env.objects = []; +env.sources = []; +env.headers = []; +env.targets = []; + +Export('env') +SConscript('SConscript') + +env.Alias('cdb', env.cdb); +env.Alias('all', [env.targets]); +env.Default('all'); + +env.Alias('install', '$prefix') diff --git a/modules/io-tls/tls.cpp b/modules/io-tls/tls.cpp new file mode 100644 index 0000000..9fa143c --- /dev/null +++ b/modules/io-tls/tls.cpp @@ -0,0 +1,252 @@ +#include "tls.h" + +#include <gnutls/gnutls.h> +#include <gnutls/x509.h> + +#include <forstio/io/io_helpers.h> + +#include <cassert> + +#include <iostream> + +namespace saw { + +class tls::impl { +public: + gnutls_certificate_credentials_t xcred; + +public: + impl() { + gnutls_global_init(); + gnutls_certificate_allocate_credentials(&xcred); + gnutls_certificate_set_x509_system_trust(xcred); + } + + ~impl() { + gnutls_certificate_free_credentials(xcred); + gnutls_global_deinit(); + } +}; + +static ssize_t forst_tls_push_func(gnutls_transport_ptr_t p, const void *data, + size_t size); +static ssize_t forst_tls_pull_func(gnutls_transport_ptr_t p, void *data, size_t size); + +tls::tls() : impl_{heap<tls::impl>()} {} + +tls::~tls() {} + +tls::impl &tls::get_impl() { return *impl_; } + +class tls_io_stream final : public io_stream { +private: + own<io_stream> internal; + gnutls_session_t session_handle; + +public: + tls_io_stream(own<io_stream> internal_) : internal{std::move(internal_)} {} + + ~tls_io_stream() { gnutls_bye(session_handle, GNUTLS_SHUT_RDWR); } + + error_or<size_t> read(void *buffer, size_t length) override { + ssize_t size = gnutls_record_recv(session_handle, buffer, length); + if (size < 0) { + if(gnutls_error_is_fatal(size) == 0){ + return make_error<err::recoverable>("Recoverable error on read in gnutls. TODO better error msg handling"); + // Leaving proper message handling done in previous error framework + //return recoverable_error([size](){return std::string{"Read recoverable Error "}+std::string{gnutls_strerror(size)};}, "Error read r"); + }else{ + return make_error<err::critical>("Fatal error on read in gnutls. TODO better error msg handling"); + } + }else if(size == 0){ + return make_error<err::disconnected>(); + } + + return static_cast<size_t>(length); + } + + conveyor<void> read_ready() override { return internal->read_ready(); } + + conveyor<void> on_read_disconnected() override { + return internal->on_read_disconnected(); + } + + error_or<size_t> write(const void *buffer, size_t length) override { + ssize_t size = gnutls_record_send(session_handle, buffer, length); + if(size < 0){ + if(gnutls_error_is_fatal(size) == 0){ + return make_error<err::recoverable>("Recoverable error on write in gnutls. TODO better error msg handling"); + }else{ + return make_error<err::critical>("Fatal error on write in gnutls. TODO better error msg handling"); + } + } + + return static_cast<size_t>(size); + } + + conveyor<void> write_ready() override { return internal->write_ready(); } + + gnutls_session_t &session() { return session_handle; } +}; + +tls_server::tls_server(own<server> srv) : internal{std::move(srv)} {} + +conveyor<own<io_stream>> tls_server::accept() { + SAW_ASSERT(internal) { return conveyor<own<io_stream>>{fix_void<own<io_stream>>{nullptr}}; } + return internal->accept().then([](own<io_stream> stream) -> own<io_stream> { + /// @todo handshake + + + return heap<tls_io_stream>(std::move(stream)); + }); +} + +namespace { +/* +* Small helper for setting up the nonblocking connection handshake +*/ +struct tls_client_stream_helper { +public: + own<conveyor_feeder<own<io_stream>>> feeder; + conveyor_sink connection_sink; + conveyor_sink stream_reader; + conveyor_sink stream_writer; + + own<tls_io_stream> stream = nullptr; +public: + tls_client_stream_helper(own<conveyor_feeder<own<io_stream>>> f): + feeder{std::move(f)} + {} + + void setupTurn(){ + SAW_ASSERT(stream){ + return; + } + + stream_reader = stream->read_ready().then([this](){ + turn(); + }).sink(); + + stream_writer = stream->write_ready().then([this](){ + turn(); + }).sink(); + } + + void turn(){ + if(stream){ + // Guarantee that the receiving end is already setup + SAW_ASSERT(feeder){ + return; + } + + auto &session = stream->session(); + + int ret; + do { + ret = gnutls_handshake(session); + } while ( (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED) && gnutls_error_is_fatal(ret) == 0); + + if(gnutls_error_is_fatal(ret)){ + feeder->fail(make_error<err::critical>("Couldn't create Tls connection")); + stream = nullptr; + }else if(ret == GNUTLS_E_SUCCESS){ + feeder->feed(std::move(stream)); + } + } + } +}; +} + +own<server> tls_network::listen(network_address& address) { + return heap<tls_server>(internal.listen(address)); +} + +conveyor<own<io_stream>> tls_network::connect(network_address& address) { + // Helper setups + auto caf = new_conveyor_and_feeder<own<io_stream>>(); + own<tls_client_stream_helper> helper = heap<tls_client_stream_helper>(std::move(caf.feeder)); + tls_client_stream_helper* hlp_ptr = helper.get(); + + // Conveyor entangled structure + auto prim_conv = internal.connect(address).then([this, hlp_ptr, addr = address.address()]( + own<io_stream> stream) -> error_or<void> { + io_stream* inner_stream = stream.get(); + auto tls_stream = heap<tls_io_stream>(std::move(stream)); + + auto &session = tls_stream->session(); + + gnutls_init(&session, GNUTLS_CLIENT); + + gnutls_server_name_set(session, GNUTLS_NAME_DNS, addr.c_str(), + addr.size()); + + gnutls_set_default_priority(session); + gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + tls_.get_impl().xcred); + gnutls_session_set_verify_cert(session, addr.c_str(), 0); + + gnutls_transport_set_ptr(session, reinterpret_cast<gnutls_transport_ptr_t>(inner_stream)); + gnutls_transport_set_push_function(session, forst_tls_push_func); + gnutls_transport_set_pull_function(session, forst_tls_pull_func); + + // gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + hlp_ptr->stream = std::move(tls_stream); + hlp_ptr->setupTurn(); + hlp_ptr->turn(); + + return void_t{}; + }); + + helper->connection_sink = prim_conv.sink(); + + return caf.conveyor.attach(std::move(helper)); +} + +own<datagram> tls_network::datagram(network_address& address){ + ///@unimplemented + return nullptr; +} + +static ssize_t forst_tls_push_func(gnutls_transport_ptr_t p, const void *data, + size_t size) { + io_stream *stream = reinterpret_cast<io_stream *>(p); + if (!stream) { + return -1; + } + + error_or<size_t> length = stream->write(data, size); + if (length.is_error() || !length.is_value()) { + return -1; + } + + return static_cast<ssize_t>(length.get_value()); +} + +static ssize_t forst_tls_pull_func(gnutls_transport_ptr_t p, void *data, size_t size) { + io_stream *stream = reinterpret_cast<io_stream *>(p); + if (!stream) { + return -1; + } + + error_or<size_t> length = stream->read(data, size); + if (length.is_error() || !length.is_value()) { + return -1; + } + + return static_cast<ssize_t>(length.get_value()); +} + +tls_network::tls_network(tls& tls_, network &network) : tls_{tls_},internal{network} {} + +conveyor<own<network_address>> tls_network::resolve_address(const std::string &addr, + uint16_t port) { + /// @todo tls server name needed. Check validity. Won't matter later on, because gnutls should fail anyway. But + /// it's better to find the error source sooner rather than later + return internal.resolve_address(addr, port); +} + +std::optional<own<tls_network>> setup_tls_network(network &network) { + return std::nullopt; +} +} // namespace saw diff --git a/modules/io-tls/tls.h b/modules/io-tls/tls.h new file mode 100644 index 0000000..74b39ff --- /dev/null +++ b/modules/io-tls/tls.h @@ -0,0 +1,68 @@ +#pragma once + +#include <forstio/core/common.h> +#include <forstio/io/io.h> + +#include <optional> +#include <variant> + +namespace saw { +class tls; + +class tls_server final : public server { +private: + own<server> internal; + +public: + tls_server(own<server> srv); + + conveyor<own<io_stream>> accept() override; +}; + +class tls_network final : public network { +private: + tls& tls_; + network &internal; +public: + tls_network(tls& tls_, network &network_); + + conveyor<own<network_address>> resolve_address(const std::string &addr, uint16_t port = 0) override; + + own<server> listen(network_address& address) override; + + conveyor<own<io_stream>> connect(network_address& address) override; + + own<class datagram> datagram(network_address& address) override; +}; + +/** +* tls context class. +* Provides tls network class which ensures the usage of tls encrypted connections +*/ +class tls { +private: + class impl; + own<impl> impl_; +public: + tls(); + ~tls(); + + struct version { + struct tls_1_0{}; + struct tls_1_1{}; + struct tls_1_2{}; + }; + + struct options { + public: + version version; + }; + + impl &get_impl(); +private: + options options_; +}; + +std::optional<own<tls_network>> setup_tls_network(network &network); + +} // namespace saw |