Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package libfilezilla for openSUSE:Factory checked in at 2021-05-07 16:45:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/libfilezilla (Old) and /work/SRC/openSUSE:Factory/.libfilezilla.new.2988 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "libfilezilla" Fri May 7 16:45:58 2021 rev:39 rq:891273 version:0.28.0 Changes: -------- --- /work/SRC/openSUSE:Factory/libfilezilla/libfilezilla.changes 2021-03-20 21:26:36.413221010 +0100 +++ /work/SRC/openSUSE:Factory/.libfilezilla.new.2988/libfilezilla.changes 2021-05-07 16:46:24.276151980 +0200 @@ -1,0 +2,15 @@ +Fri May 7 10:49:25 UTC 2021 - ecsos <[email protected]> + +- Update to 0.28.0 + * New features: + - Added fz::hostname_lookup + - Added fz::datetime::set_rfc3339 + - Added fz::load_certificates and fz::load_certificates_file + - Added fz::base64_encode_append + * Bugfixes and minor changes: + - Moved some common functionality shared by multiple layers to + socket_layer itself + - Fixed a crash with older versions of GnuTLS if the system + trust store cannot be loaded + +------------------------------------------------------------------- Old: ---- libfilezilla-0.27.1.tar.bz2 New: ---- libfilezilla-0.28.0.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ libfilezilla.spec ++++++ --- /var/tmp/diff_new_pack.ueSkzQ/_old 2021-05-07 16:46:24.756149805 +0200 +++ /var/tmp/diff_new_pack.ueSkzQ/_new 2021-05-07 16:46:24.760149787 +0200 @@ -16,11 +16,11 @@ # -%define major 12 +%define major 13 %define libname %{name}%{major} %define develname %{name}-devel Name: libfilezilla -Version: 0.27.1 +Version: 0.28.0 Release: 0 Summary: C++ library for filezilla License: GPL-2.0-or-later ++++++ libfilezilla-0.27.1.tar.bz2 -> libfilezilla-0.28.0.tar.bz2 ++++++ ++++ 1634 lines of diff (skipped) ++++ retrying with extended exclude list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/NEWS new/libfilezilla-0.28.0/NEWS --- old/libfilezilla-0.27.1/NEWS 2021-03-15 09:01:00.000000000 +0100 +++ new/libfilezilla-0.28.0/NEWS 2021-05-04 10:46:55.000000000 +0200 @@ -1,3 +1,12 @@ +0.28.0 (2021-05-03) + ++ Added fz::hostname_lookup ++ Added fz::datetime::set_rfc3339 ++ Added fz::load_certificates and fz::load_certificates_file ++ Added fz::base64_encode_append +- Moved some common functionality shared by multiple layers to socket_layer itself +- Fixed a crash with older versions of GnuTLS if the system trust store cannot be loaded + 0.27.1 (2021-03-15) - Fixed a socket event sequencing invariant violation in tls_layer diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/configure.ac new/libfilezilla-0.28.0/configure.ac --- old/libfilezilla-0.27.1/configure.ac 2021-03-15 09:01:00.000000000 +0100 +++ new/libfilezilla-0.28.0/configure.ac 2021-05-04 10:46:55.000000000 +0200 @@ -1,4 +1,4 @@ -???AC_INIT([libfilezilla],[0.27.1],[[email protected]],[],[https://lib.filezilla-project.org/]) +???AC_INIT([libfilezilla],[0.28.0],[[email protected]],[],[https://lib.filezilla-project.org/]) # Update the version information only immediately before a public release of your software # If the library source code has changed at all since the last update, then increment revision (???c:r:a??? becomes ???c:r+1:a???). @@ -6,7 +6,7 @@ # If any interfaces have been added since the last public release, then increment age. # If any interfaces have been removed or changed since the last public release, then set age to 0. # CURRENT:REVISION:AGE -LIBRARY_VERSION=12:1:0 +LIBRARY_VERSION=13:0:0 AC_CONFIG_HEADERS([config/config.hpp]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/Makefile.am new/libfilezilla-0.28.0/lib/Makefile.am --- old/libfilezilla-0.27.1/lib/Makefile.am 2021-03-04 14:27:18.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/Makefile.am 2021-05-04 10:46:55.000000000 +0200 @@ -9,6 +9,7 @@ event_loop.cpp \ file.cpp \ hash.cpp \ + hostname_lookup.cpp \ invoker.cpp \ iputils.cpp \ local_filesys.cpp \ @@ -45,6 +46,7 @@ libfilezilla/file.hpp \ libfilezilla/format.hpp \ libfilezilla/hash.hpp \ + libfilezilla/hostname_lookup.hpp \ libfilezilla/invoker.hpp \ libfilezilla/iputils.hpp \ libfilezilla/libfilezilla.hpp \ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/buffer.cpp new/libfilezilla-0.28.0/lib/buffer.cpp --- old/libfilezilla-0.27.1/lib/buffer.cpp 2020-12-17 14:46:00.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/buffer.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -2,6 +2,7 @@ #include <algorithm> #include <cstdlib> +#include <limits> #include <string.h> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/encode.cpp new/libfilezilla-0.28.0/lib/encode.cpp --- old/libfilezilla-0.27.1/lib/encode.cpp 2020-07-07 14:06:30.000000000 +0200 +++ new/libfilezilla-0.28.0/lib/encode.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -4,7 +4,7 @@ namespace { template<typename DataContainer> -std::string base64_encode_impl(DataContainer const& in, base64_type type, bool pad) +void base64_encode_impl(std::string & out, DataContainer const& in, base64_type type, bool pad) { static_assert(sizeof(typename DataContainer::value_type) == 1, "Bad container type"); @@ -13,12 +13,13 @@ ? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; - std::string ret; - size_t len = in.size(); size_t pos{}; - ret.reserve(((len + 2) / 3) * 4); + size_t const newcap = out.size() + ((len + 2) / 3) * 4; + if (out.capacity() < newcap) { + out.reserve(newcap); + } while (len >= 3) { len -= 3; @@ -26,42 +27,49 @@ auto const c2 = static_cast<unsigned char>(in[pos++]); auto const c3 = static_cast<unsigned char>(in[pos++]); - ret += base64_chars[(c1 >> 2) & 0x3fu]; - ret += base64_chars[((c1 & 0x3u) << 4) | ((c2 >> 4) & 0xfu)]; - ret += base64_chars[((c2 & 0xfu) << 2) | ((c3 >> 6) & 0x3u)]; - ret += base64_chars[(c3 & 0x3fu)]; + out += base64_chars[(c1 >> 2) & 0x3fu]; + out += base64_chars[((c1 & 0x3u) << 4) | ((c2 >> 4) & 0xfu)]; + out += base64_chars[((c2 & 0xfu) << 2) | ((c3 >> 6) & 0x3u)]; + out += base64_chars[(c3 & 0x3fu)]; } if (len) { auto const c1 = static_cast<unsigned char>(in[pos++]); - ret += base64_chars[(c1 >> 2) & 0x3fu]; + out += base64_chars[(c1 >> 2) & 0x3fu]; if (len == 2) { auto const c2 = static_cast<unsigned char>(in[pos++]); - ret += base64_chars[((c1 & 0x3u) << 4) | ((c2 >> 4) & 0xfu)]; - ret += base64_chars[(c2 & 0xfu) << 2]; + out += base64_chars[((c1 & 0x3u) << 4) | ((c2 >> 4) & 0xfu)]; + out += base64_chars[(c2 & 0xfu) << 2]; } else { - ret += base64_chars[(c1 & 0x3u) << 4]; + out += base64_chars[(c1 & 0x3u) << 4]; if (pad) { - ret += '='; + out += '='; } } if (pad) { - ret += '='; + out += '='; } } - - return ret; } } std::string base64_encode(std::string_view const& in, base64_type type, bool pad) { - return base64_encode_impl(in, type, pad); + std::string ret; + base64_encode_impl(ret, in, type, pad); + return ret; } std::string base64_encode(std::vector<uint8_t> const& in, base64_type type, bool pad) { - return base64_encode_impl(in, type, pad); + std::string ret; + base64_encode_impl(ret, in, type, pad); + return ret; +} + +void base64_encode_append(std::string& result, std::string_view const& in, base64_type type, bool pad) +{ + base64_encode_impl(result, in, type, pad); } namespace { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/hostname_lookup.cpp new/libfilezilla-0.28.0/lib/hostname_lookup.cpp --- old/libfilezilla-0.27.1/lib/hostname_lookup.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/hostname_lookup.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -0,0 +1,197 @@ +#include "libfilezilla/hostname_lookup.hpp" + +#ifdef FZ_WINDOWS +#include "libfilezilla/private/windows.hpp" +#include <winsock2.h> +#include <ws2tcpip.h> +#include <mstcpip.h> +#endif + +#include "libfilezilla/socket.hpp" +#include "libfilezilla/thread_pool.hpp" + +#ifndef FZ_WINDOWS +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#endif + +namespace fz { + +class hostname_lookup::impl +{ +public: + impl(hostname_lookup * parent, thread_pool& pool, event_handler* h) + : parent_(parent), pool_(pool), handler_(h) + { + } + + bool spawn() { + if (!thread_) { + thread_ = pool_.spawn([this](){ entry(); }); + } + return thread_.operator bool(); + } + + void entry(); + void do_lookup(scoped_lock & l); + + mutex mtx_{false}; + hostname_lookup* parent_; + thread_pool & pool_; + event_handler* handler_{}; + condition cond_; + async_task thread_; + std::string host_; + address_type family_{}; +}; + +void hostname_lookup::impl::entry() +{ + scoped_lock l(mtx_); + if (thread_) { + do { + cond_.wait(l); + do_lookup(l); + + } while (thread_); + } + + l.unlock(); + delete this; +} + +int convert_msw_error_code(int error); + +void hostname_lookup::impl::do_lookup(scoped_lock& l) +{ + if (host_.empty()) { + return; + } + + l.unlock(); + + addrinfo hints{}; + switch (family_) { + case address_type::ipv4: + hints.ai_family = AF_INET; + break; + case address_type::ipv6: + hints.ai_family = AF_INET6; + break; + default: + hints.ai_family = AF_UNSPEC; + break; + } + hints.ai_socktype = SOCK_STREAM; +#ifdef AI_IDN + hints.ai_flags |= AI_IDN; +#endif + + addrinfo* addressList{}; + int res = getaddrinfo(host_.c_str(), nullptr, &hints, &addressList); + + l.lock(); + + if (!thread_) { + if (!res) { + freeaddrinfo(addressList); + } + return; + } + + std::vector<std::string> addrs; + if (res) { +#ifdef FZ_WINDOWS + res = convert_msw_error_code(res); +#endif + } + else { + for (addrinfo* addr = addressList; addr && !res; addr = addr->ai_next) { + auto s = socket::address_to_string(addr->ai_addr, addr->ai_addrlen, false); + if (!s.empty()) { + addrs.emplace_back(std::move(s)); + } + } + } + + freeaddrinfo(addressList); + + handler_->send_event<hostname_lookup_event>(parent_, res, std::move(addrs)); + host_.clear(); +} + +hostname_lookup::hostname_lookup(thread_pool& pool, event_handler& evt_handler) + : impl_(new impl(this, pool, &evt_handler)) +{ +} + +bool hostname_lookup::lookup(native_string const& host, address_type family) +{ + if (host.empty()) { + return false; + } + + scoped_lock l(impl_->mtx_); + if (!impl_->host_.empty()) { + return false; + } + + if (!impl_->spawn()) { + return false; + } + + impl_->host_ = fz::to_string(host); + impl_->family_ = family; + impl_->cond_.signal(l); + return true; +} + +namespace { +void filter_hostname_events(fz::hostname_lookup* lookup, fz::event_handler* handler) +{ + auto filter = [&](event_loop::Events::value_type const& ev) -> bool { + if (ev.first != handler) { + return false; + } + else if (ev.second->derived_type() != hostname_lookup_event::type()) { + return false; + } + return std::get<0>(static_cast<hostname_lookup_event const&>(*ev.second).v_) == lookup; + }; + + handler->event_loop_.filter_events(filter); +} +} + +hostname_lookup::~hostname_lookup() +{ + scoped_lock l(impl_->mtx_); + if (!impl_->thread_) { + l.unlock(); + delete impl_; + } + else { + filter_hostname_events(this, impl_->handler_); + + impl_->thread_.detach(); + impl_->cond_.signal(l); + } +} + +void hostname_lookup::reset() +{ + scoped_lock l(impl_->mtx_); + if (!impl_->thread_) { + return; + } + + filter_hostname_events(this, impl_->handler_); + if (!impl_->host_.empty()) { + impl_->thread_.detach(); + impl_->cond_.signal(l); + impl_ = new impl(this, impl_->pool_, impl_->handler_); + } +} + +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla/encode.hpp new/libfilezilla-0.28.0/lib/libfilezilla/encode.hpp --- old/libfilezilla-0.27.1/lib/libfilezilla/encode.hpp 2020-10-13 14:16:48.000000000 +0200 +++ new/libfilezilla-0.28.0/lib/libfilezilla/encode.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -114,6 +114,14 @@ std::string FZ_PUBLIC_SYMBOL base64_encode(std::vector<uint8_t> const& in, base64_type type = base64_type::standard, bool pad = true); /** + * \brief base64-encodes input and appends it to result. + * + * Multiple inputs concatenated this way cannot be passed to a single base64_decode. The parts need to be + * individually decoded. + */ +void FZ_PUBLIC_SYMBOL base64_encode_append(std::string& result, std::string_view const& in, base64_type type = base64_type::standard, bool pad = true); + +/** * \brief Decodes base64, ignores whitespace. Returns empty string on invalid input. * * Padding is optional, alphabet is auto-detected. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla/format.hpp new/libfilezilla-0.28.0/lib/libfilezilla/format.hpp --- old/libfilezilla-0.27.1/lib/libfilezilla/format.hpp 2020-10-13 14:16:48.000000000 +0200 +++ new/libfilezilla-0.28.0/lib/libfilezilla/format.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -7,7 +7,7 @@ #include <cstdlib> #include <type_traits> -#ifdef FORMAT_DEBUG +#ifdef LFZ_FORMAT_DEBUG #include <assert.h> #define format_assert(pred) assert((pred)) #else diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla/glue/wxinvoker.hpp new/libfilezilla-0.28.0/lib/libfilezilla/glue/wxinvoker.hpp --- old/libfilezilla-0.27.1/lib/libfilezilla/glue/wxinvoker.hpp 2021-03-04 14:27:18.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/libfilezilla/glue/wxinvoker.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -11,19 +11,25 @@ namespace fz { -/// \brief Alternative version of fz::invoke that accepts wxEvtHandler -template<typename F> -auto make_invoker(wxEvtHandler& handler, F && f) +/// \private +template<typename... Args> +std::function<void(Args...)> do_make_invoker(wxEvtHandler& handler, std::function<void(Args...)> && f) { - return [&handler, cf = std::forward<F>(f)](auto &&... args) mutable -> decltype(f(std::forward<decltype(args)>(args)...), void()) - { - auto cb = [cf = std::move(cf), targs = std::make_tuple(std::forward<decltype(args)>(args)...)] { + return [&handler, cf = f](Args&&... args) mutable { + auto cb = [cf, targs = std::make_tuple(std::forward<Args>(args)...)] { std::apply(cf, targs); }; handler.CallAfter(cb); }; } +/// \brief Alternative version of fz::invoke that accepts wxEvtHandler +template<typename F> +auto make_invoker(wxEvtHandler& handler, F && f) +{ + return do_make_invoker(handler, decltype(get_func_type(&F::operator()))(std::forward<F>(f))); +} + /// \brief Returns an invoker factory utilizing the event system of of wx. inline invoker_factory get_invoker_factory(wxEvtHandler& handler) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla/hostname_lookup.hpp new/libfilezilla-0.28.0/lib/libfilezilla/hostname_lookup.hpp --- old/libfilezilla-0.27.1/lib/libfilezilla/hostname_lookup.hpp 1970-01-01 01:00:00.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/libfilezilla/hostname_lookup.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -0,0 +1,46 @@ +#ifndef LIBFILEZILLA_HOSTNAME_LOOKUP_HEADER +#define LIBFILEZILLA_HOSTNAME_LOOKUP_HEADER + +/** \file + * \brief Header for the \ref fz::hostname_lookup class + */ + +#include "libfilezilla.hpp" +#include "iputils.hpp" +#include "event_handler.hpp" + +namespace fz { +class FZ_PUBLIC_SYMBOL hostname_lookup +{ +public: + hostname_lookup(thread_pool& pool, event_handler& evt_handler); + ~hostname_lookup(); + + hostname_lookup(hostname_lookup const&) = delete; + hostname_lookup& operator=(hostname_lookup const&) = delete; + + /** + * \brief Looks up the passed host + * + * If family is unknown, looks up both IPv4 and IPv6 addresses, if the operating system + * is configured to resolve IPv6 addresses. + * + * If the function returns true, wait for the \ref hostname_lookup_event before you can call it again. + */ + bool lookup(native_string const& host, address_type family = address_type::unknown); + + void reset(); + +private: + class impl; + impl* impl_{}; +}; + +/// \private +struct hostname_lookup_event_type {}; + +/// Results of hostname_lookup. On success, second argument is zero, otherwise an error code. +typedef simple_event<hostname_lookup_event_type, hostname_lookup*, int, std::vector<std::string>> hostname_lookup_event; +} + +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla/invoker.hpp new/libfilezilla-0.28.0/lib/libfilezilla/invoker.hpp --- old/libfilezilla-0.27.1/lib/libfilezilla/invoker.hpp 2021-03-04 14:27:18.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/libfilezilla/invoker.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -25,6 +25,24 @@ virtual void operator()(event_base const& ev) override; }; +/// \private +template<typename... Args> +std::function<void(Args...)> do_make_invoker(event_loop& loop, std::function<void(Args...)> && f) +{ + return [handler = thread_invoker(loop), f](Args&&... args) mutable { + auto cb = [f, targs = std::make_tuple(std::forward<Args>(args)...)] { + std::apply(f, targs); + }; + handler.send_event<invoker_event>(std::move(cb)); + }; +} + +// libc++ as shipped with Xcode does not have deduction guides for lambda -> std::function +// Help it along a bit. +/// \private +template<typename Ret, typename F, typename ... Args> +constexpr std::function<Ret(Args...)> get_func_type(Ret(F::*)(Args...) const); + /** * \brief Wraps the passed function, so that it is always invoked in the context of the loop. * @@ -35,19 +53,12 @@ template<typename F> auto make_invoker(event_loop& loop, F && f) { - return [handler = thread_invoker(loop), cf = std::forward<F>(f)](auto &&... args) mutable -> decltype(f(std::forward<decltype(args)>(args)...), void()) - { - auto cb = [cf = std::move(cf), targs = std::make_tuple(std::forward<decltype(args)>(args)...)] { - std::apply(cf, targs); - }; - handler.send_event<invoker_event>(std::move(cb)); - }; + return do_make_invoker(loop, decltype(get_func_type(&F::operator()))(std::forward<F>(f))); } - template<typename F> auto make_invoker(event_handler& h, F && f) { - return make_invoker(h.event_loop_, std::forward<F>(f)); + return do_make_invoker(h.event_loop_, decltype(get_func_type(&F::operator()))(std::forward<F>(f))); } @@ -61,6 +72,18 @@ */ invoker_factory FZ_PUBLIC_SYMBOL get_invoker_factory(event_loop& loop); +/// \private +template<typename... Args> +std::function<void(Args...)> do_make_invoker(invoker_factory const& inv, std::function<void(Args...)> && f) +{ + return [inv, f](Args&&... args) mutable { + auto cb = [f, targs = std::make_tuple(std::forward<Args>(args)...)] { + std::apply(f, targs); + }; + inv(cb); + }; +} + /** * \brief Creates an invoker using the given factory * @@ -71,13 +94,7 @@ template<typename F> auto make_invoker(invoker_factory const& inv, F && f) { - return [inv, cf = std::forward<F>(f)](auto &&... args) mutable -> decltype(f(std::forward<decltype(args)>(args)...), void()) - { - auto cb = [cf = std::move(cf), targs = std::make_tuple(std::forward<decltype(args)>(args)...)] { - std::apply(cf, targs); - }; - inv(cb); - }; + return do_make_invoker(inv, decltype(get_func_type(&F::operator()))(std::forward<F>(f))); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla/rate_limited_layer.hpp new/libfilezilla-0.28.0/lib/libfilezilla/rate_limited_layer.hpp --- old/libfilezilla-0.27.1/lib/libfilezilla/rate_limited_layer.hpp 2021-03-04 14:27:18.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/libfilezilla/rate_limited_layer.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -24,18 +24,6 @@ virtual int read(void* buffer, unsigned int size, int& error) override; virtual int write(void const* buffer, unsigned int size, int& error) override; - virtual socket_state get_state() const override { - return next_layer_.get_state(); - } - - virtual int connect(native_string const& host, unsigned int port, address_type family = address_type::unknown) override { - return next_layer_.connect(host, port, family); - } - - virtual int shutdown() override { - return next_layer_.shutdown(); - } - virtual void set_event_handler(event_handler* handler, socket_event_flag retrigger_block = socket_event_flag{}) override; protected: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla/socket.hpp new/libfilezilla-0.28.0/lib/libfilezilla/socket.hpp --- old/libfilezilla-0.27.1/lib/libfilezilla/socket.hpp 2021-03-04 14:27:18.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/libfilezilla/socket.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -94,7 +94,7 @@ } protected: - socket_event_source() = default; + socket_event_source() = delete; explicit socket_event_source(socket_event_source* root) : root_(root) {} @@ -364,6 +364,44 @@ virtual int read(void* buffer, unsigned int size, int& error) = 0; virtual int write(void const* buffer, unsigned int size, int& error) = 0; + template<typename T, std::enable_if_t<std::is_signed_v<T>, int> = 0> + int read(void* buffer, T size, int& error) + { + if (size < 0) { + error = EINVAL; + return -1; + } + + return read(buffer, static_cast<unsigned int>(size), error); + } + template<typename T, std::enable_if_t<std::is_unsigned_v<T> && (sizeof(T) > sizeof(unsigned int)), int> = 0> + int read(void* buffer, T size, int& error) + { + if (size > std::numeric_limits<unsigned int>::max()) { + size = std::numeric_limits<unsigned int>::max(); + } + return read(buffer, static_cast<unsigned int>(size), error); + } + + template<typename T, std::enable_if_t<std::is_signed_v<T>, int> = 0> + int write(void const* buffer, T size, int& error) + { + if (size < 0) { + error = EINVAL; + return -1; + } + + return write(buffer, static_cast<std::make_unsigned_t<T>>(size), error); + } + template<typename T, std::enable_if_t<std::is_unsigned_v<T> && (sizeof(T) > sizeof(unsigned int)), int> = 0> + int write(void const* buffer, T size, int& error) + { + if (size > std::numeric_limits<unsigned int>::max()) { + size = std::numeric_limits<unsigned int>::max(); + } + return write(buffer, static_cast<unsigned int>(size), error); + } + virtual void set_event_handler(event_handler* pEvtHandler, fz::socket_event_flag retrigger_block = fz::socket_event_flag{}) = 0; virtual native_string peer_host() const = 0; @@ -616,6 +654,18 @@ */ virtual int shutdown_read() override; + virtual int connect(native_string const& host, unsigned int port, address_type family = address_type::unknown) override { + return next_layer_.connect(host, port, family); + } + + virtual int shutdown() override { + return next_layer_.shutdown(); + } + + virtual socket_state get_state() const override { + return next_layer_.get_state(); + } + protected: /** * Call in a derived classes handler for fz::socket_event. Results in diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla/time.hpp new/libfilezilla-0.28.0/lib/libfilezilla/time.hpp --- old/libfilezilla-0.27.1/lib/libfilezilla/time.hpp 2020-10-13 14:16:49.000000000 +0200 +++ new/libfilezilla-0.28.0/lib/libfilezilla/time.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -248,6 +248,20 @@ bool set_rfc822(std::string_view const& str); bool set_rfc822(std::wstring_view const& str); + /** + * Parses a date in the format specified in RFC 3339 + * + * On errors, object gets clears + * + * \return whether setting the timestamp succeeded. On failure object gets cleared. + * + * \par Examples: + * 1985-04-12T23:20:50.52Z + * 1996-12-19T16:39:57-08:00 + */ + bool set_rfc3339(std::string_view const& str); + bool set_rfc3339(std::wstring_view const& str); + private: int FZ_PRIVATE_SYMBOL compare_slow(datetime const& op) const; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla/tls_info.hpp new/libfilezilla-0.28.0/lib/libfilezilla/tls_info.hpp --- old/libfilezilla-0.27.1/lib/libfilezilla/tls_info.hpp 2020-07-07 14:06:31.000000000 +0200 +++ new/libfilezilla-0.28.0/lib/libfilezilla/tls_info.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -8,6 +8,8 @@ #include "time.hpp" namespace fz { +class logger_interface; + /** * \brief Represents all relevant information of a X.509 certificate as used by TLS. */ @@ -120,6 +122,16 @@ }; /** + * \brief Gets the certificate information for the certificates in the file. + * + * If the sort flag is not set, certificates are returned in input order. + * If the sort flag is set, a chain is built, with certificate i signed by i+1. + * If building the chain fails, nothing is returned. + */ +std::vector<x509_certificate> FZ_PUBLIC_SYMBOL load_certificates_file(native_string const& certsfile, bool pem, bool sort, logger_interface * logger = nullptr); +std::vector<x509_certificate> FZ_PUBLIC_SYMBOL load_certificates(std::string_view const& certdata, bool pem, bool sort, logger_interface * logger = nullptr); + +/** * \brief Information about a TLS session * * Includes information about the used ciphers and details on the certificates diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla/tls_layer.hpp new/libfilezilla-0.28.0/lib/libfilezilla/tls_layer.hpp --- old/libfilezilla-0.27.1/lib/libfilezilla/tls_layer.hpp 2021-03-04 14:27:18.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/libfilezilla/tls_layer.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -133,7 +133,7 @@ * * If the pem flag is set, the input is assumed to be in PEM, otherwise DER. */ - bool set_certificate(std::string const& key, std::string const& certs, native_string const& password, bool pem = true); + bool set_certificate(std::string_view const& key, std::string_view const& certs, native_string const& password, bool pem = true); /// Returns the version of the loaded GnuTLS library, may be different than the version used at compile-time. static std::string get_gnutls_version(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/libfilezilla.vcxproj new/libfilezilla-0.28.0/lib/libfilezilla.vcxproj --- old/libfilezilla-0.27.1/lib/libfilezilla.vcxproj 2020-12-17 14:46:00.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/libfilezilla.vcxproj 2021-05-04 10:46:55.000000000 +0200 @@ -76,6 +76,9 @@ <PropertyGroup> <IntDir>$(OutDir)</IntDir> </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='static_debug|x64'"> + <LibraryPath>$(gnutls_lib);$(nettle_lib);$(LibraryPath)</LibraryPath> + </PropertyGroup> <ItemDefinitionGroup> <ClCompile> <LanguageStandard>stdcpp17</LanguageStandard> @@ -201,6 +204,7 @@ <ClCompile Include="event_loop.cpp" /> <ClCompile Include="file.cpp" /> <ClCompile Include="hash.cpp" /> + <ClCompile Include="hostname_lookup.cpp" /> <ClCompile Include="invoker.cpp" /> <ClCompile Include="iputils.cpp" /> <ClCompile Include="local_filesys.cpp" /> @@ -237,6 +241,7 @@ <ClInclude Include="libfilezilla\file.hpp" /> <ClInclude Include="libfilezilla\format.hpp" /> <ClInclude Include="libfilezilla\hash.hpp" /> + <ClInclude Include="libfilezilla\hostname_lookup.hpp" /> <ClInclude Include="libfilezilla\invoker.hpp" /> <ClInclude Include="libfilezilla\iputils.hpp" /> <ClInclude Include="libfilezilla\libfilezilla.hpp" /> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/rate_limited_layer.cpp new/libfilezilla-0.28.0/lib/rate_limited_layer.cpp --- old/libfilezilla-0.27.1/lib/rate_limited_layer.cpp 2021-03-04 14:27:18.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/rate_limited_layer.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -142,7 +142,7 @@ rate::type max_{}; - std::atomic<bool> waiting_[2]; + std::atomic<bool> waiting_[2]{}; }; compound_rate_limited_layer::compound_rate_limited_layer(event_handler* handler, socket_interface& next_layer) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/rate_limiter.cpp new/libfilezilla-0.28.0/lib/rate_limiter.cpp --- old/libfilezilla-0.27.1/lib/rate_limiter.cpp 2021-03-04 14:27:18.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/rate_limiter.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -648,7 +648,7 @@ return ret; } -bool bucket::waiting(scoped_lock & l, direction::type d) +bool bucket::waiting(scoped_lock &, direction::type d) { if (d != direction::inbound && d != direction::outbound) { return false; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/socket.cpp new/libfilezilla-0.28.0/lib/socket.cpp --- old/libfilezilla-0.27.1/lib/socket.cpp 2021-03-04 14:27:18.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/socket.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -185,9 +185,8 @@ } #endif -namespace { #ifdef FZ_WINDOWS -static int convert_msw_error_code(int error) +int convert_msw_error_code(int error) { // Takes an MSW socket error and converts it into an equivalent POSIX error code. switch (error) @@ -236,7 +235,10 @@ return error; } } +#endif +namespace { +#ifdef FZ_WINDOWS int last_socket_error() { return convert_msw_error_code(WSAGetLastError()); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/time.cpp new/libfilezilla-0.28.0/lib/time.cpp --- old/libfilezilla-0.27.1/lib/time.cpp 2020-07-07 14:06:31.000000000 +0200 +++ new/libfilezilla-0.28.0/lib/time.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -879,4 +879,98 @@ return do_set_rfc822(*this, str); } +namespace { +template<typename String> +bool do_set_rfc3339(fz::datetime& dt, String str) +{ + if (str.size() < 19) { + dt.clear(); + return false; + } + + auto separator_pos = str.find_first_of(fzS(typename String::value_type, "tT ")); // Including space, there is a lowercase 'may' in section 5.6 of the RFC + if (separator_pos == String::npos) { + dt.clear(); + return false; + } + + auto date_part = str.substr(0, separator_pos); + auto const date_tokens = fz::strtok_view(date_part, fzS(typename String::value_type, "-")); + + auto offset_pos = str.find_first_of(fzS(typename String::value_type, "+-Zz"), separator_pos); + + String time_part; + if (offset_pos == String::npos) { + // Allow the offset part to be missing + time_part = str.substr(separator_pos + 1); + } + else { + time_part = str.substr(separator_pos + 1, offset_pos - separator_pos - 1); + } + + auto const time_tokens = fz::strtok_view(time_part, fzS(typename String::value_type, ":.")); + if (date_tokens.size() == 3 && (time_tokens.size() == 3 || time_tokens.size() == 4)) { + int year = fz::to_integral<int>(date_tokens[0]); + if (year < 1000) { + if (year < 1000) { + year += 1900; + } + } + int month = fz::to_integral<int>(date_tokens[1]); + int day = fz::to_integral<int>(date_tokens[2]); + + int hour = fz::to_integral<int>(time_tokens[0]); + int minute = fz::to_integral<int>(time_tokens[1]); + int second = fz::to_integral<int>(time_tokens[2]); + + bool set{}; + if (time_tokens.size() == 4) { + // Convert fraction, .82 is 820ms + int ms = fz::to_integral<int>(time_tokens[3].substr(0, 3)); + if (time_tokens[3].size() == 1) { + ms *= 100; + } + else if (time_tokens[3].size() == 2) { + ms *= 10; + } + set = dt.set(fz::datetime::utc, year, month, day, hour, minute, second, ms); + } + else { + set = dt.set(fz::datetime::utc, year, month, day, hour, minute, second); + } + + if (set && offset_pos != String::npos && str[offset_pos] != 'Z') { + auto const offset_tokens = fz::strtok_view(str.substr(offset_pos + 1), ':'); + if (offset_tokens.size() != 2) { + dt.clear(); + return false; + } + + int minutes = fz::to_integral<int>(offset_tokens[0], 10009) * 60 + fz::to_integral<int>(offset_tokens[1], 10000); + if (minutes < 10000) { + if (str[offset_pos] == '+') { + minutes = -minutes; + } + dt += fz::duration::from_minutes(minutes); + } + } + + return set; + } + + dt.clear(); + return false; +} +} + +bool datetime::set_rfc3339(std::string_view const& str) +{ + return do_set_rfc3339(*this, str); +} + +bool datetime::set_rfc3339(std::wstring_view const& str) +{ + return do_set_rfc3339(*this, str); +} + } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/tls_info.cpp new/libfilezilla-0.28.0/lib/tls_info.cpp --- old/libfilezilla-0.27.1/lib/tls_info.cpp 2020-07-07 14:06:31.000000000 +0200 +++ new/libfilezilla-0.28.0/lib/tls_info.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -1,4 +1,5 @@ #include "libfilezilla/tls_info.hpp" +#include "tls_layer_impl.hpp" namespace fz { x509_certificate::x509_certificate( @@ -78,4 +79,38 @@ , hostname_mismatch_(hostname_mismatch) { } + +std::vector<x509_certificate> load_certificates_file(native_string const& certsfile, bool pem, bool sort, logger_interface * logger) +{ + std::string certdata = read_certificates_file(certsfile, logger); + if (certdata.empty()) { + return {}; + } + + return load_certificates(certdata, pem, sort, logger); +} + +std::vector<x509_certificate> load_certificates(std::string_view const& certdata, bool pem, bool sort, logger_interface * logger) +{ + cert_list_holder certs; + if (tls_layer_impl::load_certificates(certdata, pem, certs.certs, certs.certs_size, sort) != GNUTLS_E_SUCCESS) { + return {}; + } + + std::vector<x509_certificate> certificates; + certificates.reserve(certs.certs_size); + for (unsigned int i = 0; i < certs.certs_size; ++i) { + x509_certificate cert; + if (tls_layer_impl::extract_cert(certs.certs[i], cert, i + 1 == certs.certs_size, logger)) { + certificates.emplace_back(std::move(cert)); + } + else { + certificates.clear(); + break; + } + } + + return certificates; +} + } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/tls_layer.cpp new/libfilezilla-0.28.0/lib/tls_layer.cpp --- old/libfilezilla-0.27.1/lib/tls_layer.cpp 2021-03-04 14:27:18.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/tls_layer.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -96,7 +96,7 @@ return impl_->set_certificate_file(keyfile, certsfile, password, pem); } -bool tls_layer::set_certificate(std::string const& key, std::string const& certs, native_string const& password, bool pem) +bool tls_layer::set_certificate(std::string_view const& key, std::string_view const& certs, native_string const& password, bool pem) { return impl_->set_certificate(key, certs, password, pem); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/tls_layer_impl.cpp new/libfilezilla-0.28.0/lib/tls_layer_impl.cpp --- old/libfilezilla-0.27.1/lib/tls_layer_impl.cpp 2021-03-15 09:01:00.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/tls_layer_impl.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -15,6 +15,8 @@ #include <string.h> +using namespace std::literals; + #if DEBUG_SOCKETEVENTS #include <assert.h> @@ -129,27 +131,18 @@ return tls_layerCallbacks::handshake_hook_func(session, htype, post, incoming); } -struct cert_list_holder final +std::string to_string(gnutls_datum_t const& d) { - cert_list_holder() = default; - ~cert_list_holder() { - for (unsigned int i = 0; i < certs_size; ++i) { - gnutls_x509_crt_deinit(certs[i]); - } - gnutls_free(certs); + if (d.data && d.size) { + return std::string(d.data, d.data + d.size); } + return {}; +} - cert_list_holder(cert_list_holder const&) = delete; - cert_list_holder& operator=(cert_list_holder const&) = delete; - - gnutls_x509_crt_t * certs{}; - unsigned int certs_size{}; -}; - -std::string to_string(gnutls_datum_t const& d) +std::string_view to_view(gnutls_datum_t const& d) { if (d.data && d.size) { - return std::string(d.data, d.data + d.size); + return std::string_view(reinterpret_cast<char const*>(d.data), d.size); } return {}; } @@ -247,6 +240,34 @@ return true; } +std::string read_certificates_file(native_string const& certsfile, logger_interface * logger) +{ + file cf(certsfile, file::reading, file::existing); + if (!cf.opened()) { + if (logger) { + logger->log(logmsg::error, fztranslate("Could not open certificate file.")); + } + return {}; + } + int64_t const cs = cf.size(); + if (cs < 0 || cs > 1024 * 1024) { + if (logger) { + logger->log(logmsg::error, fztranslate("Certificate file too big.")); + } + return {}; + } + std::string c; + c.resize(cs); + auto read = cf.read(c.data(), cs); + if (read != cs) { + if (logger) { + logger->log(logmsg::error, fztranslate("Could not read certificate file.")); + } + return {}; + } + return c; +} + bool tls_layer_impl::set_certificate_file(native_string const& keyfile, native_string const& certsfile, native_string const& password, bool pem) { // Load the files ourselves instead of calling gnutls_certificate_set_x509_key_file2 @@ -270,29 +291,15 @@ return false; } - - file cf(certsfile, file::reading, file::existing); - if (!cf.opened()) { - logger_.log(logmsg::error, fztranslate("Could not open certificate file.")); - return false; - } - int64_t const cs = cf.size(); - if (cs < 0 || cs > 1024 * 1024) { - logger_.log(logmsg::error, fztranslate("Certificate file too big.")); - return false; - } - std::string c; - c.resize(cs); - read = cf.read(c.data(), cs); - if (read != cs) { - logger_.log(logmsg::error, fztranslate("Could not read certificate file.")); + std::string c = read_certificates_file(certsfile, &logger_); + if (c.empty()) { return false; } return set_certificate(k, c, password, pem); } -bool tls_layer_impl::set_certificate(std::string const& key, std::string const& certs, native_string const& password, bool pem) +bool tls_layer_impl::set_certificate(std::string_view const& key, std::string_view const& certs, native_string const& password, bool pem) { if (!init()) { return false; @@ -320,6 +327,7 @@ return true; } +// Convert them all to PEM bool tls_layer_impl::init_session(bool client) { @@ -1169,13 +1177,15 @@ } -bool tls_layer_impl::extract_cert(gnutls_x509_crt_t const& cert, x509_certificate& out, bool last) +bool tls_layer_impl::extract_cert(gnutls_x509_crt_t const& cert, x509_certificate& out, bool last, logger_interface * logger) { datetime expiration_time(gnutls_x509_crt_get_expiration_time(cert), datetime::seconds); datetime activation_time(gnutls_x509_crt_get_activation_time(cert), datetime::seconds); if (!activation_time || !expiration_time || expiration_time < activation_time) { - logger_.log(logmsg::error, fztranslate("Could not extract validity period of certificate")); + if (logger) { + logger->log(logmsg::error, fztranslate("Could not extract validity period of certificate")); + } return false; } @@ -1211,28 +1221,38 @@ std::string subject, issuer; datum_holder raw_subject; - if (!gnutls_x509_crt_get_dn3(cert, &raw_subject, 0)) { + res = gnutls_x509_crt_get_dn3(cert, &raw_subject, 0); + if (!res) { subject = raw_subject.to_string_view(); } else { - log_error(res, L"gnutls_x509_crt_get_dn3"); + if (logger) { + logger->log(logmsg::debug_warning, "gnutls_x509_crt_get_dn3 failed with %d", res); + } } if (subject.empty()) { - logger_.log(logmsg::error, fztranslate("Could not get distinguished name of certificate subject, gnutls_x509_get_dn failed")); + if (logger) { + logger->log(logmsg::error, fztranslate("Could not get distinguished name of certificate subject, gnutls_x509_get_dn failed")); + } return false; } std::vector<x509_certificate::subject_name> alt_subject_names = get_cert_subject_alt_names(cert); datum_holder raw_issuer; - if (!gnutls_x509_crt_get_issuer_dn3(cert, &raw_issuer, 0)) { + res = gnutls_x509_crt_get_issuer_dn3(cert, &raw_issuer, 0); + if (!res) { issuer = raw_issuer.to_string_view(); } else { - log_error(res, L"gnutls_x509_crt_get_issuer_dn3"); + if (logger) { + logger->log(logmsg::debug_warning, "gnutls_x509_crt_get_issuer_dn3 failed with %d", res); + } } if (issuer.empty() ) { - logger_.log(logmsg::error, fztranslate("Could not get distinguished name of certificate issuer, gnutls_x509_get_issuer_dn failed")); + if (logger) { + logger->log(logmsg::error, fztranslate("Could not get distinguished name of certificate issuer, gnutls_x509_get_issuer_dn failed")); + } return false; } @@ -1253,7 +1273,9 @@ datum_holder der; if (gnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_DER, &der) != GNUTLS_E_SUCCESS || !der.data || !der.size) { - logger_.log(logmsg::error, L"gnutls_x509_crt_get_issuer_dn"); + if (logger) { + logger->log(logmsg::error, L"gnutls_x509_crt_get_issuer_dn"); + } return false; } std::vector<uint8_t> data(der.data, der.data + der.size); @@ -1389,6 +1411,29 @@ return algorithmWarnings; } +int tls_layer_impl::load_certificates(std::string_view const& in, bool pem, gnutls_x509_crt_t *& certs, unsigned int & certs_size, bool & sort) +{ + gnutls_datum_t dpem; + dpem.data = reinterpret_cast<unsigned char*>(const_cast<char *>(in.data())); + dpem.size = in.size(); + unsigned int flags{}; + if (sort) { + flags |= GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED; + } + + int res = gnutls_x509_crt_list_import2(&certs, &certs_size, &dpem, pem ? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER, flags); + if (res == GNUTLS_E_CERTIFICATE_LIST_UNSORTED) { + sort = false; + flags |= GNUTLS_X509_CRT_LIST_SORT; + res = gnutls_x509_crt_list_import2(&certs, &certs_size, &dpem, pem ? GNUTLS_X509_FMT_PEM : GNUTLS_X509_FMT_DER, flags); + } + + if (res != GNUTLS_E_SUCCESS) { + certs = nullptr; + certs_size = 0; + } + return res; +} bool tls_layer_impl::get_sorted_peer_certificates(gnutls_x509_crt_t *& certs, unsigned int & certs_size) { @@ -1404,52 +1449,35 @@ } // Convert them all to PEM - gnutls_datum_t *pem_cert_list = new gnutls_datum_t[cert_list_size]; - for (unsigned i = 0; i < cert_list_size; ++i) { - if (gnutls_pem_base64_encode2("CERTIFICATE", cert_list + i, pem_cert_list + i) != 0) { - for (unsigned int j = 0; j < i; ++j) { - gnutls_free(pem_cert_list[j].data); - } - delete [] pem_cert_list; - logger_.log(logmsg::error, fztranslate("gnutls_pem_base64_encode2 failed")); - return false; - } - } + // Avoid gnutls_pem_base64_encode2, excessive allocations. + auto constexpr header = "-----BEGIN CERTIFICATE-----\r\n"sv; + auto constexpr footer = "\r\n-----END CERTIFICATE-----\r\n"sv; - // Concatenate them - gnutls_datum_t concated_certs{}; + size_t cap = cert_list_size * header.size() + footer.size(); for (unsigned i = 0; i < cert_list_size; ++i) { - concated_certs.size += pem_cert_list[i].size; - } - concated_certs.data = new unsigned char[concated_certs.size]; - concated_certs.size = 0; - for (unsigned i = 0; i < cert_list_size; ++i) { - memcpy(concated_certs.data + concated_certs.size, pem_cert_list[i].data, pem_cert_list[i].size); - concated_certs.size += pem_cert_list[i].size; + cap += ((cert_list[i].size + 2) / 3) * 4; } + std::string pem; + pem.reserve(cap); + for (unsigned i = 0; i < cert_list_size; ++i) { - gnutls_free(pem_cert_list[i].data); + pem += header; + base64_encode_append(pem, to_view(cert_list[i]), base64_type::standard, true); + pem += footer; } - delete[] pem_cert_list; // And now import the certificates - int res = gnutls_x509_crt_list_import2(&certs, &certs_size, &concated_certs, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED); + bool sort = true; + int res = load_certificates(pem, true, certs, certs_size, sort); if (res == GNUTLS_E_CERTIFICATE_LIST_UNSORTED) { - logger_.log(logmsg::error, fztranslate("Server sent unsorted certificate chain in violation of the TLS specifications")); - res = gnutls_x509_crt_list_import2(&certs, &certs_size, &concated_certs, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_SORT); - } - - delete[] concated_certs.data; - - if (res != GNUTLS_E_SUCCESS) { - certs = nullptr; - certs_size = 0; logger_.log(logmsg::error, fztranslate("Could not sort peer certificates")); - return false; + } + else if (!sort) { + logger_.log(logmsg::error, fztranslate("Server sent unsorted certificate chain in violation of the TLS specifications")); } - return true; + return res == GNUTLS_E_SUCCESS; } void tls_layer_impl::log_verification_error(int status) @@ -1661,8 +1689,8 @@ certificates.reserve(certs.certs_size); for (unsigned int i = 0; i < certs.certs_size; ++i) { x509_certificate cert; - if (extract_cert(certs.certs[i], cert, i + 1 == certs.certs_size)) { - certificates.push_back(cert); + if (extract_cert(certs.certs[i], cert, i + 1 == certs.certs_size, &logger_)) { + certificates.emplace_back(std::move(cert)); } else { failure(0, true); @@ -1690,7 +1718,7 @@ } x509_certificate out; - if (!extract_cert(issuer, out, true)) { + if (!extract_cert(issuer, out, true, &logger_)) { failure(0, true); return ECONNABORTED; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/tls_layer_impl.hpp new/libfilezilla-0.28.0/lib/tls_layer_impl.hpp --- old/libfilezilla-0.27.1/lib/tls_layer_impl.hpp 2021-03-15 09:01:00.000000000 +0100 +++ new/libfilezilla-0.28.0/lib/tls_layer_impl.hpp 2021-05-04 10:46:55.000000000 +0200 @@ -6,6 +6,7 @@ #endif #include <gnutls/gnutls.h> +#include <gnutls/x509.h> #include "libfilezilla/buffer.hpp" #include "libfilezilla/logger.hpp" @@ -15,9 +16,23 @@ namespace fz { class tls_system_trust_store; class logger_interface; -namespace { -struct cert_list_holder; -} + +struct cert_list_holder final +{ + cert_list_holder() = default; + ~cert_list_holder() { + for (unsigned int i = 0; i < certs_size; ++i) { + gnutls_x509_crt_deinit(certs[i]); + } + gnutls_free(certs); + } + + cert_list_holder(cert_list_holder const&) = delete; + cert_list_holder& operator=(cert_list_holder const&) = delete; + + gnutls_x509_crt_t * certs{}; + unsigned int certs_size{}; +}; class tls_layer; class tls_layer_impl final @@ -58,7 +73,7 @@ bool set_certificate_file(native_string const& keyfile, native_string const& certsfile, native_string const& password, bool pem); - bool set_certificate(std::string const& key, std::string const& certs, native_string const& password, bool pem); + bool set_certificate(std::string_view const& key, std::string_view const& certs, native_string const& password, bool pem); static std::string get_gnutls_version(); @@ -74,6 +89,9 @@ std::string get_alpn() const; native_string get_hostname() const; + static int load_certificates(std::string_view const& in, bool pem, gnutls_x509_crt_t *& certs, unsigned int & certs_size, bool & sort); + static bool extract_cert(gnutls_x509_crt_t const& cert, x509_certificate& out, bool last, logger_interface * logger); + private: bool init(); void deinit(); @@ -106,8 +124,7 @@ bool get_sorted_peer_certificates(gnutls_x509_crt_t *& certs, unsigned int & certs_size); - bool extract_cert(gnutls_x509_crt_t const& cert, x509_certificate& out, bool last); - std::vector<x509_certificate::subject_name> get_cert_subject_alt_names(gnutls_x509_crt_t cert); + static std::vector<x509_certificate::subject_name> get_cert_subject_alt_names(gnutls_x509_crt_t cert); void log_verification_error(int status); @@ -175,6 +192,9 @@ bool debug_can_write_{}; #endif }; + +std::string read_certificates_file(native_string const& certsfile, logger_interface * logger); + } #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/lib/tls_system_trust_store.cpp new/libfilezilla-0.28.0/lib/tls_system_trust_store.cpp --- old/libfilezilla-0.27.1/lib/tls_system_trust_store.cpp 2020-07-07 14:06:31.000000000 +0200 +++ new/libfilezilla-0.28.0/lib/tls_system_trust_store.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -25,7 +25,9 @@ { task_.join(); - gnutls_certificate_free_credentials(credentials_); + if (credentials_) { + gnutls_certificate_free_credentials(credentials_); + } } std::tuple<gnutls_certificate_credentials_t, scoped_lock> tls_system_trust_store_impl::lease() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/libfilezilla-0.27.1/tests/time.cpp new/libfilezilla-0.28.0/tests/time.cpp --- old/libfilezilla-0.27.1/tests/time.cpp 2020-07-07 14:06:31.000000000 +0200 +++ new/libfilezilla-0.28.0/tests/time.cpp 2021-05-04 10:46:55.000000000 +0200 @@ -12,6 +12,7 @@ CPPUNIT_TEST(testPreEpoch); CPPUNIT_TEST(testAlternateMidnight); CPPUNIT_TEST(testRFC822); + CPPUNIT_TEST(testRFC3339); CPPUNIT_TEST_SUITE_END(); public: @@ -24,6 +25,7 @@ void testAlternateMidnight(); void testRFC822(); + void testRFC3339(); }; CPPUNIT_TEST_SUITE_REGISTRATION(TimeTest); @@ -124,3 +126,18 @@ CPPUNIT_ASSERT(t.set_rfc822(offset2)); CPPUNIT_ASSERT(t == t1); } + +void TimeTest::testRFC3339() +{ + fz::datetime const t1(fz::datetime::utc, 1985, 4, 12, 23, 20, 50, 520); + fz::datetime const t2(fz::datetime::utc, 1996, 12, 20, 0, 39, 57); + + std::string const s1 = "1985-04-12T23:20:50.52Z"; + std::string const s2 = "1996-12-19T16:39:57-08:00"; + + fz::datetime t; + CPPUNIT_ASSERT(t.set_rfc3339(s1)); + CPPUNIT_ASSERT(t == t1); + CPPUNIT_ASSERT(t.set_rfc3339(s2)); + CPPUNIT_ASSERT(t == t2); +}
