From 6bf0af0e206a6431b4f3357e7b8d5ff155c610f5 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Mon, 11 Apr 2022 23:49:30 +0200 Subject: [PATCH 01/24] update catch2 to version 2.13.7 Signed-off-by: Heiko Hund --- extern/catch2/catch.hpp | 779 ++++++++++++++++++++++++++-------------- 1 file changed, 516 insertions(+), 263 deletions(-) diff --git a/extern/catch2/catch.hpp b/extern/catch2/catch.hpp index f64422a..7e706f9 100644 --- a/extern/catch2/catch.hpp +++ b/extern/catch2/catch.hpp @@ -1,9 +1,9 @@ /* - * Catch v2.12.2 - * Generated: 2020-05-25 15:09:23.791719 + * Catch v2.13.7 + * Generated: 2021-07-28 20:29:27.753164 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -14,8 +14,8 @@ #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 12 -#define CATCH_VERSION_PATCH 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 7 #ifdef __clang__ # pragma clang system_header @@ -66,13 +66,16 @@ #if !defined(CATCH_CONFIG_IMPL_ONLY) // start catch_platform.h +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html #ifdef __APPLE__ -# include -# if TARGET_OS_OSX == 1 -# define CATCH_PLATFORM_MAC -# elif TARGET_OS_IPHONE == 1 -# define CATCH_PLATFORM_IPHONE -# endif +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif #elif defined(linux) || defined(__linux) || defined(__linux__) # define CATCH_PLATFORM_LINUX @@ -132,13 +135,9 @@ namespace Catch { #endif -#if defined(__cpp_lib_uncaught_exceptions) -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - -// We have to avoid both ICC and Clang, because they try to mask themselves -// as gcc, and we want only GCC in this block -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) @@ -162,7 +161,7 @@ namespace Catch { // ``` // // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) +# if !defined(__ibmxl__) && !defined(__CUDACC__) # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ # endif @@ -244,10 +243,6 @@ namespace Catch { # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -# if _MSC_VER >= 1900 // Visual Studio 2015 or newer -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -# endif - // Universal Windows platform does not support SEH // Or console colours (or console at all...) # if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) @@ -330,7 +325,10 @@ namespace Catch { // Check if byte is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # include + # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) // Check if variant is available and usable @@ -373,10 +371,6 @@ namespace Catch { # define CATCH_CONFIG_CPP17_OPTIONAL #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) -# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -#endif - #if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) # define CATCH_CONFIG_CPP17_STRING_VIEW #endif @@ -775,7 +769,7 @@ constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) n #define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) #define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) #define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) #define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) #define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) #define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) @@ -1105,7 +1099,7 @@ struct AutoReg : NonCopyable { int index = 0; \ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ using expander = int[];\ - (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \ + (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ @@ -1151,7 +1145,7 @@ struct AutoReg : NonCopyable { constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\ } \ }; \ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ @@ -1195,7 +1189,7 @@ struct AutoReg : NonCopyable { void reg_tests() { \ int index = 0; \ using expander = int[]; \ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++, 0)... };/* NOLINT */\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ } \ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ @@ -1229,7 +1223,7 @@ struct AutoReg : NonCopyable { int index = 0; \ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\ using expander = int[];\ - (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \ + (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ @@ -1278,7 +1272,7 @@ struct AutoReg : NonCopyable { constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */ \ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ @@ -1325,7 +1319,7 @@ struct AutoReg : NonCopyable { void reg_tests(){\ int index = 0;\ using expander = int[];\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++, 0)... };/* NOLINT */ \ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ @@ -1829,8 +1823,8 @@ namespace Catch { #endif namespace Detail { - template - std::string rangeToString(InputIterator first, InputIterator last) { + template + std::string rangeToString(InputIterator first, Sentinel last) { ReusableStringStream rss; rss << "{ "; if (first != last) { @@ -2468,7 +2462,7 @@ namespace Catch { virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; - virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; + virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) virtual void benchmarkPreparing( std::string const& name ) = 0; @@ -4080,16 +4074,16 @@ namespace Generators { return makeGenerators( value( T( std::forward( val ) ) ), std::forward( moreGenerators )... ); } - auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; + auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&; template // Note: The type after -> is weird, because VS2015 cannot parse // the expression used in the typedef inside, when it is in // return type. Yeah. - auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval().get()) { + auto generate( StringRef generatorName, SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval().get()) { using UnderlyingType = typename decltype(generatorExpression())::type; - IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo ); + IGeneratorTracker& tracker = acquireGeneratorTracker( generatorName, lineInfo ); if (!tracker.hasGenerator()) { tracker.setGenerator(pf::make_unique>(generatorExpression())); } @@ -4102,11 +4096,17 @@ namespace Generators { } // namespace Catch #define GENERATE( ... ) \ - Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) #define GENERATE_COPY( ... ) \ - Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) #define GENERATE_REF( ... ) \ - Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) // end catch_generators.hpp // start catch_generators_generic.hpp @@ -4516,6 +4516,7 @@ namespace Catch { virtual int abortAfter() const = 0; virtual bool showInvisibles() const = 0; virtual ShowDurations::OrNot showDurations() const = 0; + virtual double minDuration() const = 0; virtual TestSpec const& testSpec() const = 0; virtual bool hasTestFilters() const = 0; virtual std::vector const& getTestsOrTags() const = 0; @@ -5288,6 +5289,7 @@ namespace Catch { Verbosity verbosity = Verbosity::Normal; WarnAbout::What warnings = WarnAbout::Nothing; ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + double minDuration = -1; RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; UseColour::YesOrNo useColour = UseColour::Auto; WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; @@ -5338,6 +5340,7 @@ namespace Catch { bool warnAboutMissingAssertions() const override; bool warnAboutNoTests() const override; ShowDurations::OrNot showDurations() const override; + double minDuration() const override; RunTests::InWhatOrder runOrder() const override; unsigned int rngSeed() const override; UseColour::YesOrNo useColour() const override; @@ -5455,6 +5458,8 @@ namespace Catch { } // namespace Catch // end catch_outlier_classification.hpp + +#include #endif // CATCH_CONFIG_ENABLE_BENCHMARKING #include @@ -5715,6 +5720,9 @@ namespace Catch { // Returns double formatted as %.3f (format expected on output) std::string getFormattedDuration( double duration ); + //! Should the reporter show + bool shouldShowDuration( IConfig const& config, double duration ); + std::string serializeFilters( std::vector const& container ); template @@ -6108,8 +6116,6 @@ namespace Catch { static std::string getDescription(); - ReporterPreferences getPreferences() const override; - void noMatchingTestCases(std::string const& spec) override; void assertionStarting(AssertionInfo const&) override; @@ -6338,9 +6344,10 @@ namespace Catch { void writeTestCase(TestCaseNode const& testCaseNode); - void writeSection(std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode); + void writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode, + bool testOkToFail ); void writeAssertions(SectionNode const& sectionNode); void writeAssertion(AssertionStats const& stats); @@ -6875,7 +6882,7 @@ namespace Catch { } iters *= 2; } - throw optimized_away_error{}; + Catch::throw_exception(optimized_away_error{}); } } // namespace Detail } // namespace Benchmark @@ -6883,6 +6890,7 @@ namespace Catch { // end catch_run_for_at_least.hpp #include +#include namespace Catch { namespace Benchmark { @@ -7053,8 +7061,8 @@ namespace Catch { double b2 = bias - z1; double a1 = a(b1); double a2 = a(b2); - auto lo = std::max(cumn(a1), 0); - auto hi = std::min(cumn(a2), n - 1); + auto lo = (std::max)(cumn(a1), 0); + auto hi = (std::min)(cumn(a2), n - 1); return { point, resample[lo], resample[hi], confidence_level }; } @@ -7123,7 +7131,9 @@ namespace Catch { } template EnvironmentEstimate> estimate_clock_cost(FloatDuration resolution) { - auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration(clock_cost_estimation_time_limit)); + auto time_limit = (std::min)( + resolution * clock_cost_estimation_tick_limit, + FloatDuration(clock_cost_estimation_time_limit)); auto time_clock = [](int k) { return Detail::measure([k] { for (int i = 0; i < k; ++i) { @@ -7463,23 +7473,37 @@ namespace TestCaseTracking { SourceLineInfo location; NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) { + return lhs.name == rhs.name + && lhs.location == rhs.location; + } }; - struct ITracker; + class ITracker; using ITrackerPtr = std::shared_ptr; - struct ITracker { - virtual ~ITracker(); + class ITracker { + NameAndLocation m_nameAndLocation; + + public: + ITracker(NameAndLocation const& nameAndLoc) : + m_nameAndLocation(nameAndLoc) + {} // static queries - virtual NameAndLocation const& nameAndLocation() const = 0; + NameAndLocation const& nameAndLocation() const { + return m_nameAndLocation; + } + + virtual ~ITracker(); // dynamic queries virtual bool isComplete() const = 0; // Successfully completed or failed virtual bool isSuccessfullyCompleted() const = 0; virtual bool isOpen() const = 0; // Started but not complete virtual bool hasChildren() const = 0; + virtual bool hasStarted() const = 0; virtual ITracker& parent() = 0; @@ -7534,7 +7558,6 @@ namespace TestCaseTracking { }; using Children = std::vector; - NameAndLocation m_nameAndLocation; TrackerContext& m_ctx; ITracker* m_parent; Children m_children; @@ -7543,11 +7566,13 @@ namespace TestCaseTracking { public: TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); - NameAndLocation const& nameAndLocation() const override; bool isComplete() const override; bool isSuccessfullyCompleted() const override; bool isOpen() const override; bool hasChildren() const override; + bool hasStarted() const override { + return m_runState != NotStarted; + } void addChild( ITrackerPtr const& child ) override; @@ -7586,6 +7611,10 @@ namespace TestCaseTracking { void addInitialFilters( std::vector const& filters ); void addNextFilters( std::vector const& filters ); + //! Returns filters active in this tracker + std::vector const& getFilters() const; + //! Returns whitespace-trimmed name of the tracked section + std::string const& trimmedName() const; }; } // namespace TestCaseTracking @@ -7751,7 +7780,7 @@ namespace Catch { double sb = stddev.point; double mn = mean.point / n; double mg_min = mn / 2.; - double sg = std::min(mg_min / 4., sb / std::sqrt(n)); + double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); double sg2 = sg * sg; double sb2 = sb * sb; @@ -7770,7 +7799,7 @@ namespace Catch { return (nc / n) * (sb2 - nc * sg2); }; - return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2; + return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; } bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector::iterator first, std::vector::iterator last) { @@ -7910,7 +7939,11 @@ namespace Catch { #ifdef CATCH_PLATFORM_MAC - #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + #if defined(__i386__) || defined(__x86_64__) + #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ + #elif defined(__aarch64__) + #define CATCH_TRAP() __asm__(".inst 0xd4200000") + #endif #elif defined(CATCH_PLATFORM_IPHONE) @@ -7956,86 +7989,58 @@ namespace Catch { // start catch_fatal_condition.h -// start catch_windows_h_proxy.h - - -#if defined(CATCH_PLATFORM_WINDOWS) - -#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) -# define CATCH_DEFINED_NOMINMAX -# define NOMINMAX -#endif -#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) -# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - -#ifdef CATCH_DEFINED_NOMINMAX -# undef NOMINMAX -#endif -#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN -# undef WIN32_LEAN_AND_MEAN -#endif - -#endif // defined(CATCH_PLATFORM_WINDOWS) - -// end catch_windows_h_proxy.h -#if defined( CATCH_CONFIG_WINDOWS_SEH ) +#include namespace Catch { - struct FatalConditionHandler { - - static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); + // Wrapper for platform-specific fatal error (signals/SEH) handlers + // + // Tries to be cooperative with other handlers, and not step over + // other handlers. This means that unknown structured exceptions + // are passed on, previous signal handlers are called, and so on. + // + // Can only be instantiated once, and assumes that once a signal + // is caught, the binary will end up terminating. Thus, there + class FatalConditionHandler { + bool m_started = false; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform(); + void disengage_platform(); + public: + // Should also have platform-specific implementations as needed FatalConditionHandler(); - static void reset(); ~FatalConditionHandler(); - private: - static bool isSet; - static ULONG guaranteeSize; - static PVOID exceptionHandlerHandle; - }; - -} // namespace Catch - -#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) - -#include - -namespace Catch { - - struct FatalConditionHandler { - - static bool isSet; - static struct sigaction oldSigActions[]; - static stack_t oldSigStack; - static char altStackMem[]; - - static void handleSignal( int sig ); + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } - FatalConditionHandler(); - ~FatalConditionHandler(); - static void reset(); + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } }; -} // namespace Catch - -#else - -namespace Catch { - struct FatalConditionHandler { - void reset(); + //! Simple RAII guard for (dis)engaging the FatalConditionHandler + class FatalConditionHandlerGuard { + FatalConditionHandler* m_handler; + public: + FatalConditionHandlerGuard(FatalConditionHandler* handler): + m_handler(handler) { + m_handler->engage(); + } + ~FatalConditionHandlerGuard() { + m_handler->disengage(); + } }; -} -#endif +} // end namespace Catch // end catch_fatal_condition.h #include @@ -8095,7 +8100,7 @@ namespace Catch { void sectionEnded( SectionEndInfo const& endInfo ) override; void sectionEndedEarly( SectionEndInfo const& endInfo ) override; - auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; + auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) void benchmarkPreparing( std::string const& name ) override; @@ -8161,6 +8166,7 @@ namespace Catch { std::vector m_unfinishedSections; std::vector m_activeSections; TrackerContext m_trackerContext; + FatalConditionHandler m_fatalConditionhandler; bool m_lastAssertionPassed = false; bool m_shouldReportUnexpected = true; bool m_includeSuccessfulResults; @@ -9071,7 +9077,7 @@ namespace detail { } inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { std::string srcLC = source; - std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( std::tolower(c) ); } ); + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( unsigned char c ) { return static_cast( std::tolower(c) ); } ); if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") target = true; else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") @@ -9840,6 +9846,9 @@ namespace Catch { | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) ["-d"]["--durations"] ( "show test durations" ) + | Opt( config.minDuration, "seconds" ) + ["-D"]["--min-duration"] + ( "show test durations for tests taking at least the given number of seconds" ) | Opt( loadTestNamesFromFile, "filename" ) ["-f"]["--input-file"] ( "load test names to run from a file" ) @@ -9987,6 +9996,7 @@ namespace Catch { bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + double Config::minDuration() const { return m_data.minDuration; } RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } unsigned int Config::rngSeed() const { return m_data.rngSeed; } UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } @@ -10029,6 +10039,36 @@ namespace Catch { } // end catch_errno_guard.h +// start catch_windows_h_proxy.h + + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINED_NOMINMAX +# define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h #include namespace Catch { @@ -10545,7 +10585,7 @@ namespace Catch { // Extracts the actual name part of an enum instance // In other words, it returns the Blue part of Bikeshed::Colour::Blue StringRef extractInstanceName(StringRef enumInstance) { - // Find last occurence of ":" + // Find last occurrence of ":" size_t name_start = enumInstance.size(); while (name_start > 0 && enumInstance[name_start - 1] != ':') { --name_start; @@ -10707,25 +10747,47 @@ namespace Catch { // end catch_exception_translator_registry.cpp // start catch_fatal_condition.cpp -#if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif +#include + +#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS ) + +namespace Catch { + + // If neither SEH nor signal handling is required, the handler impls + // do not have to do anything, and can be empty. + void FatalConditionHandler::engage_platform() {} + void FatalConditionHandler::disengage_platform() {} + FatalConditionHandler::FatalConditionHandler() = default; + FatalConditionHandler::~FatalConditionHandler() = default; + +} // end namespace Catch + +#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS + +#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS ) +#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" +#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) namespace { - // Report the error condition + //! Signals fatal error message to the run context void reportFatal( char const * const message ) { Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); } -} -#endif // signals/SEH handling + //! Minimal size Catch2 needs for its own fatal error handling. + //! Picked anecdotally, so it might not be sufficient on all + //! platforms, and for all configurations. + constexpr std::size_t minStackSizeForErrors = 32 * 1024; +} // end unnamed namespace + +#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS #if defined( CATCH_CONFIG_WINDOWS_SEH ) namespace Catch { + struct SignalDefs { DWORD id; const char* name; }; // There is no 1-1 mapping between signals and windows exceptions. @@ -10738,7 +10800,7 @@ namespace Catch { { static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" }, }; - LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { for (auto const& def : signalDefs) { if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { reportFatal(def.name); @@ -10749,38 +10811,50 @@ namespace Catch { return EXCEPTION_CONTINUE_SEARCH; } + // Since we do not support multiple instantiations, we put these + // into global variables and rely on cleaning them up in outlined + // constructors/destructors + static PVOID exceptionHandlerHandle = nullptr; + + // For MSVC, we reserve part of the stack memory for handling + // memory overflow structured exception. FatalConditionHandler::FatalConditionHandler() { - isSet = true; - // 32k seems enough for Catch to handle stack overflow, - // but the value was found experimentally, so there is no strong guarantee - guaranteeSize = 32 * 1024; - exceptionHandlerHandle = nullptr; + ULONG guaranteeSize = static_cast(minStackSizeForErrors); + if (!SetThreadStackGuarantee(&guaranteeSize)) { + // We do not want to fully error out, because needing + // the stack reserve should be rare enough anyway. + Catch::cerr() + << "Failed to reserve piece of stack." + << " Stack overflows will not be reported successfully."; + } + } + + // We do not attempt to unset the stack guarantee, because + // Windows does not support lowering the stack size guarantee. + FatalConditionHandler::~FatalConditionHandler() = default; + + void FatalConditionHandler::engage_platform() { // Register as first handler in current chain exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); - // Pass in guarantee size to be filled - SetThreadStackGuarantee(&guaranteeSize); + if (!exceptionHandlerHandle) { + CATCH_RUNTIME_ERROR("Could not register vectored exception handler"); + } } - void FatalConditionHandler::reset() { - if (isSet) { - RemoveVectoredExceptionHandler(exceptionHandlerHandle); - SetThreadStackGuarantee(&guaranteeSize); - exceptionHandlerHandle = nullptr; - isSet = false; + void FatalConditionHandler::disengage_platform() { + if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { + CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler"); } + exceptionHandlerHandle = nullptr; } - FatalConditionHandler::~FatalConditionHandler() { - reset(); - } +} // end namespace Catch -bool FatalConditionHandler::isSet = false; -ULONG FatalConditionHandler::guaranteeSize = 0; -PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; +#endif // CATCH_CONFIG_WINDOWS_SEH -} // namespace Catch +#if defined( CATCH_CONFIG_POSIX_SIGNALS ) -#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) +#include namespace Catch { @@ -10789,10 +10863,6 @@ namespace Catch { const char* name; }; - // 32kb for the alternate stack seems to be sufficient. However, this value - // is experimentally determined, so that's not guaranteed. - static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; - static SignalDefs signalDefs[] = { { SIGINT, "SIGINT - Terminal interrupt signal" }, { SIGILL, "SIGILL - Illegal instruction signal" }, @@ -10802,7 +10872,32 @@ namespace Catch { { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } }; - void FatalConditionHandler::handleSignal( int sig ) { +// Older GCCs trigger -Wmissing-field-initializers for T foo = {} +// which is zero initialization, but not explicit. We want to avoid +// that. +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + + static char* altStackMem = nullptr; + static std::size_t altStackSize = 0; + static stack_t oldSigStack{}; + static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; + + static void restorePreviousSignalHandlers() { + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + } + + static void handleSignal( int sig ) { char const * name = ""; for (auto const& def : signalDefs) { if (sig == def.id) { @@ -10810,16 +10905,33 @@ namespace Catch { break; } } - reset(); - reportFatal(name); + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); + reportFatal( name ); raise( sig ); } FatalConditionHandler::FatalConditionHandler() { - isSet = true; + assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); + } + altStackMem = new char[altStackSize](); + } + + FatalConditionHandler::~FatalConditionHandler() { + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = nullptr; + } + + void FatalConditionHandler::engage_platform() { stack_t sigStack; sigStack.ss_sp = altStackMem; - sigStack.ss_size = sigStackSize; + sigStack.ss_size = altStackSize; sigStack.ss_flags = 0; sigaltstack(&sigStack, &oldSigStack); struct sigaction sa = { }; @@ -10831,40 +10943,17 @@ namespace Catch { } } - FatalConditionHandler::~FatalConditionHandler() { - reset(); - } +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif - void FatalConditionHandler::reset() { - if( isSet ) { - // Set signals back to previous values -- hopefully nobody overwrote them in the meantime - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { - sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); - } - // Return the old stack - sigaltstack(&oldSigStack, nullptr); - isSet = false; - } + void FatalConditionHandler::disengage_platform() { + restorePreviousSignalHandlers(); } - bool FatalConditionHandler::isSet = false; - struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; - stack_t FatalConditionHandler::oldSigStack = {}; - char FatalConditionHandler::altStackMem[sigStackSize] = {}; - -} // namespace Catch - -#else - -namespace Catch { - void FatalConditionHandler::reset() {} -} - -#endif // signals/SEH handling +} // end namespace Catch -#if defined(__GNUC__) -# pragma GCC diagnostic pop -#endif +#endif // CATCH_CONFIG_POSIX_SIGNALS // end catch_fatal_condition.cpp // start catch_generators.cpp @@ -10883,8 +10972,8 @@ namespace Generators { GeneratorUntypedBase::~GeneratorUntypedBase() {} - auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { - return getResultCapture().acquireGeneratorTracker( lineInfo ); + auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + return getResultCapture().acquireGeneratorTracker( generatorName, lineInfo ); } } // namespace Generators @@ -11419,7 +11508,8 @@ namespace { return lhs == rhs; } - auto ulpDiff = std::abs(lc - rc); + // static cast as a workaround for IBM XLC + auto ulpDiff = std::abs(static_cast(lc - rc)); return static_cast(ulpDiff) <= maxUlpDiff; } @@ -11593,7 +11683,6 @@ Floating::WithinRelMatcher WithinRel(float target) { } // namespace Matchers } // namespace Catch - // end catch_matchers_floating.cpp // start catch_matchers_generic.cpp @@ -12009,7 +12098,7 @@ namespace Catch { if (tmpnam_s(m_buffer)) { CATCH_RUNTIME_ERROR("Could not get a temp filename"); } - if (fopen_s(&m_file, m_buffer, "w")) { + if (fopen_s(&m_file, m_buffer, "w+")) { char buffer[100]; if (strerror_s(buffer, errno)) { CATCH_RUNTIME_ERROR("Could not translate errno to a string"); @@ -12304,11 +12393,13 @@ namespace Catch { namespace Catch { class StartupExceptionRegistry { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) public: void add(std::exception_ptr const& exception) noexcept; std::vector const& getExceptions() const noexcept; private: std::vector m_exceptions; +#endif }; } // end namespace Catch @@ -12391,7 +12482,11 @@ namespace Catch { m_tagAliasRegistry.add( alias, tag, lineInfo ); } void registerStartupException() noexcept override { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) m_exceptionRegistry.add(std::current_exception()); +#else + CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +#endif } IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override { return m_enumValuesRegistry; @@ -12495,17 +12590,32 @@ namespace Catch { std::shared_ptr tracker; ITracker& currentTracker = ctx.currentTracker(); - if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if (currentTracker.nameAndLocation() == nameAndLocation) { + auto thisTracker = currentTracker.parent().findChild(nameAndLocation); + assert(thisTracker); + assert(thisTracker->isGeneratorTracker()); + tracker = std::static_pointer_cast(thisTracker); + } else if ( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { assert( childTracker ); assert( childTracker->isGeneratorTracker() ); tracker = std::static_pointer_cast( childTracker ); - } - else { + } else { tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker ); currentTracker.addChild( tracker ); } - if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( !tracker->isComplete() ) { tracker->open(); } @@ -12519,8 +12629,68 @@ namespace Catch { } void close() override { TrackerBase::close(); - // Generator interface only finds out if it has another item on atual move - if (m_runState == CompletedSuccessfully && m_generator->next()) { + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if ( m_children.empty() ) { + return false; + } + // If at least one child started executing, don't wait + if ( std::find_if( + m_children.begin(), + m_children.end(), + []( TestCaseTracking::ITrackerPtr tracker ) { + return tracker->hasStarted(); + } ) != m_children.end() ) { + return false; + } + + // No children have started. We need to check if they _can_ + // start, and thus we should wait for them, or they cannot + // start (due to filters), and we shouldn't wait for them + auto* parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while ( !parent->isSectionTracker() ) { + parent = &( parent->parent() ); + } + assert( parent && + "Missing root (test case) level section" ); + + auto const& parentSection = + static_cast( *parent ); + auto const& filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if ( filters.empty() ) { + return true; + } + + for ( auto const& child : m_children ) { + if ( child->isSectionTracker() && + std::find( filters.begin(), + filters.end(), + static_cast( *child ) + .trimmedName() ) != + filters.end() ) { + return true; + } + } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + if ( should_wait_for_child || + ( m_runState == CompletedSuccessfully && + m_generator->next() ) ) { m_children.clear(); m_runState = Executing; } @@ -12656,10 +12826,10 @@ namespace Catch { return true; } - auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { + auto RunContext::acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& { using namespace Generators; - GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) ); - assert( tracker.isOpen() ); + GeneratorTracker& tracker = GeneratorTracker::acquire(m_trackerContext, + TestCaseTracking::NameAndLocation( static_cast(generatorName), lineInfo ) ); m_lastAssertionInfo.lineInfo = lineInfo; return tracker; } @@ -12702,17 +12872,17 @@ namespace Catch { #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) void RunContext::benchmarkPreparing(std::string const& name) { - m_reporter->benchmarkPreparing(name); - } + m_reporter->benchmarkPreparing(name); + } void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { m_reporter->benchmarkStarting( info ); } void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) { m_reporter->benchmarkEnded( stats ); } - void RunContext::benchmarkFailed(std::string const & error) { - m_reporter->benchmarkFailed(error); - } + void RunContext::benchmarkFailed(std::string const & error) { + m_reporter->benchmarkFailed(error); + } #endif // CATCH_CONFIG_ENABLE_BENCHMARKING void RunContext::pushScopedMessage(MessageInfo const & message) { @@ -12846,9 +13016,8 @@ namespace Catch { } void RunContext::invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals + FatalConditionHandlerGuard _(&m_fatalConditionhandler); m_activeTestCase->invoke(); - fatalConditionHandler.reset(); } void RunContext::handleUnfinishedSections() { @@ -13433,6 +13602,7 @@ namespace Catch { // end catch_singletons.cpp // start catch_startup_exception_registry.cpp +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) namespace Catch { void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { CATCH_TRY { @@ -13448,6 +13618,7 @@ void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexce } } // end namespace Catch +#endif // end catch_startup_exception_registry.cpp // start catch_stream.cpp @@ -13632,7 +13803,7 @@ namespace Catch { namespace { char toLowerCh(char c) { - return static_cast( std::tolower( c ) ); + return static_cast( std::tolower( static_cast(c) ) ); } } @@ -14015,24 +14186,28 @@ namespace Catch { namespace { struct TestHasher { - explicit TestHasher(Catch::SimplePcg32& rng) { - basis = rng(); - basis <<= 32; - basis |= rng(); - } + using hash_t = uint64_t; - uint64_t basis; + explicit TestHasher( hash_t hashSuffix ): + m_hashSuffix{ hashSuffix } {} - uint64_t operator()(TestCase const& t) const { - // Modified FNV-1a hash - static constexpr uint64_t prime = 1099511628211; - uint64_t hash = basis; - for (const char c : t.name) { + uint32_t operator()( TestCase const& t ) const { + // FNV-1a hash with multiplication fold. + const hash_t prime = 1099511628211u; + hash_t hash = 14695981039346656037u; + for ( const char c : t.name ) { hash ^= c; hash *= prime; } - return hash; + hash ^= m_hashSuffix; + hash *= prime; + const uint32_t low{ static_cast( hash ) }; + const uint32_t high{ static_cast( hash >> 32 ) }; + return low * high; } + + private: + hash_t m_hashSuffix; }; } // end unnamed namespace @@ -14050,9 +14225,9 @@ namespace Catch { case RunTests::InRandomOrder: { seedRng( config ); - TestHasher h( rng() ); + TestHasher h{ config.rngSeed() }; - using hashedTest = std::pair; + using hashedTest = std::pair; std::vector indexed_tests; indexed_tests.reserve( unsortedTestCases.size() ); @@ -14215,15 +14390,12 @@ namespace TestCaseTracking { m_currentTracker = tracker; } - TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : m_nameAndLocation( nameAndLocation ), + TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ): + ITracker(nameAndLocation), m_ctx( ctx ), m_parent( parent ) {} - NameAndLocation const& TrackerBase::nameAndLocation() const { - return m_nameAndLocation; - } bool TrackerBase::isComplete() const { return m_runState == CompletedSuccessfully || m_runState == Failed; } @@ -14339,7 +14511,8 @@ namespace TestCaseTracking { bool SectionTracker::isComplete() const { bool complete = true; - if ((m_filters.empty() || m_filters[0] == "") + if (m_filters.empty() + || m_filters[0] == "" || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { complete = TrackerBase::isComplete(); } @@ -14384,6 +14557,14 @@ namespace TestCaseTracking { m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); } + std::vector const& SectionTracker::getFilters() const { + return m_filters; + } + + std::string const& SectionTracker::trimmedName() const { + return m_trimmed_name; + } + } // namespace TestCaseTracking using TestCaseTracking::ITracker; @@ -15118,11 +15299,48 @@ namespace Catch { // end catch_totals.cpp // start catch_uncaught_exceptions.cpp +// start catch_config_uncaught_exceptions.hpp + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP + +#if defined(_MSC_VER) +# if _MSC_VER >= 1900 // Visual Studio 2015 or newer +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +# endif +#endif + +#include + +#if defined(__cpp_lib_uncaught_exceptions) \ + && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif // __cpp_lib_uncaught_exceptions + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) \ + && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \ + && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +// end catch_config_uncaught_exceptions.hpp #include namespace Catch { bool uncaught_exceptions() { -#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return false; +#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) return std::uncaught_exceptions() > 0; #else return std::uncaught_exception(); @@ -15162,7 +15380,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 2, 12, 2, "", 0 ); + static Version version( 2, 13, 7, "", 0 ); return version; } @@ -15564,6 +15782,17 @@ namespace Catch { return std::string(buffer); } + bool shouldShowDuration( IConfig const& config, double duration ) { + if ( config.showDurations() == ShowDurations::Always ) { + return true; + } + if ( config.showDurations() == ShowDurations::Never ) { + return false; + } + const double min = config.minDuration(); + return min >= 0 && duration >= min; + } + std::string serializeFilters( std::vector const& container ) { ReusableStringStream oss; bool first = true; @@ -15830,10 +16059,6 @@ class AssertionPrinter { return "Reports test results on a single line, suitable for IDEs"; } - ReporterPreferences CompactReporter::getPreferences() const { - return m_reporterPrefs; - } - void CompactReporter::noMatchingTestCases( std::string const& spec ) { stream << "No test cases matched '" << spec << '\'' << std::endl; } @@ -15860,8 +16085,9 @@ class AssertionPrinter { } void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { - if (m_config->showDurations() == ShowDurations::Always) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + double dur = _sectionStats.durationInSeconds; + if ( shouldShowDuration( *m_config, dur ) ) { + stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl; } } @@ -16281,8 +16507,9 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { stream << "\nNo assertions in test case"; stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; } - if (m_config->showDurations() == ShowDurations::Always) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; } if (m_headerPrinted) { m_headerPrinted = false; @@ -16566,6 +16793,7 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter) #include #include #include +#include namespace Catch { @@ -16593,7 +16821,7 @@ namespace Catch { #else std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif - return std::string(timeStamp); + return std::string(timeStamp, timeStampSize-1); } std::string fileNameTag(const std::vector &tags) { @@ -16604,6 +16832,17 @@ namespace Catch { return it->substr(1); return std::string(); } + + // Formats the duration in seconds to 3 decimal places. + // This is done because some genius defined Maven Surefire schema + // in a way that only accepts 3 decimal places, and tools like + // Jenkins use that schema for validation JUnit reporter output. + std::string formatDuration( double seconds ) { + ReusableStringStream rss; + rss << std::fixed << std::setprecision( 3 ) << seconds; + return rss.str(); + } + } // anonymous namespace JunitReporter::JunitReporter( ReporterConfig const& _config ) @@ -16673,7 +16912,7 @@ namespace Catch { if( m_config->showDurations() == ShowDurations::Never ) xml.writeAttribute( "time", "" ); else - xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "time", formatDuration( suiteTime ) ); xml.writeAttribute( "timestamp", getCurrentTimestamp() ); // Write properties if there are any @@ -16718,12 +16957,13 @@ namespace Catch { if ( !m_config->name().empty() ) className = m_config->name() + "." + className; - writeSection( className, "", rootSection ); + writeSection( className, "", rootSection, stats.testInfo.okToFail() ); } - void JunitReporter::writeSection( std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode ) { + void JunitReporter::writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode, + bool testOkToFail) { std::string name = trim( sectionNode.stats.sectionInfo.name ); if( !rootName.empty() ) name = rootName + '/' + name; @@ -16740,13 +16980,18 @@ namespace Catch { xml.writeAttribute( "classname", className ); xml.writeAttribute( "name", name ); } - xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); + xml.writeAttribute( "time", formatDuration( sectionNode.stats.durationInSeconds ) ); // This is not ideal, but it should be enough to mimic gtest's // junit output. // Ideally the JUnit reporter would also handle `skipTest` // events and write those out appropriately. xml.writeAttribute( "status", "run" ); + if (sectionNode.stats.assertions.failedButOk) { + xml.scopedElement("skipped") + .writeAttribute("message", "TEST_CASE tagged with !mayfail"); + } + writeAssertions( sectionNode ); if( !sectionNode.stdOut.empty() ) @@ -16756,9 +17001,9 @@ namespace Catch { } for( auto const& childNode : sectionNode.childSections ) if( className.empty() ) - writeSection( name, "", *childNode ); + writeSection( name, "", *childNode, testOkToFail ); else - writeSection( className, name, *childNode ); + writeSection( className, name, *childNode, testOkToFail ); } void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { @@ -17180,6 +17425,10 @@ namespace Catch { .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.scopedElement( "OverallResultsCases") + .writeAttribute( "successes", testGroupStats.totals.testCases.passed ) + .writeAttribute( "failures", testGroupStats.totals.testCases.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk ); m_xml.endElement(); } @@ -17189,6 +17438,10 @@ namespace Catch { .writeAttribute( "successes", testRunStats.totals.assertions.passed ) .writeAttribute( "failures", testRunStats.totals.assertions.failed ) .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.scopedElement( "OverallResultsCases") + .writeAttribute( "successes", testRunStats.totals.testCases.passed ) + .writeAttribute( "failures", testRunStats.totals.testCases.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk ); m_xml.endElement(); } From 01e11181228f11fd0726cf71e7cb97d13910afaa Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Tue, 12 Apr 2022 00:32:38 +0200 Subject: [PATCH 02/24] update pkg-config file * remove quotes from name (not needed) * while at it rephrase * remove empty Requires Signed-off-by: Heiko Hund --- dbus-asio.pc.in | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dbus-asio.pc.in b/dbus-asio.pc.in index 4b71eb2..da95323 100644 --- a/dbus-asio.pc.in +++ b/dbus-asio.pc.in @@ -4,9 +4,8 @@ libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@/libdbus-asio Name: @PROJECT_NAME@ -Description: "D-Bus ASIO Library" +Description: asynchronous C++ DBus library Version: @PROJECT_VERSION@ -Requires: Libs: -L${libdir} @PLATFORM_LIBS@ Cflags: -I${includedir} From b035a2c7b5caa7de4976675f78d42b02925f7d7d Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Tue, 12 Apr 2022 00:50:34 +0200 Subject: [PATCH 03/24] add validating types for basic DBus names This set of types makes it possible to validate strings according to the requirements for certain "names" as specified in the DBus spec. The sections "Valid Names", "Valid Object Paths" and "Match Rules" in the spec define the rules for validaton. The types are: * UniqueName - a unique connection name * WellKnownName - a unique name alias, aka well-known bus name * BusName - either one of the previous two, a "bus address" * ObjectPath - a object path as used in the message header * InterfaceName - a interface name as used in the message header * MemberName - a member name as used in the message header * ErrorName - a error name as used in the message header * NamespaceName - a special kind of bus name used in match rules The types are opaque, since they take a C or std:: string and have an operator std::string(). Signed-off-by: Heiko Hund --- CMakeLists.txt | 2 + src/dbus.h | 7 +- src/dbus_names.cpp | 222 +++++++++++++++++++++++++++++++ src/dbus_names.h | 102 +++++++++++++++ tests/CMakeLists.txt | 5 + tests/dbus_names_unittest.cpp | 237 ++++++++++++++++++++++++++++++++++ 6 files changed, 571 insertions(+), 4 deletions(-) create mode 100644 src/dbus_names.cpp create mode 100644 src/dbus_names.h create mode 100644 tests/dbus_names_unittest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fd4c9a5..a094785 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ set(HEADER_FILES src/dbus_messageostream.h src/dbus_messageistream.h src/dbus_native.h + src/dbus_names.h src/dbus_octetbuffer.h src/dbus_platform.h src/dbus_transport.h @@ -55,6 +56,7 @@ add_library(dbus-asio SHARED src/dbus_message_signal.cpp src/dbus_native.cpp src/dbus_native_messages.cpp + src/dbus_names.cpp src/dbus_octetbuffer.cpp src/dbus_platform.cpp src/dbus_transport.cpp diff --git a/src/dbus.h b/src/dbus.h index 9f5016d..6ca3e01 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,11 +16,11 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_H -#define DBUS_H +#pragma once // Core elements and types #include "dbus_log.h" +#include "dbus_names.h" #include "dbus_platform.h" #include "dbus_utils.h" @@ -54,5 +55,3 @@ // Introspective library #include "dbus_introspectable.h" - -#endif // DBUS_H diff --git a/src/dbus_names.cpp b/src/dbus_names.cpp new file mode 100644 index 0000000..24171e0 --- /dev/null +++ b/src/dbus_names.cpp @@ -0,0 +1,222 @@ +// This file is part of dbus-asio +// Copyright 2022 OpenVPN Inc. +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, version 3, or at your +// option any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The GNU Lesser General Public License version 3 is included in the +// file named COPYING. If you do not have this file see +// . + +#include "dbus_names.h" + +static constexpr std::array nameStr = { + "bus name", + "unique connection name", + "well-known name", + "error name", + "interface name", + "namespace name", + "member name" +}; + +DBus::Name::operator std::string() const +{ + return m_name; +} + +// Restrictions that apply to names in general: +// +// * Each name must only contain the ASCII characters +// "[A-Z][a-z][0-9]_" and must not begin with a digit. +// * Names must not exceed the maximum name length of 255. +// * Names must contain at least one character. +// +// Additions for interface, error and bus name: +// +// * Names are composed of 2 or more elements separated by a +// period ('.') character. +// * All elements must contain at least one character. +// +// Additions for bus names: +// +// * Bus names that start with a colon (':') character are unique +// connection names. Other bus names are called well-known bus names. +// * Elements that are part of a unique connection name may +// begin with a digit. +// * Elements can also contain the hyphen character ('-'), while it +// being discouraged in new bus names. +void +DBus::Name::validate(const std::string& name, Type type) const +{ + if (name.empty()) + throw InvalidName("name is empty"); + if (name.size() > MaximumSize) + throw InvalidName(name.substr(0, 16) + "... exceeds " + + std::to_string(MaximumSize) + " characters"); + + bool isUnique = false; + if (name[0] == ':') { + if (type != BusName && type != UniqueName) + throw InvalidName(name + " is not a " + nameStr[type]); + isUnique = true; + } else if (type == UniqueName) { + throw InvalidName(name + " is not a " + nameStr[type]); + } + + const bool allowHyphen = + type == BusName || type == UniqueName || type == WellKnownName; + const bool needPeriod = type != MemberName && type != NamespaceName; + const bool allowPeriod = type != MemberName; + bool havePeriod = false; + + char prev = '.'; + for (int i = isUnique ? 1 : 0; i < name.size(); ++i) { + const char ch = name[i]; + if (allowPeriod && ch == '.') { + if (prev == '.') + throw InvalidName(name +" has empty element"); + havePeriod = true; + } + + if (!isUnique && prev == '.' && (ch >= '0' && ch <= '9')) { + const char *element = allowPeriod ? " element" : ""; + throw InvalidName(name + element + " starts with digit"); + } + + if ((allowHyphen && ch == '-') || (allowPeriod && ch == '.') || + ch == '_' || (ch >= 'A' && ch <= 'Z') || + (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) + prev = ch; + else + throw InvalidName(name +" has invalid character"); + } + + if (needPeriod && !havePeriod) + throw InvalidName(name +" doesn't have two elements"); +} + + +DBus::BusName::BusName(const std::string& name) +{ + validate(name, Type::BusName); + m_name = name; +} + +DBus::UniqueName::UniqueName(const std::string& name) +{ + validate(name, Type::UniqueName); + m_name = name; +} + +DBus::WellKnownName::WellKnownName(const std::string& name) +{ + validate(name, Type::WellKnownName); + m_name = name; +} + +DBus::InterfaceName::InterfaceName(const std::string& name) +{ + validate(name, Type::InterfaceName); + m_name = name; +} + +DBus::ErrorName::ErrorName(const std::string& name) +{ + validate(name, Type::ErrorName); + m_name = name; +} + +DBus::NamespaceName::NamespaceName(const std::string& name) +{ + validate(name, Type::NamespaceName); + m_name = name; +} + +DBus::MemberName::MemberName(const std::string& name) +{ + validate(name, Type::MemberName); + m_name = name; +} + + + +DBus::BusName::BusName(const char* name) + : BusName(std::string(name)) +{} + +DBus::UniqueName::UniqueName(const char* name) + : UniqueName(std::string(name)) +{} + +DBus::WellKnownName::WellKnownName(const char* name) + : WellKnownName(std::string(name)) +{} + +DBus::InterfaceName::InterfaceName(const char* name) + : InterfaceName(std::string(name)) +{} + +DBus::ErrorName::ErrorName(const char* name) + : ErrorName(std::string(name)) +{} + +DBus::NamespaceName::NamespaceName(const char* name) + : NamespaceName(std::string(name)) +{} + +DBus::MemberName::MemberName(const char* name) + : MemberName(std::string(name)) +{} + + + +// The following rules define a valid object path. +// +// * The path may be of any length. +// * The path must begin with an ASCII '/' character, and must consist of +// elements separated by slash characters. +// * Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_" +// * No element may be the empty string. +// * Multiple '/' characters cannot occur in sequence. +// * A trailing '/' character is not allowed unless the path is the root +// path (a single '/' character). +// +DBus::ObjectPath::ObjectPath(const std::string& path) +{ + if (path.empty()) + throw InvalidObjectPath("path is empty"); + + if (path.front() != '/') + throw InvalidObjectPath(path + " doesn't start with slash"); + + if (path.size() > 1) { + if (path.back() == '/') + throw InvalidObjectPath(path + " ends with slash"); + + char prev = ' '; + for (const auto& ch : path) { + if (ch == '/' && prev == '/') + throw InvalidObjectPath(path + " has // sequence"); + + if ( ch == '/' || ch == '_' || (ch >= 'A' && ch <= 'Z') || + (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') ) + prev = ch; + else + throw InvalidObjectPath(path + " has invalid character"); + } + } + m_name = path; +} + +DBus::ObjectPath::ObjectPath(const char* path) + : ObjectPath(std::string(path)) +{} + diff --git a/src/dbus_names.h b/src/dbus_names.h new file mode 100644 index 0000000..9830478 --- /dev/null +++ b/src/dbus_names.h @@ -0,0 +1,102 @@ +// This file is part of dbus-asio +// Copyright 2022 OpenVPN Inc. +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, version 3, or at your +// option any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The GNU Lesser General Public License version 3 is included in the +// file named COPYING. If you do not have this file see +// . + +#pragma once + +#include +#include +#include + +namespace DBus { + + using InvalidName = std::runtime_error; + using InvalidObjectPath = std::runtime_error; + + + class Name { + public: + static constexpr std::size_t MaximumSize = 255; + operator std::string() const; + + protected: + enum Type { + BusName, UniqueName, WellKnownName, ErrorName, + InterfaceName, NamespaceName, MemberName }; + Name() = default; + void validate(const std::string& name, Type type) const; + std::string m_name; + }; + + struct BusName : public Name { + BusName(BusName&&) = default; + BusName(const BusName&) = default; + BusName(const char* name); + BusName(const std::string& name); + protected: + BusName() = default; + }; + + struct UniqueName : public BusName { + UniqueName(UniqueName&&) = default; + UniqueName(const UniqueName&) = default; + UniqueName(const char* name); + UniqueName(const std::string& name); + }; + + struct WellKnownName : public BusName { + WellKnownName(WellKnownName&&) = default; + WellKnownName(const WellKnownName&) = default; + WellKnownName(const char* name); + WellKnownName(const std::string& name); + }; + + struct InterfaceName : public Name { + InterfaceName(InterfaceName&&) = default; + InterfaceName(const InterfaceName&) = default; + InterfaceName(const char* name); + InterfaceName(const std::string& name); + }; + + struct ErrorName : public Name { + ErrorName(ErrorName&&) = default; + ErrorName(const ErrorName&) = default; + ErrorName(const char* name); + ErrorName(const std::string& name); + }; + + struct NamespaceName : public Name { + NamespaceName(NamespaceName&&) = default; + NamespaceName(const NamespaceName&) = default; + NamespaceName(const char* name); + NamespaceName(const std::string& name); + }; + + struct MemberName : public Name { + MemberName(MemberName&&) = default; + MemberName(const MemberName&) = default; + MemberName(const char* name); + MemberName(const std::string& name); + }; + + struct ObjectPath : public Name { + ObjectPath(ObjectPath&&) = default; + ObjectPath(const ObjectPath&) = default; + ObjectPath(const char* path); + ObjectPath(const std::string& path); + }; + +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7424c3e..5bb3163 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,6 +2,11 @@ cmake_minimum_required(VERSION 3.7.1) add_executable(dbus_asio_test dbus_asio_test.cpp) target_link_libraries(dbus_asio_test LINK_PRIVATE dbus-asio) +add_executable(dbus_names_unittest dbus_names_unittest.cpp test_main.cpp) +target_include_directories(dbus_names_unittest PRIVATE ../extern) +target_link_libraries(dbus_names_unittest LINK_PRIVATE dbus-asio) +add_test(NAME run_dbus_names_unittest COMMAND dbus_names_unittest) + add_executable(dbus_platform_unittest dbus_platform_unittest.cpp test_main.cpp) target_include_directories(dbus_platform_unittest PRIVATE ../extern) target_link_libraries(dbus_platform_unittest LINK_PRIVATE dbus-asio) diff --git a/tests/dbus_names_unittest.cpp b/tests/dbus_names_unittest.cpp new file mode 100644 index 0000000..2c3d601 --- /dev/null +++ b/tests/dbus_names_unittest.cpp @@ -0,0 +1,237 @@ +#include "dbus_names.h" +#include +#include + +namespace DBus { +namespace test { + +using Catch::Matchers::Contains; + +TEST_CASE("BusName Validation") +{ + REQUIRE_NOTHROW(BusName("-._")); + REQUIRE_NOTHROW(BusName(":ab.7")); + REQUIRE_NOTHROW(BusName(":1.234")); + REQUIRE_NOTHROW(BusName("a.b.c.d")); + REQUIRE_NOTHROW(BusName("_1.valid-name")); + REQUIRE_NOTHROW(BusName("abcdefghijklmnopqrstuvwxyz.ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-")); + REQUIRE_NOTHROW(BusName( + "OKAY456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE")); + + REQUIRE_THROWS_WITH(BusName(""), "name is empty"); + REQUIRE_THROWS_WITH(BusName("."), ". has empty element"); + REQUIRE_THROWS_WITH(BusName(":"), ": doesn't have two elements"); + REQUIRE_THROWS_WITH(BusName(":.b"), ":.b has empty element"); + REQUIRE_THROWS_WITH(BusName("a..b"), "a..b has empty element"); + REQUIRE_THROWS_WITH(BusName("a!.b"), "a!.b has invalid character"); + REQUIRE_THROWS_WITH(BusName(".invalid.name"), ".invalid.name has empty element"); + REQUIRE_THROWS_WITH(BusName("invalid_name"), "invalid_name doesn't have two elements"); + REQUIRE_THROWS_WITH(BusName("1.invalid.name"), "1.invalid.name element starts with digit"); + REQUIRE_THROWS_WITH(BusName("invalid.name.2nd"), "invalid.name.2nd element starts with digit"); + REQUIRE_THROWS_WITH(BusName( + "INVALID789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF"), + Contains(" exceeds 255 characters")); +} + +TEST_CASE("WellKnownName Validation") +{ + REQUIRE_NOTHROW(WellKnownName("-._")); + REQUIRE_NOTHROW(WellKnownName("a.b.c.d")); + REQUIRE_NOTHROW(WellKnownName("_1.valid-name")); + REQUIRE_NOTHROW(WellKnownName("abcdefghijklmnopqrstuvwxyz.ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-")); + REQUIRE_NOTHROW(WellKnownName( + "OKAY456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE")); + + REQUIRE_THROWS_WITH(WellKnownName(""), "name is empty"); + REQUIRE_THROWS_WITH(WellKnownName("."), ". has empty element"); + REQUIRE_THROWS_WITH(WellKnownName(":"), ": is not a well-known name"); + REQUIRE_THROWS_WITH(WellKnownName(":.b"), ":.b is not a well-known name"); + REQUIRE_THROWS_WITH(WellKnownName("a..b"), "a..b has empty element"); + REQUIRE_THROWS_WITH(WellKnownName("a!.b"), "a!.b has invalid character"); + REQUIRE_THROWS_WITH(WellKnownName(":ab.7"), ":ab.7 is not a well-known name"); + REQUIRE_THROWS_WITH(WellKnownName(":1.234"), ":1.234 is not a well-known name"); + REQUIRE_THROWS_WITH(WellKnownName(".invalid.name"), ".invalid.name has empty element"); + REQUIRE_THROWS_WITH(WellKnownName("invalid_name"), "invalid_name doesn't have two elements"); + REQUIRE_THROWS_WITH(WellKnownName("1.invalid.name"), "1.invalid.name element starts with digit"); + REQUIRE_THROWS_WITH(WellKnownName("invalid.name.2nd"), "invalid.name.2nd element starts with digit"); + REQUIRE_THROWS_WITH(WellKnownName( + "INVALID789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF"), + Contains(" exceeds 255 characters")); +} + +TEST_CASE("UniqueName Validation") +{ + REQUIRE_NOTHROW(UniqueName(":_.-")); + REQUIRE_NOTHROW(UniqueName(":ab.7")); + REQUIRE_NOTHROW(UniqueName(":1.234")); + REQUIRE_NOTHROW(UniqueName(":a.b.c.d")); + REQUIRE_NOTHROW(UniqueName(":_1.valid-name")); + REQUIRE_NOTHROW(UniqueName(":abcdefghijklmnopqrstuvwxyz.ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789-")); + REQUIRE_NOTHROW(UniqueName( + ":OKAY56789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE")); + + REQUIRE_THROWS_WITH(UniqueName(""), "name is empty"); + REQUIRE_THROWS_WITH(UniqueName("."), ". is not a unique connection name"); + REQUIRE_THROWS_WITH(UniqueName(":"), ": doesn't have two elements"); + REQUIRE_THROWS_WITH(UniqueName("-._"), "-._ is not a unique connection name"); + REQUIRE_THROWS_WITH(UniqueName(":.b"), ":.b has empty element"); + REQUIRE_THROWS_WITH(UniqueName(":a..b"), ":a..b has empty element"); + REQUIRE_THROWS_WITH(UniqueName(":a:.b"), ":a:.b has invalid character"); + REQUIRE_THROWS_WITH(UniqueName("a.b.c.d"), "a.b.c.d is not a unique connection name"); + REQUIRE_THROWS_WITH(UniqueName(":.invalid.name"), ":.invalid.name has empty element"); + REQUIRE_THROWS_WITH(UniqueName(":invalid_name"), ":invalid_name doesn't have two elements"); + REQUIRE_THROWS_WITH(UniqueName( + "INVALID789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF"), + Contains(" exceeds 255 characters")); +} + +TEST_CASE("ErrorName Validation") +{ + REQUIRE_NOTHROW(ErrorName("_._")); + REQUIRE_NOTHROW(ErrorName("a.b.c.d")); + REQUIRE_NOTHROW(ErrorName("_1.valid.Name")); + REQUIRE_NOTHROW(ErrorName("abcdefghijklmnopqrstuvwxyz.ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789")); + REQUIRE_NOTHROW(ErrorName( + "OKAY456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE")); + + REQUIRE_THROWS_WITH(ErrorName(""), "name is empty"); + REQUIRE_THROWS_WITH(ErrorName("."), ". has empty element"); + REQUIRE_THROWS_WITH(ErrorName(":"), ": is not a error name"); + REQUIRE_THROWS_WITH(ErrorName("-._"), "-._ has invalid character"); + REQUIRE_THROWS_WITH(ErrorName(".b"), ".b has empty element"); + REQUIRE_THROWS_WITH(ErrorName("ab.7"), "ab.7 element starts with digit"); + REQUIRE_THROWS_WITH(ErrorName("a..b"), "a..b has empty element"); + REQUIRE_THROWS_WITH(ErrorName("a .b"), "a .b has invalid character"); + REQUIRE_THROWS_WITH(ErrorName(".invalid.name"), ".invalid.name has empty element"); + REQUIRE_THROWS_WITH(ErrorName("invalid_name"), "invalid_name doesn't have two elements"); + REQUIRE_THROWS_WITH(ErrorName( + "INVALID789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF"), + "INVALID789abcdef... exceeds 255 characters"); +} + +TEST_CASE("InterfaceName Validation") +{ + REQUIRE_NOTHROW(InterfaceName("_._")); + REQUIRE_NOTHROW(InterfaceName("a.b.c.d")); + REQUIRE_NOTHROW(InterfaceName("_1.valid.Name")); + REQUIRE_NOTHROW(InterfaceName("abcdefghijklmnopqrstuvwxyz.ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789")); + REQUIRE_NOTHROW(InterfaceName( + "OKAY456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE")); + + REQUIRE_THROWS_WITH(InterfaceName(""), "name is empty"); + REQUIRE_THROWS_WITH(InterfaceName("."), ". has empty element"); + REQUIRE_THROWS_WITH(InterfaceName(":"), ": is not a interface name"); + REQUIRE_THROWS_WITH(InterfaceName("-._"), "-._ has invalid character"); + REQUIRE_THROWS_WITH(InterfaceName(".b"), ".b has empty element"); + REQUIRE_THROWS_WITH(InterfaceName("ab.7"), "ab.7 element starts with digit"); + REQUIRE_THROWS_WITH(InterfaceName("a..b"), "a..b has empty element"); + REQUIRE_THROWS_WITH(InterfaceName("a .b"), "a .b has invalid character"); + REQUIRE_THROWS_WITH(InterfaceName(".invalid.name"), ".invalid.name has empty element"); + REQUIRE_THROWS_WITH(InterfaceName("invalid_name"), "invalid_name doesn't have two elements"); + REQUIRE_THROWS_WITH(InterfaceName( + "INVALID789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF"), + "INVALID789abcdef... exceeds 255 characters"); +} + +TEST_CASE("NamespaceName Validation") +{ + REQUIRE_NOTHROW(NamespaceName("_")); + REQUIRE_NOTHROW(NamespaceName("ValidName")); + REQUIRE_NOTHROW(NamespaceName("a.b.c.d")); + REQUIRE_NOTHROW(NamespaceName("_1.valid.Name")); + REQUIRE_NOTHROW(NamespaceName("abcdefghijklmnopqrstuvwxyz.ABCDEFGHIJKLMNOPQRSTUVWXYZ._0123456789")); + REQUIRE_NOTHROW(NamespaceName( + "OKAY456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE")); + + REQUIRE_THROWS_WITH(NamespaceName(""), "name is empty"); + REQUIRE_THROWS_WITH(NamespaceName("."), ". has empty element"); + REQUIRE_THROWS_WITH(NamespaceName(":"), ": is not a namespace name"); + REQUIRE_THROWS_WITH(NamespaceName("-._"), "-._ has invalid character"); + REQUIRE_THROWS_WITH(NamespaceName(".b"), ".b has empty element"); + REQUIRE_THROWS_WITH(NamespaceName("ab.7"), "ab.7 element starts with digit"); + REQUIRE_THROWS_WITH(NamespaceName("a..b"), "a..b has empty element"); + REQUIRE_THROWS_WITH(NamespaceName("a .b"), "a .b has invalid character"); + REQUIRE_THROWS_WITH(NamespaceName(".invalid.name"), ".invalid.name has empty element"); + REQUIRE_THROWS_WITH(NamespaceName( + "INVALID789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCD.F" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF"), + "INVALID789abcdef... exceeds 255 characters"); +} + +TEST_CASE("MemberName Validation") +{ + REQUIRE_NOTHROW(MemberName("_")); + REQUIRE_NOTHROW(MemberName("Abcd")); + REQUIRE_NOTHROW(MemberName("_1validName")); + REQUIRE_NOTHROW(MemberName("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_")); + REQUIRE_NOTHROW(MemberName( + "OKAY456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDE")); + + REQUIRE_THROWS_WITH(MemberName(""), "name is empty"); + REQUIRE_THROWS_WITH(MemberName("."), ". has invalid character"); + REQUIRE_THROWS_WITH(MemberName(":"), ": is not a member name"); + REQUIRE_THROWS_WITH(MemberName("-"), "- has invalid character"); + REQUIRE_THROWS_WITH(MemberName("7"), "7 starts with digit"); + REQUIRE_THROWS_WITH(MemberName( + "INVALID789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF" + "0123456789abcdef0123456789ABCDEF0123456789abcdef0123456789ABCDEF"), + "INVALID789abcdef... exceeds 255 characters"); +} + +TEST_CASE("ObjectPath Validation") +{ + REQUIRE_NOTHROW(ObjectPath("/")); + REQUIRE_NOTHROW(ObjectPath("/foo")); + REQUIRE_NOTHROW(ObjectPath("/foo/bar")); + REQUIRE_NOTHROW(ObjectPath("/___/1___")); + REQUIRE_NOTHROW(ObjectPath("/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_")); + + REQUIRE_THROWS_WITH(ObjectPath(""), "path is empty"); + REQUIRE_THROWS_WITH(ObjectPath("foo/"), "foo/ doesn't start with slash"); + REQUIRE_THROWS_WITH(ObjectPath("/foo/"), "/foo/ ends with slash"); + REQUIRE_THROWS_WITH(ObjectPath("/foo!/bar"), "/foo!/bar has invalid character"); + REQUIRE_THROWS_WITH(ObjectPath("/foo//bar"), "/foo//bar has // sequence"); +} + +} // namespace test +} // namespace DBus From a541905fbec1aad3f8f87295937459ac1c2809c0 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Tue, 12 Apr 2022 01:21:04 +0200 Subject: [PATCH 04/24] extend and overhaul MatchRule type Change the way MatchRules are constructed. Instead of taking a string and then parsing it, take the match keys in a structured way, validate them and generate a match rule string from them. Added support for missing match rules keys, except "eavesdrop", since that one is deprecated in favor of the "BecomeMonitor" method. Signed-off-by: Heiko Hund --- src/dbus_matchrule.cpp | 218 +++++++++++++++++------------- src/dbus_matchrule.h | 74 ++++++---- tests/CMakeLists.txt | 5 + tests/dbus_matchrule_unittest.cpp | 77 +++++++++++ 4 files changed, 257 insertions(+), 117 deletions(-) create mode 100644 tests/dbus_matchrule_unittest.cpp diff --git a/src/dbus_matchrule.cpp b/src/dbus_matchrule.cpp index 38c828e..36dcbdc 100644 --- a/src/dbus_matchrule.cpp +++ b/src/dbus_matchrule.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,117 +16,148 @@ // file named COPYING. If you do not have this file see // . -#include -#include -#include -#include - -#include "dbus_log.h" #include "dbus_matchrule.h" -#include "dbus_message.h" +#include "dbus_type_objectpath.h" +#include "dbus_names.h" + +#include // https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules -DBus::MatchRule::MatchRule(const std::string& rule, - const Message::CallbackFunctionSignal& handler) +DBus::MatchRule& +DBus::MatchRule::type(Type type) { - // Parse the rule string into something programmatic - - // 1. Split by the comma - boost::char_separator separator { "," }; - boost::tokenizer> tokenizer { rule, separator }; - for (auto&& param : tokenizer) { - // 2. Then split each at the equals - std::vector keyvalue; - boost::split(keyvalue, param, boost::is_any_of("=")); - - // Valid keys are: type, sender, interface, member, path, path_namespace, - // destination, arg*, eavesdrop We're happy for [] to throw on malformed - // rules, since that's watch we'd do anyway in this case - - // 3. Strip the single quotes from the value - keyvalue[1].pop_back(); - keyvalue[1].erase(0, 1); - - // We store it somewhere convenient to speed the isMateched checks - if (keyvalue[0] == "type") { - type = keyvalue[1]; - } else if (keyvalue[0] == "sender") { - sender = keyvalue[1]; - } else if (keyvalue[0] == "interface") { - interface = keyvalue[1]; - } else if (keyvalue[0] == "member") { - member = keyvalue[1]; - } else if (keyvalue[0] == "path") { - path = keyvalue[1]; - } else if (keyvalue[0] == "path_namespace") { - path_namespace = keyvalue[1]; - } else if (keyvalue[0] == "destination") { - destination = keyvalue[1]; - } - - // TODO: eavesdrop - // TODO: arg - } + if (type == Type::MethodCall) + m_type = "method_call"; + else if (type == Type::MethodReturn) + m_type = "method_return"; + else if (type == Type::Signal) + m_type = "signal"; + else if (type == Type::Error) + m_type = "error"; + return *this; +} - // Using both path and path_namespace in the same match rule is not allowed. - if (path != "" && path_namespace != "") { - throw std::runtime_error( - "Match rules with both 'path' and 'path_namespace' are not allowed."); - } +DBus::MatchRule& +DBus::MatchRule::sender(const BusName& name) +{ + m_sender = name; + return *this; +} - // Finally, remember the callback - callback = handler; +DBus::MatchRule& +DBus::MatchRule::interface(const InterfaceName& name) +{ + m_interface = name; + return *this; } -bool DBus::MatchRule::isMatched(const DBus::Message::Signal& signal) +DBus::MatchRule& +DBus::MatchRule::member(const MemberName& name) { - // These checks are AND, so all set parameters must agree - if (sender != "" && sender != signal.getHeaderSender()) { - return false; - } + m_member = name; + return *this; +} - if (interface != "" && interface != signal.getHeaderInterface()) { - return false; - } +DBus::MatchRule& +DBus::MatchRule::path(const ObjectPath& name) +{ + if (!m_pathNamespace.empty()) + throw InvalidMatchRule( + "path and path_namespace are not allowed together."); + m_path = name; + return *this; +} - if (member != "" && member != signal.getHeaderMember()) { - return false; - } +DBus::MatchRule& +DBus::MatchRule::pathNamespace(const ObjectPath& name) +{ + if (!m_path.empty()) + throw InvalidMatchRule( + "path and path_namespace are not allowed together."); + m_pathNamespace = name; + return *this; +} - if (destination != "" && destination != signal.getHeaderDestination()) { - return false; - } +DBus::MatchRule& +DBus::MatchRule::destination(const UniqueName& name) +{ + m_destination = name; + return *this; +} - std::string signal_path(signal.getHeaderPath()); - if (path != "" && path != signal_path) { - return false; - } +DBus::MatchRule& +DBus::MatchRule::arg0Namespace(const NamespaceName& name) +{ + m_arg0Namespace = name; + return *this; +} - // For example, path_namespace='/com/example/foo' would match signals sent - // by /com/example/foo or by /com/example/foo/bar, but not by - // /com/example/foobar. - if (path_namespace != "") { - if (signal_path.find(path_namespace) != 0) { - return false; - } - - // This handles the 3rd case, by checking the character _after_ the match is - // found: if it's not the terminator or another path (i.e. a new /) the - // search fails. - auto next_char = signal_path.at(path_namespace.size()); - if (next_char != '\0' && next_char != '/') { - return false; - } - } +DBus::MatchRule& +DBus::MatchRule::arg(std::uint8_t index, const std::string& string) +{ + if (index > MaximumIndex) + throw InvalidMatchRule( + "arg index exceeds " + std::to_string(MaximumIndex)); + m_arg[index] = string; + escapeApostrophes(m_arg[index]); + return *this; +} - // TODO: eavesdrop - // TODO: arg +DBus::MatchRule& +DBus::MatchRule::argPath(std::uint8_t index, const std::string& string) +{ + if (index > MaximumIndex) + throw InvalidMatchRule( + "arg path index exceeds " + std::to_string(MaximumIndex)); + m_argPath[index] = string; + escapeApostrophes(m_argPath[index]); + return *this; +} - return true; +std::string DBus::MatchRule::str() const +{ + std::ostringstream matchRule; + + if (!m_type.empty()) + matchRule << ",type='" << m_type << "'"; + if (!m_sender.empty()) + matchRule << ",sender='" << m_sender << "'"; + if (!m_interface.empty()) + matchRule << ",interface='" << m_interface << "'"; + if (!m_member.empty()) + matchRule << ",member='" << m_member << "'"; + if (!m_path.empty()) + matchRule << ",path='" << m_path << "'"; + if (!m_pathNamespace.empty()) + matchRule << ",path_namespace='" << m_pathNamespace << "'"; + if (!m_destination.empty()) + matchRule << ",destination='" << m_destination << "'"; + if (!m_arg0Namespace.empty()) + matchRule << ",arg0namespace='" << m_arg0Namespace << "'"; + + for (auto it = m_arg.cbegin(); it != m_arg.cend(); ++it) + matchRule << ",arg" + std::to_string(it->first) + "='" << it->second << "'"; + + for (auto it = m_argPath.cbegin(); it != m_argPath.cend(); ++it) + matchRule << ",arg" + std::to_string(it->first) + "path='" << it->second << "'"; + + // Return empty string for match all rule + if (matchRule.tellp() == 0) + return ""; + + // Remove the leading comma from match rule + return matchRule.str().substr(1); } -void DBus::MatchRule::invoke(const DBus::Message::Signal& signal) +void DBus::MatchRule::escapeApostrophes(std::string& value) { - callback(signal); + const std::string what("'"); + const std::string with("'\\''"); + + std::size_t pos = 0; + while ((pos = value.find(what, pos)) != value.npos) { + value.replace(pos, what.size(), with); + pos += with.size(); + } } diff --git a/src/dbus_matchrule.h b/src/dbus_matchrule.h index 91786e6..e0429d8 100644 --- a/src/dbus_matchrule.h +++ b/src/dbus_matchrule.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,33 +16,58 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_MATCHRULE -#define DBUS_MATCHRULE +#pragma once -#include "dbus_message.h" +#include #include +#include namespace DBus { -class MatchRule { -public: - MatchRule(const std::string& rule, - const Message::CallbackFunctionSignal& handler); - - bool isMatched(const DBus::Message::Signal& signal); - void invoke(const DBus::Message::Signal& signal); - -protected: - std::string type; // NOTE: Only signals are currentl used/supported - std::string sender; - std::string interface; - std::string member; - std::string path; - std::string path_namespace; - std::string destination; - - Message::CallbackFunctionSignal callback; -}; -} // namespace DBus +// https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules + +struct BusName; +struct UniqueName; +struct MemberName; +struct InterfaceName; +struct NamespaceName; + +class ObjectPath; + +using InvalidMatchRule = std::runtime_error; + + class MatchRule { + public: + static constexpr std::size_t MaximumIndex = 63; + enum class Type{ MethodCall, MethodReturn, Signal, Error }; + + MatchRule& type(Type type); + MatchRule& sender(const BusName& name); + MatchRule& interface(const InterfaceName& name); + MatchRule& member(const MemberName& name); + MatchRule& path(const ObjectPath& name); + MatchRule& pathNamespace(const ObjectPath& name); + MatchRule& destination(const UniqueName& name); + MatchRule& arg0Namespace(const NamespaceName& name); + MatchRule& arg(std::uint8_t index, const std::string& string); + MatchRule& argPath(std::uint8_t index, const std::string& string); -#endif // DBUS_MATCHRULE + std::string str() const; + + protected: + void escapeApostrophes(std::string& value); + + std::string m_type; + std::string m_sender; + std::string m_interface; + std::string m_member; + std::string m_path; + std::string m_pathNamespace; + std::string m_destination; + std::string m_arg0Namespace; + + std::map m_arg; + std::map m_argPath; + }; + +} // namespace DBus diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5bb3163..ad430e0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,6 +7,11 @@ target_include_directories(dbus_names_unittest PRIVATE ../extern) target_link_libraries(dbus_names_unittest LINK_PRIVATE dbus-asio) add_test(NAME run_dbus_names_unittest COMMAND dbus_names_unittest) +add_executable(dbus_matchrule_unittest dbus_matchrule_unittest.cpp test_main.cpp) +target_include_directories(dbus_matchrule_unittest PRIVATE ../extern) +target_link_libraries(dbus_matchrule_unittest LINK_PRIVATE dbus-asio) +add_test(NAME run_dbus_matchrule_unittest COMMAND dbus_matchrule_unittest) + add_executable(dbus_platform_unittest dbus_platform_unittest.cpp test_main.cpp) target_include_directories(dbus_platform_unittest PRIVATE ../extern) target_link_libraries(dbus_platform_unittest LINK_PRIVATE dbus-asio) diff --git a/tests/dbus_matchrule_unittest.cpp b/tests/dbus_matchrule_unittest.cpp new file mode 100644 index 0000000..60808ef --- /dev/null +++ b/tests/dbus_matchrule_unittest.cpp @@ -0,0 +1,77 @@ +#include "dbus_matchrule.h" +#include "dbus_names.h" +#include +#include + +namespace DBus { +namespace test { + +TEST_CASE("MatchRule wildcard") +{ + REQUIRE(MatchRule().str() == ""); +} + +TEST_CASE("MatchRule::type()") +{ + REQUIRE(MatchRule().type(MatchRule::Type::MethodCall).str() == "type='method_call'"); + REQUIRE(MatchRule().type(MatchRule::Type::MethodReturn).str() == "type='method_return'"); + REQUIRE(MatchRule().type(MatchRule::Type::Error).str() == "type='error'"); + REQUIRE(MatchRule().type(MatchRule::Type::Signal).str() == "type='signal'"); +} + +TEST_CASE("MatchRule::sender()") +{ + REQUIRE(MatchRule().sender(":1.234").str() == "sender=':1.234'"); + REQUIRE(MatchRule().sender("well-known.name").str() == "sender='well-known.name'"); +} + +TEST_CASE("MatchRule::interface()") +{ + REQUIRE(MatchRule().interface("inter_face.Name").str() == "interface='inter_face.Name'"); +} + +TEST_CASE("MatchRule::member()") +{ + REQUIRE(MatchRule().member("MemberName_").str() == "member='MemberName_'"); +} + +TEST_CASE("MatchRule::path()") +{ + REQUIRE(MatchRule().path("/Org/Foo").str() == "path='/Org/Foo'"); + REQUIRE_THROWS_AS(MatchRule().path("/Org/Foo").pathNamespace("/Org/Bar"), InvalidMatchRule); +} + +TEST_CASE("MatchRule::pathNamespace()") +{ + REQUIRE(MatchRule().pathNamespace("/Org/Foo").str() == "path_namespace='/Org/Foo'"); + REQUIRE_THROWS_AS(MatchRule().pathNamespace("/Org/Foo").path("/Org/Bar"), InvalidMatchRule); +} + +TEST_CASE("MatchRule::destination()") +{ + REQUIRE(MatchRule().destination(":1.234").str() == "destination=':1.234'"); +} + +TEST_CASE("MatchRule::arg0Namespace()") +{ + REQUIRE(MatchRule().arg0Namespace("Name.Space").str() == "arg0namespace='Name.Space'"); +} + +TEST_CASE("MatchRule::arg()") +{ + REQUIRE(MatchRule().arg(0,"foo").arg(42,"bar").str() == "arg0='foo',arg42='bar'"); + REQUIRE(MatchRule().arg(0,"'").str() == "arg0=''\\'''"); + REQUIRE(MatchRule().arg(63,"\\").str() == "arg63='\\'"); + REQUIRE_THROWS_AS(MatchRule().arg(64,"/Org/Foo"), InvalidMatchRule); +} + +TEST_CASE("MatchRule::argPath()") +{ + REQUIRE(MatchRule().argPath(63,"/foo/bar/").str() == "arg63path='/foo/bar/'"); + REQUIRE(MatchRule().argPath(0,"'").str() == "arg0path=''\\'''"); + REQUIRE(MatchRule().argPath(63,"\\").str() == "arg63path='\\'"); + REQUIRE_THROWS_AS(MatchRule().argPath(64,"/Org/Foo"), InvalidMatchRule); +} + +} // namespace test +} // namespace DBus From ba2311305ebda78d2b1c252e70c60dab0433a925 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Tue, 12 Apr 2022 14:03:58 +0200 Subject: [PATCH 05/24] add option to use standalone ASIO Let the user decide which version of ASIO to build against. Users of dbus-asio need to link against the same version of asio, so this adds some flexibility if they can't or don't want to switch to boost. Signed-off-by: Heiko Hund --- .gitignore | 1 + CMakeLists.txt | 21 +++++++++++++++++++++ src/dbus.h | 2 ++ src/dbus_asio.h.in | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 .gitignore create mode 100644 src/dbus_asio.h.in diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e236ce7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +src/dbus_asio.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a094785..4746d70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(CMAKE_CXX_VISIBILITY_PRESET protected) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=gold") set(HEADER_FILES + src/dbus_asio.h src/dbus_auth.h src/dbus.h src/dbus_introspectable.h @@ -91,6 +92,26 @@ link_directories(${Boost_LIBRARY_DIRS}) # PTHREAD DEPENDENCIES find_package (Threads REQUIRED) +# ASIO +option(DBUS_ASIO_STANDALONE "Use ASIO standalone library (non-boost)" OFF) +if(DBUS_ASIO_STANDALONE) + add_compile_definitions(ASIO_STANDALONE) + set(DBUS_ASIO_INCLUDE "asio.hpp") + set(DBUS_ASIO_NAMESPACE "::asio") + set(DBUS_ERROR_CODE_NAMESPACE "::std") + set(DBUS_ERROR_CODE_ERRC "::std::errc") + set(DBUS_MAKE_ERROR_CODE "::std::make_error_code") +else() + set(DBUS_ASIO_INCLUDE "boost/asio.hpp") + set(DBUS_ASIO_NAMESPACE "::boost::asio") + set(DBUS_ERROR_CODE_NAMESPACE "::boost::system") + set(DBUS_ERROR_CODE_ERRC "::boost::system::errc::errc_t") + set(DBUS_MAKE_ERROR_CODE "::boost::asio::error::make_error_code") +endif() +set(DBUS_ASIO_H ${CMAKE_CURRENT_SOURCE_DIR}/src/dbus_asio.h) +configure_file(${DBUS_ASIO_H}.in ${DBUS_ASIO_H}) +unset(DBUS_ASIO_H) + target_link_libraries (dbus-asio ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) set_target_properties(dbus-asio PROPERTIES diff --git a/src/dbus.h b/src/dbus.h index 6ca3e01..a315e23 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -18,6 +18,8 @@ #pragma once +#include "dbus_asio.h" + // Core elements and types #include "dbus_log.h" #include "dbus_names.h" diff --git a/src/dbus_asio.h.in b/src/dbus_asio.h.in new file mode 100644 index 0000000..012988a --- /dev/null +++ b/src/dbus_asio.h.in @@ -0,0 +1,34 @@ +// This file is part of dbus-asio +// Copyright 2022 OpenVPN Inc. +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, version 3, or at your +// option any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The GNU Lesser General Public License version 3 is included in the +// file named COPYING. If you do not have this file see +// . + +#pragma once + +#include <@DBUS_ASIO_INCLUDE@> + +#include + +namespace DBus { + +namespace asio = @DBUS_ASIO_NAMESPACE@; + +using errc = @DBUS_ERROR_CODE_ERRC@; +using @DBUS_ERROR_CODE_NAMESPACE@::error_code; +using @DBUS_ERROR_CODE_NAMESPACE@::system_category; +using @DBUS_MAKE_ERROR_CODE@; + +} // namespace DBus + From c06503d1e424d69cdba0e050ef1aa9dd18b81793 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Tue, 12 Apr 2022 15:26:28 +0200 Subject: [PATCH 06/24] add DBus::Error type as a generic error wrapper This error abstraction is able to hold either a error_code as used by asio, or a DBus::Message::Error as received from dbus. Signed-off-by: Heiko Hund --- CMakeLists.txt | 1 + src/dbus.h | 1 + src/dbus_error.h | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 src/dbus_error.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 4746d70..79652af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=gold") set(HEADER_FILES src/dbus_asio.h src/dbus_auth.h + src/dbus_error.h src/dbus.h src/dbus_introspectable.h src/dbus_log.h diff --git a/src/dbus.h b/src/dbus.h index a315e23..0f9116a 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -25,6 +25,7 @@ #include "dbus_names.h" #include "dbus_platform.h" #include "dbus_utils.h" +#include "dbus_error.h" // Marshall/unmarshalling types #include "dbus_type.h" diff --git a/src/dbus_error.h b/src/dbus_error.h new file mode 100644 index 0000000..d8aa82a --- /dev/null +++ b/src/dbus_error.h @@ -0,0 +1,59 @@ +// This file is part of dbus-asio +// Copyright 2022 OpenVPN Inc. +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, version 3, or at your +// option any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The GNU Lesser General Public License version 3 is included in the +// file named COPYING. If you do not have this file see +// . + +// Wrapper class for the ASIO ErrorCode and DBus::Message::Error + +#pragma once + +#include "dbus_message.h" +#include "dbus_asio.h" + +namespace DBus { + +struct Error { + Error() + : m_error(false) + {} + + Error(const std::string& message, const std::string& category = "") + : category(category) + , message(message) + , m_error(true) + {} + + Error(const error_code& error) + : category(error.category().name()) + , message(error.message()) + , m_error(error) + {} + + Error(const Message::Error& error) + : category(error.getName()) + , message(error.getMessage()) + , m_error(!category.empty()) + {} + + explicit operator bool() const { return m_error; } + + std::string category; + std::string message; + +protected: + bool m_error; +}; + +} From b75c1000b267d4a533c630b1138a6c2cd747e184 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Tue, 12 Apr 2022 15:51:55 +0200 Subject: [PATCH 07/24] make Log::writeHex display ASCII on the side Also added some spacing after 8 bytes to make things look prettier. Output looks like this now (hex shortened): 6c ... 00 02 ... 00 06 ... 00 | l....... ........ ..s..... 6f ... 65 64 ... 2e 44 ... 00 | org.free desktop. DBus.... 01 ... 00 2f ... 65 65 ... 70 | ..o..... /org/fre edesktop 2f ... 00 02 ... 00 6f ... 65 | /DBus... ..s..... org.free 64 ... 2e 44 ... 00 03 ... 00 | desktop. DBus.... ..s..... 52 ... 4e 61 ... 00 08 ... 00 | RequestN ame..... ..g..su. 0a ... 74 2e ... 00 00 ... | ....test .steev.. .... So, you can tell pretty good what's going on in the message blob without dissecting the bits carefully. Signed-off-by: Heiko Hund --- src/dbus_log.cpp | 50 +++++++++++++++++++++++++++++++++++------------- src/dbus_log.h | 4 ++++ 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/dbus_log.cpp b/src/dbus_log.cpp index b5e7982..227210a 100644 --- a/src/dbus_log.cpp +++ b/src/dbus_log.cpp @@ -44,28 +44,52 @@ void DBus::Log::write(size_t type, const char* msg, ...) void DBus::Log::writeHex(size_t type, const std::string& prefix, const std::string& hex) { - if (!isActive(type)) { + writeHex(type, prefix, hex.data(), hex.size()); +} + +void DBus::Log::writeHex(std::size_t type, const std::string& prefix, + const void *data, std::size_t size) +{ + if (data == nullptr || !isActive(type)) { return; } - write(type, prefix.c_str()); + std::size_t column = 0; + constexpr std::size_t maxColumns = 24; + constexpr std::size_t dividerAfter = 8; + char asciiBuf[maxColumns + maxColumns / dividerAfter + 1] = {}; + char *ascii = asciiBuf; + const unsigned char *first = static_cast(data); - size_t column = 0; - for (auto it : hex) { - write(type, "%.2x ", (uint8_t)it); - if (++column == 32) { - write(type, "\n"); + std::ostringstream oss; + oss << prefix; + for (std::size_t i = 0; i < size; ++i) { + const unsigned char byte = *(first + i); + oss << std::hex << std::setw(2) << std::setfill('0') + << static_cast(byte) << ' '; + *ascii++ = ::isprint(byte) ? byte : '.'; + if (++column == maxColumns) { column = 0; - if (hex.size() % 32) { // pad the next line if there's likely to be one. - // i.e. not 32, 64, 96 length etc - write(type, std::string(prefix.length(), ' ').c_str()); // pad 2nd line to match prefix + oss << "| " << asciiBuf << '\n'; + std::memset(asciiBuf, 0, sizeof(asciiBuf)); + ascii = asciiBuf; + if (i + 1 < size) { // pad the next line if there is one. + oss << std::string(prefix.size(), ' '); } } + else if (column % dividerAfter == 0) { + oss << ' '; + *ascii++ = ' '; + } } - // Tidy up the last line - if (column) { - write(type, "\n"); + // Finish the last line + if (column > 0) { + const std::size_t bytes = (maxColumns - column) * 3; + const std::size_t dividers = ((maxColumns - 1) / dividerAfter) - (column / dividerAfter); + oss << std::string(bytes + dividers, ' ') << "| " << asciiBuf << '\n'; } + + write(type, oss.str().c_str()); } void DBus::Log::flush() { fflush(stderr); } diff --git a/src/dbus_log.h b/src/dbus_log.h index 34e1f40..11e3c53 100644 --- a/src/dbus_log.h +++ b/src/dbus_log.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include namespace DBus { @@ -39,6 +41,8 @@ class Log { static void writeHex(size_t type, const std::string& prefix, const std::string& hex); + static void writeHex(std::size_t type, const std::string& prefix, + const void *data, std::size_t size); static void flush(); static void setLevel(size_t lowest_visible_level); From 1ea92a060cbfdba54c42bc8b5ee63812071a9060 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Wed, 13 Apr 2022 23:02:40 +0200 Subject: [PATCH 08/24] redesign DBus type system The previous approach using boost::any as the generic DBus type had the downside that any data(type) could be fed into it, no matter if DBus actually supports the datatype. Instead, there's now a DBus::Type::Any which behaves more like a std::variant with some DBus specific extras. It serves as the container for the others inheriting from DBus::Type and as a generic type the DBus container types can store. This type system is more type safe than before, so there's no need to compare types by their serialized string form, as each can be cast back to the native C++ type. The Signature type now validates the signature string passed to it. So, it is no longer possible to use DictEntry's outside of an Array, like the standard mandates. The ObjectPath type makes use of the DBus::ObjectPath "name" and the validation done therein. The Array type checks the all Array::add()ed elements have the correct signature and/or type. Made the DBus::Type::toString() output a bit more compact and reflect the container types a bit better by using [], {} and () in the right places. Signed-off-by: Heiko Hund --- CMakeLists.txt | 10 +- src/dbus.h | 2 +- src/dbus_type.cpp | 425 +++++---------------- src/dbus_type.h | 104 ++++-- src/dbus_type_any.cpp | 492 +++++++++++++++++++++++++ src/dbus_type_any.h | 157 ++++++++ src/dbus_type_array.cpp | 173 ++------- src/dbus_type_array.h | 80 ++-- src/dbus_type_base.cpp | 32 -- src/dbus_type_base.h | 71 ---- src/dbus_type_boolean.cpp | 30 +- src/dbus_type_boolean.h | 40 +- src/dbus_type_byte.cpp | 29 +- src/dbus_type_byte.h | 42 +-- src/dbus_type_dictentry.cpp | 78 ++-- src/dbus_type_dictentry.h | 52 +-- src/dbus_type_double.cpp | 30 +- src/dbus_type_double.h | 41 ++- src/dbus_type_int16.cpp | 29 +- src/dbus_type_int16.h | 41 ++- src/dbus_type_int32.cpp | 33 +- src/dbus_type_int32.h | 42 +-- src/dbus_type_int64.cpp | 33 +- src/dbus_type_int64.h | 41 ++- src/dbus_type_objectpath.cpp | 19 +- src/dbus_type_objectpath.h | 29 +- src/dbus_type_signature.cpp | 185 +++++++++- src/dbus_type_signature.h | 56 ++- src/dbus_type_string.cpp | 23 +- src/dbus_type_string.h | 39 +- src/dbus_type_struct.cpp | 141 ++----- src/dbus_type_struct.h | 82 ++--- src/dbus_type_uint16.cpp | 33 +- src/dbus_type_uint16.h | 41 ++- src/dbus_type_uint32.cpp | 33 +- src/dbus_type_uint32.h | 44 ++- src/dbus_type_uint64.cpp | 33 +- src/dbus_type_uint64.h | 41 ++- src/dbus_type_variant.cpp | 66 +--- src/dbus_type_variant.h | 50 ++- src/dbus_validation.cpp | 56 --- src/dbus_validation.h | 32 -- tests/CMakeLists.txt | 5 - tests/dbus_type_array_unittest.cpp | 19 +- tests/dbus_type_dictentry_unittest.cpp | 23 +- tests/dbus_type_double_unittest.cpp | 8 +- tests/dbus_type_multiple_unittest.cpp | 103 +++--- tests/dbus_type_signature_unittest.cpp | 2 +- tests/dbus_type_struct_unittest.cpp | 9 +- tests/dbus_type_variant_unittest.cpp | 3 +- tests/dbus_validation_unittest.cpp | 45 --- 51 files changed, 1678 insertions(+), 1649 deletions(-) create mode 100644 src/dbus_type_any.cpp create mode 100644 src/dbus_type_any.h delete mode 100644 src/dbus_type_base.cpp delete mode 100644 src/dbus_type_base.h delete mode 100644 src/dbus_validation.cpp delete mode 100644 src/dbus_validation.h delete mode 100644 tests/dbus_validation_unittest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 79652af..ab497bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,13 +22,13 @@ set(HEADER_FILES src/dbus_octetbuffer.h src/dbus_platform.h src/dbus_transport.h + src/dbus_type.h + src/dbus_type_any.h src/dbus_type_array.h - src/dbus_type_base.h src/dbus_type_boolean.h src/dbus_type_byte.h src/dbus_type_dictentry.h src/dbus_type_double.h - src/dbus_type.h src/dbus_type_int16.h src/dbus_type_int32.h src/dbus_type_int64.h @@ -41,7 +41,6 @@ set(HEADER_FILES src/dbus_type_uint64.h src/dbus_type_variant.h src/dbus_utils.h - src/dbus_validation.h ) add_library(dbus-asio SHARED @@ -62,11 +61,11 @@ add_library(dbus-asio SHARED src/dbus_octetbuffer.cpp src/dbus_platform.cpp src/dbus_transport.cpp + src/dbus_type.cpp + src/dbus_type_any.cpp src/dbus_type_array.cpp - src/dbus_type_base.cpp src/dbus_type_boolean.cpp src/dbus_type_byte.cpp - src/dbus_type.cpp src/dbus_type_dictentry.cpp src/dbus_type_double.cpp src/dbus_type_int16.cpp @@ -81,7 +80,6 @@ add_library(dbus-asio SHARED src/dbus_type_uint64.cpp src/dbus_type_variant.cpp src/dbus_utils.cpp - src/dbus_validation.cpp ) # DBUS DEPENDENCIES diff --git a/src/dbus.h b/src/dbus.h index 0f9116a..544af75 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -29,8 +29,8 @@ // Marshall/unmarshalling types #include "dbus_type.h" +#include "dbus_type_any.h" #include "dbus_type_array.h" -#include "dbus_type_base.h" #include "dbus_type_boolean.h" #include "dbus_type_byte.h" #include "dbus_type_dictentry.h" diff --git a/src/dbus_type.cpp b/src/dbus_type.cpp index ae8c90f..8f85419 100644 --- a/src/dbus_type.cpp +++ b/src/dbus_type.cpp @@ -15,12 +15,11 @@ // file named COPYING. If you do not have this file see // . -#include "dbus_type.h" #include "dbus_log.h" #include "dbus_messageistream.h" #include "dbus_messageostream.h" +#include "dbus_type_any.h" #include "dbus_type_array.h" -#include "dbus_type_base.h" #include "dbus_type_boolean.h" #include "dbus_type_byte.h" #include "dbus_type_dictentry.h" @@ -38,388 +37,148 @@ #include "dbus_type_variant.h" // -// Helper methods to extract navtive types from the opaque 'Generic' type +// Helper methods to extract native types from the opaque 'Any' type // -uint8_t DBus::Type::asByte(const Generic& v) + +bool DBus::Type::asBoolean(const Any& v) { - return std::stoi(Type::asString(v)); + return static_cast(v); } -uint32_t DBus::Type::asUint32(const Generic& v) +double DBus::Type::asDouble(const Any& v) { - return std::stoi(Type::asString(v)); + return static_cast(v); } -// Generate a compact, machine-friendly, version of the data. -// (compare to toString, which is intended for humans debugging) -std::string DBus::Type::asString(const DBus::Type::Generic& value) +std::string DBus::Type::asString(const Any& v) { - // It is possible to receive an empty value when dealing with void parameter - // lists, or with uninitialised types from header fields. - if (value.empty()) { - return ""; - } -#define TYPE_ASSTRING(typename) \ - { \ - static Type::Generic is_type = typename(); \ - if (ti == is_type.type()) { \ - return boost::any_cast(value).asString(); \ - } \ - } - const std::type_info& ti = value.type(); - - TYPE_ASSTRING(Type::Byte); - TYPE_ASSTRING(Type::Boolean); - TYPE_ASSTRING(Type::Int16); - TYPE_ASSTRING(Type::Uint16); - TYPE_ASSTRING(Type::Int32); - TYPE_ASSTRING(Type::Uint32); - TYPE_ASSTRING(Type::Int64); - TYPE_ASSTRING(Type::Uint64); - TYPE_ASSTRING(Type::Double); - TYPE_ASSTRING(Type::String); - TYPE_ASSTRING(Type::Array); - TYPE_ASSTRING(Type::Variant); - TYPE_ASSTRING(Type::Struct); - TYPE_ASSTRING(Type::ObjectPath); - TYPE_ASSTRING(Type::Signature); - TYPE_ASSTRING(Type::DictEntry); - -#undef TYPE_ASSTRING - - Log::write(DBus::Log::ERROR, "DBus :: asString type not implemented (%s)\n", - ti.name()); - - return ""; + return static_cast(v); } -const DBus::Type::Array& DBus::Type::refArray(const Generic& value) +std::uint8_t DBus::Type::asByte(const Any& v) { - return boost::any_cast(value); + return static_cast(v); } -const DBus::Type::Struct& DBus::Type::refStruct(const Generic& value) +std::int16_t DBus::Type::asInt16(const Any& v) { - return boost::any_cast(value); + return static_cast(v); } -const DBus::Type::Signature& DBus::Type::refSignature(const Generic& value) +std::int32_t DBus::Type::asInt32(const Any& v) { - return boost::any_cast(value); + return static_cast(v); } -const DBus::Type::Variant& DBus::Type::refVariant(const Generic& value) +std::int64_t DBus::Type::asInt64(const Any& v) { - return boost::any_cast(value); + return static_cast(v); } -// -// Mapping methods to convert between abstract Generic types, to specific -// classes -// -DBus::Type::Generic DBus::Type::create(const std::string& type) +std::uint16_t DBus::Type::asUint16(const Any& v) { -#define TYPE_CREATE(typename) \ - if (type[0] == typename ::s_StaticTypeCode[0]) { \ - typename vv; \ - vv.setSignature(type); \ - Type::Generic v(vv); \ - return v; \ - } - - TYPE_CREATE(Type::Byte); - TYPE_CREATE(Type::Boolean); - TYPE_CREATE(Type::Int16); - TYPE_CREATE(Type::Uint16); - TYPE_CREATE(Type::Int32); - TYPE_CREATE(Type::Uint32); - TYPE_CREATE(Type::Int64); - TYPE_CREATE(Type::Uint64); - TYPE_CREATE(Type::Double); - TYPE_CREATE(Type::String); - TYPE_CREATE(Type::Array); - TYPE_CREATE(Type::Variant); - TYPE_CREATE(Type::Struct); - TYPE_CREATE(Type::ObjectPath); - TYPE_CREATE(Type::Signature); - TYPE_CREATE(Type::DictEntry); - -#undef TYPE_CREATE - - Log::write(DBus::Log::ERROR, - "DBus :: Can not create a signature type of (%s)\n", type.c_str()); - - return Type::Generic(); + return static_cast(v); } -std::string -DBus::Type::getMarshallingSignature(const DBus::Type::Generic& value) +std::uint32_t DBus::Type::asUint32(const Any& v) { - // It is possible to receive an empty value when dealing with void parameter - // lists. - if (value.empty()) { - return ""; - } -#define TYPE_MARSHALL_SIGNATURE(typename) \ - { \ - static Type::Generic is_type = typename(); \ - if (ti == is_type.type()) { \ - return typename ::s_StaticTypeCode; \ - } \ - } -// if (ti == is_type.type()) { return -//boost::any_cast(value).getSignature(); } } -#define TYPE_MARSHALL_SIGNATURE2(typename) \ - { \ - static Type::Generic is_type = typename(); \ - if (ti == is_type.type()) { \ - return boost::any_cast(value).getSignature(); \ - } \ - } - const std::type_info& ti = value.type(); - - TYPE_MARSHALL_SIGNATURE(Type::Byte); - TYPE_MARSHALL_SIGNATURE(Type::Boolean); - TYPE_MARSHALL_SIGNATURE(Type::Int16); - TYPE_MARSHALL_SIGNATURE(Type::Uint16); - TYPE_MARSHALL_SIGNATURE(Type::Int32); - TYPE_MARSHALL_SIGNATURE(Type::Uint32); - TYPE_MARSHALL_SIGNATURE(Type::Int64); - TYPE_MARSHALL_SIGNATURE(Type::Uint64); - TYPE_MARSHALL_SIGNATURE(Type::Double); - TYPE_MARSHALL_SIGNATURE(Type::String); - TYPE_MARSHALL_SIGNATURE2(Type::Array); - TYPE_MARSHALL_SIGNATURE(Type::Variant); - TYPE_MARSHALL_SIGNATURE2(Type::Struct); - TYPE_MARSHALL_SIGNATURE(Type::ObjectPath); - TYPE_MARSHALL_SIGNATURE(Type::Signature); - TYPE_MARSHALL_SIGNATURE2(Type::DictEntry); - - Log::write(DBus::Log::ERROR, - "DBus :: Marshalling signature type not implemented (%s)\n", - ti.name()); - -#undef TYPE_MARSHALL_SIGNATURE - - return "?"; + return static_cast(v); } -// TODO: macro-in-a-macro to try and reduce duplicated list of types - -void DBus::Type::marshallData(const DBus::Type::Generic& value, - MessageOStream& stream) +std::uint64_t DBus::Type::asUint64(const Any& v) { - // It is possible to receive an empty value when dealing with void parameter - // lists. - if (value.empty()) { - return; - } - std::string type(getMarshallingSignature(value)); - -#define TYPE_MARSHALL_DATA(typename) \ - if (type[0] == typename ::s_StaticTypeCode[0]) { \ - boost::any_cast(value).marshall(stream); \ - return; \ - } + return static_cast(v); +} - TYPE_MARSHALL_DATA(Type::Byte); - TYPE_MARSHALL_DATA(Type::Boolean); - TYPE_MARSHALL_DATA(Type::Int16); - TYPE_MARSHALL_DATA(Type::Uint16); - TYPE_MARSHALL_DATA(Type::Int32); - TYPE_MARSHALL_DATA(Type::Uint32); - TYPE_MARSHALL_DATA(Type::Int64); - TYPE_MARSHALL_DATA(Type::Uint64); - TYPE_MARSHALL_DATA(Type::Double); - TYPE_MARSHALL_DATA(Type::String); - TYPE_MARSHALL_DATA(Type::Array); - TYPE_MARSHALL_DATA(Type::Variant); - TYPE_MARSHALL_DATA(Type::Struct); - TYPE_MARSHALL_DATA(Type::ObjectPath); - TYPE_MARSHALL_DATA(Type::Signature); - TYPE_MARSHALL_DATA(Type::DictEntry); - Log::write(DBus::Log::ERROR, - "DBus :: Marshalling data of type %s is not implemented.\n", - type.c_str()); -#undef TYPE_MARSHALL_DATA +const DBus::Type::Array& DBus::Type::refArray(const Any& value) +{ + return static_cast(value); } -// Process one octet of data, into the given type. Returns true if that type has -// completed. -void DBus::Type::unmarshallData(DBus::Type::Generic& result, - MessageIStream& stream) +const DBus::Type::Struct& DBus::Type::refStruct(const Any& value) { -// TODO: Consider whether a template could handle this -#define TYPE_UNMARSHALL_DATA(typename) \ - if (type[0] == typename ::s_StaticTypeCode[0]) { \ - typename& v = boost::any_cast(result); \ - v.unmarshall(stream); \ - return; \ - } - - std::string type = getMarshallingSignature(result); - - TYPE_UNMARSHALL_DATA(Type::Byte); - TYPE_UNMARSHALL_DATA(Type::Boolean); - TYPE_UNMARSHALL_DATA(Type::Int16); - TYPE_UNMARSHALL_DATA(Type::Uint16); - TYPE_UNMARSHALL_DATA(Type::Int32); - TYPE_UNMARSHALL_DATA(Type::Uint32); - TYPE_UNMARSHALL_DATA(Type::Int64); - TYPE_UNMARSHALL_DATA(Type::Uint64); - TYPE_UNMARSHALL_DATA(Type::Double); - TYPE_UNMARSHALL_DATA(Type::String); - TYPE_UNMARSHALL_DATA(Type::Array); - TYPE_UNMARSHALL_DATA(Type::Variant); - TYPE_UNMARSHALL_DATA(Type::Struct); - TYPE_UNMARSHALL_DATA(Type::ObjectPath); - TYPE_UNMARSHALL_DATA(Type::Signature); - TYPE_UNMARSHALL_DATA(Type::DictEntry); - - Log::write(DBus::Log::ERROR, - "DBus :: Unmarshalling type not implemented (%s)\n", type.c_str()); - -#undef TYPE_UNMARSHALL_DATA + return static_cast(value); } -// Generate an expanded description of the data type provided. -std::string DBus::Type::toString(const DBus::Type::Generic& value, - const std::string& prefix) +const DBus::Type::Variant& DBus::Type::refVariant(const Any& value) { -#define TYPE_TOSTRING(typename) \ - { \ - static Type::Generic is_type = typename(); \ - if (ti == is_type.type()) { \ - return boost::any_cast(value).toString(prefix); \ - } \ - } - const std::type_info& ti = value.type(); - - TYPE_TOSTRING(Type::Byte); - TYPE_TOSTRING(Type::Boolean); - TYPE_TOSTRING(Type::Int16); - TYPE_TOSTRING(Type::Uint16); - TYPE_TOSTRING(Type::Int32); - TYPE_TOSTRING(Type::Uint32); - TYPE_TOSTRING(Type::Int64); - TYPE_TOSTRING(Type::Uint64); - TYPE_TOSTRING(Type::Double); - TYPE_TOSTRING(Type::String); - TYPE_TOSTRING(Type::Array); - TYPE_TOSTRING(Type::Variant); - TYPE_TOSTRING(Type::Struct); - TYPE_TOSTRING(Type::ObjectPath); - TYPE_TOSTRING(Type::Signature); - TYPE_TOSTRING(Type::DictEntry); - -#undef TYPE_TOSTRING - - Log::write(DBus::Log::ERROR, "DBus :: toString type not implemented (%s)\n", - ti.name()); - - return "?unknown?"; + return static_cast(value); } -size_t DBus::Type::getAlignment(const std::string& declaration) +const DBus::Type::Signature& DBus::Type::refSignature(const Any& value) { -#define TYPE_UNMARSHALL_ALIGNMENT(typename) \ - if (declaration[0] == typename ::s_StaticTypeCode[0]) { \ - typename vv; \ - return vv.getAlignment(); \ - } + return static_cast(value); +} - TYPE_UNMARSHALL_ALIGNMENT(Type::Byte); - TYPE_UNMARSHALL_ALIGNMENT(Type::Boolean); - TYPE_UNMARSHALL_ALIGNMENT(Type::Int16); - TYPE_UNMARSHALL_ALIGNMENT(Type::Uint16); - TYPE_UNMARSHALL_ALIGNMENT(Type::Int32); - TYPE_UNMARSHALL_ALIGNMENT(Type::Uint32); - TYPE_UNMARSHALL_ALIGNMENT(Type::Int64); - TYPE_UNMARSHALL_ALIGNMENT(Type::Uint64); - TYPE_UNMARSHALL_ALIGNMENT(Type::Double); - TYPE_UNMARSHALL_ALIGNMENT(Type::String); - TYPE_UNMARSHALL_ALIGNMENT(Type::Array); - TYPE_UNMARSHALL_ALIGNMENT(Type::Variant); - TYPE_UNMARSHALL_ALIGNMENT(Type::Struct); - TYPE_UNMARSHALL_ALIGNMENT(Type::ObjectPath); - TYPE_UNMARSHALL_ALIGNMENT(Type::Signature); - TYPE_UNMARSHALL_ALIGNMENT(Type::DictEntry); +const DBus::Type::DictEntry& DBus::Type::refDictEntry(const Any& value) +{ + return static_cast(value); +} -#undef TYPE_UNMARSHALL_ALIGNMENT - return 1; -} // -// Other methods +// Mapping methods to convert between abstract Generic types, to specific +// classes // -std::string DBus::Type::extractSignature(const std::string& declaration, - size_t idx) +std::size_t DBus::Type::getAlignment(const std::string& typeCode) { - std::string result; - - // Handle arrays - if (declaration[idx] == '(') { - // Find the matching bracket, and skip all internal signatures - size_t number_of_brackets = 0; - for (size_t i = idx; i < declaration.size(); ++i) { - result += declaration[i]; - - if (declaration[i] == '(') { - ++number_of_brackets; - } else if (declaration[i] == ')') { - if (--number_of_brackets == 0) { - return result; - } - } - } - // - Log::write(Log::ERROR, "DBus :: The declaration is invalid due to " - "mis-matched brackets in the array signature."); - } - // Handle dictentry - else if (declaration[idx] == '{') { - if (idx == 0) { - Log::write(Log::ERROR, - "DBus :: The declaration is invalid because a dictentry must " - "be inside an array container type."); - } - // Find the matching brace, and skip all internal signatures - size_t number_of_brackets = 0; - for (size_t i = idx; i < declaration.size(); ++i) { - result += declaration[i]; - - if (declaration[i] == '{') { - ++number_of_brackets; - } else if (declaration[i] == '}') { - if (--number_of_brackets == 0) { - return result; - } - } - } - // - Log::write(Log::ERROR, "DBus :: The declaration is invalid due to " - "mis-matched braces in the dictentry signature."); - } - - result += declaration[idx]; - - if (declaration[idx] == 'a') { - result += extractSignature(declaration, idx + 1); + switch (typeCode[0]) + { + case Byte::code: return Byte::alignment; + case Boolean::code: return Boolean::alignment; + case Int16::code: return Int16::alignment; + case Int32::code: return Int32::alignment; + case Int64::code: return Int64::alignment; + case Uint16::code: return Uint16::alignment; + case Uint32::code: return Uint32::alignment; + case Uint64::code: return Uint64::alignment; + case Double::code: return Double::alignment; + case String::code: return String::alignment; + case ObjectPath::code: return ObjectPath::alignment; + case Signature::code: return Signature::alignment; + case Array::code: return Array::alignment; + case Variant::code: return Variant::alignment; + case Struct::code: return Struct::alignment; + case DictEntry::code: return DictEntry::alignment; + default: + throw std::runtime_error("Type::getAlignment() called with invalid type code " + typeCode); } - - return result; } -std::string DBus::Type::getMarshallingSignature( - const std::vector& value) +DBus::Type::Any DBus::Type::create(const std::string& typeCode) { - std::string result; - - for (auto it : value) { - result += getMarshallingSignature(it); + switch (typeCode[0]) + { + case Byte::code: return Byte(); + case Boolean::code: return Boolean(); + case Int16::code: return Int16(); + case Int32::code: return Int32(); + case Int64::code: return Int64(); + case Uint16::code: return Uint16(); + case Uint32::code: return Uint32(); + case Uint64::code: return Uint64(); + case Double::code: return Double(); + case String::code: return String(); + case ObjectPath::code: return ObjectPath(); + case Signature::code: return Signature(); + case Array::code: return Array(typeCode); + case Variant::code: return Variant(); + case Struct::code: return Struct(typeCode); + case DictEntry::code: return DictEntry(typeCode); + default: + throw std::runtime_error("Type::create() called with invalid type code " + typeCode); } - return result; } + +bool DBus::Type::isBasicTypeCode(const int code) { + return (code == Byte::code || code == Boolean::code || + code == Int16::code || code == Uint16::code || + code == Int32::code || code == Uint32::code || + code == Int64::code || code == Uint64::code || + code == Double::code || code == String::code || + code == ObjectPath::code || code == Signature::code); +} + diff --git a/src/dbus_type.h b/src/dbus_type.h index 7179518..39cce1a 100644 --- a/src/dbus_type.h +++ b/src/dbus_type.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,54 +16,85 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_H -#define DBUS_TYPE_H +#pragma once #include #include - -// Our base type, used for implementation convenience -#include "dbus_type_base.h" +#include namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - class Array; - class Struct; - class Signature; - class Variant; + class MessageIStream; + class MessageOStream; - // Helper methods to return native types - uint8_t asByte(const Generic& v); - uint32_t asUint32(const Generic& v); - std::string asString(const Generic& v); - std::string asObjectPath(const Generic& v); + class Type { + public: + class Any; + class Basic; + class Container; - // Helper methods to return type-safe DBus references - const Type::Array& refArray(const Generic& v); - const Type::Struct& refStruct(const Generic& v); - const Type::Signature& refSignature(const Generic& v); - const Type::Variant& refVariant(const Generic& v); + class Int16; + class Int32; + class Int64; - // Generic type handling code - DBus::Type::Generic create(const std::string& type); + class Uint16; + class Uint32; + class Uint64; - void marshallData(const DBus::Type::Generic& any, MessageOStream& stream); - void unmarshallData(DBus::Type::Generic& result, MessageIStream& stream); + class Byte; + class Boolean; + class Double; - std::string toString(const DBus::Type::Generic& data, - const std::string& prefix = ""); + class String; + class ObjectPath; + class Signature; - size_t getAlignment(const std::string& declaration); + class Array; + class Struct; + class DictEntry; + class Variant; - std::string extractSignature(const std::string& declaration, size_t idx); + // Virtual methods subclasses implement + virtual ~Type() = default; + virtual std::string getName() const = 0; + virtual std::string getSignature() const = 0; + virtual std::size_t getAlignment() const = 0; - std::string getMarshallingSignature(const DBus::Type::Generic& value); - std::string - getMarshallingSignature(const std::vector& value); -} // namespace Type -} // namespace DBus + virtual void marshall(MessageOStream&) const = 0; + virtual void unmarshall(MessageIStream&) = 0; + + // each non-container type should end with \n + // as convenience to the calling function + virtual std::string toString(const std::string& /*prefix*/) const = 0; + virtual std::string asString() const = 0; + + // Helper methods to return native types + static bool asBoolean(const Any& v); + static double asDouble(const Any& v); + static std::string asString(const Any& v); + static std::uint8_t asByte(const Any& v); + static std::int16_t asInt16(const Any& v); + static std::int32_t asInt32(const Any& v); + static std::int64_t asInt64(const Any& v); + static std::uint16_t asUint16(const Any& v); + static std::uint32_t asUint32(const Any& v); + static std::uint64_t asUint64(const Any& v); + + // Helper methods to return type-safe DBus references + static const Array& refArray(const Any& v); + static const Struct& refStruct(const Any& v); + static const Variant& refVariant(const Any& v); + static const Signature& refSignature(const Any& v); + static const DictEntry& refDictEntry(const Any& v); -#endif + // Helper Methods for during marshalling + static DBus::Type::Any create(const std::string& type); + static std::size_t getAlignment(const std::string& typeCode); + static bool isBasicTypeCode(const int code); + }; + + class Type::Basic : public Type {}; + + class Type::Container : public Type {}; + +} // namespace DBus diff --git a/src/dbus_type_any.cpp b/src/dbus_type_any.cpp new file mode 100644 index 0000000..9d61dd9 --- /dev/null +++ b/src/dbus_type_any.cpp @@ -0,0 +1,492 @@ +// This file is part of dbus-asio +// Copyright 2022 OpenVPN Inc. +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, version 3, or at your +// option any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The GNU Lesser General Public License version 3 is included in the +// file named COPYING. If you do not have this file see +// . + +#include "dbus_names.h" +#include "dbus_type_any.h" +#include "dbus_type_array.h" +#include "dbus_type_struct.h" +#include "dbus_type_variant.h" +#include "dbus_type_dictentry.h" + +#include + +DBus::Type::Any& DBus::Type::Any::clone(const Any& v) +{ + m_valueType = v.m_valueType; + switch (m_valueType) { + case Value::Byte: new(&m_byte) Byte(v); break; + case Value::Double: new(&m_double) Double(v); break; + case Value::Boolean: new(&m_boolean) Boolean(v); break; + case Value::Int16: new(&m_int16) Int16(v); break; + case Value::Int32: new(&m_int32) Int32(v); break; + case Value::Int64: new(&m_int64) Int64(v); break; + case Value::Uint16: new(&m_uint16) Uint16(v); break; + case Value::Uint32: new(&m_uint32) Uint32(v); break; + case Value::Uint64: new(&m_uint64) Uint64(v); break; + case Value::String: new(&m_string) String(v); break; + case Value::Signature: new(&m_signature) Signature(v); break; + case Value::ObjectPath: new(&m_objectPath) ObjectPath(v); break; + + case Value::Array: m_array = new Array(*v.m_array); break; + case Value::Struct: m_struct = new Struct(*v.m_struct); break; + case Value::Variant: m_variant = new Variant(*v.m_variant); break; + case Value::DictEntry: m_dictEntry = new DictEntry(*v.m_dictEntry); break; + + case Value::None: break; + } + return *this; +} + +void DBus::Type::Any::destroy() +{ + switch(m_valueType) { + case Value::Byte: m_byte.~Byte(); break; + case Value::Signature: m_signature.~Signature(); break; + case Value::ObjectPath: m_objectPath.~ObjectPath(); break; + case Value::Boolean: m_boolean.~Boolean(); break; + case Value::Int16: m_int16.~Int16(); break; + case Value::Int32: m_int32.~Int32(); break; + case Value::Int64: m_int64.~Int64(); break; + case Value::Uint16: m_uint16.~Uint16(); break; + case Value::Uint32: m_uint32.~Uint32(); break; + case Value::Uint64: m_uint64.~Uint64(); break; + case Value::Double: m_double.~Double(); break; + case Value::String: m_string.~String(); break; + + case Value::Array: delete m_array; break; + case Value::Struct: delete m_struct; break; + case Value::Variant: delete m_variant; break; + case Value::DictEntry: delete m_dictEntry; break; + + case Value::None: break; + } +} + + + +DBus::Type::Any::Any(const Any& v) +{ + clone(v); +} + +DBus::Type::Any::Any(const std::uint8_t& v) + : m_valueType(Value::Byte) +{ + new(&m_byte) Byte(v); +} + +DBus::Type::Any::Any(const Byte& v) + : m_valueType(Value::Byte) +{ + new(&m_byte) Byte(v); +} + +DBus::Type::Any::Any(const bool& v) + : m_valueType(Value::Boolean) +{ + new(&m_boolean) Boolean(v); +} + +DBus::Type::Any::Any(const Boolean& v) + : m_valueType(Value::Boolean) +{ + new(&m_boolean) Boolean(v); +} + +DBus::Type::Any::Any(const std::int16_t& v) + : m_valueType(Value::Int16) +{ + new(&m_int16) Int16(v); +} + +DBus::Type::Any::Any(const Int16& v) + : m_valueType(Value::Int16) +{ + new(&m_int16) Int16(v); +} + +DBus::Type::Any::Any(const std::int32_t& v) + : m_valueType(Value::Int32) +{ + new(&m_int32) Int32(v); +} + +DBus::Type::Any::Any(const Int32& v) + : m_valueType(Value::Int32) +{ + new(&m_int32) Int32(v); +} + +DBus::Type::Any::Any(const std::int64_t& v) + : m_valueType(Value::Int64) +{ + new(&m_int64) Int64(v); +} + +DBus::Type::Any::Any(const Int64& v) + : m_valueType(Value::Int64) +{ + new(&m_int64) Int64(v); +} + +DBus::Type::Any::Any(const std::uint16_t& v) + : m_valueType(Value::Uint16) +{ + new(&m_uint16) Uint16(v); +} + +DBus::Type::Any::Any(const Uint16& v) + : m_valueType(Value::Uint16) +{ + new(&m_uint16) Uint16(v); +} + +DBus::Type::Any::Any(const std::uint32_t& v) + : m_valueType(Value::Uint32) +{ + new(&m_uint32) Uint32(v); +} + +DBus::Type::Any::Any(const Uint32& v) + : m_valueType(Value::Uint32) +{ + new(&m_uint32) Uint32(v); +} + +DBus::Type::Any::Any(const std::uint64_t& v) + : m_valueType(Value::Uint64) +{ + new(&m_uint64) Uint64(v); +} + +DBus::Type::Any::Any(const Uint64& v) + : m_valueType(Value::Uint64) +{ + new(&m_uint64) Uint64(v); +} + +DBus::Type::Any::Any(const double& v) + : m_valueType(Value::Double) +{ + new(&m_double) Double(v); +} + +DBus::Type::Any::Any(const Double& v) + : m_valueType(Value::Double) +{ + new(&m_double) Double(v); +} + +DBus::Type::Any::Any(const char* v) + : m_valueType(Value::String) +{ + new(&m_string) String(v); +} + +DBus::Type::Any::Any(const std::string& v) + : m_valueType(Value::String) +{ + new(&m_string) String(v); +} + +DBus::Type::Any::Any(const Name& v) + : m_valueType(Value::String) +{ + new(&m_string) String(v); +} + +DBus::Type::Any::Any(const String& v) + : m_valueType(Value::String) +{ + new(&m_string) String(v); +} + +DBus::Type::Any::Any(const Signature& v) + : m_valueType(Value::Signature) +{ + new(&m_signature) Signature(v); +} + +DBus::Type::Any::Any(const DBus::ObjectPath& v) + : m_valueType(Value::ObjectPath) +{ + new(&m_objectPath) ObjectPath(v); +} + +DBus::Type::Any::Any(const DBus::Type::ObjectPath& v) + : m_valueType(Value::ObjectPath) +{ + new(&m_objectPath) ObjectPath(v); +} + + +DBus::Type::Any::Any(const Array& v) + : m_valueType(Value::Array) + , m_array(new Array(v)) +{} + +DBus::Type::Any::Any(const Struct& v) + : m_valueType(Value::Struct) + , m_struct(new Struct(v)) +{} + +DBus::Type::Any::Any(const Variant& v) + : m_valueType(Value::Variant) + , m_variant(new Variant(v)) +{} + +DBus::Type::Any::Any(const DictEntry& v) + : m_valueType(Value::DictEntry) + , m_dictEntry(new DictEntry(v)) +{} + + + +DBus::Type::Any& DBus::Type::Any::operator=(const Any& v) +{ + destroy(); + return clone(v); +} + + + +template +const T& DBus::Type::Any::tryVariantTo() const +{ + std::string what; + if (m_valueType == Value::None) + what = "empty object"; + else + what = value().getName(); + + if (m_valueType == Value::Variant) { + try { + return static_cast(m_variant->getValue()); + } catch (const BadCast& e) { + throw BadCast(what + " > " + e.what()); + } catch (...) { + throw; + } + } + + throw BadCast(what + ": cannot cast to " + T::name); +} + +DBus::Type::Any::operator const Byte&() const +{ + if (m_valueType == Value::Byte) + return m_byte; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Boolean&() const +{ + if (m_valueType == Value::Boolean) + return m_boolean; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Int16&() const +{ + if (m_valueType == Value::Int16) + return m_int16; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Int32&() const +{ + if (m_valueType == Value::Int32) + return m_int32; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Int64&() const +{ + if (m_valueType == Value::Int64) + return m_int64; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Uint16&() const +{ + if (m_valueType == Value::Uint16) + return m_uint16; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Uint32&() const +{ + if (m_valueType == Value::Uint32) + return m_uint32; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Uint64&() const +{ + if (m_valueType == Value::Uint64) + return m_uint64; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Double&() const +{ + if (m_valueType == Value::Double) + return m_double; + return tryVariantTo(); +} + +DBus::Type::Any::operator const String&() const +{ + if (m_valueType == Value::String) + return m_string; + if (m_valueType == Value::ObjectPath) + return m_objectPath; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Signature&() const +{ + if (m_valueType == Value::Signature) + return m_signature; + return tryVariantTo(); +} + +DBus::Type::Any::operator const ObjectPath&() const +{ + if (m_valueType == Value::ObjectPath) + return m_objectPath; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Array&() const +{ + if (m_valueType == Value::Array) + return *m_array; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Struct&() const +{ + if (m_valueType == Value::Struct) + return *m_struct; + return tryVariantTo(); +} + +DBus::Type::Any::operator const Variant&() const +{ + if (m_valueType == Value::Variant) + return *m_variant; + std::string what; + if (m_valueType == Value::None) + what = "empty object"; + else + what = value().getName(); + throw BadCast(what + ": can't cast to Variant"); +} + +DBus::Type::Any::operator const DictEntry&() const +{ + if (m_valueType == Value::DictEntry) + return *m_dictEntry; + return tryVariantTo(); +} + +bool DBus::Type::Any::isBasicType() const +{ + return Type::isBasicTypeCode(getSignature()[0]); +} + +const DBus::Type& DBus::Type::Any::value() const +{ + switch(m_valueType) { + case Value::Byte: return m_byte; + case Value::Boolean: return m_boolean; + case Value::Int16: return m_int16; + case Value::Int32: return m_int32; + case Value::Int64: return m_int64; + case Value::Uint16: return m_uint16; + case Value::Uint32: return m_uint32; + case Value::Uint64: return m_uint64; + case Value::Double: return m_double; + case Value::String: return m_string; + case Value::Signature: return m_signature; + case Value::ObjectPath: return m_objectPath; + case Value::Array: return *m_array; + case Value::Struct: return *m_struct; + case Value::Variant: return *m_variant; + case Value::DictEntry: return *m_dictEntry; + default: + throw std::runtime_error("value: Any object has no type"); + } +} + +std::string DBus::Type::Any::getName() const +{ + if (m_valueType == Value::None) + return "Any{none}"; + else + return "Any{" + value().getName() + "}"; +} + +std::size_t DBus::Type::Any::getAlignment() const +{ + return value().getAlignment(); +} + +std::string DBus::Type::Any::getSignature() const +{ + return value().getSignature(); +} + +void DBus::Type::Any::marshall(MessageOStream& stream) const +{ + return value().marshall(stream); +} + +void DBus::Type::Any::unmarshall(MessageIStream& stream) +{ + switch(m_valueType) { + case Value::Byte: return m_byte.unmarshall(stream); + case Value::Boolean: return m_boolean.unmarshall(stream); + case Value::Int16: return m_int16.unmarshall(stream); + case Value::Int32: return m_int32.unmarshall(stream); + case Value::Int64: return m_int64.unmarshall(stream); + case Value::Uint16: return m_uint16.unmarshall(stream); + case Value::Uint32: return m_uint32.unmarshall(stream); + case Value::Uint64: return m_uint64.unmarshall(stream); + case Value::Double: return m_double.unmarshall(stream); + case Value::String: return m_string.unmarshall(stream); + case Value::Signature: return m_signature.unmarshall(stream); + case Value::ObjectPath: return m_objectPath.unmarshall(stream); + case Value::Array: return m_array->unmarshall(stream); + case Value::Struct: return m_struct->unmarshall(stream); + case Value::Variant: return m_variant->unmarshall(stream); + case Value::DictEntry: return m_dictEntry->unmarshall(stream); + case Value::None: + throw std::runtime_error("unmarshall: Any object has no type"); + } +} + +std::string DBus::Type::Any::toString(const std::string& prefix) const +{ + return value().toString(prefix); +} + +std::string DBus::Type::Any::asString() const +{ + return value().asString(); +} diff --git a/src/dbus_type_any.h b/src/dbus_type_any.h new file mode 100644 index 0000000..b53a9d3 --- /dev/null +++ b/src/dbus_type_any.h @@ -0,0 +1,157 @@ +// This file is part of dbus-asio +// Copyright 2022 OpenVPN Inc. +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, version 3, or at your +// option any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The GNU Lesser General Public License version 3 is included in the +// file named COPYING. If you do not have this file see +// . + +#pragma once + +#include "dbus_type_boolean.h" +#include "dbus_type_byte.h" +#include "dbus_type_double.h" +#include "dbus_type_int16.h" +#include "dbus_type_int32.h" +#include "dbus_type_int64.h" +#include "dbus_type_objectpath.h" +#include "dbus_type_signature.h" +#include "dbus_type_string.h" +#include "dbus_type_uint16.h" +#include "dbus_type_uint32.h" +#include "dbus_type_uint64.h" + +#include + +namespace DBus { + + struct Name; + struct ObjectPath; + + class Type::Any : public Type { + protected: + enum class Value { + None, Byte, Boolean, Double, + Int16, Int32, Int64, Uint16, Uint32, Uint64, + String, ObjectPath, Signature, + Array, Struct, Variant, DictEntry + }; + + Value m_valueType = Value::None; + union { + Byte m_byte; + Boolean m_boolean; + Double m_double; + + Int16 m_int16; + Int32 m_int32; + Int64 m_int64; + + Uint16 m_uint16; + Uint32 m_uint32; + Uint64 m_uint64; + + String m_string; + ObjectPath m_objectPath; + Signature m_signature; + + Array *m_array; + Struct *m_struct; + Variant *m_variant; + DictEntry *m_dictEntry; + }; + + template const T& tryVariantTo() const; + + const Type& value() const; + Any& clone(const Any& v); + void destroy(); + + public: + Any() {} + Any(const Any& v); + Any(const std::uint8_t& v); + Any(const Byte& v); + Any(const bool& v); + Any(const Boolean& v); + Any(const double& v); + Any(const Double& v); + Any(const std::int16_t& v); + Any(const std::int32_t& v); + Any(const std::int64_t& v); + Any(const Int16& v); + Any(const Int32& v); + Any(const Int64& v); + Any(const std::uint16_t& v); + Any(const std::uint32_t& v); + Any(const std::uint64_t& v); + Any(const Uint16& v); + Any(const Uint32& v); + Any(const Uint64& v); + Any(const char* v); + Any(const std::string& v); + Any(const Name& v); + Any(const String& v); + Any(const DBus::ObjectPath& v); + Any(const DBus::Type::ObjectPath& v); + Any(const Signature& v); + Any(const Array& v); + Any(const Struct& v); + Any(const Variant& v); + Any(const DictEntry& v); + ~Any() override { destroy(); } + + Any& operator=(const Any&); + + explicit operator const Byte&() const; + explicit operator const Boolean&() const; + explicit operator const Double&() const; + explicit operator const Int16&() const; + explicit operator const Int32&() const; + explicit operator const Int64&() const; + explicit operator const Uint16&() const; + explicit operator const Uint32&() const; + explicit operator const Uint64&() const; + explicit operator const String&() const; + explicit operator const ObjectPath&() const; + explicit operator const Signature&() const; + explicit operator const Array&() const; + explicit operator const Struct&() const; + explicit operator const Variant&() const; + explicit operator const DictEntry&() const; + + bool isBasicType() const; + + std::string getName() const override; + std::size_t getAlignment() const override; + std::string getSignature() const override; + + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; + + std::string toString(const std::string& prefix = "") const override; + std::string asString() const override; + + class BadCast : public std::bad_cast { + const std::string m_what; + public: + BadCast(const std::string& what) + : m_what(what) + {} + + const char* what() const noexcept override { + return m_what.c_str(); + } + }; + }; + +} // namespace DBus diff --git a/src/dbus_type_array.cpp b/src/dbus_type_array.cpp index da4fbf4..2c9fc47 100644 --- a/src/dbus_type_array.cpp +++ b/src/dbus_type_array.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -16,182 +17,84 @@ // . #include "dbus_type_array.h" -#include "dbus_type.h" -#include "dbus_type_boolean.h" -#include "dbus_type_byte.h" -#include "dbus_type_dictentry.h" -#include "dbus_type_double.h" -#include "dbus_type_int16.h" -#include "dbus_type_int32.h" -#include "dbus_type_int64.h" -#include "dbus_type_objectpath.h" -#include "dbus_type_signature.h" -#include "dbus_type_string.h" -#include "dbus_type_struct.h" -#include "dbus_type_uint16.h" -#include "dbus_type_uint32.h" -#include "dbus_type_uint64.h" -#include "dbus_type_variant.h" -#include - +#include "dbus_type_any.h" #include "dbus_messageistream.h" #include "dbus_messageostream.h" +#include "dbus_log.h" -const std::string DBus::Type::Array::s_StaticTypeCode("a"); - -size_t DBus::Type::Array::size() const { return contents.size(); } - -size_t DBus::Type::Array::add(const DBus::Type::Byte& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::Boolean& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::ObjectPath& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::Int16& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::Uint16& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::Int32& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::Uint32& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::Int64& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::Uint64& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::Double& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::String& v) -{ - contents.push_back(v); - return size(); -} - -size_t DBus::Type::Array::add(const DBus::Type::Variant& v) -{ - contents.push_back(v); - return size(); -} +#include -size_t DBus::Type::Array::add(const DBus::Type::Signature& v) -{ - contents.push_back(v); - return size(); -} +DBus::Type::Array::Array(const std::string& signature) + : m_signature(signature.substr(1)) +{} -size_t DBus::Type::Array::add(const DBus::Type::Struct& s) +std::size_t DBus::Type::Array::size() const { - contents.push_back(s); - return size(); + return m_contents.size(); } -size_t DBus::Type::Array::add(const DBus::Type::DictEntry& s) +std::size_t DBus::Type::Array::add(const DBus::Type::Any& v) { - contents.push_back(s); + if (m_signature.empty()) + m_signature = v.getSignature(); + else if (m_signature != v.getSignature()) + throw std::runtime_error( + "adding value to Array with wrong signature: " + v.getSignature()); + m_contents.push_back(v); return size(); } -const std::vector& DBus::Type::Array::getContents() const -{ - return contents; -} - void DBus::Type::Array::marshall(MessageOStream& stream) const { const size_t sizePos = stream.size(); stream.writeUint32(0); // Size does not include any padding to the first element - stream.pad(Type::getAlignment(Type::extractSignature(getSignature(), 1))); + stream.pad(Type::getAlignment(m_signature)); const size_t contentsStartPos = stream.size(); - marshallContents(stream); + for (auto& elem : m_contents) { + elem.marshall(stream); + } const uint32_t contentsSize = stream.size() - contentsStartPos; + if (contentsSize > Array::MaximumSize) + throw std::out_of_range("Array " + getSignature() + + ": size " + std::to_string(contentsSize) + " exceeds 64 MiB"); stream.data.replace(sizePos, 4, (char*)&contentsSize, sizeof(uint32_t)); } -void DBus::Type::Array::marshallContents(MessageOStream& stream) const -{ - for (size_t i = 0; i < contents.size(); ++i) { - DBus::Type::marshallData(contents[i], stream); - } -} - void DBus::Type::Array::unmarshall(MessageIStream& stream) { uint32_t size = 0; stream.read(&size); - std::string signature = Type::extractSignature(getSignature(), 1); - stream.align(Type::getAlignment(signature)); + if (size > Array::MaximumSize) + throw std::out_of_range("Array " + getSignature() + + ": size " + std::to_string(size) + " exceeds 64 MiB"); + + stream.align(Type::getAlignment(m_signature)); MessageIStream arrayStream(stream, size); while (!arrayStream.empty()) { - contents.push_back(DBus::Type::create(signature)); - DBus::Type::unmarshallData(contents.back(), arrayStream); + m_contents.push_back(DBus::Type::create(m_signature)); + m_contents.back().unmarshall(arrayStream); }; } -std::string DBus::Type::Array::getSignature() const -{ - if (contents.size()) { - return "a" + DBus::Type::getMarshallingSignature(contents[0]); - } - - return m_Signature; -} - std::string DBus::Type::Array::toString(const std::string& prefix) const { std::stringstream ss; - ss << prefix << "Array (" << getSignature() << ") [\n"; - for (size_t i = 0; i < contents.size(); ++i) { - ss << prefix << " [" << i << "] =\n"; - ss << DBus::Type::toString(contents[i], prefix + " "); + ss << name << " " << getSignature() << " : [\n"; + for (size_t i = 0; i < m_contents.size(); ++i) { + ss << prefix << " [" << i << "] " + << m_contents[i].toString(prefix + " "); } ss << prefix << "]\n"; return ss.str(); } -std::string DBus::Type::Array::asString() const { return "[array]"; } +std::string DBus::Type::Array::asString() const +{ + return getName() + " " + getSignature(); +} diff --git a/src/dbus_type_array.h b/src/dbus_type_array.h index 132aa4f..312b224 100644 --- a/src/dbus_type_array.h +++ b/src/dbus_type_array.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,70 +16,47 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_ARRAY_H -#define DBUS_TYPE_ARRAY_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type_any.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - class Byte; - class Boolean; - class ObjectPath; - class Int16; - class Uint16; - class Int32; - class Uint32; - class Int64; - class Uint64; - class Double; - class String; - class Signature; - class Variant; - class Struct; - class DictEntry; - - class Array : public Base { + class Type::Array : public Container { public: - size_t size() const; + Array() = default; + Array(const std::string& signature); + + static constexpr std::size_t MaximumSize = 67108864 /*64 MiB*/; - size_t add(const DBus::Type::Byte& v); - size_t add(const DBus::Type::Boolean& v); - size_t add(const DBus::Type::ObjectPath& v); - size_t add(const DBus::Type::Int16& v); - size_t add(const DBus::Type::Uint16& v); - size_t add(const DBus::Type::Int32& v); - size_t add(const DBus::Type::Uint32& v); - size_t add(const DBus::Type::Int64& v); - size_t add(const DBus::Type::Uint64& v); - size_t add(const DBus::Type::Double& v); - size_t add(const DBus::Type::String& v); - size_t add(const DBus::Type::Variant& v); - size_t add(const DBus::Type::Signature& v); - size_t add(const DBus::Type::Struct& s); - size_t add(const DBus::Type::DictEntry& s); + static constexpr const char *name = "Array"; + static constexpr std::size_t alignment = 4; + static constexpr const char code = 'a'; - size_t getAlignment() const { return 4; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); - void marshallContents(MessageOStream& stream) const; + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; } + std::string getSignature() const override { return code + m_signature; } - std::string getSignature() const; + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + std::string toString(const std::string& prefix = "") const override; + std::string asString() const override; - const std::vector& getContents() const; + std::size_t size() const; + std::size_t add(const Any& v); - static const std::string s_StaticTypeCode; + using ConstIterator = std::vector::const_iterator; + ConstIterator begin() const { return m_contents.cbegin(); } + ConstIterator end() const { return m_contents.cend(); } + + const Any& operator[](std::size_t pos) const { return m_contents.at(pos); } + const Array* sis() const {return this;} protected: - std::vector contents; + std::string m_signature; + std::vector m_contents; }; -} // namespace Type + } // namespace DBus -#endif diff --git a/src/dbus_type_base.cpp b/src/dbus_type_base.cpp deleted file mode 100644 index fc90979..0000000 --- a/src/dbus_type_base.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . - -#include "dbus_type_base.h" - -void DBus::Type::Base::setSignature(const std::string& type) -{ - m_Signature = type; -} - -std::string DBus::Type::Base::getSignature() const { return m_Signature; } - -std::string DBus::Type::Base::toString(const std::string& prefix) const -{ - return std::string(prefix + "\n"); -} - -std::string DBus::Type::Base::asString() const { return ""; } diff --git a/src/dbus_type_base.h b/src/dbus_type_base.h deleted file mode 100644 index 19bf869..0000000 --- a/src/dbus_type_base.h +++ /dev/null @@ -1,71 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . - -#ifndef DBUS_TYPE_BASE_H -#define DBUS_TYPE_BASE_H - -#include -#include - -namespace DBus { - -namespace Type { - - // variant didn't work because the DBus variant type allows structs within - // structs, meaning we need a forward reference. Any suggestsions? - // typedef boost::variant Generic; - - // I dropped any when attempting to retrieve the specific type at compile time. - // (Since I needed it to bind the unmarshall method) Even grabbing a ptr to the - // base class seemed impossible. I returned to it when a custom class seemed too - // much effort. https://theboostcpplibraries.com/boost.any - typedef boost::any Generic; - // TODO: I would like to have Generic derive from boost:any so the asByte - // methods can be added to it. But there's a lot of plumbing since operator= et - // al need templating. It'd be as quick to copy+paste boost::any as Generic and - // add the methods there. (I don't like that, tho.) - - // This describes multiple complete types. e.g. ss, i(ii) - // (There doesn't appear to be an official name other than "multiple complete - // type" and I couldn't think of better) - struct CompositeBlock { - std::vector m_TypeList; - }; - - // This base class exists primarily to act as marshalling helpers. - // The use of boost::any prohibits our ability to grab typed data, and use - // virtual methods. - class Base { - public: - Base() {} - - size_t getAlignment() const { return 1; } - void setSignature(const std::string& type); - std::string getSignature() const; - - std::string toString(const std::string& prefix) - const; // each non-container type should end with \n as as convenience to - // the calling function - std::string asString() const; - - protected: - std::string m_Signature; - }; -} // namespace Type -} // namespace DBus - -#endif diff --git a/src/dbus_type_boolean.cpp b/src/dbus_type_boolean.cpp index 4651dc5..b2eb5aa 100644 --- a/src/dbus_type_boolean.cpp +++ b/src/dbus_type_boolean.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -20,43 +21,32 @@ #include "dbus_messageostream.h" #include -const std::string DBus::Type::Boolean::s_StaticTypeCode("b"); - -DBus::Type::Boolean::Boolean() - : m_Value(0) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Boolean::Boolean(uint32_t v) +DBus::Type::Boolean::Boolean(bool v) : m_Value(v) -{ - setSignature(s_StaticTypeCode); -} +{} void DBus::Type::Boolean::marshall(MessageOStream& stream) const { - stream.writeBoolean(m_Value ? true : false); + stream.writeBoolean(m_Value); } void DBus::Type::Boolean::unmarshall(MessageIStream& stream) { - stream.read(&m_Value); + uint32_t tmp; + stream.read(&tmp); + m_Value = tmp == 0 ? false : true; } -std::string DBus::Type::Boolean::toString(const std::string& prefix) const +std::string DBus::Type::Boolean::toString(const std::string&) const { std::stringstream ss; - - ss << prefix << "Boolean "; - ss << (m_Value ? "True" : "Talse") << "\n"; - + ss << "Boolean " << m_Value << '\n'; return ss.str(); } std::string DBus::Type::Boolean::asString() const { std::stringstream ss; - ss << (size_t)m_Value; + ss << m_Value; return ss.str(); } diff --git a/src/dbus_type_boolean.h b/src/dbus_type_boolean.h index deaa268..1443315 100644 --- a/src/dbus_type_boolean.h +++ b/src/dbus_type_boolean.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,35 +16,36 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_BOOLEAN_H -#define DBUS_TYPE_BOOLEAN_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - - class Boolean : public Base { + class Type::Boolean : public Basic { public: - Boolean(); - Boolean(uint32_t v); + Boolean() = default; + Boolean(bool v); + + static constexpr const char *name = "Boolean"; + static constexpr std::size_t alignment = 4; + static constexpr const char code = 'b'; - size_t getAlignment() const { return 4; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); }; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - static const std::string s_StaticTypeCode; + std::string toString(const std::string&) const override; + std::string asString() const override; + + operator bool() const { return m_Value; }; protected: - uint32_t m_Value; + bool m_Value = false; }; -} // namespace Type + } // namespace DBus -#endif diff --git a/src/dbus_type_byte.cpp b/src/dbus_type_byte.cpp index 528fc63..e29d355 100644 --- a/src/dbus_type_byte.cpp +++ b/src/dbus_type_byte.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -21,21 +22,9 @@ #include #include -const std::string DBus::Type::Byte::s_StaticTypeCode("y"); - -DBus::Type::Byte::Byte() - : m_Value(0) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Byte::Byte(size_t v) +DBus::Type::Byte::Byte(const std::uint8_t& v) : m_Value(v) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Byte::Byte(const Byte& b) { m_Value = b.m_Value; } +{} void DBus::Type::Byte::marshall(MessageOStream& stream) const { @@ -47,15 +36,15 @@ void DBus::Type::Byte::unmarshall(MessageIStream& data) m_Value = data.read(); } -std::string DBus::Type::Byte::toString(const std::string& prefix) const +std::string DBus::Type::Byte::toString(const std::string&) const { - std::stringstream ss; + std::ostringstream oss; - ss << prefix << "Byte "; - ss << (size_t)m_Value << " (0x" << std::hex << std::setfill('0') - << std::setw(2) << int(uint8_t(m_Value)) << ")\n"; + oss << "Byte " + << (size_t)m_Value << " (0x" << std::hex << std::setfill('0') + << std::setw(2) << int(uint8_t(m_Value)) << ")\n"; - return ss.str(); + return oss.str(); } std::string DBus::Type::Byte::asString() const diff --git a/src/dbus_type_byte.h b/src/dbus_type_byte.h index a51d72d..044b8aa 100644 --- a/src/dbus_type_byte.h +++ b/src/dbus_type_byte.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,36 +16,35 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_BYTE_H -#define DBUS_TYPE_BYTE_H +#pragma once -#include "dbus_type_base.h" -namespace DBus { -class MessageOStream; -class MessageIStream; +#include "dbus_type.h" -namespace Type { +namespace DBus { - class Byte : public Base { + class Type::Byte : public Basic { public: - Byte(); - Byte(size_t v); - Byte(const Byte& b); + Byte() = default; + Byte(const std::uint8_t& b); + + static constexpr const char *name = "Byte"; + static constexpr const char alignment = 1; + static constexpr const char code = 'y'; - std::string getSignature() const { return s_StaticTypeCode; } + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); }; - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& data); + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& data) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + std::string toString(const std::string&) const override; + std::string asString() const override; - static const std::string s_StaticTypeCode; + operator std::uint8_t() const { return m_Value; }; protected: - uint8_t m_Value; + std::uint8_t m_Value = 0; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_dictentry.cpp b/src/dbus_type_dictentry.cpp index 55c62c0..f1a7c57 100644 --- a/src/dbus_type_dictentry.cpp +++ b/src/dbus_type_dictentry.cpp @@ -15,18 +15,18 @@ // file named COPYING. If you do not have this file see // . +#include "dbus_type_signature.h" #include "dbus_type_dictentry.h" #include "dbus_type.h" #include "dbus_type_string.h" #include "dbus_type_uint32.h" -#include #include "dbus_message.h" #include "dbus_messageistream.h" #include "dbus_messageostream.h" #include "dbus_messageprotocol.h" -#include "dbus_validation.h" +#include /* Structs and dict entries are marshalled in the same way as their contents, but their alignment is always to an 8-byte boundary, even if their contents would @@ -45,73 +45,67 @@ The first field in the DICT_ENTRY is always the key. A message is considered corrupt if the same key occurs twice in the same array of DICT_ENTRY. However, for performance reasons implementations are not required to reject dicts with duplicate keys. - - */ -const std::string DBus::Type::DictEntry::s_StaticTypeCode("{"); - -DBus::Type::DictEntry::DictEntry(const DBus::Type::Generic& key, - const DBus::Type::Generic& value) +DBus::Type::DictEntry::DictEntry(const std::string& signature) + : m_signature(signature.substr(1, signature.size() - 2)) { - set(key, value); } -DBus::Type::DictEntry::DictEntry(const std::string& key, std::string& value) +DBus::Type::DictEntry::DictEntry(const DBus::Type::Any& key, + const DBus::Type::Any& value) { - set(DBus::Type::String(key), DBus::Type::String(value)); + set(key, value); } -DBus::Type::DictEntry::DictEntry(const std::string& key, uint32_t value) +void DBus::Type::DictEntry::set(const DBus::Type::Any& key, + const DBus::Type::Any& value) { - set(DBus::Type::String(key), DBus::Type::Uint32(value)); + if (!key.isBasicType()) + throw std::runtime_error("DictEntry key has invalid basic type: " + + key.getSignature()); + m_Value = std::make_pair(key, value); } -// -void DBus::Type::DictEntry::set(const DBus::Type::Generic& key, - const DBus::Type::Generic& value) +std::string DBus::Type::DictEntry::getSignature() const { - m_Value = std::make_pair(key, value); - m_Signature = "{" + Type::getMarshallingSignature(m_Value.first) + Type::getMarshallingSignature(m_Value.second) + "}"; + return code_start + key().getSignature() + value().getSignature() + code_end; } -std::string DBus::Type::DictEntry::getSignature() const { return m_Signature; } - void DBus::Type::DictEntry::marshall(MessageOStream& stream) const { - stream.pad8(); - DBus::Type::marshallData(m_Value.first, stream); - DBus::Type::marshallData(m_Value.second, stream); + key().marshall(stream); + value().marshall(stream); } void DBus::Type::DictEntry::unmarshall(MessageIStream& stream) { stream.align(8); - const char key_type = getSignature().at(1); - DBus::Validation::throwOnInvalidBasicType(key_type); - m_Value.first = Type::create(std::string(1, key_type)); - m_Value.second = Type::create(DBus::Type::extractSignature(getSignature(), 2)); - DBus::Type::unmarshallData(m_Value.first, stream); - DBus::Type::unmarshallData(m_Value.second, stream); + Any key = Type::create(m_signature.getNextTypeCode()); + Any value = Type::create(m_signature.getNextTypeCode()); + + key.unmarshall(stream); + value.unmarshall(stream); + + set(key, value); } std::string DBus::Type::DictEntry::toString(const std::string& prefix) const { - std::stringstream ss; - std::string contents_prefix(prefix); - contents_prefix += " "; - - ss << prefix << "DictEntry (" << getSignature() << ") : {\n"; - ss << prefix - << " key: " << DBus::Type::toString(m_Value.first, contents_prefix); - ss << prefix - << " value: " << DBus::Type::toString(m_Value.second, contents_prefix); - ss << prefix << "}\n"; - - return ss.str(); + std::ostringstream oss; + + oss << "DictEntry " << getSignature() << " : {\n"; + oss << prefix << " key: " << key().toString(); + oss << prefix << " value: " << value().toString(prefix + " "); + oss << prefix << "}\n"; + + return oss.str(); } -std::string DBus::Type::DictEntry::asString() const { return "[DictEntry]"; } +std::string DBus::Type::DictEntry::asString() const +{ + return getName() + " " + getSignature(); +} diff --git a/src/dbus_type_dictentry.h b/src/dbus_type_dictentry.h index 5713714..5a5f1a4 100644 --- a/src/dbus_type_dictentry.h +++ b/src/dbus_type_dictentry.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,41 +16,44 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_DICTENTRY_H -#define DBUS_TYPE_DICTENTRY_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type_any.h" namespace DBus { -class MessageOStream; -class MessageIStream; + class Signature; -namespace Type { - - class DictEntry : public Base { + class Type::DictEntry : public Container { public: - DictEntry() {} - DictEntry(const DBus::Type::Generic& key, const DBus::Type::Generic& value); - DictEntry(const std::string& key, std::string& value); - DictEntry(const std::string& key, uint32_t value); + DictEntry(const std::string& signature); + DictEntry(const DBus::Type::Any& key, const DBus::Type::Any& value); + + static constexpr const char *name = "DictEntry"; + static constexpr const char code_start = '{'; + static constexpr const char code_end = '}'; + + static constexpr std::size_t alignment = 8; + static constexpr const char code = code_start; - std::string getSignature() const; - size_t getAlignment() const { return 8; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - void set(const DBus::Type::Generic& key, const DBus::Type::Generic& value); + const Any& key() const { return m_Value.first; }; + const Any& value() const { return m_Value.second; }; - static const std::string s_StaticTypeCode; + std::string toString(const std::string& prefix = "") const override; + std::string asString() const override; + + void set(const DBus::Type::Any& key, const DBus::Type::Any& value); protected: - std::pair m_Value; + Signature m_signature; + std::pair m_Value = {}; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_double.cpp b/src/dbus_type_double.cpp index 439eadd..8c8de3b 100644 --- a/src/dbus_type_double.cpp +++ b/src/dbus_type_double.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -20,19 +21,9 @@ #include "dbus_messageostream.h" #include -const std::string DBus::Type::Double::s_StaticTypeCode("d"); - -DBus::Type::Double::Double() - : m_Value(0) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Double::Double(double v) +DBus::Type::Double::Double(const double& v) : m_Value(v) -{ - setSignature(s_StaticTypeCode); -} +{} void DBus::Type::Double::marshall(MessageOStream& stream) const { @@ -44,19 +35,14 @@ void DBus::Type::Double::unmarshall(MessageIStream& stream) stream.read(&m_Value); } -std::string DBus::Type::Double::toString(const std::string& prefix) const +std::string DBus::Type::Double::toString(const std::string&) const { - std::stringstream ss; - - ss << prefix << "Double "; - ss << m_Value << "\n"; - - return ss.str(); + std::ostringstream oss; + oss << "Double " << m_Value << "\n"; + return oss.str(); } std::string DBus::Type::Double::asString() const { - std::stringstream ss; - ss << m_Value; - return ss.str(); + return std::to_string(m_Value); } diff --git a/src/dbus_type_double.h b/src/dbus_type_double.h index c20589f..c8afe34 100644 --- a/src/dbus_type_double.h +++ b/src/dbus_type_double.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,35 +16,35 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_DOUBLE_H -#define DBUS_TYPE_DOUBLE_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - - class Double : public Base { + class Type::Double : public Basic { public: - Double(); - Double(double v); + Double() = default; + Double(const double& v); + + static constexpr const char *name = "Double"; + static constexpr std::size_t alignment = 8; + static constexpr const char code = 'd'; + + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); }; - size_t getAlignment() const { return 8; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + std::string toString(const std::string&) const override; + std::string asString() const override; - static const std::string s_StaticTypeCode; + operator double() const { return m_Value; }; protected: - double m_Value; + double m_Value = 0.0; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_int16.cpp b/src/dbus_type_int16.cpp index 58dd02b..2f10e52 100644 --- a/src/dbus_type_int16.cpp +++ b/src/dbus_type_int16.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -21,19 +22,9 @@ #include #include -const std::string DBus::Type::Int16::s_StaticTypeCode("n"); - -DBus::Type::Int16::Int16() - : m_Value(0) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Int16::Int16(int16_t v) +DBus::Type::Int16::Int16(const std::int16_t v) : m_Value(v) -{ - setSignature(s_StaticTypeCode); -} +{} void DBus::Type::Int16::marshall(MessageOStream& stream) const { @@ -42,18 +33,18 @@ void DBus::Type::Int16::marshall(MessageOStream& stream) const void DBus::Type::Int16::unmarshall(MessageIStream& stream) { - stream.read(&m_Value); + stream.read(&m_Value); } -std::string DBus::Type::Int16::toString(const std::string& prefix) const +std::string DBus::Type::Int16::toString(const std::string&) const { - std::stringstream ss; + std::ostringstream oss; - ss << prefix << "Int16 "; - ss << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(2) - << m_Value << ")\n"; + oss << "Int16 " + << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(4) + << m_Value << ")\n"; - return ss.str(); + return oss.str(); } std::string DBus::Type::Int16::asString() const diff --git a/src/dbus_type_int16.h b/src/dbus_type_int16.h index 093a0dd..169b669 100644 --- a/src/dbus_type_int16.h +++ b/src/dbus_type_int16.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,35 +16,35 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_INT16_H -#define DBUS_TYPE_INT16_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - - class Int16 : public Base { + class Type::Int16 : public Basic { public: - Int16(); - Int16(int16_t v); + Int16() = default; + Int16(const std::int16_t v); + + static constexpr const char *name = "Int16"; + static constexpr std::size_t alignment = 2; + static constexpr const char code = 'n'; + + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); }; - size_t getAlignment() const { return 2; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + std::string toString(const std::string&) const override; + std::string asString() const override; - static const std::string s_StaticTypeCode; + operator std::int16_t() const { return m_Value; }; protected: - int16_t m_Value; + std::int16_t m_Value; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_int32.cpp b/src/dbus_type_int32.cpp index 5f3509d..f818061 100644 --- a/src/dbus_type_int32.cpp +++ b/src/dbus_type_int32.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -21,19 +22,9 @@ #include #include -const std::string DBus::Type::Int32::s_StaticTypeCode("i"); - -DBus::Type::Int32::Int32() - : m_Value(0) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Int32::Int32(int32_t v) +DBus::Type::Int32::Int32(const std::int32_t v) : m_Value(v) -{ - setSignature(s_StaticTypeCode); -} +{} void DBus::Type::Int32::marshall(MessageOStream& stream) const { @@ -42,23 +33,21 @@ void DBus::Type::Int32::marshall(MessageOStream& stream) const void DBus::Type::Int32::unmarshall(MessageIStream& stream) { - stream.read(&m_Value); + stream.read(&m_Value); } -std::string DBus::Type::Int32::toString(const std::string& prefix) const +std::string DBus::Type::Int32::toString(const std::string&) const { - std::stringstream ss; + std::ostringstream oss; - ss << prefix << "Int32 "; - ss << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(4) - << m_Value << ")\n"; + oss << "Int32 " + << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(8) + << m_Value << ")\n"; - return ss.str(); + return oss.str(); } std::string DBus::Type::Int32::asString() const { - std::stringstream ss; - ss << m_Value; - return ss.str(); + return std::to_string(m_Value); } diff --git a/src/dbus_type_int32.h b/src/dbus_type_int32.h index c45c2f0..ec3bd8f 100644 --- a/src/dbus_type_int32.h +++ b/src/dbus_type_int32.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,36 +16,35 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_INT32_H -#define DBUS_TYPE_INT32_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type.h" +#include namespace DBus { -class MessageOStream; -class MessageIStream; - -namespace Type { - - class Int32 : public Base { + class Type::Int32 : public Basic { public: - Int32(); - Int32(int32_t v); + Int32() = default; + Int32(const std::int32_t v); + + static constexpr const char *name = "Int32"; + static constexpr std::size_t alignment = 4; + static constexpr const char code = 'i'; - size_t getAlignment() const { return 4; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); }; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - static const std::string s_StaticTypeCode; + std::string toString(const std::string&) const override; + std::string asString() const override; + operator std::int32_t() const { return m_Value; }; protected: - int32_t m_Value; + std::int32_t m_Value = 0; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_int64.cpp b/src/dbus_type_int64.cpp index 7c8024e..4d5156f 100644 --- a/src/dbus_type_int64.cpp +++ b/src/dbus_type_int64.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -21,19 +22,9 @@ #include #include -const std::string DBus::Type::Int64::s_StaticTypeCode("x"); - -DBus::Type::Int64::Int64() - : m_Value(0) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Int64::Int64(int64_t v) +DBus::Type::Int64::Int64(const std::int64_t v) : m_Value(v) -{ - setSignature(s_StaticTypeCode); -} +{} void DBus::Type::Int64::marshall(MessageOStream& stream) const { @@ -42,23 +33,21 @@ void DBus::Type::Int64::marshall(MessageOStream& stream) const void DBus::Type::Int64::unmarshall(MessageIStream& stream) { - stream.read(&m_Value); + stream.read(&m_Value); } -std::string DBus::Type::Int64::toString(const std::string& prefix) const +std::string DBus::Type::Int64::toString(const std::string&) const { - std::stringstream ss; + std::ostringstream oss; - ss << prefix << "Int64 "; - ss << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(8) - << m_Value << ")\n"; + oss << "Int64 " + << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(16) + << m_Value << ")\n"; - return ss.str(); + return oss.str(); } std::string DBus::Type::Int64::asString() const { - std::stringstream ss; - ss << m_Value; - return ss.str(); + return std::to_string(m_Value); } diff --git a/src/dbus_type_int64.h b/src/dbus_type_int64.h index 57d1cbe..1dcc7b7 100644 --- a/src/dbus_type_int64.h +++ b/src/dbus_type_int64.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,35 +16,35 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_INT64_H -#define DBUS_TYPE_INT64_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - - class Int64 : public Base { + class Type::Int64 : public Basic { public: - Int64(); - Int64(int64_t v); + Int64() = default; + Int64(const std::int64_t v); + + static constexpr const char *name = "Int64"; + static constexpr std::size_t alignment = 8; + static constexpr const char code = 'x'; + + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); }; - size_t getAlignment() const { return 8; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + std::string toString(const std::string&) const override; + std::string asString() const override; - static const std::string s_StaticTypeCode; + operator std::int64_t() const { return m_Value; }; protected: - int64_t m_Value; + std::int64_t m_Value = 0; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_objectpath.cpp b/src/dbus_type_objectpath.cpp index 5694f8a..981ef6f 100644 --- a/src/dbus_type_objectpath.cpp +++ b/src/dbus_type_objectpath.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,23 +16,23 @@ // file named COPYING. If you do not have this file see // . +#include "dbus_names.h" #include "dbus_type_objectpath.h" #include "dbus_messageostream.h" -#include "dbus_type_string.h" -const std::string DBus::Type::ObjectPath::s_StaticTypeCode("o"); - -DBus::Type::ObjectPath::ObjectPath() { setSignature(s_StaticTypeCode); } +DBus::Type::ObjectPath::ObjectPath(const char* v) + : ObjectPath(DBus::ObjectPath(v)) +{} DBus::Type::ObjectPath::ObjectPath(const std::string& v) + : ObjectPath(DBus::ObjectPath(v)) +{} + +DBus::Type::ObjectPath::ObjectPath(const DBus::ObjectPath& v) : String(v) -{ - setSignature(s_StaticTypeCode); -} +{} void DBus::Type::ObjectPath::marshall(MessageOStream& stream) const { - // Exactly the same as STRING except the content must be a valid object path - // (see above). Type::String::marshall(stream); } diff --git a/src/dbus_type_objectpath.h b/src/dbus_type_objectpath.h index aad658a..29db86c 100644 --- a/src/dbus_type_objectpath.h +++ b/src/dbus_type_objectpath.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,26 +16,30 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_OBJECTPATH_H -#define DBUS_TYPE_OBJECTPATH_H +#pragma once #include "dbus_type_string.h" +#include + namespace DBus { -class MessageOStream; -namespace Type { + struct ObjectPath; - class ObjectPath : public String { + class Type::ObjectPath : public String { public: - ObjectPath(); + ObjectPath() = default; + ObjectPath(const char* v); ObjectPath(const std::string& v); + ObjectPath(const DBus::ObjectPath& v); + + static constexpr const char *name = "ObjectPath"; + static constexpr const char code = 'o'; + + std::string getName() const override { return name; } + std::string getSignature() const override { return std::string(1, code); }; - std::string getSignature() const { return s_StaticTypeCode; } - void marshall(MessageOStream& stream) const; - static const std::string s_StaticTypeCode; + void marshall(MessageOStream& stream) const override; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_signature.cpp b/src/dbus_type_signature.cpp index 6d0b95f..598e38b 100644 --- a/src/dbus_type_signature.cpp +++ b/src/dbus_type_signature.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -16,21 +17,132 @@ // . #include "dbus_type_signature.h" +#include "dbus_type_struct.h" +#include "dbus_type_array.h" +#include "dbus_type_dictentry.h" +#include "dbus_type_variant.h" #include "dbus_messageistream.h" #include "dbus_messageostream.h" -#include - -const std::string DBus::Type::Signature::s_StaticTypeCode("g"); +#include "dbus_log.h" -DBus::Type::Signature::Signature() { setSignature(s_StaticTypeCode); } +#include -DBus::Type::Signature::Signature(const std::string& v) +DBus::Type::Signature::Signature(const std::string& v, std::size_t level) : m_Value(v) { - setSignature(s_StaticTypeCode); -} + auto logAndThrow = [this](const char *error) { + Log::write(Log::ERROR, + "DBus :: signature '%s' invalid : %s\n", + m_Value.c_str(), error); + throw InvalidSignature(m_Value + "' : " + error); + }; + + enum Container { Array, Struct, DictEntry }; + + struct ContainerStack { + inline std::size_t size() { return m_stack.size(); }; + inline std::size_t level() { return m_arrayLevel + m_structLevel; }; + + inline bool push(Container type) { + m_stack.push_back(type); + if (type == Struct) + return ++m_structLevel <= MaxStructDepth; + else if (type == Array) + return ++m_arrayLevel <= MaxArrayDepth; + return true; + }; + + inline void pop() { + if (m_stack.empty()) + return; + if (topmostIs(Struct)) + --m_structLevel; + else if (topmostIs(Array)) + --m_arrayLevel; + m_stack.resize(m_stack.size() - 1); + while (topmostIs(Array)) + pop(); + }; + + inline bool topmostIs(const Container type) const { + return m_stack.size() && m_stack.back() == type; + }; + + protected: + std::size_t m_arrayLevel = 0; + std::size_t m_structLevel = 0; + std::vector m_stack; + }; + + if (m_Value.size() > MaxLength) + logAndThrow("length exceeds 255"); -const std::string& DBus::Type::Signature::getValue() const { return m_Value; } + ContainerStack stack; + bool haveStructElement; + bool haveDictEntryKey; + bool haveDictEntryVal; + + for (const int code : m_Value) { + // Check the key type if we're inside a DICT_ENTRY + if (stack.topmostIs(DictEntry)) { + if (!haveDictEntryKey) { + if (!isBasicTypeCode(code)) + logAndThrow("DictEntry key is not a basic type"); + haveDictEntryKey = true; + continue; + } else if (code != Type::DictEntry::code_end) { + if (haveDictEntryVal) + logAndThrow("DictEntry has multiple values"); + haveDictEntryVal = true; + } + } + // Count anything as element if we're inside a STRUCT + else if (stack.topmostIs(Struct) && code != Type::Struct::code_end) { + haveStructElement = true; + } + + if (code == Array::code) { + if (stack.push(Array) == false) + logAndThrow("Arrays nested more than 32 times"); + if (level + stack.level() > MaxDepth) + logAndThrow("total message depth larger then 64"); + } else if (code == Struct::code_start) { + if (stack.push(Struct) == false) + logAndThrow("Structs nested more than 32 times"); + if (level + stack.level() > MaxDepth) + logAndThrow("total message depth larger then 64"); + haveStructElement = false; + } else if (code == Struct::code_end) { + if (!stack.topmostIs(Struct) || !haveStructElement) + logAndThrow("Struct end unexpected"); + stack.pop(); + } else if (code == DictEntry::code_start) { + if (!stack.topmostIs(Array)) + logAndThrow("DictEntry outside of an ARRAY"); + stack.push(DictEntry); + haveDictEntryKey = false; + haveDictEntryVal = false; + } else if (code == DictEntry::code_end) { + if (!stack.topmostIs(DictEntry) || !haveDictEntryVal) + logAndThrow("DictEntry end unexpected"); + stack.pop(); + } else if (isBasicTypeCode(code) || code == Variant::code) { + if (stack.topmostIs(Array)) + stack.pop(); + } else { + logAndThrow("unknown type code"); + } + } + + if (stack.size()) { + if (stack.topmostIs(DictEntry)) + logAndThrow("DictEntry incomplete"); + else if (stack.topmostIs(Array)) + logAndThrow("Array incomplete"); + else if (stack.topmostIs(Struct)) + logAndThrow("Struct incomplete"); + } +} void DBus::Type::Signature::marshall(MessageOStream& stream) const { @@ -44,13 +156,60 @@ void DBus::Type::Signature::unmarshall(MessageIStream& stream) stream.read(); // read null; } -std::string DBus::Type::Signature::toString(const std::string& prefix) const +std::string DBus::Type::Signature::getNextTypeCode() { - std::stringstream ss; + if (m_typeCodeIndex >= m_Value.size()) { + m_typeCodeIndex = 0; + return ""; + } + + // Handle STRUCT + if (m_Value[m_typeCodeIndex] == Struct::code_start) { + // Find the matching parenthesis, and skip over element codes + std::size_t open_brackets = 1; + std::size_t start = m_typeCodeIndex; + while (++m_typeCodeIndex < m_Value.size()) { + if (m_Value[m_typeCodeIndex] == Struct::code_start) { + ++open_brackets; + } else if (m_Value[m_typeCodeIndex] == Struct::code_end) { + if (--open_brackets == 0) { + return m_Value.substr(start, ++m_typeCodeIndex - start); + } + } + } + } + // Handle DICT_ENTRY + else if (m_Value[m_typeCodeIndex] == DictEntry::code_start) { + // Find the matching brace, and skip over element codes + std::size_t open_brackets = 1; + std::size_t start = m_typeCodeIndex; + while (++m_typeCodeIndex < m_Value.size()) { + if (m_Value[m_typeCodeIndex] == DictEntry::code_start) { + ++open_brackets; + } else if (m_Value[m_typeCodeIndex] == DictEntry::code_end) { + if (--open_brackets == 0) { + return m_Value.substr(start, ++m_typeCodeIndex - start); + } + } + } + } - ss << prefix << "Signature (" << m_Value << ")\n"; + std::string typeCode(1, m_Value[m_typeCodeIndex]); + // Handle ARRAY + if (m_Value[m_typeCodeIndex++] == Array::code) + typeCode += getNextTypeCode(); - return ss.str(); + return typeCode; } -std::string DBus::Type::Signature::asString() const { return m_Value; } +std::string DBus::Type::Signature::toString(const std::string&) const +{ + std::ostringstream oss; + oss << "Signature '" << m_Value << "'\n"; + return oss.str(); +} + +std::string DBus::Type::Signature::asString() const +{ + return m_Value; +} diff --git a/src/dbus_type_signature.h b/src/dbus_type_signature.h index 8231601..54c58a5 100644 --- a/src/dbus_type_signature.h +++ b/src/dbus_type_signature.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,35 +16,54 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_SIGNATURE_H -#define DBUS_TYPE_SIGNATURE_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type.h" + +#include namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { + using InvalidSignature = std::runtime_error; - class Signature : public Base { + class Type::Signature : public Basic { public: - Signature(); - Signature(const std::string& v); + Signature() = default; + Signature(const Signature& other) = default; + Signature(const std::string& v, std::size_t height = 0); + + Signature& operator=(const Signature& other) = default; + + bool operator==(const Signature& other) { + return m_Value == other.m_Value; } + bool operator!=(const Signature& other) { + return m_Value != other.m_Value; } + + static constexpr const char *name = "Signature"; + static constexpr std::size_t alignment = 1; + static constexpr const char code = 'g'; - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; } + std::string getSignature() const override { return std::string(1, code); } - std::string toString(const std::string& prefix = "") const; - std::string asString() const; - const std::string& getValue() const; + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - static const std::string s_StaticTypeCode; + std::string toString(const std::string&) const override; + std::string asString() const override; + + bool empty() const { return m_Value.empty(); } + std::string getNextTypeCode(); protected: + static constexpr std::size_t MaxLength = 255; + static constexpr std::size_t MaxStructDepth = 32; + static constexpr std::size_t MaxArrayDepth = 32; + static constexpr std::size_t MaxDepth = MaxStructDepth + MaxArrayDepth; + + std::size_t m_typeCodeIndex = 0; std::string m_Value; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_string.cpp b/src/dbus_type_string.cpp index 9fb2db7..18e6edb 100644 --- a/src/dbus_type_string.cpp +++ b/src/dbus_type_string.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -21,15 +22,14 @@ #include "dbus_type.h" #include -const std::string DBus::Type::String::s_StaticTypeCode("s"); -DBus::Type::String::String() { setSignature(s_StaticTypeCode); } +DBus::Type::String::String(const char* v) + : m_Value(v) +{} DBus::Type::String::String(const std::string& v) : m_Value(v) -{ - setSignature(s_StaticTypeCode); -} +{} void DBus::Type::String::marshall(MessageOStream& stream) const { @@ -47,13 +47,16 @@ void DBus::Type::String::unmarshall(MessageIStream& stream) stream.read(); // null byte } -std::string DBus::Type::String::toString(const std::string& prefix) const +std::string DBus::Type::String::toString(const std::string&) const { - std::stringstream ss; + std::ostringstream oss; - ss << prefix << "String (" << m_Value.size() << ") \"" << m_Value << "\"\n"; + oss << "String (" << m_Value.size() << ") \"" << m_Value << "\"\n"; - return ss.str(); + return oss.str(); } -std::string DBus::Type::String::asString() const { return m_Value; } +std::string DBus::Type::String::asString() const +{ + return m_Value; +} diff --git a/src/dbus_type_string.h b/src/dbus_type_string.h index 1963f59..087af34 100644 --- a/src/dbus_type_string.h +++ b/src/dbus_type_string.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,36 +16,36 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_STRING_H -#define DBUS_TYPE_STRING_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - - class String : public Base { + class Type::String : public Basic { public: - String(); + String() = default; + String(const char* v); String(const std::string& v); - std::string getSignature() const { return s_StaticTypeCode; } - size_t getAlignment() const { return 4; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + static constexpr const char *name = "String"; + static constexpr std::size_t alignment = 4; + static constexpr const char code = 's'; + + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); }; + + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + std::string toString(const std::string&) const override; + std::string asString() const override; - static const std::string s_StaticTypeCode; + operator std::string() const { return m_Value; }; protected: std::string m_Value; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_struct.cpp b/src/dbus_type_struct.cpp index 56313ed..8213b26 100644 --- a/src/dbus_type_struct.cpp +++ b/src/dbus_type_struct.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,24 +16,13 @@ // file named COPYING. If you do not have this file see // . +#include "dbus_type_signature.h" #include "dbus_type_struct.h" #include "dbus_messageistream.h" #include "dbus_messageostream.h" -#include "dbus_type_boolean.h" -#include "dbus_type_byte.h" -#include "dbus_type_dictentry.h" -#include "dbus_type_double.h" -#include "dbus_type_int16.h" -#include "dbus_type_int32.h" -#include "dbus_type_int64.h" -#include "dbus_type_objectpath.h" -#include "dbus_type_signature.h" -#include "dbus_type_string.h" -#include "dbus_type_uint16.h" -#include "dbus_type_uint32.h" -#include "dbus_type_uint64.h" -#include "dbus_type_variant.h" + #include +#include /* Structs and dict entries are marshalled in the same way as their contents, but @@ -40,101 +30,45 @@ their alignment is always to an 8-byte boundary, even if their contents would normally be less strictly aligned. */ -std::string DBus::Type::Struct::s_StaticTypeCode("("); - -DBus::Type::Struct::Struct() -{ - // setSignature(s_StaticTypeCode); -} - -void DBus::Type::Struct::add(const DBus::Type::Byte& v) -{ - m_Value.push_back(v); -} - -void DBus::Type::Struct::add(const DBus::Type::Boolean& v) +DBus::Type::Struct::Struct(const Signature& signature) + : m_Signature(signature) { - m_Value.push_back(v); } -void DBus::Type::Struct::add(const DBus::Type::ObjectPath& v) +DBus::Type::Struct::Struct(const std::string& signature) + : m_Signature(signature.substr(1, signature.size() - 2)) { - m_Value.push_back(v); } -void DBus::Type::Struct::add(const DBus::Type::Int16& v) +void DBus::Type::Struct::add(const DBus::Type::Any& v) { - m_Value.push_back(v); + m_Signature = m_Signature.asString() + v.getSignature(); + m_elements.push_back(v); } -void DBus::Type::Struct::add(const DBus::Type::Uint16& v) +std::string DBus::Type::Struct::getSignature() const { - m_Value.push_back(v); -} + if (m_Signature.empty()) + throw std::runtime_error("Struct::getSignature() called when undefined"); -void DBus::Type::Struct::add(const DBus::Type::Int32& v) -{ - m_Value.push_back(v); -} - -void DBus::Type::Struct::add(const DBus::Type::Uint32& v) -{ - m_Value.push_back(v); -} - -void DBus::Type::Struct::add(const DBus::Type::Int64& v) -{ - m_Value.push_back(v); -} - -void DBus::Type::Struct::add(const DBus::Type::Uint64& v) -{ - m_Value.push_back(v); -} - -void DBus::Type::Struct::add(const DBus::Type::Double& v) -{ - m_Value.push_back(v); -} - -void DBus::Type::Struct::add(const DBus::Type::String& v) -{ - m_Value.push_back(v); -} - -void DBus::Type::Struct::add(const DBus::Type::Variant& v) -{ - m_Value.push_back(v); -} - -void DBus::Type::Struct::add(const DBus::Type::Signature& v) -{ - m_Value.push_back(v); -} - -void DBus::Type::Struct::add(const DBus::Type::Struct& v) -{ - m_Value.push_back(v); -} - -void DBus::Type::Struct::add(const DBus::Type::DictEntry& v) -{ - m_Value.push_back(v); + return code_start + m_Signature.asString() + code_end; } // BUGWARN: This should work in all cases, but I'm thinking of moving it into // a utility method (like all unmarshall). -void DBus::Type::Struct::clear() { m_Value.clear(); } +void DBus::Type::Struct::clear() +{ + m_elements.clear(); +} void DBus::Type::Struct::marshall(MessageOStream& stream) const { - // A struct must start on an 8-byte boundary regardless of the type of the // struct fields. stream.pad8(); - for (auto it : m_Value) { - DBus::Type::marshallData(it, stream); + for (auto element : m_elements) { + element.marshall(stream); } } @@ -143,28 +77,29 @@ void DBus::Type::Struct::unmarshall(MessageIStream& stream) // A struct must start on an 8-byte boundary regardless of the type of the // struct fields. stream.align(8); - size_t signatureIndex = 1; // skip first '(' - do { - const std::string signature = Type::extractSignature(m_Signature, signatureIndex); - m_Value.push_back(DBus::Type::create(signature)); - DBus::Type::unmarshallData(m_Value.back(), stream); - signatureIndex += signature.size(); - } while (signatureIndex < m_Signature.size() - 1); + std::string code = m_Signature.getNextTypeCode(); + while (!code.empty()) { + m_elements.push_back(DBus::Type::create(code)); + m_elements.back().unmarshall(stream); + code = m_Signature.getNextTypeCode(); + } } std::string DBus::Type::Struct::toString(const std::string& prefix) const { - std::stringstream ss; + std::ostringstream oss; + const std::string contents_prefix(prefix + " "); - ss << prefix << "Struct " << getSignature() << " <\n"; - std::string contents_prefix(prefix); - contents_prefix += " "; - for (auto it : m_Value) { - ss << DBus::Type::toString(it, contents_prefix); + oss << name << " " << getSignature() << " : (\n"; + for (auto element : m_elements) { + oss << contents_prefix << element.toString(contents_prefix); } - ss << prefix << ">\n"; + oss << prefix << ")\n"; - return ss.str(); + return oss.str(); } -std::string DBus::Type::Struct::asString() const { return "[struct]"; } +std::string DBus::Type::Struct::asString() const +{ + return getName() + " " + getSignature(); +} diff --git a/src/dbus_type_struct.h b/src/dbus_type_struct.h index 143cca3..4c39c4d 100644 --- a/src/dbus_type_struct.h +++ b/src/dbus_type_struct.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,75 +16,44 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_STRUCT_H -#define DBUS_TYPE_STRUCT_H +#pragma once -#include "dbus_type.h" +#include "dbus_type_any.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - class Byte; - class Boolean; - class ObjectPath; - class Int16; - class Uint16; - class Int32; - class Uint32; - class Int64; - class Uint64; - class Double; - class String; - class Variant; - class Struct; - class DictEntry; - - class Struct : public Base { + class Type::Struct : public Container { public: - Struct(); + Struct() = default; + Struct(const Signature& signature); + Struct(const std::string& signature); - void clear(); + static constexpr const char code_start = '('; + static constexpr const char code_end = ')'; - void add(const DBus::Type::Byte& v); - void add(const DBus::Type::Boolean& v); - void add(const DBus::Type::ObjectPath& v); - void add(const DBus::Type::Int16& v); - void add(const DBus::Type::Uint16& v); - void add(const DBus::Type::Int32& v); - void add(const DBus::Type::Uint32& v); - void add(const DBus::Type::Int64& v); - void add(const DBus::Type::Uint64& v); - void add(const DBus::Type::Double& v); - void add(const DBus::Type::String& v); - void add(const DBus::Type::Variant& v); - void add(const DBus::Type::Signature& v); - void add(const DBus::Type::Struct& v); - void add(const DBus::Type::DictEntry& v); + static constexpr const char *name = "Struct"; + static constexpr std::size_t alignment = 8; + static constexpr const char code = code_start; - std::string getSignature() const - { - // if no entries??? - return "(" + DBus::Type::getMarshallingSignature(m_Value) + ")"; - } + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override; - size_t getAlignment() const { return 8; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + std::string toString(const std::string& prefix = "") const override; + std::string asString() const override; - size_t getEntries() const { return m_Value.size(); } - const Generic& operator[](std::size_t idx) const { return m_Value[idx]; } + void clear(); + void add(const DBus::Type::Any& v); - static std::string s_StaticTypeCode; + std::size_t size() const { return m_elements.size(); } + const Any& operator[](std::size_t pos) const { return m_elements.at(pos); } private: - std::vector m_Value; + Signature m_Signature; + std::vector m_elements; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_uint16.cpp b/src/dbus_type_uint16.cpp index e422bd6..6ee5f24 100644 --- a/src/dbus_type_uint16.cpp +++ b/src/dbus_type_uint16.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -21,19 +22,9 @@ #include #include -const std::string DBus::Type::Uint16::s_StaticTypeCode("q"); - -DBus::Type::Uint16::Uint16() - : m_Value(0) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Uint16::Uint16(uint16_t v) +DBus::Type::Uint16::Uint16(const std::uint16_t v) : m_Value(v) -{ - setSignature(s_StaticTypeCode); -} +{} void DBus::Type::Uint16::marshall(MessageOStream& stream) const { @@ -42,23 +33,21 @@ void DBus::Type::Uint16::marshall(MessageOStream& stream) const void DBus::Type::Uint16::unmarshall(MessageIStream& stream) { - stream.read(&m_Value); + stream.read(&m_Value); } -std::string DBus::Type::Uint16::toString(const std::string& prefix) const +std::string DBus::Type::Uint16::toString(const std::string&) const { - std::stringstream ss; + std::ostringstream oss; - ss << prefix << "Uint16 "; - ss << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(2) - << m_Value << ")\n"; + oss << "Uint16 " + << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(4) + << m_Value << ")\n"; - return ss.str(); + return oss.str(); } std::string DBus::Type::Uint16::asString() const { - std::stringstream ss; - ss << m_Value; - return ss.str(); + return std::to_string(m_Value); } diff --git a/src/dbus_type_uint16.h b/src/dbus_type_uint16.h index 6355c7a..b71dccb 100644 --- a/src/dbus_type_uint16.h +++ b/src/dbus_type_uint16.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,35 +16,35 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_UINT16_H -#define DBUS_TYPE_UINT16_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - - class Uint16 : public Base { + class Type::Uint16 : public Basic { public: - Uint16(); - Uint16(uint16_t v); + Uint16() = default; + Uint16(const std::uint16_t v); + + static constexpr const char *name = "Uint16"; + static constexpr std::size_t alignment = 2; + static constexpr const char code = 'q'; + + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); }; - size_t getAlignment() const { return 2; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + std::string toString(const std::string&) const override; + std::string asString() const override; - static const std::string s_StaticTypeCode; + operator std::uint16_t() const { return m_Value; }; protected: - uint16_t m_Value; + std::uint16_t m_Value = 0; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_uint32.cpp b/src/dbus_type_uint32.cpp index 9afbbc9..a5936fc 100644 --- a/src/dbus_type_uint32.cpp +++ b/src/dbus_type_uint32.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -21,19 +22,9 @@ #include #include -const std::string DBus::Type::Uint32::s_StaticTypeCode("u"); - -DBus::Type::Uint32::Uint32() - : m_Value(0) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Uint32::Uint32(uint32_t v) +DBus::Type::Uint32::Uint32(const std::uint32_t v) : m_Value(v) -{ - setSignature(s_StaticTypeCode); -} +{} void DBus::Type::Uint32::marshall(MessageOStream& stream) const { @@ -42,23 +33,21 @@ void DBus::Type::Uint32::marshall(MessageOStream& stream) const void DBus::Type::Uint32::unmarshall(MessageIStream& stream) { - stream.read(&m_Value); + stream.read(&m_Value); } -std::string DBus::Type::Uint32::toString(const std::string& prefix) const +std::string DBus::Type::Uint32::toString(const std::string&) const { - std::stringstream ss; + std::ostringstream oss; - ss << prefix << "Uint32 "; - ss << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(4) - << m_Value << ")\n"; + oss << "Uint32 " + << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(8) + << m_Value << ")\n"; - return ss.str(); + return oss.str(); } std::string DBus::Type::Uint32::asString() const { - std::stringstream ss; - ss << m_Value; - return ss.str(); + return std::to_string(m_Value); } diff --git a/src/dbus_type_uint32.h b/src/dbus_type_uint32.h index 577adbc..8949398 100644 --- a/src/dbus_type_uint32.h +++ b/src/dbus_type_uint32.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,38 +16,35 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_UINT32_H -#define DBUS_TYPE_UINT32_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - - class Uint32 : public Base { + class Type::Uint32 : public Basic { public: - Uint32(); - Uint32(uint32_t v); + Uint32() = default; + Uint32(const std::uint32_t v); + + static constexpr const char *name = "Uint32"; + static constexpr std::size_t alignment = 4; + static constexpr const char code = 'u'; + + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); }; - // std::string getSignature() const { return - //s_StaticTypeCode; - //} - size_t getAlignment() const { return 4; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + std::string toString(const std::string&) const override; + std::string asString() const override; - static const std::string s_StaticTypeCode; + operator std::uint32_t() const { return m_Value; }; protected: - uint32_t m_Value; + std::uint32_t m_Value = 0; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_uint64.cpp b/src/dbus_type_uint64.cpp index 7ec60c1..76c701c 100644 --- a/src/dbus_type_uint64.cpp +++ b/src/dbus_type_uint64.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -21,19 +22,9 @@ #include #include -const std::string DBus::Type::Uint64::s_StaticTypeCode("t"); - -DBus::Type::Uint64::Uint64() - : m_Value(0) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Uint64::Uint64(uint64_t v) +DBus::Type::Uint64::Uint64(const std::uint64_t v) : m_Value(v) -{ - setSignature(s_StaticTypeCode); -} +{} void DBus::Type::Uint64::marshall(MessageOStream& stream) const { @@ -42,23 +33,21 @@ void DBus::Type::Uint64::marshall(MessageOStream& stream) const void DBus::Type::Uint64::unmarshall(MessageIStream& stream) { - stream.read(&m_Value); + stream.read(&m_Value); } -std::string DBus::Type::Uint64::toString(const std::string& prefix) const +std::string DBus::Type::Uint64::toString(const std::string&) const { - std::stringstream ss; + std::ostringstream oss; - ss << prefix << "Uint64 "; - ss << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(8) - << m_Value << ")\n"; + oss << "Uint64 " + << m_Value << " (0x" << std::hex << std::setfill('0') << std::setw(16) + << m_Value << ")\n"; - return ss.str(); + return oss.str(); } std::string DBus::Type::Uint64::asString() const { - std::stringstream ss; - ss << m_Value; - return ss.str(); + return std::to_string(m_Value); } diff --git a/src/dbus_type_uint64.h b/src/dbus_type_uint64.h index 8e528da..71a497b 100644 --- a/src/dbus_type_uint64.h +++ b/src/dbus_type_uint64.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,35 +16,35 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_UINT64_H -#define DBUS_TYPE_UINT64_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - - class Uint64 : public Base { + class Type::Uint64 : public Basic { public: - Uint64(); - Uint64(uint64_t v); + Uint64() = default; + Uint64(const std::uint64_t v); + + static constexpr const char *name = "Uint64"; + static constexpr std::size_t alignment = 8; + static constexpr const char code = 't'; + + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); }; - size_t getAlignment() const { return 8; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; + std::string toString(const std::string&) const override; + std::string asString() const override; - static const std::string s_StaticTypeCode; + operator std::uint64_t() const { return m_Value; }; protected: - uint64_t m_Value; + std::uint64_t m_Value = 0; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_type_variant.cpp b/src/dbus_type_variant.cpp index 0d16a6d..7c86f3f 100644 --- a/src/dbus_type_variant.cpp +++ b/src/dbus_type_variant.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -16,20 +17,16 @@ // . #include "dbus_type_variant.h" +#include "dbus_type_signature.h" +#include "dbus_type.h" #include "dbus_messageistream.h" #include "dbus_messageostream.h" -#include "dbus_type.h" -#include "dbus_type_objectpath.h" -#include "dbus_type_signature.h" -#include "dbus_type_string.h" -#include "dbus_type_uint32.h" -#include -const std::string DBus::Type::Variant::s_StaticTypeCode("v"); +#include /* Variants are marshalled as the SIGNATURE of the contents (which must be a single -complete type), followed by a marshalled m_Value with the type given by that +complete type), followed by a marshalled value with the type given by that signature. The variant has the same 1-byte alignment as the signature, which means that @@ -38,37 +35,12 @@ cause a total message depth to be larger than 64, including other container types such as structures. (See Valid Signatures.) */ -DBus::Type::Variant::Variant() { setSignature(s_StaticTypeCode); } - -DBus::Type::Variant::Variant(const DBus::Type::ObjectPath& v) - : m_Value(v) - , m_ContainedSignature(v.getSignature()) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Variant::Variant(const DBus::Type::String& v) - : m_Value(v) - , m_ContainedSignature(v.getSignature()) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Variant::Variant(const DBus::Type::Uint32& v) - : m_Value(v) - , m_ContainedSignature(v.getSignature()) -{ - setSignature(s_StaticTypeCode); -} - -DBus::Type::Variant::Variant(const DBus::Type::Signature& v) +DBus::Type::Variant::Variant(const DBus::Type::Any& v) : m_Value(v) - , m_ContainedSignature(v.getSignature()) { - setSignature(s_StaticTypeCode); } -const DBus::Type::Generic& DBus::Type::Variant::getValue() const +const DBus::Type::Any& DBus::Type::Variant::getValue() const { return m_Value; } @@ -77,34 +49,32 @@ void DBus::Type::Variant::marshall(MessageOStream& stream) const { // The marshalled SIGNATURE of a single complete type... - stream.writeSignature(m_ContainedSignature); + stream.writeSignature(m_Value.getSignature()); - // ...followed by a marshaled m_Value with the type given in the signature. - DBus::Type::marshallData(m_Value, stream); + // ...followed by a marshaled value with the type given in the signature. + m_Value.marshall(stream); } void DBus::Type::Variant::unmarshall(MessageIStream& stream) { Type::Signature signature; signature.unmarshall(stream); - m_ContainedSignature = signature.getValue(); - m_Value = DBus::Type::create(signature.getValue()); - DBus::Type::unmarshallData(m_Value, stream); + m_Value = DBus::Type::create(signature.asString()); + m_Value.unmarshall(stream); } std::string DBus::Type::Variant::toString(const std::string& prefix) const { - std::stringstream ss; + std::ostringstream oss; + const std::string content_prefix(prefix + " "); - ss << prefix << "Variant (" << m_ContainedSignature << ")\n"; - std::string contents_prefix(prefix); - contents_prefix += " "; - ss << DBus::Type::toString(m_Value, contents_prefix); + oss << "Variant (" << m_Value.getSignature() << ")\n"; + oss << content_prefix << m_Value.toString(content_prefix); - return ss.str(); + return oss.str(); } std::string DBus::Type::Variant::asString() const { - return DBus::Type::asString(m_Value); + return m_Value.asString(); } diff --git a/src/dbus_type_variant.h b/src/dbus_type_variant.h index 343c288..9984614 100644 --- a/src/dbus_type_variant.h +++ b/src/dbus_type_variant.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,44 +16,35 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TYPE_VARIANT_H -#define DBUS_TYPE_VARIANT_H +#pragma once -#include "dbus_type_base.h" +#include "dbus_type_any.h" namespace DBus { -class MessageOStream; -class MessageIStream; -namespace Type { - class ObjectPath; - class Signature; - class String; - class Uint32; - - class Variant : public Base { + class Type::Variant : public Container { public: - Variant(); - Variant(const DBus::Type::ObjectPath& v); - Variant(const DBus::Type::String& v); - Variant(const DBus::Type::Uint32& v); - Variant(const DBus::Type::Signature& v); + Variant() = default; + Variant(const Any& v); + + static constexpr const char *name = "Variant"; + static constexpr const char code = 'v'; + static constexpr std::size_t alignment = 1; + + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; }; + std::string getSignature() const override { return std::string(1, code); } - size_t getAlignment() const { return 8; } - void marshall(MessageOStream& stream) const; - void unmarshall(MessageIStream& stream); + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; - std::string toString(const std::string& prefix = "") const; - std::string asString() const; - const DBus::Type::Generic& getValue() const; + std::string toString(const std::string& prefix = "") const override; + std::string asString() const override; - static const std::string s_StaticTypeCode; + const Any& getValue() const; private: - DBus::Type::Generic m_Value; - std::string m_ContainedSignature; + Any m_Value; }; -} // namespace Type -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_validation.cpp b/src/dbus_validation.cpp deleted file mode 100644 index b17af83..0000000 --- a/src/dbus_validation.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . -#include "dbus_validation.h" -#include - -namespace DBus { -namespace Validation { - bool isValidBasicType(char type) - { - switch (type) { - case 'y': - case 'b': - case 'n': - case 'q': - case 'i': - case 'u': - case 'x': - case 't': - case 'd': - case 'h': - case 's': - case 'o': - case 'g': - return true; - default: - return false; - } - } - - void throwOnInvalidBasicType(char type) - { - if (!isValidBasicType(type)) - throw std::runtime_error(std::string("Invalid basic type: ") + type); - } - - void throwOnInvalidBasicType(const std::string& type) - { - if (type.size() != 1 || !isValidBasicType(type[0])) - throw std::runtime_error(std::string("Invalid basic type: ") + type); - } -} // namespace Validation -} // namespace DBus diff --git a/src/dbus_validation.h b/src/dbus_validation.h deleted file mode 100644 index d33ff35..0000000 --- a/src/dbus_validation.h +++ /dev/null @@ -1,32 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018-2020 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . - -#ifndef DBUS_VALIDATION_H -#define DBUS_VALIDATION_H - -#include -#include - -namespace DBus { -namespace Validation { - bool isValidBasicType(char type); - void throwOnInvalidBasicType(char type); - void throwOnInvalidBasicType(const std::string& type); -} // namespace Validation -} // namespace DBus - -#endif // DBUS_VALIDATION_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ad430e0..354da3c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,11 +27,6 @@ target_include_directories(dbus_messageistream_unittest PRIVATE ../extern) target_link_libraries(dbus_messageistream_unittest LINK_PRIVATE dbus-asio) add_test(NAME run_dbus_messageistream_unittest COMMAND dbus_messageistream_unittest) -add_executable(dbus_validation_unittest dbus_validation_unittest.cpp test_main.cpp) -target_include_directories(dbus_validation_unittest PRIVATE ../extern) -target_link_libraries(dbus_validation_unittest LINK_PRIVATE dbus-asio) -add_test(NAME run_dbus_validation_unittest COMMAND dbus_validation_unittest) - add_executable(dbus_type_array_unittest dbus_type_array_unittest.cpp test_main.cpp) target_include_directories(dbus_type_array_unittest PRIVATE ../extern) target_link_libraries(dbus_type_array_unittest LINK_PRIVATE dbus-asio) diff --git a/tests/dbus_type_array_unittest.cpp b/tests/dbus_type_array_unittest.cpp index 3fa2469..9fbcad3 100644 --- a/tests/dbus_type_array_unittest.cpp +++ b/tests/dbus_type_array_unittest.cpp @@ -27,8 +27,7 @@ namespace test { int32_t v2) { // An array of structures - Type::Array array; - array.setSignature("a(i)"); + Type::Array array("a(i)"); MessageIStream istream((uint8_t*)stream.data(), stream.size(), byteOrder != __LITTLE_ENDIAN); @@ -36,11 +35,11 @@ namespace test { REQUIRE(array.size() == 2); - const Type::Struct& struct1 = boost::any_cast(array.getContents()[0]); - const Type::Struct& struct2 = boost::any_cast(array.getContents()[1]); + const Type::Struct& struct1 = DBus::Type::refStruct(array[0]); + const Type::Struct& struct2 = DBus::Type::refStruct(array[1]); - REQUIRE(Type::asString(struct1[0]) == std::to_string(v1)); - REQUIRE(Type::asString(struct2[0]) == std::to_string(v2)); + REQUIRE(Type::asInt32(struct1[0]) == v1); + REQUIRE(Type::asInt32(struct2[0]) == v2); } void TestUnmarshallFromMessageIStream(unsigned byteOrder, int32_t v1, @@ -75,7 +74,7 @@ namespace test { TEST_CASE("Marshall and unmarshall array of structs") { - Type::Array array; + Type::Array array("a(i)"); Type::Struct struct1; DBus::Type::Int32 value1(-1); Type::Struct struct2; @@ -83,7 +82,6 @@ namespace test { struct1.add(value1); struct2.add(value2); - array.setSignature("a(i)"); array.add(struct1); array.add(struct2); MessageOStream stream; @@ -164,7 +162,7 @@ namespace test { { input.add(DBus::Type::Signature("i")); input.add(DBus::Type::Signature("(id)")); - input.add(DBus::Type::Signature("{id}")); + input.add(DBus::Type::Signature("a{id}")); input.add(DBus::Type::Signature("as")); } @@ -173,8 +171,7 @@ namespace test { MessageOStream ostream; input.marshall(ostream); - Type::Array output; - output.setSignature(input.getSignature()); + Type::Array output(input.getSignature()); MessageIStream istream((uint8_t*)ostream.data.data(), ostream.data.size(), false); output.unmarshall(istream); diff --git a/tests/dbus_type_dictentry_unittest.cpp b/tests/dbus_type_dictentry_unittest.cpp index 414c0f6..d8bc5b2 100644 --- a/tests/dbus_type_dictentry_unittest.cpp +++ b/tests/dbus_type_dictentry_unittest.cpp @@ -21,18 +21,17 @@ namespace test { const std::string& stream, const std::string& key, uint32_t value) { - Type::DictEntry dictEntry; - dictEntry.setSignature("{su}"); + Type::DictEntry dictEntry("{su}"); MessageIStream istream((uint8_t*)stream.data(), stream.size(), byteOrder != __LITTLE_ENDIAN); dictEntry.unmarshall(istream); std::stringstream ss; - ss << "DictEntry ({su}) : {" << std::endl; + ss << "DictEntry {su} : {" << std::endl; ss << " key: String (" << key.size() << ") \"" << key << "\"" << std::endl; ss << " value: Uint32 " << value << " (0x"; - ss << std::hex << std::setw(4) << std::setfill('0') << value; + ss << std::hex << std::setw(8) << std::setfill('0') << value; ss << std::dec << std::setw(0) << ")" << std::endl; ss << "}" << std::endl; @@ -66,24 +65,23 @@ namespace test { const int32_t key, const std::pair& value) { - Type::DictEntry dictEntry; - dictEntry.setSignature("{i(dy)}"); + Type::DictEntry dictEntry("{i(dy)}"); MessageIStream istream((uint8_t*)stream.data(), stream.size(), byteOrder != __LITTLE_ENDIAN); dictEntry.unmarshall(istream); std::stringstream ss; - ss << "DictEntry ({i(dy)}) : {" << std::endl; + ss << "DictEntry {i(dy)} : {" << std::endl; ss << " key: Int32 " << key << " (0x"; - ss << std::hex << std::setw(4) << std::setfill('0') << key; + ss << std::hex << std::setw(8) << std::setfill('0') << key; ss << std::dec << std::setw(0) << ")" << std::endl; - ss << " value: Struct (dy) <" << std::endl; + ss << " value: Struct (dy) : (" << std::endl; ss << " Double " << value.first << std::endl; ss << " Byte " << static_cast(value.second) << " (0x"; ss << std::hex << std::setw(2) << std::setfill('0') << static_cast(value.second); ss << std::dec << std::setw(0) << ")" << std::endl; - ss << " >" << std::endl; + ss << " )" << std::endl; ss << "}" << std::endl; REQUIRE(dictEntry.toString() == ss.str()); @@ -195,10 +193,9 @@ namespace test { 0x00, }; - Type::DictEntry dictEntry; - dictEntry.setSignature("{vu}"); + Type::DictEntry dictEntry("{vu}"); MessageIStream istream(encoded.data(), encoded.size(), false); - REQUIRE_THROWS_WITH(dictEntry.unmarshall(istream), "Invalid basic type: v"); + REQUIRE_THROWS_WITH(dictEntry.unmarshall(istream), "DictEntry key has invalid basic type: v"); } } // namespace test diff --git a/tests/dbus_type_double_unittest.cpp b/tests/dbus_type_double_unittest.cpp index 2ac51da..4a22600 100644 --- a/tests/dbus_type_double_unittest.cpp +++ b/tests/dbus_type_double_unittest.cpp @@ -27,9 +27,7 @@ namespace test { MessageIStream stream((uint8_t*)&u, sizeof(u), byteOrder != __BYTE_ORDER); dbusType.unmarshall(stream); - std::stringstream ss; - ss << value; - REQUIRE(dbusType.asString() == ss.str()); + REQUIRE(static_cast(dbusType) == value); } TEST_CASE("Unmarshall double little endian from MessageIStream") @@ -54,9 +52,7 @@ namespace test { MessageIStream istream((uint8_t*)stream.data.data(), stream.size(), false); dbusType.unmarshall(istream); - std::stringstream ss; - ss << value; - REQUIRE(dbusType.asString() == ss.str()); + REQUIRE(static_cast(dbusType) == value); } } // namespace test diff --git a/tests/dbus_type_multiple_unittest.cpp b/tests/dbus_type_multiple_unittest.cpp index bfa79ad..6940785 100644 --- a/tests/dbus_type_multiple_unittest.cpp +++ b/tests/dbus_type_multiple_unittest.cpp @@ -23,76 +23,80 @@ namespace test { { Type::Struct dbusStruct1; dbusStruct1.add(Type::Byte(85)); - dbusStruct1.add(Type::String("BrightSign")); + dbusStruct1.add("BrightSign"); Type::Struct dbusStruct2; dbusStruct2.add(Type::Byte(42)); - dbusStruct2.add(Type::String("Arthur")); + dbusStruct2.add("Arthur"); Type::Struct dbusStruct3; dbusStruct3.add(Type::Byte(99)); - dbusStruct3.add(Type::String("Red Balloons")); + dbusStruct3.add("Red Balloons"); - Type::Array dbusArray; - dbusArray.add(Type::DictEntry(Type::String("KeyKey"), dbusStruct1)); - dbusArray.add(Type::DictEntry(Type::String("Another key"), dbusStruct2)); - dbusArray.add(Type::DictEntry(Type::String("Anarchy"), dbusStruct3)); + Type::Array dbusArray1; + dbusArray1.add(Type::DictEntry("KeyKey", dbusStruct1)); + dbusArray1.add(Type::DictEntry("Another key", dbusStruct2)); + dbusArray1.add(Type::DictEntry("Anarchy", dbusStruct3)); + + Type::Array dbusArray2; + dbusArray2.add(Type::DictEntry("Key", dbusArray1)); Type::Struct dbusStructTop; - dbusStructTop.add(Type::String("This is a long string value")); - dbusStructTop.add(Type::DictEntry(Type::String("Key"), dbusArray)); + dbusStructTop.add("This is a long string value"); + dbusStructTop.add(dbusArray2); - Type::DictEntry input(Type::Int32(131073), dbusStructTop); + Type::Array input; + input.add(Type::DictEntry(Type::Int32(131073), dbusStructTop)); - REQUIRE(DBus::Type::getMarshallingSignature(input) == "{i(s{sa{s(ys)}})}"); + REQUIRE(input.getSignature() == "a{i(sa{sa{s(ys)}})}"); REQUIRE('\n' + input.toString() == R"( -DictEntry ({i(s{sa{s(ys)}})}) : { - key: Int32 131073 (0x20001) - value: Struct (s{sa{s(ys)}}) < - String (27) "This is a long string value" - DictEntry ({sa{s(ys)}}) : { - key: String (3) "Key" - value: Array (a{s(ys)}) [ - [0] = - DictEntry ({s(ys)}) : { - key: String (6) "KeyKey" - value: Struct (ys) < - Byte 85 (0x55) - String (10) "BrightSign" - > - } - [1] = - DictEntry ({s(ys)}) : { - key: String (11) "Another key" - value: Struct (ys) < - Byte 42 (0x2a) - String (6) "Arthur" - > - } - [2] = - DictEntry ({s(ys)}) : { - key: String (7) "Anarchy" - value: Struct (ys) < - Byte 99 (0x63) - String (12) "Red Balloons" - > - } +Array a{i(sa{sa{s(ys)}})} : [ + [0] DictEntry {i(sa{sa{s(ys)}})} : { + key: Int32 131073 (0x00020001) + value: Struct (sa{sa{s(ys)}}) : ( + String (27) "This is a long string value" + Array a{sa{s(ys)}} : [ + [0] DictEntry {sa{s(ys)}} : { + key: String (3) "Key" + value: Array a{s(ys)} : [ + [0] DictEntry {s(ys)} : { + key: String (6) "KeyKey" + value: Struct (ys) : ( + Byte 85 (0x55) + String (10) "BrightSign" + ) + } + [1] DictEntry {s(ys)} : { + key: String (11) "Another key" + value: Struct (ys) : ( + Byte 42 (0x2a) + String (6) "Arthur" + ) + } + [2] DictEntry {s(ys)} : { + key: String (7) "Anarchy" + value: Struct (ys) : ( + Byte 99 (0x63) + String (12) "Red Balloons" + ) + } + ] + } ] - } - > -} + ) + } +] )"); MessageOStream ostream; input.marshall(ostream); - REQUIRE(ostream.data.size() == 165); + REQUIRE(ostream.data.size() == 181); SECTION("Complete") { - Type::DictEntry output; - output.setSignature(input.getSignature()); + Type::Array output(input.getSignature()); MessageIStream istream((uint8_t*)ostream.data.data(), ostream.data.size(), false); output.unmarshall(istream); @@ -110,8 +114,7 @@ DictEntry ({i(s{sa{s(ys)}})}) : { { INFO("Truncate at " << truncate_at << " from " << ostream.data.size()); std::string data = ostream.data.substr(0, truncate_at); - Type::DictEntry output; - output.setSignature(input.getSignature()); + Type::Array output(input.getSignature()); MessageIStream istream((uint8_t*)data.data(), data.size(), false); REQUIRE_THROWS(output.unmarshall(istream)); diff --git a/tests/dbus_type_signature_unittest.cpp b/tests/dbus_type_signature_unittest.cpp index 2604c7d..fc0debd 100644 --- a/tests/dbus_type_signature_unittest.cpp +++ b/tests/dbus_type_signature_unittest.cpp @@ -47,7 +47,7 @@ namespace test { TEST_CASE("Marshall and unmarshall signature") { - const std::string str("{ii}"); + const std::string str("a{ii}"); Type::Signature dbusString(str); MessageOStream stream; diff --git a/tests/dbus_type_struct_unittest.cpp b/tests/dbus_type_struct_unittest.cpp index d1e9c17..0075d21 100644 --- a/tests/dbus_type_struct_unittest.cpp +++ b/tests/dbus_type_struct_unittest.cpp @@ -13,16 +13,15 @@ namespace test { const std::string& stream, uint32_t v1, uint32_t v2) { - Type::Struct dbusStruct; - dbusStruct.setSignature("(uu)"); + Type::Struct dbusStruct("(uu)"); MessageIStream istream((uint8_t*)stream.data(), stream.size(), byteOrder != __LITTLE_ENDIAN); dbusStruct.unmarshall(istream); - REQUIRE(dbusStruct.getEntries() == 2); - REQUIRE(Type::asString(dbusStruct[0]) == std::to_string(v1)); - REQUIRE(Type::asString(dbusStruct[1]) == std::to_string(v2)); + REQUIRE(dbusStruct.size() == 2); + REQUIRE(Type::asUint32(dbusStruct[0]) == v1); + REQUIRE(Type::asUint32(dbusStruct[1]) == v2); } void TestUnmarshallMessageIStream(unsigned byteOrder, uint32_t v1, diff --git a/tests/dbus_type_variant_unittest.cpp b/tests/dbus_type_variant_unittest.cpp index ae3be2a..c17474a 100644 --- a/tests/dbus_type_variant_unittest.cpp +++ b/tests/dbus_type_variant_unittest.cpp @@ -77,8 +77,7 @@ namespace test { MessageOStream ostream; input.marshall(ostream); - Type::Variant output; - output.setSignature(input.getSignature()); + Type::Variant output(input.getSignature()); MessageIStream istream((uint8_t*)ostream.data.data(), ostream.data.size(), false); output.unmarshall(istream); diff --git a/tests/dbus_validation_unittest.cpp b/tests/dbus_validation_unittest.cpp deleted file mode 100644 index 17beb49..0000000 --- a/tests/dbus_validation_unittest.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "dbus_validation.h" -#include - -TEST_CASE("Basic types char") -{ - using namespace DBus::Validation; - - CHECK_NOTHROW(throwOnInvalidBasicType('y')); - CHECK_NOTHROW(throwOnInvalidBasicType('b')); - CHECK_NOTHROW(throwOnInvalidBasicType('n')); - CHECK_NOTHROW(throwOnInvalidBasicType('q')); - CHECK_NOTHROW(throwOnInvalidBasicType('u')); - CHECK_NOTHROW(throwOnInvalidBasicType('x')); - CHECK_NOTHROW(throwOnInvalidBasicType('t')); - CHECK_NOTHROW(throwOnInvalidBasicType('d')); - CHECK_NOTHROW(throwOnInvalidBasicType('h')); - CHECK_NOTHROW(throwOnInvalidBasicType('s')); - CHECK_NOTHROW(throwOnInvalidBasicType('o')); - CHECK_NOTHROW(throwOnInvalidBasicType('g')); - - CHECK_THROWS(throwOnInvalidBasicType('a')); - CHECK_THROWS(throwOnInvalidBasicType('v')); - CHECK_THROWS(throwOnInvalidBasicType('(')); - CHECK_THROWS(throwOnInvalidBasicType('{')); - CHECK_THROWS(throwOnInvalidBasicType('m')); - CHECK_THROWS(throwOnInvalidBasicType('*')); - CHECK_THROWS(throwOnInvalidBasicType('?')); - CHECK_THROWS(throwOnInvalidBasicType('@')); - CHECK_THROWS(throwOnInvalidBasicType('&')); - CHECK_THROWS(throwOnInvalidBasicType('^')); -} - -TEST_CASE("Basic types string") -{ - using namespace DBus::Validation; - - CHECK_NOTHROW(throwOnInvalidBasicType("y")); - CHECK_NOTHROW(throwOnInvalidBasicType("g")); - - CHECK_THROWS(throwOnInvalidBasicType("")); - CHECK_THROWS(throwOnInvalidBasicType("ai")); - CHECK_THROWS(throwOnInvalidBasicType("v")); - CHECK_THROWS(throwOnInvalidBasicType("(ii)")); - CHECK_THROWS(throwOnInvalidBasicType("{su}")); -} From 0bd67576521cfd3288c2f9dcb97a7ddc273aea61 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Wed, 13 Apr 2022 23:49:03 +0200 Subject: [PATCH 09/24] add support for the UnixFd DBus type DBus has support for passing file descriptors between processes via the Unix sockets. This adds the respective type and prepares buffer and streams to handle such FDs when sending and receiving messages. Signed-off-by: Heiko Hund --- CMakeLists.txt | 2 + src/dbus.h | 1 + src/dbus_messageistream.cpp | 14 +++-- src/dbus_messageistream.h | 3 +- src/dbus_messageostream.h | 37 +++++++++-- src/dbus_octetbuffer.cpp | 25 ++++++++ src/dbus_octetbuffer.h | 14 +++-- src/dbus_type.cpp | 12 +++- src/dbus_type.h | 2 + src/dbus_type_any.cpp | 17 +++++ src/dbus_type_any.h | 6 +- src/dbus_type_unixfd.cpp | 86 ++++++++++++++++++++++++++ src/dbus_type_unixfd.h | 52 ++++++++++++++++ tests/CMakeLists.txt | 5 ++ tests/dbus_messageistream_unittest.cpp | 24 ++++--- tests/dbus_type_array_unittest.cpp | 9 +-- tests/dbus_type_boolean_unittest.cpp | 4 +- tests/dbus_type_byte_unittest.cpp | 7 ++- tests/dbus_type_dictentry_unittest.cpp | 11 ++-- tests/dbus_type_double_unittest.cpp | 6 +- tests/dbus_type_int16_unittest.cpp | 4 +- tests/dbus_type_int32_unittest.cpp | 4 +- tests/dbus_type_int64_unittest.cpp | 4 +- tests/dbus_type_multiple_unittest.cpp | 7 ++- tests/dbus_type_signature_unittest.cpp | 4 +- tests/dbus_type_string_unittest.cpp | 4 +- tests/dbus_type_struct_unittest.cpp | 4 +- tests/dbus_type_uint16_unittest.cpp | 4 +- tests/dbus_type_uint32_unittest.cpp | 4 +- tests/dbus_type_uint64_unittest.cpp | 4 +- tests/dbus_type_unixfd_unittest.cpp | 47 ++++++++++++++ tests/dbus_type_variant_unittest.cpp | 8 +-- tests/test_unmarshall.h | 3 +- 33 files changed, 369 insertions(+), 69 deletions(-) create mode 100644 src/dbus_type_unixfd.cpp create mode 100644 src/dbus_type_unixfd.h create mode 100644 tests/dbus_type_unixfd_unittest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ab497bd..f829036 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ set(HEADER_FILES src/dbus_type_uint16.h src/dbus_type_uint32.h src/dbus_type_uint64.h + src/dbus_type_unixfd.h src/dbus_type_variant.h src/dbus_utils.h ) @@ -78,6 +79,7 @@ add_library(dbus-asio SHARED src/dbus_type_uint16.cpp src/dbus_type_uint32.cpp src/dbus_type_uint64.cpp + src/dbus_type_unixfd.cpp src/dbus_type_variant.cpp src/dbus_utils.cpp ) diff --git a/src/dbus.h b/src/dbus.h index 544af75..d10f469 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -45,6 +45,7 @@ #include "dbus_type_uint16.h" #include "dbus_type_uint32.h" #include "dbus_type_uint64.h" +#include "dbus_type_unixfd.h" #include "dbus_type_variant.h" // Functionality diff --git a/src/dbus_messageistream.cpp b/src/dbus_messageistream.cpp index 9b2c48f..7c8a66c 100644 --- a/src/dbus_messageistream.cpp +++ b/src/dbus_messageistream.cpp @@ -23,16 +23,15 @@ namespace DBus { -MessageIStream::MessageIStream(const uint8_t* data, size_t size, - bool swapByteOrder) - : m_data(data, size) +MessageIStream::MessageIStream(OctetBuffer& data, bool swapByteOrder) + : m_data(data) , m_offset(0) , m_swapByteOrder(swapByteOrder) { } MessageIStream::MessageIStream(MessageIStream& stream, size_t size) - : m_data(stream.m_data.data(), size) + : m_data(stream.m_data, size) , m_offset(stream.m_offset) , m_swapByteOrder(stream.m_swapByteOrder) { @@ -85,6 +84,13 @@ void MessageIStream::read(std::string& string, size_t size) m_offset += size; } +int MessageIStream::readUnixFd() +{ + std::uint32_t index; + read(&index); + return m_data.getUnixFd(index); +} + bool MessageIStream::empty() { return m_data.empty(); } } // namespace DBus diff --git a/src/dbus_messageistream.h b/src/dbus_messageistream.h index eadaa79..67b4b0f 100644 --- a/src/dbus_messageistream.h +++ b/src/dbus_messageistream.h @@ -49,7 +49,7 @@ class MessageIStream { } public: - MessageIStream(const uint8_t* data, size_t size, bool swapByteOrder); + MessageIStream(OctetBuffer& data, bool swapByteOrder); MessageIStream(MessageIStream& stream, size_t size); bool empty(); @@ -58,6 +58,7 @@ class MessageIStream { void read(uint8_t* value, size_t size); void read(double* value); void read(std::string& string, size_t size); + int readUnixFd(); template void read(T* value) diff --git a/src/dbus_messageostream.h b/src/dbus_messageostream.h index d02a70c..6c8ca12 100644 --- a/src/dbus_messageostream.h +++ b/src/dbus_messageostream.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,17 +16,24 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_MESSAGEOSTREAM_H -#define DBUS_MESSAGEOSTREAM_H +#pragma once #include "dbus_utils.h" +#include "dbus_octetbuffer.h" // for UnixFdBuffer +#include #include +#include +#include +#include namespace DBus { class MessageOStream { public: + using Ptr = std::shared_ptr; + std::string data; + UnixFdBuffer fds; size_t size() const { return data.length(); } @@ -77,9 +85,12 @@ class MessageOStream { void write(const std::string& str) { data.append(str.data(), str.length()); } - void write(const MessageOStream& stream) + void write(const MessageOStream& other) { - data.append(stream.data.data(), stream.data.length()); + data.append(other.data.data(), other.data.length()); + if (fds.size()) + throw std::runtime_error("internal error: overwriting UnixFds"); + fds = other.fds; } void writeString(const std::string& str) @@ -107,6 +118,21 @@ class MessageOStream { writeByte(0); } + void writeUnixFd(int fd) + { + if (fds.size() > 253) + throw std::runtime_error("more than 253 UnixFds"); + fds.push_back(fd); + writeUint32(fds.size() - 1); + } + + void clearUnixFds() + { + for (auto fd : fds) + ::close(fd); + fds.clear(); + } + void pad(size_t padding) { data.append(DBus::Utils::getPadding(padding, data.length()), 0); @@ -118,6 +144,5 @@ class MessageOStream { void pad8() { pad(8); } }; -} // namespace DBus -#endif +} // namespace DBus diff --git a/src/dbus_octetbuffer.cpp b/src/dbus_octetbuffer.cpp index 6b64513..dfc4394 100644 --- a/src/dbus_octetbuffer.cpp +++ b/src/dbus_octetbuffer.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -16,6 +17,7 @@ // . #include "dbus_octetbuffer.h" +#include "dbus_log.h" #include #include #include @@ -25,6 +27,22 @@ namespace DBus { OctetBuffer::OctetBuffer(const uint8_t* data, size_t size) : m_data(data) , m_size(size) + , m_fds() +{ +} + +OctetBuffer::OctetBuffer(OctetBuffer& other, std::size_t size) + : m_data(other.m_data) + , m_size(size) + , m_fds(other.m_fds) +{ +} + +OctetBuffer::OctetBuffer(const std::uint8_t* data, std::size_t dataSize, + const UnixFdBuffer& fds) + : m_data(data) + , m_size(dataSize) + , m_fds(fds) { } @@ -71,4 +89,11 @@ size_t OctetBuffer::find(uint8_t byte) const return std::string::npos; } +int OctetBuffer::getUnixFd(std::uint32_t index) const +{ + if (index < m_fds.size()) + return m_fds[index]; + throw std::out_of_range("OctetBuffer::getUnixFd index out of range"); +} + } // namespace DBus diff --git a/src/dbus_octetbuffer.h b/src/dbus_octetbuffer.h index caaecc7..6d0ac4a 100644 --- a/src/dbus_octetbuffer.h +++ b/src/dbus_octetbuffer.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,20 +16,26 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_OCTECTSTREAM_H -#define DBUS_OCTECTSTREAM_H +#pragma once #include #include +#include namespace DBus { +using UnixFdBuffer = std::vector; + class OctetBuffer { const uint8_t* m_data; size_t m_size; + UnixFdBuffer m_fds; public: OctetBuffer(const uint8_t* data, size_t size); + OctetBuffer(OctetBuffer& other, std::size_t size); + OctetBuffer(const uint8_t* data, std::size_t dataSize, + const UnixFdBuffer& fds); size_t size() const; const uint8_t* data() const; @@ -37,8 +44,7 @@ class OctetBuffer { uint8_t operator[](unsigned long index) const; void copy(uint8_t* data, size_t size) const; size_t find(uint8_t byte) const; + int getUnixFd(std::uint32_t index) const; }; } // namespace DBus - -#endif diff --git a/src/dbus_type.cpp b/src/dbus_type.cpp index 8f85419..2e37dac 100644 --- a/src/dbus_type.cpp +++ b/src/dbus_type.cpp @@ -34,11 +34,16 @@ #include "dbus_type_uint16.h" #include "dbus_type_uint32.h" #include "dbus_type_uint64.h" +#include "dbus_type_unixfd.h" #include "dbus_type_variant.h" // // Helper methods to extract native types from the opaque 'Any' type // +int DBus::Type::asUnixFd(const Any& v) +{ + return static_cast(v).dup(); +} bool DBus::Type::asBoolean(const Any& v) { @@ -136,6 +141,7 @@ std::size_t DBus::Type::getAlignment(const std::string& typeCode) case Uint32::code: return Uint32::alignment; case Uint64::code: return Uint64::alignment; case Double::code: return Double::alignment; + case UnixFd::code: return UnixFd::alignment; case String::code: return String::alignment; case ObjectPath::code: return ObjectPath::alignment; case Signature::code: return Signature::alignment; @@ -161,6 +167,7 @@ DBus::Type::Any DBus::Type::create(const std::string& typeCode) case Uint32::code: return Uint32(); case Uint64::code: return Uint64(); case Double::code: return Double(); + case UnixFd::code: return UnixFd(); case String::code: return String(); case ObjectPath::code: return ObjectPath(); case Signature::code: return Signature(); @@ -178,7 +185,8 @@ bool DBus::Type::isBasicTypeCode(const int code) { code == Int16::code || code == Uint16::code || code == Int32::code || code == Uint32::code || code == Int64::code || code == Uint64::code || - code == Double::code || code == String::code || - code == ObjectPath::code || code == Signature::code); + code == Double::code || code == UnixFd::code || + code == String::code || code == ObjectPath::code || + code == Signature::code); } diff --git a/src/dbus_type.h b/src/dbus_type.h index 39cce1a..2ac916c 100644 --- a/src/dbus_type.h +++ b/src/dbus_type.h @@ -43,6 +43,7 @@ namespace DBus { class Byte; class Boolean; + class UnixFd; class Double; class String; @@ -69,6 +70,7 @@ namespace DBus { virtual std::string asString() const = 0; // Helper methods to return native types + static int asUnixFd(const Any& v); static bool asBoolean(const Any& v); static double asDouble(const Any& v); static std::string asString(const Any& v); diff --git a/src/dbus_type_any.cpp b/src/dbus_type_any.cpp index 9d61dd9..5771f92 100644 --- a/src/dbus_type_any.cpp +++ b/src/dbus_type_any.cpp @@ -30,6 +30,7 @@ DBus::Type::Any& DBus::Type::Any::clone(const Any& v) switch (m_valueType) { case Value::Byte: new(&m_byte) Byte(v); break; case Value::Double: new(&m_double) Double(v); break; + case Value::UnixFd: new(&m_unixFd) UnixFd(v); break; case Value::Boolean: new(&m_boolean) Boolean(v); break; case Value::Int16: new(&m_int16) Int16(v); break; case Value::Int32: new(&m_int32) Int32(v); break; @@ -65,6 +66,7 @@ void DBus::Type::Any::destroy() case Value::Uint32: m_uint32.~Uint32(); break; case Value::Uint64: m_uint64.~Uint64(); break; case Value::Double: m_double.~Double(); break; + case Value::UnixFd: m_unixFd.~UnixFd(); break; case Value::String: m_string.~String(); break; case Value::Array: delete m_array; break; @@ -179,6 +181,12 @@ DBus::Type::Any::Any(const Uint64& v) new(&m_uint64) Uint64(v); } +DBus::Type::Any::Any(const UnixFd& v) + : m_valueType(Value::UnixFd) +{ + new(&m_unixFd) UnixFd(v); +} + DBus::Type::Any::Any(const double& v) : m_valueType(Value::Double) { @@ -349,6 +357,13 @@ DBus::Type::Any::operator const Double&() const return tryVariantTo(); } +DBus::Type::Any::operator const UnixFd&() const +{ + if (m_valueType == Value::UnixFd) + return m_unixFd; + return tryVariantTo(); +} + DBus::Type::Any::operator const String&() const { if (m_valueType == Value::String) @@ -422,6 +437,7 @@ const DBus::Type& DBus::Type::Any::value() const case Value::Uint32: return m_uint32; case Value::Uint64: return m_uint64; case Value::Double: return m_double; + case Value::UnixFd: return m_unixFd; case Value::String: return m_string; case Value::Signature: return m_signature; case Value::ObjectPath: return m_objectPath; @@ -469,6 +485,7 @@ void DBus::Type::Any::unmarshall(MessageIStream& stream) case Value::Uint32: return m_uint32.unmarshall(stream); case Value::Uint64: return m_uint64.unmarshall(stream); case Value::Double: return m_double.unmarshall(stream); + case Value::UnixFd: return m_unixFd.unmarshall(stream); case Value::String: return m_string.unmarshall(stream); case Value::Signature: return m_signature.unmarshall(stream); case Value::ObjectPath: return m_objectPath.unmarshall(stream); diff --git a/src/dbus_type_any.h b/src/dbus_type_any.h index b53a9d3..9c24ea7 100644 --- a/src/dbus_type_any.h +++ b/src/dbus_type_any.h @@ -29,6 +29,7 @@ #include "dbus_type_uint16.h" #include "dbus_type_uint32.h" #include "dbus_type_uint64.h" +#include "dbus_type_unixfd.h" #include @@ -40,7 +41,7 @@ namespace DBus { class Type::Any : public Type { protected: enum class Value { - None, Byte, Boolean, Double, + None, Byte, Boolean, UnixFd, Double, Int16, Int32, Int64, Uint16, Uint32, Uint64, String, ObjectPath, Signature, Array, Struct, Variant, DictEntry @@ -50,6 +51,7 @@ namespace DBus { union { Byte m_byte; Boolean m_boolean; + UnixFd m_unixFd; Double m_double; Int16 m_int16; @@ -83,6 +85,7 @@ namespace DBus { Any(const Byte& v); Any(const bool& v); Any(const Boolean& v); + Any(const UnixFd& v); Any(const double& v); Any(const Double& v); Any(const std::int16_t& v); @@ -114,6 +117,7 @@ namespace DBus { explicit operator const Byte&() const; explicit operator const Boolean&() const; + explicit operator const UnixFd&() const; explicit operator const Double&() const; explicit operator const Int16&() const; explicit operator const Int32&() const; diff --git a/src/dbus_type_unixfd.cpp b/src/dbus_type_unixfd.cpp new file mode 100644 index 0000000..c181683 --- /dev/null +++ b/src/dbus_type_unixfd.cpp @@ -0,0 +1,86 @@ +// This file is part of dbus-asio +// Copyright 2022 OpenVPN Inc. +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, version 3, or at your +// option any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The GNU Lesser General Public License version 3 is included in the +// file named COPYING. If you do not have this file see +// . + +#include "dbus_type_unixfd.h" +#include "dbus_messageistream.h" +#include "dbus_messageostream.h" + +#include +#include + +#include +#include + +DBus::Type::UnixFd::UnixFd() + : m_fd(-1) +{} + +DBus::Type::UnixFd::UnixFd(int fd) + : m_fd(::dup(fd)) +{} + +DBus::Type::UnixFd::UnixFd(const UnixFd& other) + : m_fd(::dup(other.m_fd)) +{} + +DBus::Type::UnixFd::~UnixFd() { + ::close(m_fd); +} + +void DBus::Type::UnixFd::marshall(MessageOStream& stream) const +{ + stream.writeUnixFd(dup()); +} + +#include "dbus_log.h" +void DBus::Type::UnixFd::unmarshall(MessageIStream& stream) +{ + m_fd = ::dup(stream.readUnixFd()); +} + +std::string DBus::Type::UnixFd::toString(const std::string&) const +{ + std::ostringstream oss; + oss << name << " " << fdName() << " (" << m_fd << ")\n"; + return oss.str(); +} + +std::string DBus::Type::UnixFd::asString() const +{ + return std::to_string(m_fd); +} + +std::string DBus::Type::UnixFd::fdName() const +{ + if (m_fd < 0) + return "invalid"; + + char fd_name[512] = "unknown"; + int dirfd = ::open("/proc/self/fd/", O_DIRECTORY|O_PATH); + + if (dirfd != -1) { + ::readlinkat(dirfd, asString().c_str(), fd_name, sizeof(fd_name)); + ::close(dirfd); + } + + return fd_name; +} + +int DBus::Type::UnixFd::dup() const { + return ::dup(m_fd); +} + diff --git a/src/dbus_type_unixfd.h b/src/dbus_type_unixfd.h new file mode 100644 index 0000000..6088d34 --- /dev/null +++ b/src/dbus_type_unixfd.h @@ -0,0 +1,52 @@ +// This file is part of dbus-asio +// Copyright 2022 OpenVPN Inc. +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, version 3, or at your +// option any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The GNU Lesser General Public License version 3 is included in the +// file named COPYING. If you do not have this file see +// . + +#pragma once + +#include "dbus_type.h" + +namespace DBus { + + class Type::UnixFd : public Basic { + public: + UnixFd(); + UnixFd(int fd); + UnixFd(const UnixFd& other); + ~UnixFd() override; + + static constexpr const char *name = "UnixFd"; + static constexpr std::size_t alignment = 4; + static constexpr const char code = 'h'; + + std::string getName() const override { return name; } + std::size_t getAlignment() const override { return alignment; } + std::string getSignature() const override { return std::string(1, code); } + + void marshall(MessageOStream& stream) const override; + void unmarshall(MessageIStream& stream) override; + + std::string toString(const std::string& = "") const override; + std::string asString() const override; + std::string fdName() const; + + int dup() const; + + protected: + int m_fd; + }; + +} // namespace DBus diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 354da3c..5a07d91 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -82,6 +82,11 @@ target_include_directories(dbus_type_uint64_unittest PRIVATE ../extern) target_link_libraries(dbus_type_uint64_unittest LINK_PRIVATE dbus-asio) add_test(NAME run_dbus_type_uint64_unittest COMMAND dbus_type_uint64_unittest) +add_executable(dbus_type_unixfd_unittest dbus_type_unixfd_unittest.cpp test_main.cpp) +target_include_directories(dbus_type_unixfd_unittest PRIVATE ../extern) +target_link_libraries(dbus_type_unixfd_unittest LINK_PRIVATE dbus-asio) +add_test(NAME run_dbus_type_unixfd_unittest COMMAND dbus_type_unixfd_unittest) + add_executable(dbus_type_double_unittest dbus_type_double_unittest.cpp test_main.cpp) target_include_directories(dbus_type_double_unittest PRIVATE ../extern) target_link_libraries(dbus_type_double_unittest LINK_PRIVATE dbus-asio) diff --git a/tests/dbus_messageistream_unittest.cpp b/tests/dbus_messageistream_unittest.cpp index 093607a..7bc488d 100644 --- a/tests/dbus_messageistream_unittest.cpp +++ b/tests/dbus_messageistream_unittest.cpp @@ -32,8 +32,9 @@ namespace test { for (size_t i = 0; i < sizeof(T); i++) { memcpy(&data[i > 0 ? sizeof(T) : 0], &value, sizeof(T)); - MessageIStream leStream(data.data(), data.size(), false); - MessageIStream beStream(data.data(), data.size(), true); + OctetBuffer buf(data.data(), data.size()); + MessageIStream leStream(buf, false); + MessageIStream beStream(buf, true); std::string str; leStream.read(str, i); @@ -52,7 +53,8 @@ namespace test { TEST_CASE("Read byte") { const uint8_t data = 34; - MessageIStream stream(&data, 1, false); + OctetBuffer buf(&data, 1); + MessageIStream stream(buf, false); REQUIRE(stream.read() == 34); CHECK_THROWS(stream.read()); @@ -61,8 +63,9 @@ namespace test { TEST_CASE("Read data") { const uint8_t data[4] = { 34, 35, 36, 37 }; + OctetBuffer buf(data, sizeof(data)); uint8_t readData[3]; - MessageIStream stream(data, 4, false); + MessageIStream stream(buf, false); stream.read(readData, 3); REQUIRE(memcmp(data, readData, 3) == 0); @@ -72,13 +75,14 @@ namespace test { TEST_CASE("Read double") { double value = 0.75483; - MessageIStream leStream((const uint8_t*)&value, sizeof(double), false); + OctetBuffer buf((const uint8_t*)&value, sizeof(double)); + MessageIStream leStream(buf, false); uint64_t swapped_value; memcpy(&swapped_value, &value, sizeof(value)); swapped_value = __bswap_64(swapped_value); - MessageIStream beStream((const uint8_t*)&swapped_value, sizeof(double), - true); + buf = {(const uint8_t*)&swapped_value, sizeof(double)}; + MessageIStream beStream(buf, true); double readValue = 0.0; leStream.read(&readValue); @@ -94,7 +98,8 @@ namespace test { TEST_CASE("Read string") { std::string value("Hello world"); - MessageIStream stream((const uint8_t*)value.data(), value.size(), false); + OctetBuffer buf((const uint8_t*)value.data(), value.size()); + MessageIStream stream(buf, false); std::string readValue = "Test"; stream.read(readValue, value.size()); @@ -106,7 +111,8 @@ namespace test { TEST_CASE("Is empty") { std::string value("Hello world"); - MessageIStream stream((const uint8_t*)value.data(), value.size(), false); + OctetBuffer buf((const uint8_t*)value.data(), value.size()); + MessageIStream stream(buf, false); REQUIRE(stream.empty() == false); diff --git a/tests/dbus_type_array_unittest.cpp b/tests/dbus_type_array_unittest.cpp index 9fbcad3..6998dee 100644 --- a/tests/dbus_type_array_unittest.cpp +++ b/tests/dbus_type_array_unittest.cpp @@ -1,5 +1,6 @@ #include "dbus_messageistream.h" #include "dbus_messageostream.h" +#include "dbus_octetbuffer.h" #include "dbus_messageprotocol.h" #include "dbus_type.h" #include "dbus_type_array.h" @@ -29,8 +30,8 @@ namespace test { // An array of structures Type::Array array("a(i)"); - MessageIStream istream((uint8_t*)stream.data(), stream.size(), - byteOrder != __LITTLE_ENDIAN); + OctetBuffer buf((uint8_t*)stream.data(), stream.size()); + MessageIStream istream(buf, byteOrder != __LITTLE_ENDIAN); array.unmarshall(istream); REQUIRE(array.size() == 2); @@ -172,8 +173,8 @@ namespace test { input.marshall(ostream); Type::Array output(input.getSignature()); - MessageIStream istream((uint8_t*)ostream.data.data(), ostream.data.size(), - false); + OctetBuffer buf((uint8_t*)ostream.data.data(), ostream.data.size()); + MessageIStream istream(buf, false); output.unmarshall(istream); REQUIRE(input.toString() == output.toString()); diff --git a/tests/dbus_type_boolean_unittest.cpp b/tests/dbus_type_boolean_unittest.cpp index f464fb3..0fc1cf2 100644 --- a/tests/dbus_type_boolean_unittest.cpp +++ b/tests/dbus_type_boolean_unittest.cpp @@ -34,8 +34,8 @@ namespace test { stream.writeInt32(true); Type::Boolean boolean; - MessageIStream istream((uint8_t*)stream.data.data(), stream.data.size(), - false); + OctetBuffer buf((uint8_t*)stream.data.data(), stream.data.size()); + MessageIStream istream(buf, false); TestUnmarshallFromMessageIStream(1, boolean, istream); } diff --git a/tests/dbus_type_byte_unittest.cpp b/tests/dbus_type_byte_unittest.cpp index 0f461da..1632e8a 100644 --- a/tests/dbus_type_byte_unittest.cpp +++ b/tests/dbus_type_byte_unittest.cpp @@ -11,7 +11,8 @@ namespace test { { std::basic_string data; data.push_back(65); - MessageIStream istream(data.data(), data.size(), false); + OctetBuffer buf(data.data(), data.size()); + MessageIStream istream(buf, false); Type::Byte byte; byte.unmarshall(istream); @@ -23,8 +24,8 @@ namespace test { MessageOStream stream; stream.writeByte(213); - MessageIStream istream((uint8_t*)stream.data.data(), stream.data.size(), - false); + OctetBuffer buf((uint8_t*)stream.data.data(), stream.data.size()); + MessageIStream istream(buf, false); Type::Byte byte; byte.unmarshall(istream); REQUIRE(byte.asString() == std::to_string(213)); diff --git a/tests/dbus_type_dictentry_unittest.cpp b/tests/dbus_type_dictentry_unittest.cpp index d8bc5b2..92a14bc 100644 --- a/tests/dbus_type_dictentry_unittest.cpp +++ b/tests/dbus_type_dictentry_unittest.cpp @@ -22,8 +22,8 @@ namespace test { const std::string& key, uint32_t value) { Type::DictEntry dictEntry("{su}"); - MessageIStream istream((uint8_t*)stream.data(), stream.size(), - byteOrder != __LITTLE_ENDIAN); + OctetBuffer buf((uint8_t*)stream.data(), stream.size()); + MessageIStream istream(buf, byteOrder != __LITTLE_ENDIAN); dictEntry.unmarshall(istream); std::stringstream ss; @@ -66,8 +66,8 @@ namespace test { const std::pair& value) { Type::DictEntry dictEntry("{i(dy)}"); - MessageIStream istream((uint8_t*)stream.data(), stream.size(), - byteOrder != __LITTLE_ENDIAN); + OctetBuffer buf((uint8_t*)stream.data(), stream.size()); + MessageIStream istream(buf, byteOrder != __LITTLE_ENDIAN); dictEntry.unmarshall(istream); std::stringstream ss; @@ -194,7 +194,8 @@ namespace test { }; Type::DictEntry dictEntry("{vu}"); - MessageIStream istream(encoded.data(), encoded.size(), false); + OctetBuffer buf(encoded.data(), encoded.size()); + MessageIStream istream(buf, false); REQUIRE_THROWS_WITH(dictEntry.unmarshall(istream), "DictEntry key has invalid basic type: v"); } diff --git a/tests/dbus_type_double_unittest.cpp b/tests/dbus_type_double_unittest.cpp index 4a22600..626adaa 100644 --- a/tests/dbus_type_double_unittest.cpp +++ b/tests/dbus_type_double_unittest.cpp @@ -24,7 +24,8 @@ namespace test { if (byteOrder != __BYTE_ORDER) u = __bswap_64(u); #endif - MessageIStream stream((uint8_t*)&u, sizeof(u), byteOrder != __BYTE_ORDER); + OctetBuffer buf((uint8_t*)&u, sizeof(u)); + MessageIStream stream(buf, byteOrder != __BYTE_ORDER); dbusType.unmarshall(stream); REQUIRE(static_cast(dbusType) == value); @@ -49,7 +50,8 @@ namespace test { stream.writeDouble(value); Type::Double dbusType; - MessageIStream istream((uint8_t*)stream.data.data(), stream.size(), false); + OctetBuffer buf((uint8_t*)stream.data.data(), stream.size()); + MessageIStream istream(buf, false); dbusType.unmarshall(istream); REQUIRE(static_cast(dbusType) == value); diff --git a/tests/dbus_type_int16_unittest.cpp b/tests/dbus_type_int16_unittest.cpp index e0ff688..8db227d 100644 --- a/tests/dbus_type_int16_unittest.cpp +++ b/tests/dbus_type_int16_unittest.cpp @@ -29,8 +29,8 @@ namespace test { stream.writeInt16(value); Type::Int16 dbusType; - MessageIStream istream((uint8_t*)stream.data.data(), stream.data.size(), - false); + OctetBuffer buf((uint8_t*)stream.data.data(), stream.data.size()); + MessageIStream istream(buf, false); TestUnmarshallFromMessageIStream(value, dbusType, istream); } diff --git a/tests/dbus_type_int32_unittest.cpp b/tests/dbus_type_int32_unittest.cpp index 29dfac3..d7608d1 100644 --- a/tests/dbus_type_int32_unittest.cpp +++ b/tests/dbus_type_int32_unittest.cpp @@ -29,8 +29,8 @@ namespace test { stream.writeInt32(value); Type::Int32 dbusType; - MessageIStream istream((uint8_t*)stream.data.data(), stream.data.size(), - false); + OctetBuffer buf((uint8_t*)stream.data.data(), stream.data.size()); + MessageIStream istream(buf, false); TestUnmarshallFromMessageIStream(value, dbusType, istream); } diff --git a/tests/dbus_type_int64_unittest.cpp b/tests/dbus_type_int64_unittest.cpp index 0375bc6..8931d73 100644 --- a/tests/dbus_type_int64_unittest.cpp +++ b/tests/dbus_type_int64_unittest.cpp @@ -29,8 +29,8 @@ namespace test { stream.writeInt64(value); Type::Int64 dbusType; - MessageIStream istream((uint8_t*)stream.data.data(), stream.data.size(), - false); + OctetBuffer buf((uint8_t*)stream.data.data(), stream.data.size()); + MessageIStream istream(buf, false); TestUnmarshallFromMessageIStream(value, dbusType, istream); } diff --git a/tests/dbus_type_multiple_unittest.cpp b/tests/dbus_type_multiple_unittest.cpp index 6940785..393a823 100644 --- a/tests/dbus_type_multiple_unittest.cpp +++ b/tests/dbus_type_multiple_unittest.cpp @@ -97,8 +97,8 @@ Array a{i(sa{sa{s(ys)}})} : [ SECTION("Complete") { Type::Array output(input.getSignature()); - MessageIStream istream((uint8_t*)ostream.data.data(), ostream.data.size(), - false); + OctetBuffer buf((uint8_t*)ostream.data.data(), ostream.data.size()); + MessageIStream istream(buf, false); output.unmarshall(istream); REQUIRE(input.toString() == output.toString()); @@ -116,7 +116,8 @@ Array a{i(sa{sa{s(ys)}})} : [ std::string data = ostream.data.substr(0, truncate_at); Type::Array output(input.getSignature()); - MessageIStream istream((uint8_t*)data.data(), data.size(), false); + OctetBuffer buf((uint8_t*)data.data(), data.size()); + MessageIStream istream(buf, false); REQUIRE_THROWS(output.unmarshall(istream)); } } diff --git a/tests/dbus_type_signature_unittest.cpp b/tests/dbus_type_signature_unittest.cpp index fc0debd..b73540d 100644 --- a/tests/dbus_type_signature_unittest.cpp +++ b/tests/dbus_type_signature_unittest.cpp @@ -15,8 +15,8 @@ namespace test { { // An array of structures Type::Signature signature; - MessageIStream istream((uint8_t*)stream.data(), stream.size(), - byteOrder == __BYTE_ORDER); + OctetBuffer buf((uint8_t*)stream.data(), stream.size()); + MessageIStream istream(buf, byteOrder == __BYTE_ORDER); signature.unmarshall(istream); REQUIRE(signature.asString() == str); diff --git a/tests/dbus_type_string_unittest.cpp b/tests/dbus_type_string_unittest.cpp index 27da47a..1a57e7f 100644 --- a/tests/dbus_type_string_unittest.cpp +++ b/tests/dbus_type_string_unittest.cpp @@ -16,8 +16,8 @@ namespace test { { // An array of structures Type::String dbusString; - MessageIStream istream((uint8_t*)stream.data(), stream.size(), - byteOrder != __LITTLE_ENDIAN); + OctetBuffer buf((uint8_t*)stream.data(), stream.size()); + MessageIStream istream(buf, byteOrder != __LITTLE_ENDIAN); dbusString.unmarshall(istream); REQUIRE(dbusString.asString() == str); diff --git a/tests/dbus_type_struct_unittest.cpp b/tests/dbus_type_struct_unittest.cpp index 0075d21..0434e7d 100644 --- a/tests/dbus_type_struct_unittest.cpp +++ b/tests/dbus_type_struct_unittest.cpp @@ -15,8 +15,8 @@ namespace test { { Type::Struct dbusStruct("(uu)"); - MessageIStream istream((uint8_t*)stream.data(), stream.size(), - byteOrder != __LITTLE_ENDIAN); + OctetBuffer buf((uint8_t*)stream.data(), stream.size()); + MessageIStream istream(buf, byteOrder != __LITTLE_ENDIAN); dbusStruct.unmarshall(istream); REQUIRE(dbusStruct.size() == 2); diff --git a/tests/dbus_type_uint16_unittest.cpp b/tests/dbus_type_uint16_unittest.cpp index 5e497f8..bd80313 100644 --- a/tests/dbus_type_uint16_unittest.cpp +++ b/tests/dbus_type_uint16_unittest.cpp @@ -29,8 +29,8 @@ namespace test { stream.writeInt16(value); Type::Uint16 dbusType; - MessageIStream istream((uint8_t*)stream.data.data(), stream.data.size(), - false); + OctetBuffer buf((uint8_t*)stream.data.data(), stream.data.size()); + MessageIStream istream(buf, false); TestUnmarshallFromMessageIStream(value, dbusType, istream); } diff --git a/tests/dbus_type_uint32_unittest.cpp b/tests/dbus_type_uint32_unittest.cpp index 4b023bb..ac15534 100644 --- a/tests/dbus_type_uint32_unittest.cpp +++ b/tests/dbus_type_uint32_unittest.cpp @@ -29,8 +29,8 @@ namespace test { stream.writeInt32(value); Type::Uint32 dbusType; - MessageIStream istream((uint8_t*)stream.data.data(), stream.data.size(), - false); + OctetBuffer buf((uint8_t*)stream.data.data(), stream.data.size()); + MessageIStream istream(buf, false); TestUnmarshallFromMessageIStream(value, dbusType, istream); } diff --git a/tests/dbus_type_uint64_unittest.cpp b/tests/dbus_type_uint64_unittest.cpp index 08b7c4d..34298ee 100644 --- a/tests/dbus_type_uint64_unittest.cpp +++ b/tests/dbus_type_uint64_unittest.cpp @@ -29,8 +29,8 @@ namespace test { stream.writeInt64(value); Type::Uint64 dbusType; - MessageIStream istream((uint8_t*)stream.data.data(), stream.data.size(), - false); + OctetBuffer buf((uint8_t*)stream.data.data(), stream.data.size()); + MessageIStream istream(buf, false); TestUnmarshallFromMessageIStream(value, dbusType, istream); } diff --git a/tests/dbus_type_unixfd_unittest.cpp b/tests/dbus_type_unixfd_unittest.cpp new file mode 100644 index 0000000..bfd8e89 --- /dev/null +++ b/tests/dbus_type_unixfd_unittest.cpp @@ -0,0 +1,47 @@ +#include "dbus_messageistream.h" +#include "dbus_messageostream.h" +#include "dbus_type_unixfd.h" +#include + +#include +#include + +namespace DBus { +namespace test { + +using Catch::Matchers::Equals; + + TEST_CASE("Marshall and unmarshall unix fd") + { + int fd = ::open("/tmp/unixfd_unittest", O_CREAT, S_IRUSR); + CHECK(fd != -1); + + Type::UnixFd unixFdOut(fd); + ::close(fd); // unixFdOut owns a dup(2) of fd at this point + ::unlink("/tmp/unixfd_unittest"); + + MessageOStream ostream; + unixFdOut.marshall(ostream); + REQUIRE(ostream.fds.size() == 1); + + OctetBuffer buf( + reinterpret_cast(ostream.data.data()), + ostream.data.size(), + ostream.fds); + MessageIStream istream(buf, false); + Type::UnixFd unixFdIn; + unixFdIn.unmarshall(istream); + + // UnixFds should reference the same file, but with different fds + REQUIRE_THAT(unixFdOut.fdName(), Equals(unixFdIn.fdName())); + REQUIRE_THAT(unixFdOut.asString(), !Equals(unixFdIn.asString())); + + // UnixFd::dup() should produce yet another fd to the file + int dup = unixFdIn.dup(); + REQUIRE_THAT(std::to_string(dup), !Equals(unixFdIn.asString())); + ::close(dup); + } + + +} // namespace test +} // namespace DBus diff --git a/tests/dbus_type_variant_unittest.cpp b/tests/dbus_type_variant_unittest.cpp index c17474a..701916d 100644 --- a/tests/dbus_type_variant_unittest.cpp +++ b/tests/dbus_type_variant_unittest.cpp @@ -16,8 +16,8 @@ namespace test { uint32_t value) { Type::Variant variant; - MessageIStream istream((uint8_t*)stream.data(), stream.size(), - byteOrder != __LITTLE_ENDIAN); + OctetBuffer buf((uint8_t*)stream.data(), stream.size()); + MessageIStream istream(buf, byteOrder != __LITTLE_ENDIAN); variant.unmarshall(istream); REQUIRE(variant.asString() == std::to_string(value)); @@ -78,8 +78,8 @@ namespace test { input.marshall(ostream); Type::Variant output(input.getSignature()); - MessageIStream istream((uint8_t*)ostream.data.data(), ostream.data.size(), - false); + OctetBuffer buf((uint8_t*)ostream.data.data(), ostream.data.size()); + MessageIStream istream(buf, false); output.unmarshall(istream); REQUIRE(input.toString() == output.toString()); diff --git a/tests/test_unmarshall.h b/tests/test_unmarshall.h index 677fe2e..4a8bacf 100644 --- a/tests/test_unmarshall.h +++ b/tests/test_unmarshall.h @@ -37,7 +37,8 @@ namespace test { std::basic_string data; data.append((uint8_t*)&writeValue, sizeof(T)); - MessageIStream istream(data.data(), data.size(), byteOrder != __BYTE_ORDER); + OctetBuffer buf(data.data(), data.size()); + MessageIStream istream(buf, byteOrder != __BYTE_ORDER); TestUnmarshallFromMessageIStream(value, dbusType, istream); } From cef30ba874654cf2f02a1d79873e2c5b2050c861 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Thu, 14 Apr 2022 00:23:11 +0200 Subject: [PATCH 10/24] overhaul DBus::Message Most of things are now done in the the base type DBus::Message (was: DBus::Message::Base). As a consequence the types representing the four DBus messages hardly have any code left and their implementation was moved in with the rest of the message handling code. The DBus::Message::Header now makes use of the DBus type system and has support for all header fields. It also got a toString() for logging. Same goes for the body, a.k.a parameters, which can now be logged for incoming and outgoing messages using the DBus::Type system. The MethodCallIdentifier and MethodCallParameters types are now simply known as Identifier and Paramaeters, since they are used with other messages than MethodCall as well. MethodCallParametersIn is now the non-const part of the Parameters type. Signed-off-by: Heiko Hund --- CMakeLists.txt | 4 - src/dbus_message.cpp | 516 ++++++++++++++++++++++-------- src/dbus_message.h | 455 ++++++++++++++------------ src/dbus_message_error.cpp | 101 ------ src/dbus_message_methodcall.cpp | 119 ------- src/dbus_message_methodreturn.cpp | 79 ----- src/dbus_message_signal.cpp | 102 ------ 7 files changed, 639 insertions(+), 737 deletions(-) delete mode 100644 src/dbus_message_error.cpp delete mode 100644 src/dbus_message_methodcall.cpp delete mode 100644 src/dbus_message_methodreturn.cpp delete mode 100644 src/dbus_message_signal.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f829036..7b93c79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,12 +50,8 @@ add_library(dbus-asio SHARED src/dbus_log.cpp src/dbus_matchrule.cpp src/dbus_message.cpp - src/dbus_message_error.cpp src/dbus_messageistream.cpp - src/dbus_message_methodcall.cpp - src/dbus_message_methodreturn.cpp src/dbus_messageprotocol.cpp - src/dbus_message_signal.cpp src/dbus_native.cpp src/dbus_native_messages.cpp src/dbus_names.cpp diff --git a/src/dbus_message.cpp b/src/dbus_message.cpp index 425264f..89bd564 100644 --- a/src/dbus_message.cpp +++ b/src/dbus_message.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,8 +16,7 @@ // file named COPYING. If you do not have this file see // . -#include - +#include "dbus_type_any.h" #include "dbus_type_array.h" #include "dbus_type_byte.h" #include "dbus_type_signature.h" @@ -25,230 +25,486 @@ #include "dbus_type_uint32.h" #include "dbus_type_variant.h" +#include "dbus_log.h" +#include "dbus_names.h" #include "dbus_message.h" #include "dbus_messageistream.h" #include "dbus_messageostream.h" -uint32_t DBus::Message::Base::m_SerialCounter = 1; -boost::recursive_mutex DBus::Message::Base::m_SerialCounterMutex; +#include -DBus::Message::Base::Base(uint32_t serial) +// +// Message::Header::Fields +// +DBus::Message::Header::Fields::Fields(const Header& header) + : DBus::Type::Array("a(yv)") { - boost::recursive_mutex::scoped_lock guard(m_SerialCounterMutex); - m_Header.serial = serial ? serial : m_SerialCounter++; + if (!header.destination.empty()) + add(Header::Field::Destination, header.destination); + if (!header.path.empty()) + add(Header::Field::Path, DBus::Type::ObjectPath(header.path)); + if (!header.interface.empty()) + add(Header::Field::Interface, header.interface); + if (!header.member.empty()) + add(Header::Field::Member, header.member); + if (!header.errorName.empty()) + add(Header::Field::ErrorName, header.errorName); + if (header.replySerial != 0) + add(Header::Field::ReplySerial, header.replySerial); + if (!header.sender.empty()) + add(Header::Field::Sender, header.sender); + if (!header.signature.empty()) + add(Header::Field::Signature, header.signature); + if (header.unixFds > 0) + add(Header::Field::UnixFds, header.unixFds); } -DBus::Message::Base::Base(const DBus::Type::Struct& header, - const std::string& body) +std::size_t +DBus::Message::Header::Fields::add(Field type, const DBus::Type::Any& value) { - // Capture the basic parameters - bool isLittleEndian = Type::asByte(header[0]) == 'l' ? true : false; - m_Header.type = Type::asByte(header[1]); - m_Header.flags = Type::asByte(header[2]); - m_Header.serial = Type::asUint32(header[5]); + DBus::Type::Struct field; + field.add(DBus::Type::Byte(type)); + field.add(DBus::Type::Variant(value)); + return DBus::Type::Array::add(field); +} - std::string signature; - const DBus::Type::Array& fields = DBus::Type::refArray(header[6]); - for (auto it : fields.getContents()) { - const DBus::Type::Struct& headerField = DBus::Type::refStruct(it); - uint8_t type = Type::asByte(headerField[0]); - switch (type) { - case Header::HEADER_PATH: - m_Header.path = DBus::Type::asString(headerField[1]); + + +// +// Message::Header +// +DBus::Message::Header::Header(OctetBuffer& message) +{ + size = getSize(message); + endianness = Message::endianness(message); + MessageIStream istream(message, swapRequired(endianness)); + + DBus::Type::Struct header("(yyyyuua(yv))"); + header.unmarshall(istream); + + // Capture the basic parameters + type = static_cast(DBus::Type::asByte(header[1])); + flags = DBus::Type::asByte(header[2]); + bodySize = DBus::Type::asUint32(header[4]); + serial = DBus::Type::asUint32(header[5]); + + for (auto& elem : DBus::Type::refArray(header[6])) { + const DBus::Type::Struct& field = DBus::Type::refStruct(elem); + Header::Field name = static_cast(DBus::Type::asByte(field[0])); + switch (name) { + case Header::Field::Path: + path = DBus::Type::asString(field[1]); break; - case Header::HEADER_INTERFACE: - m_Header.interface = DBus::Type::asString(headerField[1]); + case Header::Field::Interface: + interface = DBus::Type::asString(field[1]); break; - case Header::HEADER_MEMBER: - m_Header.member = DBus::Type::asString(headerField[1]); + case Header::Field::Member: + member = DBus::Type::asString(field[1]); break; - case Header::HEADER_DESTINATION: - m_Header.destination = DBus::Type::asString(headerField[1]); + case Header::Field::ErrorName: + errorName = DBus::Type::asString(field[1]); break; - case Header::HEADER_SENDER: - m_Header.sender = DBus::Type::asString(headerField[1]); + case Header::Field::ReplySerial: + replySerial = DBus::Type::asUint32(field[1]); break; - case Header::HEADER_SIGNATURE: - signature = DBus::Type::asString(headerField[1]); + case Header::Field::Destination: + destination = DBus::Type::asString(field[1]); break; - case Header::HEADER_REPLY_SERIAL: - m_Header.replySerial = DBus::Type::asUint32(headerField[1]); + case Header::Field::Sender: + sender = DBus::Type::asString(field[1]); + break; + case Header::Field::Signature: + signature = DBus::Type::refSignature(field[1]); + break; + case Header::Field::UnixFds: + unixFds = DBus::Type::asUint32(field[1]); break; - default: break; } } - // Empty bodies have no parameters - if (body.size()) { - parseParameters(isLittleEndian, body, signature); - } -} - -void DBus::Message::Base::parseParameters(bool isLittleEndian, - const std::string& bodydata, - const std::string& signature) -{ - DBus::Type::Struct parameter_fields; - - parameter_fields.setSignature("(" + signature + ")"); - - MessageIStream stream((uint8_t*)bodydata.data(), bodydata.size(), - isLittleEndian ? __BYTE_ORDER != __LITTLE_ENDIAN - : __BYTE_ORDER != __BIG_ENDIAN); - parameter_fields.unmarshall(stream); - - size_t count = parameter_fields.getEntries(); - for (size_t i = 0; i < count; ++i) { - m_Parameters.add(parameter_fields[i]); - } + message.remove_prefix(size); } -std::string -DBus::Message::Base::marshallMessage(const DBus::Type::Array& array) const +void DBus::Message::Header::marshall(MessageOStream& stream) { - MessageOStream header; - MessageOStream body; - - /* - The signature of the header is: - - "yyyyuua(yv)" - - Written out more readably, this is: - - BYTE, BYTE, BYTE, BYTE, UINT32, UINT32, ARRAY of STRUCT of - (BYTE,VARIANT) - */ + // The signature of the header is: + // "yyyyuua(yv)" + // + // Written out more readably, this is: + // BYTE, BYTE, BYTE, BYTE, UINT32, UINT32, ARRAY of STRUCT of (BYTE,VARIANT) // 1st BYTE // Endianness flag; ASCII 'l' for little-endian or ASCII 'B' for big-endian. // Both header and body are in this endianness. - header.writeByte(__BYTE_ORDER == __LITTLE_ENDIAN ? 'l' : 'B'); + stream.writeByte(__BYTE_ORDER == __LITTLE_ENDIAN ? 'l' : 'B'); // 2nd BYTE // Message type. Unknown types must be ignored. Currently-defined types are // described below. - header.writeByte(m_Header.type); + stream.writeByte(static_cast(type)); // 3rd BYTE // Bitwise OR of flags. Unknown flags must be ignored. Currently-defined flags // are described below. - header.writeByte(m_Header.flags); + stream.writeByte(flags); // 4th BYTE // Major protocol version of the sending application. If the major protocol // version of the receiving application does not match, the applications will // not be able to communicate and the D-Bus connection must be disconnected. // The major protocol version for this version of the specification is 1. - header.writeByte(1); + stream.writeByte(1); // 1st UINT32 // Length in bytes of the message body, starting from the end of the header. // The header ends after its alignment padding to an 8-boundary. - m_Parameters.marshallData(body); - - header.writeUint32(body.data.length()); + stream.writeUint32(bodySize); // 2nd UINT32 // The serial of this message, used as a cookie by the sender to identify the // reply corresponding to this request. This must not be zero. - header.writeUint32(m_Header.serial); + stream.writeUint32(serial); - // Header fields length - MessageOStream header_fields; + // ARRAY of STRUCT of(BYTE,VARIANT) i.e. the header fields + Fields headerFields(*this); + headerFields.marshall(stream); - // It appears, from studying the packet data, that the header field array is - // not marshalled with the length of the array data in bytes, as suggested in - // the spec. Presumably this is because the header fields size can be - // determined from the packet_size - body_size - standard_header_size - array.marshallContents(header_fields); - header.writeUint32((int32_t)header_fields.size()); + // Both header & header_fields constitute the header, which must end of an + // 8 byte boundary. (See the phrase: "The header ends after its alignment padding to + // an 8-boundary.") Therefore we add the padding here. + stream.pad8(); +} - // End of header preparation - // Both header & header_fields constitute the header, which must end of an 8 - // boundary. (See the phrase: "The header ends after its alignment padding to - // an 8-boundary.") Therefore we add the padding here. - MessageOStream packet; - packet.write(header); - packet.write(header_fields); +std::string +DBus::Message::Header::toString(const std::string& prefix) const +{ + std::ostringstream oss; + + oss << prefix << "Endianness: " << (endianness == Endian::Big ? "big" : "little") << '\n'; + oss << prefix << "Type: " << typeString(type) << '\n'; + oss << prefix << "Flags: " << flagString(flags) << '\n'; + oss << prefix << "Version: 1" << '\n'; + oss << prefix << "Body length: " << bodySize << '\n'; + oss << prefix << "Serial: #" << serial << '\n'; + + if (replySerial) + oss << prefix << "Reply Serial: #" << replySerial << '\n'; + if (!sender.empty()) + oss << prefix << "Sender: " << sender << '\n'; + if (!destination.empty()) + oss << prefix << "Destination: " << destination << '\n'; + if (!path.empty()) + oss << prefix << "Path: " << path << '\n'; + if (!interface.empty()) + oss << prefix << "Interface: " << interface << '\n'; + if (!member.empty()) + oss << prefix << "Member: " << member << '\n'; + if (!errorName.empty()) + oss << prefix << "Error Name: " << errorName << '\n'; + if (!signature.empty()) + oss << prefix << "Signature: " << signature.asString() << '\n'; + if (unixFds) + oss << prefix << "Unix FDs: " << unixFds << '\n'; + + return oss.str(); +} - // The body is required to start on an 8-boundary, but not end on one. - packet.pad8(); - packet.write(body); +std::size_t +DBus::Message::Header::getSize(const OctetBuffer& message) +{ + std::size_t size = Message::Header::MinimumSize; + if (message.size() < size) + return 0; // TODO: throw instead? + + // Add size of the header fields array + const Endian endianness = Message::endianness(message); + size += correctEndianess(endianness, *(uint32_t*)(message.data() + 12)); + // Add padding to an 8 byte boundary + size += (size % 8 == 0) ? 0 : 8 - (size % 8); + + if (size > Message::Header::MaximumSize) + throw std::out_of_range("DBus message error: maximum header size exceeded"); - return packet.data; + return size; } +std::size_t +DBus::Message::Header::getMessageSize(const OctetBuffer& message) +{ + if (message.size() < Message::Header::MinimumSize) + return 0; // TODO: throw instead? + + const Endian endianness = Message::endianness(message); + std::size_t body_size = correctEndianess(endianness, *(uint32_t*)(message.data() + 4)); + std::size_t msg_size = getSize(message) + body_size; + + if (msg_size > Message::MaximumSize) + throw std::out_of_range("DBus message error: maximum message size exceeded"); + + return msg_size; +} + + + // -// Parameters +// Message::Identifier +// +DBus::Message::Identifier::Identifier(const ObjectPath& path, const InterfaceName& interface, + const MemberName& member) + : path(path) + , interface(interface) + , member(member) +{} + + + +// +// Message::Parameters // std::string -DBus::Message::MethodCallParameters::getMarshallingSignature() const +DBus::Message::Parameters::getSignature() const { std::string signature; - for (auto it : m_Contents.m_TypeList) { - signature += DBus::Type::getMarshallingSignature(it); + for (const auto& type : m_parameters) { + signature += type.getSignature(); } return signature; } -void DBus::Message::MethodCallParameters::marshallData( +void DBus::Message::Parameters::marshall( MessageOStream& stream) const { - for (auto it : m_Contents.m_TypeList) { - DBus::Type::marshallData(it, stream); + for (const auto& type : m_parameters) { + type.marshall(stream); + } +} + +size_t DBus::Message::Parameters::getParameterCount() const +{ + return m_parameters.size(); +} + +const DBus::Type::Any& +DBus::Message::Parameters::getParameter(size_t idx) const +{ + return m_parameters[idx]; +} + +std::string +DBus::Message::Parameters::toString(const std::string& prefix) const +{ + std::ostringstream oss; + for (const auto& param : m_parameters) { + oss << prefix << param.toString(prefix); } + return oss.str(); } -size_t DBus::Message::MethodCallParameters::getParameterCount() const +DBus::Message::Parameters::Parameters( + const DBus::Type::Any& v) + : m_parameters({v}) +{} + +DBus::Message::Parameters::Parameters( + const DBus::Type::Any& v1, const DBus::Type::Any& v2) + : m_parameters({v1, v2}) +{} + +void DBus::Message::Parameters::add(const DBus::Type::Any& value) { - return m_Contents.m_TypeList.size(); + m_parameters.push_back(value); } -const DBus::Type::Generic& -DBus::Message::MethodCallParameters::getParameter(size_t idx) const + + +// +// Message +// +DBus::Message::Message(const Header& header, OctetBuffer& body) + : m_Header(header) { - return m_Contents.m_TypeList[idx]; + // Empty bodies have no parameters + if (body.size()) { + unmarshallBody(header.endianness, header.signature, body); + } + if (Log::isActive(Log::TRACE)) { + std::string prefix = " "; + Log::write(Log::TRACE, "DBus :: Message : Header :\n%s", + header.toString(prefix).c_str()); + if (m_Header.bodySize) + Log::write(Log::TRACE, "DBus :: Message : Data :\n%s\n", + m_Parameters.toString(prefix).c_str()); + } } -DBus::Message::MethodCallParametersIn::MethodCallParametersIn( - const Type::Generic& v) +void DBus::Message::unmarshallBody( + Endian endianness, + const DBus::Type::Signature& signature, + OctetBuffer& body) { - add(v); + MessageIStream stream(body, swapRequired(endianness)); + DBus::Type::Struct parameter_fields(signature); + parameter_fields.unmarshall(stream); + + size_t count = parameter_fields.size(); + for (size_t i = 0; i < count; ++i) { + m_Parameters.add(parameter_fields[i]); + } } -DBus::Message::MethodCallParametersIn::MethodCallParametersIn( - const std::string& v) +DBus::MessageOStream +DBus::Message::marshall(std::uint32_t serial) const { - add(v); + MessageOStream body; + m_Parameters.marshall(body); + + Header header = m_Header; + MessageOStream headerData; + header.serial = serial; + header.signature = m_Parameters.getSignature(); + header.bodySize = body.data.size(); + header.unixFds = body.fds.size(); + header.marshall(headerData); + + MessageOStream packet; + packet.write(headerData); + packet.write(body); + + if (Log::isActive(Log::TRACE)) { + std::string prefix = " "; + Log::write(Log::TRACE, "DBus :: Message : Header :\n%s", + header.toString(prefix).c_str()); + if (header.bodySize) + Log::write(Log::TRACE, "DBus :: Message : Data :\n%s", + m_Parameters.toString(prefix).c_str()); + } + + return packet; } -DBus::Message::MethodCallParametersIn::MethodCallParametersIn( - const std::string& v1, uint32_t v2) + + +// +// Message::MethodCall +// +DBus::Message::MethodCall::MethodCall(const Message::Header& header, + OctetBuffer& body) + : Message(header, body) +{} + +DBus::Message::MethodCall::MethodCall( + const BusName& destination, + const Identifier& name, + const Parameters& params, + uint32_t flags) { - add(v1); - add(v2); + + if (flags & Message::Flag::AllowInteractiveAuthorization) { + DBus::Log::write(Log::ERROR, + "DBus :: ALLOW_INTERACTIVE_AUTHORIZATION is not supported."); + flags &= ~Message::Flag::AllowInteractiveAuthorization; + } + // Ignore any extraneous flags, also. + flags &= Message::Flag::_Mask; + + m_Header.flags = flags; + m_Header.type = Message::Type::MethodCall; + m_Header.destination = destination; + m_Header.path = name.path; + m_Header.interface = name.interface; + m_Header.member = name.member; + m_Parameters = params; } -void DBus::Message::MethodCallParametersIn::add(const Type::Generic& value) + + +// +// Message::MethodReturn +// +DBus::Message::MethodReturn::MethodReturn( + const BusName& destination, + uint32_t replySerial) { - m_Contents.m_TypeList.push_back(value); + m_Header.flags = Flag::NoReplyExpected; + m_Header.type = Message::Type::MethodReturn; + m_Header.destination = destination; + m_Header.replySerial = replySerial; } -void DBus::Message::MethodCallParametersIn::add(uint8_t value) +DBus::Message::MethodReturn::MethodReturn(const Message::Header& header, + OctetBuffer& body) + : Message(header, body) +{} + + + +// +// Message::Error +// +DBus::Message::Error::Error( + const BusName& destination, + uint32_t replySerial, + const ErrorName& name, + const std::string& message) { - add(DBus::Type::Byte(value)); + m_Header.flags = Flag::NoReplyExpected; + m_Header.type = Message::Type::Error; + m_Header.destination = destination; + m_Header.replySerial = replySerial; + m_Header.errorName = name; + m_Parameters.add(message); } -void DBus::Message::MethodCallParametersIn::add(uint32_t value) +DBus::Message::Error::Error(const DBus::Message::Header& header, + OctetBuffer& body) + : Message(header, body) { - add(DBus::Type::Uint32(value)); + Log::write(Log::WARNING, + "DBus :: Error : received as reply to message #%d : %s\n", + getReplySerial(), getMessage().c_str()); } -void DBus::Message::MethodCallParametersIn::add(const std::string& value) +std::string DBus::Message::Error::getName() const { - add(DBus::Type::String(value)); + return getErrorName(); } + +std::string DBus::Message::Error::getMessage() const +{ + if (m_Parameters.getParameterCount() == 0) + return ""; + return DBus::Type::asString(m_Parameters.getParameter(0)); +} + + + +// +// Message::Signal +// +DBus::Message::Signal::Signal( + const Message::Identifier& name) +{ + m_Header.flags = Flag::NoReplyExpected; + m_Header.type = Message::Type::Signal; + m_Header.path = name.path; + m_Header.interface = name.interface; + m_Header.member = name.member; +} + +DBus::Message::Signal::Signal( + const BusName& destination, + const Message::Identifier& name) + : Signal(name) +{ + m_Header.destination = destination; +} + +DBus::Message::Signal::Signal(const Message::Header& header, + OctetBuffer& body) + : Message(header, body) +{} + + diff --git a/src/dbus_message.h b/src/dbus_message.h index 1c5e865..989da6f 100644 --- a/src/dbus_message.h +++ b/src/dbus_message.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,243 +16,293 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_MESSAGE -#define DBUS_MESSAGE +#pragma once -#include "dbus_type.h" -#include +#include "dbus_type_array.h" +#include "dbus_octetbuffer.h" #include +#include + +#include namespace DBus { -class MessageOStream; -namespace Message { +struct BusName; +struct ErrorName; +struct ObjectPath; +struct MemberName; +struct InterfaceName; +class MessageOStream; - class MethodCallIdentifier { - public: - MethodCallIdentifier(const std::string& object, const std::string& interface, - const std::string& method) - : m_Object(object) - , m_Interface(interface) - , m_Method(method) - { - } +class Message { +public: + static constexpr std::size_t MaximumSize = 134217728; /*128 MiB*/ - std::string m_Object; - std::string m_Interface; - std::string m_Method; + enum class Type { + Invalid = 0, + MethodCall = 1, + MethodReturn = 2, + Error = 3, + Signal = 4 }; - // + static std::string typeString(Type t) { + switch (t) { + case Type::MethodCall: return "MethodCall"; + case Type::MethodReturn: return "MethodReturn"; + case Type::Error: return "Error"; + case Type::Signal: return "Signal"; + default: return "INVALID"; + } + } + + class MethodCall; + class MethodReturn; + class Error; + class Signal; + + enum Flag { + None = 0, + NoReplyExpected = 0x01, + NoAutoStart = 0x02, + AllowInteractiveAuthorization = 0x04, + _Mask = 0x07, + }; - class MethodCallParameters { - public: - std::string getMarshallingSignature() const; - size_t getParameterCount() const; - void marshallData(MessageOStream& stream) const; + static std::string flagString(uint8_t flags) { + if (flags == 0) + return "None"; + std::ostringstream oss; + if (flags & NoReplyExpected) + oss << "NO_REPLY_EXPECTED "; + if (flags & NoAutoStart) + oss << "NO_AUTO_START "; + if (flags & AllowInteractiveAuthorization) + oss << "ALLOW_INTERACTIVE_AUTHORIZATION "; + return oss.str(); + } + + enum class Endian { Little, Big }; + + inline static Endian endianness(const OctetBuffer& message) + { + if (message[0] == 'l') + return Endian::Little; + else if (message[0] == 'B') + return Endian::Big; + else + throw std::runtime_error("message endian marker invalid"); + } + + inline static bool swapRequired(const Endian endian) + { + return (endian == Endian::Little && __BYTE_ORDER != __LITTLE_ENDIAN) + || (endian == Endian::Big && __BYTE_ORDER != __BIG_ENDIAN); + } + + inline static uint32_t correctEndianess(const Endian endian, uint32_t value) + { + return swapRequired(endian) ? bswap_32(value): value; + } + + struct Header { + // Initial header consists of byte, byte, byte, byte, uint32_t, uint32_t + // the next element is the size of the array of field info data + // making a total of 16 bytes + static constexpr std::size_t MinimumSize = 16; + static constexpr std::size_t MaximumSize = MinimumSize + DBus::Type::Array::MaximumSize; + + static std::size_t getSize(const OctetBuffer& message); + static std::size_t getMessageSize(const OctetBuffer& message); + + enum Field { + Invalid = 0, + // The object to send a call to, or the object a signal is emitted from. + Path = 1, + // The interface to invoke a method call on, or that a signal is emitted from. + Interface = 2, + // The member, either the method name or signal name. + Member = 3, + // The name of the error that occurred, for errors. + ErrorName = 4, + // The serial number of the message this message is a reply to. + ReplySerial = 5, + // The name of the connection this message is intended for. + Destination = 6, + // Unique name of the sending connection. + Sender = 7, + // The signature of the message body. + Signature = 8, + // The number of Unix file descriptors that accompany the message. + UnixFds = 9, + }; + + struct Fields : public DBus::Type::Array + { + Fields(const Header& header); + std::size_t add(Field type, const DBus::Type::Any& value); + }; - const Type::Generic& getParameter(size_t idx) const; + Header() = default; + Header(OctetBuffer& buffer); - protected: - Type::CompositeBlock m_Contents; - }; + void marshall(MessageOStream& stream); - class MethodCallParametersIn : public MethodCallParameters { - public: - // We have a series of ctor's to permit the basic prototypes to be declared - // inline, with MethodCallParametersIn("param") etc. Everyone else will need - // to use a temporary variable and add() - MethodCallParametersIn() {} - MethodCallParametersIn(const Type::Generic& v); - MethodCallParametersIn(const std::string& v); - MethodCallParametersIn(const std::string& v1, uint32_t v2); + std::string getFullName() const { + return interface + '.' + member; + } - void add(const Type::Generic& value); + std::string toString(const std::string& prefix = "") const; - void add(uint8_t value); - void add(uint32_t value); - void add(const std::string& value); - }; + std::size_t size = 0; - class Header { - public: - typedef enum { - TYPE_INVALID = 0, - TYPE_METHOD_CALL = 1, - TYPE_METHOD_RETURN = 2, - TYPE_ERROR = 3, - TYPE_SIGNAL = 4, - } Type; - - typedef enum { - FLAGS_NONE = 0, - FLAGS_NO_REPLY_EXPECTED = 0x01, - FLAGS_NO_AUTO_START = 0x02, - FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION = 0x04, - // - FLAGS_MASK = 0x07, - } Flags; - - enum { - HEADER_INVALID, - HEADER_PATH, // The object to send a call to, or the object a signal is - // emitted from. - HEADER_INTERFACE, // The interface to invoke a method call on, or that a - // signal is emitted from. Optional for method calls, - // required for signals. - HEADER_MEMBER, // The member, either the method name or signal name. - HEADER_ERROR_NAME, // The name of the error that occurred, for errors - HEADER_REPLY_SERIAL, // The serial number of the message this message is a - // reply to. - HEADER_DESTINATION, // The name of the connection this message is intended - // for. - HEADER_SENDER, // Unique name of the sending connection. - HEADER_SIGNATURE, // The signature of the message body. If omitted, it is - // assumed to be the empty signature "" (i.e. the body - // must be 0-length). - HEADER_UNIX_FDS, // The number of Unix file descriptors that accompany the - // message. - // - HEADER_FIELD_COUNT // = HEADER_UNIX_FDS+1 - } HeaderFields; - - uint8_t flags; - uint8_t type; - uint32_t serial; - uint32_t replySerial; + Endian endianness; + Type type = Type::Invalid; + uint8_t flags = 0; + uint32_t bodySize = 0; + uint32_t serial = 0; + // Header fields std::string path; std::string interface; std::string member; + std::string errorName; + uint32_t replySerial = 0; std::string destination; + DBus::Type::Signature signature; std::string sender; + uint32_t unixFds = 0; }; - class Base { - public: - Base(uint32_t serial = 0); - Base(const DBus::Type::Struct& header, const std::string& body); - - uint32_t getSerial() const { return m_Header.serial; } - - uint32_t getReplySerial() const { return m_Header.replySerial; } - - std::string getHeaderPath() const { return m_Header.path; } - - std::string getHeaderInterface() const { return m_Header.interface; } - - std::string getHeaderMember() const { return m_Header.member; } - - std::string getHeaderSender() const { return m_Header.sender; } - - std::string getHeaderDestination() const { return m_Header.destination; } - - const Type::Generic& getParameter(size_t idx) const - { - return m_Parameters.getParameter(idx); - } + // Tuple of PATH, INTERFACE and MEMBER for MethodCall and Signal messages + struct Identifier { + Identifier(const ObjectPath& path, const InterfaceName& interface, + const MemberName& member); - const size_t getParameterCount() const - { - return m_Parameters.getParameterCount(); - } - - const bool isReplyExpected() const - { - return !(m_Header.flags & DBus::Message::Header::FLAGS_NO_REPLY_EXPECTED) - ? true - : false; - } - - protected: - std::string marshallMessage(const DBus::Type::Array& header_fields) const; - - Header m_Header; - MethodCallParametersIn m_Parameters; - - private: - static uint32_t m_SerialCounter; - static boost::recursive_mutex m_SerialCounterMutex; - - void parseParameters(bool isLittleEndian, const std::string& bodydata, - const std::string& signature); - }; - - class MethodCall : public Message::Base { - public: - // This is for outgoing calls - MethodCall(const MethodCallIdentifier& name, - const MethodCallParametersIn& params = MethodCallParametersIn(), - uint32_t flags = 0); - // This is for receeiving method calls - MethodCall(const DBus::Type::Struct& header, const std::string& body); - - std::string getFullName() const; - std::string getObject() const; - std::string getInterface() const; - std::string getMethod() const; - - std::string marshall(const std::string& destination) const; - - private: - MethodCallIdentifier m_Name; + std::string path; + std::string interface; + std::string member; }; - class MethodReturn : public Message::Base { + class Parameters { public: - // This is for sending outgoing calls - MethodReturn(uint32_t serial); - // This is for receeiving method calls - MethodReturn(const DBus::Type::Struct& header, const std::string& body); - - void addParameter(const Type::Generic& value); - - std::string marshall(const std::string& destination) const; + // We have a series of ctor's to permit the basic prototypes to be declared + // inline, with Parameters("param") etc. Everyone else will need + // to use a temporary variable and add() + Parameters() = default; + Parameters(const DBus::Type::Any& v); + Parameters(const DBus::Type::Any& v1, const DBus::Type::Any& v2); - uint32_t m_SerialReplyingTo; - }; + void add(const DBus::Type::Any& value); - class Error : public Message::Base { - public: - // This is for outgoing calls - Error(uint32_t serial, const std::string& name, const std::string& message); - // This is for receeiving method calls - Error(const DBus::Type::Struct& header, const std::string& body); + size_t getParameterCount() const; + const DBus::Type::Any& getParameter(size_t idx) const; - uint32_t getSerialOfReply() const; - std::string getMessage() const; + std::string getSignature() const; + void marshall(MessageOStream& stream) const; - std::string marshall(const std::string& destination) const; + std::string toString(const std::string& prefix = "") const; - private: - uint32_t m_SerialReplyingTo; // NOTE: Means the query one, on rcvr side - std::string m_Errorname; // e.g. biz.brightsign.Error.InvalidParameters - std::string m_Message; // i.e. the user text to display + protected: + std::vector m_parameters; }; - class Signal : public Base { - public: - // This is for outgoing calls - Signal(const MethodCallIdentifier& name); - // This is for receeiving method calls - Signal(const DBus::Type::Struct& header, const std::string& body); - - void addParameter(const Type::Generic& value); + Message() = default; + Message(const Header& header, OctetBuffer& body); + + explicit operator bool() const { return m_Header.type != Type::Invalid; } + + // Getters for header information + uint32_t getSerial() const { return m_Header.serial; } + uint32_t getReplySerial() const { return m_Header.replySerial; } + uint32_t getUnixFdCount() const { return m_Header.unixFds; } + std::string getPath() const { return m_Header.path; } + std::string getMember() const { return m_Header.member; } + std::string getSender() const { return m_Header.sender; } + std::string getSignature() const { return m_Header.signature.asString(); } + std::string getInterface() const { return m_Header.interface; } + std::string getErrorName() const { return m_Header.errorName; } + std::string getDestination() const { return m_Header.destination; } + + void addParameter(const DBus::Type::Any& value) + { + m_Parameters.add(value); + } + + const DBus::Type::Any& getParameter(size_t idx) const + { + return m_Parameters.getParameter(idx); + } + + size_t getParameterCount() const + { + return m_Parameters.getParameterCount(); + } + + bool isReplyExpected() const + { + return (m_Header.flags & Flag::NoReplyExpected) == 0; + } + + MessageOStream marshall(std::uint32_t serial) const; + +protected: + void unmarshallBody( + Endian endianness, + const DBus::Type::Signature& signature, + OctetBuffer& bodydata); + + Header m_Header; + Parameters m_Parameters; +}; + +class Message::MethodCall : public Message { +public: + MethodCall() = default; + // This is for outgoing method calls + MethodCall( + const BusName& destination, const Identifier& name, + const Parameters& params = Parameters(), uint32_t flags = 0); + // This is for receiving method calls + MethodCall(const Header& header, OctetBuffer& body); + + inline std::string getFullName() const { return m_Header.getFullName(); } + inline std::string getObject() const { return getPath(); } + inline std::string getMethod() const { return getMember(); } +}; + +class Message::MethodReturn : public Message { +public: + MethodReturn() = default; + // This is for sending outgoing replies + MethodReturn(const BusName& destination, uint32_t replySerial); + // This is for receiving method returns + MethodReturn(const Header& header, OctetBuffer& body); +}; + +class Message::Error : public Message { +public: + Error() = default; + // This is for outgoing Errors + Error(const BusName & destination, uint32_t replySerial, + const ErrorName& name, const std::string& message); + // This is for receiving Errors + Error(const Header& header, OctetBuffer& body); + + std::string getName() const; + std::string getMessage() const; +}; + +class Message::Signal : public Message { +public: + Signal() = default; + // This is for outgoing broadcast signals + Signal(const Identifier& name); + // This is for outgoing unicast signals + Signal(const BusName& destination, const Identifier& name); + // This is for receiving signals + Signal(const Header& header, OctetBuffer& body); +}; - std::string marshall(const std::string& destination) const; - private: - MethodCallIdentifier m_SignalName; - }; - - typedef std::function - CallbackFunctionMethodCall; - typedef std::function - CallbackFunctionMethodReturn; - typedef std::function CallbackFunctionError; - typedef std::function - CallbackFunctionSignal; -} // namespace Message } // namespace DBus - -#endif // DBUS_MESSAGE diff --git a/src/dbus_message_error.cpp b/src/dbus_message_error.cpp deleted file mode 100644 index 211c5a2..0000000 --- a/src/dbus_message_error.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . - -#include "dbus_log.h" - -#include "dbus_message.h" -#include "dbus_messageistream.h" -#include "dbus_type_array.h" -#include "dbus_type_byte.h" -#include "dbus_type_signature.h" -#include "dbus_type_string.h" -#include "dbus_type_struct.h" -#include "dbus_type_uint32.h" -#include "dbus_type_variant.h" - -DBus::Message::Error::Error(uint32_t serial, const std::string& name, - const std::string& message) - : Message::Base() - , m_SerialReplyingTo(serial) - , m_Errorname(name) - , m_Message(message) -{ - m_Header.flags = 0; - m_Header.type = Message::Header::TYPE_ERROR; - // - m_Parameters.add(DBus::Type::String(m_Message)); -} - -DBus::Message::Error::Error(const DBus::Type::Struct& header, - const std::string& body) - : Message::Base(header, body) -{ - const bool isLittleEndian = Type::asByte(header[0]) == 'l' ? true : false; - - DBus::Type::Struct dest; - dest.setSignature("(s)"); - - MessageIStream stream((uint8_t*)body.data(), body.size(), - isLittleEndian ? __BYTE_ORDER != __LITTLE_ENDIAN - : __BYTE_ORDER != __BIG_ENDIAN); - dest.unmarshall(stream); - - const std::string m_Message = DBus::Type::asString(dest[0]); - m_SerialReplyingTo = getReplySerial(); - - Log::write(Log::WARNING, - "DBus :: Received an error from message serial #%d : %s\n", - m_SerialReplyingTo, m_Message.c_str()); -} - -uint32_t DBus::Message::Error::getSerialOfReply() const -{ - return m_SerialReplyingTo; -} - -std::string DBus::Message::Error::getMessage() const { return m_Message; } - -std::string -DBus::Message::Error::marshall(const std::string& destination) const -{ - DBus::Type::Array array; - DBus::Type::Struct sDestination; - sDestination.add(DBus::Type::Byte(DBus::Message::Header::HEADER_DESTINATION)); - sDestination.add(DBus::Type::Variant(DBus::Type::String(destination))); - array.add(sDestination); - - DBus::Type::Struct sErrorName; - sErrorName.add(DBus::Type::Byte(DBus::Message::Header::HEADER_ERROR_NAME)); - sErrorName.add(DBus::Type::Variant(DBus::Type::String(m_Errorname))); - array.add(sErrorName); - - DBus::Type::Struct sReplySerial; - sReplySerial.add( - DBus::Type::Byte(DBus::Message::Header::HEADER_REPLY_SERIAL)); - sReplySerial.add(DBus::Type::Variant(DBus::Type::Uint32(m_SerialReplyingTo))); - array.add(sReplySerial); - - if (m_Parameters.getParameterCount()) { - DBus::Type::Struct sSignature; - sSignature.add(DBus::Type::Byte(DBus::Message::Header::HEADER_SIGNATURE)); - sSignature.add(DBus::Type::Variant( - DBus::Type::Signature(m_Parameters.getMarshallingSignature()))); - array.add(sSignature); - } - - return marshallMessage(array); -} diff --git a/src/dbus_message_methodcall.cpp b/src/dbus_message_methodcall.cpp deleted file mode 100644 index 8f4408a..0000000 --- a/src/dbus_message_methodcall.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . - -#include "dbus_log.h" -#include "dbus_type_array.h" -#include "dbus_type_byte.h" -#include "dbus_type_objectpath.h" -#include "dbus_type_signature.h" -#include "dbus_type_string.h" -#include "dbus_type_struct.h" -#include "dbus_type_variant.h" - -#include "dbus_message.h" - -DBus::Message::MethodCall::MethodCall(const DBus::Type::Struct& header, - const std::string& body) - : Message::Base(header, body) - , m_Name(m_Header.path, m_Header.interface, m_Header.member) -{ - m_Header.flags = 0; - m_Header.type = Message::Header::TYPE_METHOD_CALL; -} - -DBus::Message::MethodCall::MethodCall(const Message::MethodCallIdentifier& name, - const MethodCallParametersIn& params, - uint32_t flags) - : Message::Base() - , m_Name(name) -{ - - if (flags & Message::Header::FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION) { - DBus::Log::write( - Log::ERROR, - "DBus :: ALLOW_INTERACTIVE_AUTHORIZATION is not yet supported."); - flags &= ~Message::Header::FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION; - } - // Ignore any extraneous flags, also. - flags &= Message::Header::FLAGS_MASK; - - m_Header.flags = flags; - m_Header.type = Message::Header::TYPE_METHOD_CALL; - m_Parameters = params; -} - -std::string -DBus::Message::MethodCall::marshall(const std::string& destination) const -{ - - // ARRAY of STRUCT of (BYTE,VARIANT) - // An array of zero or more header fields where the byte is the field code, - // and the variant is the field m_Value. The message type determines which - // fields are required. - DBus::Type::Array array; - DBus::Type::Struct sPath; - sPath.add(DBus::Type::Byte(DBus::Message::Header::HEADER_PATH)); - sPath.add(DBus::Type::Variant(DBus::Type::ObjectPath(m_Name.m_Object))); - array.add(sPath); - - // This is necessary, to prevent errors from the daemon,although it is marked - // as optional in the spec - DBus::Type::Struct sDestination; - sDestination.add(DBus::Type::Byte(DBus::Message::Header::HEADER_DESTINATION)); - sDestination.add(DBus::Type::Variant(DBus::Type::String(destination))); - array.add(sDestination); - - DBus::Type::Struct sInterface; - sInterface.add(DBus::Type::Byte(DBus::Message::Header::HEADER_INTERFACE)); - sInterface.add(DBus::Type::Variant(DBus::Type::String(m_Name.m_Interface))); - array.add(sInterface); - - DBus::Type::Struct sMember; - sMember.add(DBus::Type::Byte(DBus::Message::Header::HEADER_MEMBER)); - sMember.add(DBus::Type::Variant(DBus::Type::String(m_Name.m_Method))); - array.add(sMember); - - if (m_Parameters.getParameterCount()) { - DBus::Type::Struct sSignature; - sSignature.add(DBus::Type::Byte(DBus::Message::Header::HEADER_SIGNATURE)); - sSignature.add(DBus::Type::Variant( - DBus::Type::Signature(m_Parameters.getMarshallingSignature()))); - array.add(sSignature); - } - - return marshallMessage(array); -} - -std::string DBus::Message::MethodCall::getFullName() const -{ - return getInterface() + "." + getMethod(); -} - -std::string DBus::Message::MethodCall::getObject() const -{ - return m_Name.m_Object; -} - -std::string DBus::Message::MethodCall::getInterface() const -{ - return m_Name.m_Interface; -} - -std::string DBus::Message::MethodCall::getMethod() const -{ - return m_Name.m_Method; -} diff --git a/src/dbus_message_methodreturn.cpp b/src/dbus_message_methodreturn.cpp deleted file mode 100644 index a30b3af..0000000 --- a/src/dbus_message_methodreturn.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . - -#include "dbus_log.h" -#include "dbus_type_array.h" -#include "dbus_type_byte.h" -#include "dbus_type_signature.h" -#include "dbus_type_string.h" -#include "dbus_type_struct.h" -#include "dbus_type_uint32.h" -#include "dbus_type_variant.h" - -#include "dbus_message.h" - -DBus::Message::MethodReturn::MethodReturn(uint32_t serial) - : Message::Base() - , m_SerialReplyingTo(serial) -{ - m_Header.flags = 0; - m_Header.type = Message::Header::TYPE_METHOD_RETURN; -} - -DBus::Message::MethodReturn::MethodReturn(const DBus::Type::Struct& header, - const std::string& body) - : Message::Base(header, body) -{ - - m_Header.flags = 0; - m_Header.type = Message::Header::TYPE_METHOD_RETURN; - m_SerialReplyingTo = getReplySerial(); - - Log::write(Log::TRACE, "DBus :: Received a method return with serial #%d\n", - m_SerialReplyingTo); -} - -void DBus::Message::MethodReturn::addParameter(const Type::Generic& value) -{ - m_Parameters.add(value); -} - -std::string -DBus::Message::MethodReturn::marshall(const std::string& destination) const -{ - DBus::Type::Array array; - DBus::Type::Struct sDestination; - sDestination.add(DBus::Type::Byte(DBus::Message::Header::HEADER_DESTINATION)); - sDestination.add(DBus::Type::Variant(DBus::Type::String(destination))); - array.add(sDestination); - - DBus::Type::Struct sReplySerial; - sReplySerial.add( - DBus::Type::Byte(DBus::Message::Header::HEADER_REPLY_SERIAL)); - sReplySerial.add(DBus::Type::Variant(DBus::Type::Uint32(m_SerialReplyingTo))); - array.add(sReplySerial); - - if (m_Parameters.getParameterCount()) { - DBus::Type::Struct sSignature; - sSignature.add(DBus::Type::Byte(DBus::Message::Header::HEADER_SIGNATURE)); - sSignature.add(DBus::Type::Variant( - DBus::Type::Signature(m_Parameters.getMarshallingSignature()))); - array.add(sSignature); - } - - return marshallMessage(array); -} diff --git a/src/dbus_message_signal.cpp b/src/dbus_message_signal.cpp deleted file mode 100644 index c3ce188..0000000 --- a/src/dbus_message_signal.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . - -#include "dbus_log.h" -#include "dbus_type.h" -#include "dbus_type_array.h" -#include "dbus_type_byte.h" -#include "dbus_type_objectpath.h" -#include "dbus_type_signature.h" -#include "dbus_type_string.h" -#include "dbus_type_struct.h" -#include "dbus_type_variant.h" - -#include "dbus_message.h" - -DBus::Message::Signal::Signal(const Message::MethodCallIdentifier& name) - : Message::Base() - , m_SignalName(name) -{ - m_Header.flags = 0; - m_Header.type = Message::Header::TYPE_SIGNAL; -} - -DBus::Message::Signal::Signal(const DBus::Type::Struct& header, - const std::string& body) - : Message::Base(header, body) - , m_SignalName(m_Header.path, m_Header.interface, m_Header.member) -{ - - if (Log::isActive(Log::TRACE)) { - std::string data; - std::string postfix; - - for (size_t i = 0; i < m_Parameters.getParameterCount(); ++i) { - data += postfix; - data += DBus::Type::toString(m_Parameters.getParameter(i)); - postfix = "\n"; - } - - Log::write(Log::TRACE, "DBus :: Signal : Header :\n%s", - DBus::Type::toString(header).c_str()); - Log::write(Log::TRACE, "DBus :: Signal : Data :\n%s\n", data.c_str()); - } -} - -void DBus::Message::Signal::addParameter(const Type::Generic& value) -{ - m_Parameters.add(value); -} - -std::string -DBus::Message::Signal::marshall(const std::string& destination) const -{ - DBus::Type::Array array; - DBus::Type::Struct sPath; - sPath.add(DBus::Type::Byte(DBus::Message::Header::HEADER_PATH)); - sPath.add(DBus::Type::Variant(DBus::Type::ObjectPath(m_SignalName.m_Object))); - array.add(sPath); - - if (destination.size()) { - // Unicast signal to a single destination - DBus::Type::Struct sDestination; - sDestination.add(DBus::Type::Byte(DBus::Message::Header::HEADER_DESTINATION)); - sDestination.add(DBus::Type::Variant(DBus::Type::String(destination))); - array.add(sDestination); - } - - DBus::Type::Struct sInterface; - sInterface.add(DBus::Type::Byte(DBus::Message::Header::HEADER_INTERFACE)); - sInterface.add( - DBus::Type::Variant(DBus::Type::String(m_SignalName.m_Interface))); - array.add(sInterface); - - DBus::Type::Struct sMember; - sMember.add(DBus::Type::Byte(DBus::Message::Header::HEADER_MEMBER)); - sMember.add(DBus::Type::Variant(DBus::Type::String(m_SignalName.m_Method))); - array.add(sMember); - - if (m_Parameters.getParameterCount()) { - DBus::Type::Struct sSignature; - sSignature.add(DBus::Type::Byte(DBus::Message::Header::HEADER_SIGNATURE)); - sSignature.add(DBus::Type::Variant( - DBus::Type::Signature(m_Parameters.getMarshallingSignature()))); - array.add(sSignature); - } - - return marshallMessage(array); -} From 62caac1a3a8447498a170512d5f31eb1b6842874 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Thu, 14 Apr 2022 00:43:28 +0200 Subject: [PATCH 11/24] use Type::Signature in Introspectable Convert to use functionality from the new DBus::Type system. Signed-off-by: Heiko Hund --- src/dbus_introspectable.cpp | 25 ++++++++++++------------- src/dbus_introspectable.h | 14 +++++++------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/dbus_introspectable.cpp b/src/dbus_introspectable.cpp index b48f080..c44f643 100644 --- a/src/dbus_introspectable.cpp +++ b/src/dbus_introspectable.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -164,24 +165,22 @@ DBus::Introspectable::Method::Method(const std::string& name, { } -std::string DBus::Introspectable::Method::serialize() const +std::string DBus::Introspectable::Method::serialize() { std::string result; - result += ""; + result += "\n"; - size_t idx = 0; - while (idx < m_InParams.size()) { - std::string sig(DBus::Type::extractSignature(m_InParams, idx)); - result += "\n"; - idx += sig.size(); + std::string code = m_InParams.getNextTypeCode(); + while (!code.empty()) { + result += "\n"; + code = m_InParams.getNextTypeCode(); } - // - idx = 0; - while (idx < m_OutParams.size()) { - std::string sig(DBus::Type::extractSignature(m_OutParams, idx)); - result += "\n"; - idx += sig.size(); + + code = m_OutParams.getNextTypeCode(); + while (!code.empty()) { + result += "\n"; + code = m_OutParams.getNextTypeCode(); } result += ""; diff --git a/src/dbus_introspectable.h b/src/dbus_introspectable.h index 1551401..0ffddb6 100644 --- a/src/dbus_introspectable.h +++ b/src/dbus_introspectable.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,8 +16,9 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_INTROSPECTABLE_H -#define DBUS_INTROSPECTABLE_H +#pragma once + +#include "dbus_type_signature.h" #include #include @@ -57,11 +59,11 @@ namespace Introspectable { Method(const std::string& name, const std::string& in_params, const std::string& out_params); - std::string serialize() const; + std::string serialize(); std::string m_Name; - std::string m_InParams; - std::string m_OutParams; + Type::Signature m_InParams; + Type::Signature m_OutParams; }; class Property { @@ -97,5 +99,3 @@ namespace Introspectable { // TODO: Support names for each property and signal } // namespace Introspectable } // namespace DBus - -#endif From 37fc55530b12c276e369a174850cccae49647bf3 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Thu, 14 Apr 2022 00:55:26 +0200 Subject: [PATCH 12/24] overhaul DBus::Transport There's specialized functions for sending authentication protocol commands and DBus messages. Messages can transport file descriptors as a side load in cmsg'es. All is pretty straight forward (if you also consider cmsg that). Signed-off-by: Heiko Hund --- src/dbus_transport.cpp | 191 ++------------------------------ src/dbus_transport.h | 242 +++++++++++++++++++++++++++++++---------- 2 files changed, 194 insertions(+), 239 deletions(-) diff --git a/src/dbus_transport.cpp b/src/dbus_transport.cpp index c5b8334..b5aa47d 100644 --- a/src/dbus_transport.cpp +++ b/src/dbus_transport.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -16,191 +17,19 @@ // . #include "dbus_transport.h" -#include "dbus_log.h" -#include -#include -using namespace std::chrono_literals; - -DBus::Transport::Transport(const std::string& path) - : m_Busname(path) - , m_ReadyToSend(false) - , m_socket(m_io_context) - , m_ShuttingDown(false) -{ - setDataHandler( - std::bind(&Transport::onReceiveData, this, std::placeholders::_1)); - - m_socket.connect(m_Busname); - - // The "special credentials passing NUL byte" is required, even for protocols - // that can send credentials without needing one. Otherwise, the server may - // disconnect us. - sendOctetDirect('\0'); - - m_socket.async_read_some( - boost::asio::buffer(m_DataBuffer, BufferSize), - boost::bind(&Transport::handle_read_data, this, - boost::placeholders::_1, boost::placeholders::_2)); - - // We use a second thread for the io context until further notice - m_io_context_thread = boost::thread(boost::bind(&boost::asio::io_context::run, &m_io_context)); -} - -void DBus::Transport::handle_read_data(const boost::system::error_code& error, - std::size_t bytes_transferred) -{ - boost::recursive_mutex::scoped_lock guard(m_CallbackMutex); - OctetBuffer buffer(m_DataBuffer, bytes_transferred); - m_ReceiveDataCallback(buffer); - - if (error) { - if (error.category() == boost::asio::error::misc_category && error.value() == boost::asio::error::misc_errors::eof) { - if (!m_ShuttingDown) - Log::write( - Log::ERROR, - "DBus :: Transport received slightly unexpected end of stream\n"); - } else - Log::write(Log::ERROR, "DBus :: Transport error. %s (%d)\n", - error.message().c_str(), error.value()); - } else - m_socket.async_read_some( - boost::asio::buffer(m_DataBuffer, BufferSize), - boost::bind(&Transport::handle_read_data, this, boost::placeholders::_1, boost::placeholders::_2)); -} - -DBus::Transport::~Transport() -{ - // EOF on read is now expected - m_ShuttingDown = true; - - { - boost::recursive_mutex::scoped_lock guard(m_SendMutex); - m_ReadyToSend = false; - } - - { - // Take the callback mutex so that we don't call m_socket.shutdown() at the - // same time as handle_read_data() is calling async_read_some() on the same - // object - boost::recursive_mutex::scoped_lock guard(m_CallbackMutex); - boost::system::error_code ec; - m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - if (ec) { - DBus::Log::write( - DBus::Log::ERROR, - "DBus :: Transport :: Socket shutdown failed: (%d) \"%s\"\n", - ec.value(), ec.message().c_str()); - } - } - - // Wait for pending async_writes to complete - if (!m_io_context_thread.try_join_for(boost::chrono::seconds(30))) { - DBus::Log::write(DBus::Log::ERROR, - "DBus :: Transport :: IO service thread cannot join\n"); - abort(); - } -} - -void DBus::Transport::onAuthComplete() -{ - DBus::Log::write(DBus::Log::INFO, - "DBus :: Transport :: Authorisation has completed.\n"); - boost::recursive_mutex::scoped_lock guard(m_SendMutex); - for (auto msg : m_BufferedMessages) { - sendStringDirect(msg); - ++m_Stats.count_messagespumped; - } - m_BufferedMessages.clear(); - m_ReadyToSend = true; -} - -void DBus::Transport::addToMessageQueue(const std::string& data) +DBus::Transport::Ptr +DBus::Transport::create(asio::io_context& io_context) { - DBus::Log::write(DBus::Log::INFO, "DBus :: Transport :: No BEGIN has been " - "received, so message is queued.\n"); - boost::recursive_mutex::scoped_lock guard(m_SendMutex); - m_BufferedMessages.push_back(data); - ++m_Stats.count_messagesqueued; + struct ShareableTransport : public Transport { + ShareableTransport(asio::io_context& io_context) + : Transport(io_context) {}; + }; + return std::make_shared(io_context); } -void DBus::Transport::sendString(const std::string& data) -{ - DBus::Log::write(DBus::Log::TRACE, "DBus :: SEND: %s\n", data.c_str()); - DBus::Log::writeHex(DBus::Log::TRACE, "DBus :: DATA: ", data); - - boost::recursive_mutex::scoped_lock guard(m_SendMutex); - if (m_ReadyToSend) { - sendStringDirect(data); - } else { - addToMessageQueue(data); - } -} - -void DBus::Transport::sendOctetDirect(uint8_t data) -{ - boost::recursive_mutex::scoped_lock guard(m_SendMutex); - std::shared_ptr buf(new std::string()); - buf.get()->push_back(static_cast(data)); - boost::asio::async_write( - m_socket, boost::asio::buffer(*buf.get()), - boost::bind(&Transport::handle_write_output, this, buf, boost::placeholders::_1, boost::placeholders::_2)); -} -void DBus::Transport::sendStringDirect(const std::string& data) +DBus::Transport::Transport(asio::io_context& ioContext) + : m_socket(ioContext) { - DBus::Log::write(DBus::Log::TRACE, "DBus :: SENDDIRECT: %s\n", data.c_str()); - DBus::Log::writeHex(DBus::Log::TRACE, "DBus :: SENDDIRECT: \n", data); - boost::recursive_mutex::scoped_lock guard(m_SendMutex); - - // We don't know when the write will complete, so we copy the buffer - std::shared_ptr buf(new std::string(data)); - boost::asio::async_write( - m_socket, boost::asio::buffer(*buf.get()), - boost::bind(&Transport::handle_write_output, this, buf, boost::placeholders::_1, boost::placeholders::_2)); - ++m_Stats.count_messagessent; - m_Stats.bytes_sent += data.length(); -} - -void DBus::Transport::handle_write_output( - std::shared_ptr buf_written, - const boost::system::error_code& error, std::size_t bytes_transferred) -{ - buf_written.reset(); - if (error) { - DBus::Log::write(DBus::Log::ERROR, "DBus :: ERROR in async_write : %s\n", - error.message().c_str()); - } -} - -void DBus::Transport::setDataHandler( - const ReceiveDataCallbackFunction& callback) -{ - boost::recursive_mutex::scoped_lock guard(m_CallbackMutex); - m_ReceiveDataCallback = callback; -} - -void DBus::Transport::onReceiveData(OctetBuffer&) -{ - // NOP - This stub gives our initial callback somewhere to go - - // This could happen if the thread reads data before native.cpp updates the - // callback. - DBus::Log::write(DBus::Log::WARNING, - "DBus :: Transport : onReceiveData is processing data, " - "whereas it should really have been directed elsewhere via " - "setDataHandler\n"); -} - -std::string DBus::Transport::getStats() const -{ - std::stringstream ss; - - ss << "Transport stats:" << std::endl; - ss << " count_messages_sent: " << m_Stats.count_messagessent << std::endl; - ss << " count_messages_queued: " << m_Stats.count_messagesqueued << std::endl; - ss << " count_messages_pumped: " << m_Stats.count_messagespumped << std::endl; - ss << " bytes_sent: " << m_Stats.bytes_sent << std::endl; - ss << " bytes_read: " << m_Stats.bytes_read << std::endl; - return ss.str(); } diff --git a/src/dbus_transport.h b/src/dbus_transport.h index 130d75f..051b0cf 100644 --- a/src/dbus_transport.h +++ b/src/dbus_transport.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,74 +16,199 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_TRANSPORT_H -#define DBUS_TRANSPORT_H +#pragma once -#include "dbus_octetbuffer.h" -#include -#include +#include "dbus_asio.h" +#include "dbus_log.h" +#include "dbus_messageistream.h" +#include "dbus_messageostream.h" -#define USE_ASIO_NORMAL 1 -#define USE_ASIO_ACCESSOR 0 -#define USE_NATIVE_SOCKETS 0 +#include +#include +#include namespace DBus { -typedef std::function ReceiveDataCallbackFunction; +using DynamicStringBuffer = + asio::dynamic_string_buffer< + std::string::value_type, + std::string::traits_type, + std::string::allocator_type>; -class Transport { +class Transport : public std::enable_shared_from_this { public: - Transport(const std::string& path); - ~Transport(); - - void sendString(const std::string& data); - void handle_write_output(std::shared_ptr buf_written, - const boost::system::error_code& error, - std::size_t bytes_transferred); - void handle_read_data(const boost::system::error_code& error, - std::size_t bytes_transferred); - - void ThreadFunction(); - - void setDataHandler(const ReceiveDataCallbackFunction& callback); - void onReceiveData(OctetBuffer& buffer); - void onAuthComplete(); - void addToMessageQueue(const std::string& data); + using Ptr = std::shared_ptr; + using EndpointType = asio::local::stream_protocol::socket::endpoint_type; + + static Ptr create(asio::io_context& io_context); + + template + void asyncConnect(const std::string& bus_path, Handler&& handler) + { + m_socket.async_connect(bus_path, std::forward(handler)); + } + + void disconnect() + { + if (m_socket.is_open()) + m_socket.close(); + } + + template + void asyncPeek(const MutableBuffer& buffer, Handler&& handler) + { + m_socket.async_receive(buffer, asio::socket_base::message_peek, + [self = shared_from_this(), buffer, handler = std::move(handler)] + (const error_code& error, std::size_t bytes_read) + { + if (!self->m_socket.is_open()) + return handler({}, 0); + if (error && error.value() == EAGAIN) + return self->asyncPeek(buffer, handler); + handler(error, bytes_read); + }); + } + + template + void asyncRead(const MutableBuffer& buffer, UnixFdBuffer& fds, Handler&& handler) + { + m_socket.async_wait(asio::local::stream_protocol::socket::wait_read, + [self = shared_from_this(), buffer, &fds, handler = std::move(handler)] + (const error_code& error) mutable + { + if (!self->m_socket.is_open()) + return handler({}, 0); + if (error && error.value() == EAGAIN) + return self->asyncRead(buffer, fds, handler); + if (error) + return handler(error, 0); + + struct iovec iov; + struct msghdr msg = {}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + char fdbuf[CMSG_SPACE(self->m_maxRecvUnixFds * sizeof(int))]; + msg.msg_control = fdbuf; + msg.msg_controllen = sizeof(fdbuf); + + ssize_t read_total = 0; + do { + iov = { static_cast(buffer.data()) + read_total, + buffer.size() - read_total }; + ssize_t read = ::recvmsg(self->m_socket.native_handle(), &msg, 0); + if (read == 0) + return handler({}, 0); + if (read == -1) { + if (errno == EAGAIN) + continue; + return handler(error_code(errno, system_category()), 0); + } + + read_total += read; + + for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + + if (cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) + continue; + + int *fdptr = (int *)CMSG_DATA(cmsg); + std::size_t len = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + fds.assign(fdptr, fdptr + len); + } + } while (buffer.size() - read_total > 0); + + handler({}, read_total); + }); + } + + template + void asyncWrite(MessageOStream::Ptr payload, Handler&& handler) + { + asio::const_buffer buffer( payload->data.data(), payload->data.size() ); + Log::write(Log::TRACE, "DBus :: Send : Message Data : %ld bytes, %ld FDs\n", + buffer.size(), payload->fds.size()); + Log::writeHex(Log::TRACE, " ", buffer.data(), buffer.size()); + + if (payload->fds.empty()) { + asio::async_write(m_socket, buffer, + [self = shared_from_this(), payload, handler = std::move(handler)] + (const error_code& error, std::size_t size) mutable { + Log::write(Log::TRACE, "DBus :: Send : message complete\n"); + handler(error, size); + }); + } else { + // send unix fds along the data + m_socket.async_wait(asio::local::stream_protocol::socket::wait_write, + [self = shared_from_this(), payload, handler = std::move(handler)] + (const error_code& error) mutable + { + if (error) + return handler(error, 0); + + struct iovec iov = { + const_cast(payload->data.data()), + payload->data.size() }; + + struct msghdr msg = {}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + const size_t fdlen = payload->fds.size() * sizeof(int); + char fdbuf[CMSG_SPACE(fdlen)]; + msg.msg_control = fdbuf; + msg.msg_controllen = sizeof(fdbuf); + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(fdlen); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + std::memcpy(CMSG_DATA(cmsg), payload->fds.data(), fdlen); + ssize_t ret = ::sendmsg(self->m_socket.native_handle(), &msg, 0); + payload->clearUnixFds(); + + Log::write(Log::TRACE, "DBus :: Send : message complete\n"); + if (ret == -1) + handler(error_code(errno, system_category()), 0); + else + handler({}, ret); + }); + } + } std::string getStats() const; protected: - friend class AuthenticationProtocol; // this is to give access to - // sendStringDirect, to bypass the - // buffering - void sendOctetDirect(uint8_t data); - void sendStringDirect(const std::string& data); - - std::string m_Busname; - bool m_ReadyToSend; - boost::asio::io_context m_io_context; - boost::asio::local::stream_protocol::socket m_socket; - - const static size_t BufferSize = 1024; - uint8_t m_DataBuffer[BufferSize]; - ReceiveDataCallbackFunction m_ReceiveDataCallback; - - mutable boost::recursive_mutex m_SendMutex; - mutable boost::recursive_mutex m_CallbackMutex; - std::vector m_BufferedMessages; - std::atomic m_ShuttingDown; - - boost::thread m_io_context_thread; - -private: - struct Stats { - size_t count_messagessent = 0; - size_t count_messagesqueued = 0; - size_t count_messagespumped = 0; - size_t bytes_sent = 0; - size_t bytes_read = 0; - } m_Stats; + Transport(asio::io_context& ioContext); + + friend class AuthenticationProtocol; + using AuthProtoMode = enum { NoResponseExpected, ResponseExpected }; + + template + void asyncAuthExchange(AuthProtoMode mode, DynamicBuffer&& buffer, Handler&& handler) + { + if (mode == NoResponseExpected) + asio::async_write(m_socket, std::forward(buffer), std::forward(handler)); + else { + asio::async_write(m_socket, std::forward(buffer), + [self = shared_from_this(), &buffer, handler = std::forward(handler)] + (const error_code& error, std::size_t bytes_transferred) mutable { + if (error) { + handler(error, bytes_transferred); + return; + } + asio::async_read_until(self->m_socket, std::forward(buffer), "\n", std::forward(handler)); + } + ); + } + } + + asio::local::stream_protocol::socket m_socket; + std::size_t m_maxRecvUnixFds = 16; }; -} // namespace DBus -#endif // DBUS_TRANSPORT_H +} // namespace DBus From 46582f785f06de74e6d8c633278fa7f1877c3928 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Thu, 14 Apr 2022 01:03:31 +0200 Subject: [PATCH 13/24] overhaul DBus::AuthenticationProtocol The class now implements the complete state machine for the DBus authentication protocol and handles all commands. It's works completely async. With little work it should be doable to subclass different auth protocols from this. However, I'm not sure if there's need for more than what has been there before, so I left it as it is for now. Signed-off-by: Heiko Hund --- src/dbus_auth.cpp | 229 +++++-------------------------------- src/dbus_auth.h | 286 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 279 insertions(+), 236 deletions(-) diff --git a/src/dbus_auth.cpp b/src/dbus_auth.cpp index 21e9f5a..95388ae 100644 --- a/src/dbus_auth.cpp +++ b/src/dbus_auth.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -16,216 +17,48 @@ // . #include "dbus_auth.h" -#include "dbus_log.h" -#include "dbus_platform.h" -#include "dbus_utils.h" -/* -The AUTH handshake is: -Server Client - <--- AUTH -OK ----> - <--- (1) BEGIN (or) - <--- (2) NEGOTIATE_UNIX_FD -AGREE ----> - <--- BEGIN -*/ - -DBus::AuthenticationProtocol::AuthenticationProtocol( - std::shared_ptr& transport) - : m_Transport(transport) -{ -} - -void DBus::AuthenticationProtocol::reset() -{ - m_data.clear(); - sendAuth(m_AuthType); -} - -void DBus::AuthenticationProtocol::sendAuthListMethods() -{ - sendWire(std::string("AUTH\r\n")); -} - -// RCVD: REJECTED EXTERNAL DBUS_COOKIE_SHA1 ANONYMOUS -void DBus::AuthenticationProtocol::sendAuth( - DBus::AuthenticationProtocol::AuthRequired type /* = AUTH_BASIC*/) -{ - { - boost::recursive_mutex::scoped_lock guard(m_AuthTypeMutex); - m_AuthType = type; - } - - std::string auth("AUTH EXTERNAL "); - DBus::Utils::ConvertBinaryToHexString(auth, - std::to_string(Platform::getUID())); - auth += "\r\n"; - sendWire(auth); -} - -bool DBus::AuthenticationProtocol::processData() +DBus::AuthenticationProtocol::Ptr +DBus::AuthenticationProtocol::create() { - const size_t pos = m_data.find("\r\n"); - if (pos != std::string::npos) { - std::string command(m_data.data(), pos + 2); - return onCommand(command); - m_data = m_data.substr(pos + 2); + struct ShareableAuthProto : public AuthenticationProtocol { + ShareableAuthProto() + : AuthenticationProtocol() {}; }; - - return false; + return std::make_shared(); } -bool DBus::AuthenticationProtocol::onReceiveData(DBus::OctetBuffer& buffer) -{ - bool authenticated = false; - - while (buffer.size() && !authenticated) { - size_t pos = buffer.find('\n'); - pos = pos == std::string::npos ? buffer.size() : pos + 1; - m_data.append((const char*)buffer.data(), pos); - buffer.remove_prefix(pos); - authenticated = processData(); - }; - - return authenticated; -} +DBus::AuthenticationProtocol::AuthenticationProtocol() +{} -// -// Protected methods -// -bool DBus::AuthenticationProtocol::onOK( - const std::string& guid /* unused in this case */) +DBus::AuthenticationProtocol::Command +DBus::AuthenticationProtocol::extractCommand(std::string& response) { - std::string address; - DBus::Utils::ConvertHexStringToBinary(address, guid); - - bool send_begin = false; - - { - boost::recursive_mutex::scoped_lock guard(m_AuthTypeMutex); - if (m_AuthType == AUTH_BASIC) { - send_begin = true; + for (auto& c : m_commands) { + auto cmdSize = c.first.size(); + if (response.substr(0, cmdSize) != c.first) + continue; + if (response.size() > cmdSize) { + if (response[cmdSize] != ' ') + continue; + cmdSize += 1; } + // erase command from response + response.erase(0, cmdSize); + return c.second; } - - if (send_begin) { - sendBegin(); - return true; - } - - sendNegotiateUnixFD(); - return false; -} - -bool DBus::AuthenticationProtocol::onError(const std::string& error_message) -{ - DBus::Log::write(DBus::Log::ERROR, "DBus :: onError : %s\n", - error_message.c_str()); - return false; + return UNKNOWN; } -bool DBus::AuthenticationProtocol::onRejected( - const std::string& error_message) +std::string +DBus::AuthenticationProtocol::toHex(const std::string& input) { - // Split by space - // Store the list of available auth methods - // Optionally, retry. (determin by cb?) - DBus::Log::write(DBus::Log::WARNING, "DBus :: Reject : %s\n", - error_message.c_str()); - return false; -} - -bool DBus::AuthenticationProtocol::onAgreeUnixFD() -{ - DBus::Log::write(DBus::Log::INFO, "DBus :: onAgreeUnixFD\n"); - sendBegin(); - return true; -} - -bool DBus::AuthenticationProtocol::onAuth(const std::string&) { return false; } - -bool DBus::AuthenticationProtocol::onData(const std::string&) { return false; } - -bool DBus::AuthenticationProtocol::onCancel() { return false; } - -bool DBus::AuthenticationProtocol::onNegotiateUnixFD(const std::string&) -{ - return false; -} - -void DBus::AuthenticationProtocol::sendNegotiateUnixFD() -{ - sendWire(std::string("NEGOTIATE_UNIX_FD\r\n")); -} - -void DBus::AuthenticationProtocol::sendBegin() -{ - sendWire(std::string("BEGIN\r\n")); - m_Transport->onAuthComplete(); -} - -void DBus::AuthenticationProtocol::sendData(std::string& data) -{ - std::string packet("DATA "); - DBus::Utils::ConvertBinaryToHexString(packet, data); - packet += "\r\n"; - - sendWire(packet); -} - -void DBus::AuthenticationProtocol::sendWire(const std::string& data) -{ - m_Transport->sendStringDirect(data); -} - -// Return true once we have completed the auth state -bool DBus::AuthenticationProtocol::onCommand(const std::string& command) -{ - DBus::Log::write(DBus::Log::TRACE, "DBus :: CMD: %s\n", command.c_str()); - DBus::Log::writeHex(DBus::Log::TRACE, "DBus :: CMD: ", command); - - std::pair> - callback_list[8] = { - // Client - std::make_pair("OK", std::bind(&DBus::AuthenticationProtocol::onOK, this, std::placeholders::_1)), - std::make_pair("ERROR", - std::bind(&DBus::AuthenticationProtocol::onError, this, - std::placeholders::_1)), - std::make_pair("REJECTED", - std::bind(&DBus::AuthenticationProtocol::onRejected, - this, std::placeholders::_1)), - std::make_pair( - "AGREE_UNIX_FD", - std::bind(&DBus::AuthenticationProtocol::onAgreeUnixFD, this)), - std::make_pair("DATA", - std::bind(&DBus::AuthenticationProtocol::onData, this, - std::placeholders::_1)), - // Server (TODO) - std::make_pair("AUTH", - std::bind(&DBus::AuthenticationProtocol::onAuth, this, - std::placeholders::_1)), - std::make_pair( - "NEGOTIATE_UNIX_FD", - std::bind(&DBus::AuthenticationProtocol::onNegotiateUnixFD, this, - std::placeholders::_1)), - std::make_pair( - "CANCEL", - std::bind(&DBus::AuthenticationProtocol::onCancel, this)) - }; - - for (size_t i = 0; i < 8; ++i) { - if (command.substr(0, strlen(callback_list[i].first)) == callback_list[i].first) { - return callback_list[i].second( - command.substr(strlen(callback_list[i].first) + 1)); - } + std::ostringstream hexout; + hexout << std::hex << std::setfill('0'); + for (auto ch : input) { + hexout << std::setw(2) << static_cast(ch); } - - DBus::Log::write(DBus::Log::WARNING, - "DBus :: CMD: %s did not execute anything, so please " - "implement the method.\n", - command.c_str()); - - return false; + return hexout.str(); } + diff --git a/src/dbus_auth.h b/src/dbus_auth.h index ada3b41..fd420e4 100644 --- a/src/dbus_auth.h +++ b/src/dbus_auth.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,55 +16,264 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_AUTH_H -#define DBUS_AUTH_H +#pragma once +#include +#include + +#include "dbus_asio.h" +#include "dbus_utils.h" +#include "dbus_platform.h" #include "dbus_transport.h" -#include namespace DBus { // See Authentication state diagrams -class AuthenticationProtocol { +class AuthenticationProtocol : public std::enable_shared_from_this +{ public: - typedef enum AuthRequired { - AUTH_BASIC, - AUTH_NEGOTIATE_UNIX_FD, - } AuthRequired; + using Ptr = std::shared_ptr; + + struct Statistics { + std::size_t count_send; + std::size_t count_recv; + std::size_t bytes_send; + std::size_t bytes_recv; + }; + + static Ptr create(); + + bool unixFdNegotiated() { return m_unixFdNegotiated; } + + template + auto asyncAuthenticate( + Transport::Ptr transport, + CompletionToken&& token) + { + m_transport = transport; + m_state = Starting; + m_server_guid.clear(); + + return asio::async_compose< + CompletionToken, void(const error_code&, const std::string&)>( + [self = shared_from_this()] + (auto& composition, const error_code& error = {}, const std::size_t size = 0) mutable + { + if (size) { + ++self->m_stats.count_recv; + self->m_stats.bytes_recv += size; + } + if (error) + return composition.complete(error, ""); + + std::string& response = self->m_data; + Command command = UNKNOWN; + if (response.size()) { + Log::write(Log::TRACE, "DBus :: Recv : Auth Cmd :\n"); + Log::writeHex(Log::TRACE, " ", response); + if (response.size() < 3) + return composition.complete(make_error_code(errc::protocol_error), ""); + response.resize(response.size() - 2); // remove trailing \r\n + Log::write(Log::TRACE, " %s\n", response.c_str()); + command = self->extractCommand(response); + } - AuthenticationProtocol(std::shared_ptr& transport); + if (self->m_state == Starting) + self->sendCredentials(std::move(composition)); + else if (self->m_state == SendingCredentials) + self->sendAuth(std::move(composition)); + else if (self->m_state == WaitingForData) + if (command == DATA) + self->handleData(response, std::move(composition)); + else if (command == REJECTED) + self->handleRejected(response, std::move(composition)); + else if (command == ERROR) + self->handleError(response, std::move(composition)); + else if (command == OK) + self->handleOk(response, std::move(composition)); + else + self->sendError(response, std::move(composition)); + else if (self->m_state == WaitingForOK) + if (command == OK) + self->handleOk(response, std::move(composition)); + else if (command == REJECTED) + self->handleRejected(response, std::move(composition)); + else if (command == DATA || command == ERROR) + self->sendCancel(std::move(composition)); + else + self->sendError(response, std::move(composition)); + else if (self->m_state == WaitingForReject) + if (command == REJECTED) + self->handleRejected(response, std::move(composition)); + else + return composition.complete(make_error_code(errc::protocol_error), ""); + else if (self->m_state == WaitingForAgreeUnixFD) + if (command == AGREE_UNIX_FD) + self->handleAgreeUniXFD(std::move(composition)); + else + self->sendBegin(std::move(composition)); + else if (self->m_state == Finishing) + return composition.complete(error, self->m_server_guid); + else { + Log::write(Log::ERROR, "DBus :: ERROR : unhandled auth state"); + return composition.complete(make_error_code(errc::protocol_error), ""); + } + }, + token); + } - void reset(); - void sendAuthListMethods(); - void sendAuth(AuthenticationProtocol::AuthRequired type = AUTH_BASIC); - bool onReceiveData(OctetBuffer& buffer); + struct Statistics getStats() { return m_stats; } protected: - bool onOK(const std::string& guid /* unused in this case */); - bool onError(const std::string& error_message); - bool onRejected(const std::string& error_message); - bool onAgreeUnixFD(); - - bool onAuth(const std::string&); - bool onData(const std::string&); - bool onCancel(); - bool onNegotiateUnixFD(const std::string&); - - void sendNegotiateUnixFD(); - void sendBegin(); - void sendData(std::string& data); - void sendWire(const std::string& data); - // Return true once we have completed the auth state - bool onCommand(const std::string& command); - - bool processData(); - -private: + AuthenticationProtocol(); + + enum State { + Starting, + SendingCredentials, + WaitingForData, + WaitingForOK, + WaitingForReject, + WaitingForAgreeUnixFD, + Finishing + }; + + enum Command { OK, DATA, ERROR, REJECTED, AGREE_UNIX_FD, UNKNOWN }; + const std::array< std::pair, 5 > m_commands = { + std::make_pair("OK", OK), + std::make_pair("DATA", DATA), + std::make_pair("ERROR", ERROR), + std::make_pair("REJECTED", REJECTED), + std::make_pair("AGREE_UNIX_FD", AGREE_UNIX_FD), + }; + + Command extractCommand(std::string& response); + std::string toHex(const std::string& input); + + template + void sendCommand(bool expectResponse, std::string&& command, Handler&& handler) + { + ++m_stats.count_send; + m_stats.bytes_send += command.size(); + Log::write(Log::TRACE, "DBus :: Send : Auth Cmd : %ld bytes\n %s", + command.size(), (command[0] == 0 ? "\\0\n" : command.c_str())); + Log::writeHex(Log::TRACE, " ", command); + m_data = std::move(command); + m_transport->asyncAuthExchange( + expectResponse ? Transport::ResponseExpected : Transport::NoResponseExpected, + m_buffer, std::forward(handler)); + } + + template + void sendCredentials(Handler&& handler) + { + // No credentials sent here, but we have to send the null byte anyway + sendCommand(false, std::string(1, '\0'), std::forward(handler)); + m_state = SendingCredentials; + } + + template + void sendAuth(Handler&& handler) + { + std::ostringstream cmd; + const std::string uid = std::to_string(Platform::getUID()); + cmd << "AUTH EXTERNAL " << toHex(uid) << "\r\n"; + sendCommand(true, cmd.str(), std::forward(handler)); + m_state = WaitingForOK; + } + + template + void sendData(const std::string& data, Handler&& handler) + { + std::ostringstream cmd; + cmd << "DATA " << toHex(data) << "\r\n"; + sendCommand(true, cmd.str(), std::forward(handler)); + m_state = WaitingForData; + } + + template + void sendNegotiateUnixFD(Handler&& handler) + { + sendCommand(true, "NEGOTIATE_UNIX_FD\r\n", std::forward(handler)); + m_state = WaitingForAgreeUnixFD; + } + + template + void sendError(const std::string& message, Handler&& handler) + { + std::ostringstream cmd; + cmd << "ERROR"; + if (message.size()) + cmd << ' ' << message; + cmd << "\r\n"; + sendCommand(true, cmd.str(), std::forward(handler)); + m_state = WaitingForData; + } + + template + void sendCancel(Handler&& handler) + { + sendCommand(true, "CANCEL\r\n", std::forward(handler)); + m_state = WaitingForReject; + } + + template + void sendBegin(Handler&& handler) + { + sendCommand(false, "BEGIN\r\n", std::forward(handler)); + m_state = Finishing; + } + + template + void handleOk( + const std::string& guid, Handler&& handler) + { + m_server_guid = guid; + Log::write(Log::INFO, "DBus :: Auth OK : guid %s\n", + m_server_guid.c_str()); + sendNegotiateUnixFD(std::forward(handler)); + } + + template + void handleError( + const std::string& message, Handler&& handler) + { + Log::write(Log::ERROR, "DBus :: Auth ERROR : %s\n", + message.c_str()); + sendCancel(std::forward(handler)); + } + + template + void handleRejected( + const std::string& message, Handler&& handler) + { + Log::write(Log::WARNING, "DBus :: Auth REJECTED : %s\n", + message.c_str()); + handler(make_error_code(errc::protocol_error), 0); + } + + template + void handleAgreeUniXFD(Handler&& handler) + { + Log::write(Log::INFO, "DBus :: Auth AGREE_UNIX_FD\n"); + m_unixFdNegotiated = true; + sendBegin(std::forward(handler)); + } + + template + void handleData(const std::string& data, Handler&& handler) + { + Log::write(Log::INFO, "DBus :: Auth DATA %s\n", data.c_str()); + sendCancel(std::forward(handler)); + } + + std::shared_ptr m_transport; + State m_state = Starting; std::string m_data; - AuthenticationProtocol::AuthRequired m_AuthType; - mutable boost::recursive_mutex m_AuthTypeMutex; - std::shared_ptr m_Transport; + DynamicStringBuffer m_buffer = asio::dynamic_buffer(m_data); + std::string m_server_guid; + bool m_unixFdNegotiated = false; + + struct Statistics m_stats = {}; }; -} // namespace DBus -#endif // DBUS_AUTH_H +} // namespace DBus From 693ab55d8c288f80f6340d5283bb545242734e39 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Thu, 14 Apr 2022 01:11:19 +0200 Subject: [PATCH 14/24] overhaul DBus::MessageProtocol Sending and receiving of messages is now done asyncronously. In the receive loop, we first peek at the message to determine its size and the receive it in one go including file descriptors, potentially. Received messages are dispatched to their registered handlers, a.k.a completion tokens, to be dealt with. Added the DBus::StoredToken helper class to keep a hold of the callers completion token while we're waiting for a message event it receives to happen, as ASIO itself currently does not have support for that. Signed-off-by: Heiko Hund --- src/dbus_asio.h.in | 61 +++++ src/dbus_messageprotocol.cpp | 305 +++++++++---------------- src/dbus_messageprotocol.h | 415 ++++++++++++++++++++++++++++++----- tests/CMakeLists.txt | 8 +- 4 files changed, 526 insertions(+), 263 deletions(-) diff --git a/src/dbus_asio.h.in b/src/dbus_asio.h.in index 012988a..5a046a4 100644 --- a/src/dbus_asio.h.in +++ b/src/dbus_asio.h.in @@ -30,5 +30,66 @@ using @DBUS_ERROR_CODE_NAMESPACE@::error_code; using @DBUS_ERROR_CODE_NAMESPACE@::system_category; using @DBUS_MAKE_ERROR_CODE@; + +template +struct StoredToken : public std::enable_shared_from_this> +{ + using Ptr = std::shared_ptr>; + + template + static Ptr create(asio::io_context& ioc, CompletionToken&& token) + { + Ptr obj = std::make_shared>(ioc); + obj->store(std::move(token)); + return obj; + } + + StoredToken(asio::io_context& ioc) + : timer_(ioc) + {} + + void invoke(Args... args) + { + args_ = std::make_tuple(args...); + timer_.cancel(); + } + +protected: + template + void store(CompletionToken&& token) + { + // never expires + timer_.expires_at(std::chrono::steady_clock::time_point::max()); + + asio::async_initiate< + CompletionToken, void(Args...)>( + [self = this->shared_from_this()](auto&& handler) mutable + { + self->timer_.async_wait( + [self = self->shared_from_this(), handler = std::move(handler)] + (const error_code&) mutable + { +#if __cplusplus < 201703L + self->apply(std::move(handler), std::index_sequence_for{}); +#else + std::apply(handler, self->args_); +#endif + }); + }, + token); + } + +#if __cplusplus < 201703L + template + void apply(Handler&& handler, std::index_sequence) + { + handler(std::get(args_)...); + } +#endif + + std::tuple>...> args_; + asio::steady_timer timer_; +}; + } // namespace DBus diff --git a/src/dbus_messageprotocol.cpp b/src/dbus_messageprotocol.cpp index c4ebdb1..cd7f9c0 100644 --- a/src/dbus_messageprotocol.cpp +++ b/src/dbus_messageprotocol.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,8 +16,6 @@ // file named COPYING. If you do not have this file see // . -#include - #include "dbus_log.h" #include "dbus_utils.h" @@ -29,223 +28,123 @@ #include "dbus_messageostream.h" #include "dbus_messageprotocol.h" -#include - -namespace { - -inline bool SwapRequired(uint8_t c) -{ - return (c == 'l' && __BYTE_ORDER != __LITTLE_ENDIAN) || (c == 'B' && __BYTE_ORDER != __BIG_ENDIAN); -} - -inline uint32_t CorrectEndianess(uint8_t c, uint32_t value) -{ - if (SwapRequired(c)) { - return bswap_32(value); - } - return value; -} - -static const size_t MAX_ARRAY_SIZE = 67108864; -static const size_t MAX_MESSAGE_SIZE = 134217728; - -} // namespace - -DBus::MessageProtocol::MessageProtocol() - : m_State(STATE_GETHEADERSIZE) - , m_headerSize(0) - , m_bodySize(0) -{ - setMethodCallHandler(std::bind(&MessageProtocol::onReceiveMethodCall, this, - std::placeholders::_1)); - setMethodReturnHandler(std::bind(&MessageProtocol::onReceiveMethodReturn, - this, std::placeholders::_1)); - setErrorHandler( - std::bind(&MessageProtocol::onReceiveError, this, std::placeholders::_1)); - setSignalHandler(std::bind(&MessageProtocol::onReceiveSignal, this, - std::placeholders::_1)); - - reset(); -} - -void DBus::MessageProtocol::reset() { startMessage(); } - -void DBus::MessageProtocol::setMethodCallHandler( - const DBus::Message::CallbackFunctionMethodCall& callback) -{ - std::lock_guard lock(m_CallbackMutex); - m_MethodCallCallback = callback; -} - -void DBus::MessageProtocol::setMethodReturnHandler( - const DBus::Message::CallbackFunctionMethodReturn& callback) -{ - std::lock_guard lock(m_CallbackMutex); - m_MethodReturnCallback = callback; -} +#include -void DBus::MessageProtocol::setErrorHandler( - const DBus::Message::CallbackFunctionError& callback) +DBus::MessageProtocol::Ptr +DBus::MessageProtocol::start(asio::io_context& ioContext, Transport::Ptr transport) { - std::lock_guard lock(m_CallbackMutex); - m_ErrorCallback = callback; + struct ShareableMsgProto : public MessageProtocol { + ShareableMsgProto(asio::io_context& ioContext, Transport::Ptr transport) + : MessageProtocol(ioContext, transport) {}; + }; + auto obj = std::make_shared(ioContext, transport); + obj->asyncReadMessage(ReadState::Peek); + return obj; } -void DBus::MessageProtocol::setSignalHandler( - const DBus::Message::CallbackFunctionSignal& callback) +DBus::MessageProtocol::MessageProtocol(asio::io_context& ioContext, Transport::Ptr transport) + : m_ioContext(ioContext) + , m_transport(transport) { - std::lock_guard lock(m_CallbackMutex); - m_SignalCallback = callback; + m_messageBuffer.reserve(256); } -void DBus::MessageProtocol::startMessage() +DBus::MessageProtocol::ReadBuffer +DBus::MessageProtocol::makeReadBuffer(ReadState state, MessageBuffer& buffer) { - m_State = STATE_GETHEADERSIZE; - - m_HeaderStruct.clear(); - m_HeaderStruct.setSignature("(yyyyuua(yv))"); - m_headerSize = 0; - m_bodySize = 0; -} - -bool DBus::MessageProtocol::getHeaderSize(OctetBuffer& buffer) -{ - // Initial header consists of byte, byte, byte, byte, uint32_t, uint32_t - // the next element is the size of the array of field info data - // making a total of 16 bytes - if (buffer.size() >= 16) { - // Read the size of the array - m_headerSize = *(uint32_t*)(buffer.data() + 12); - m_headerSize = CorrectEndianess(buffer[0], m_headerSize); - if (m_headerSize > MAX_ARRAY_SIZE) { - throw std::out_of_range("DBus message error: Maximum size exceeded"); - } - - // Add the 16 bytes of the header - m_headerSize += 16; - // The header MUST finish on an 8 byte boundary - m_headerSize += (m_headerSize % 8 == 0) ? 0 : 8 - (m_headerSize % 8); - - m_State = STATE_UNMARSHALLHEADER; - return true; - } - return false; -} - -bool DBus::MessageProtocol::unmarshallHeader(OctetBuffer& buffer) -{ - if (buffer.size() >= m_headerSize) { - // When all of the header is buffered the header can be unmarshalled - MessageIStream istream(buffer.data(), m_headerSize, - SwapRequired(buffer[0])); - m_HeaderStruct.unmarshall(istream); - buffer.remove_prefix(m_headerSize); - // The body of the message is next - m_bodySize = Type::asUint32(m_HeaderStruct[4]); - - if ((m_headerSize + m_bodySize) > MAX_MESSAGE_SIZE) { - throw std::out_of_range("DBus message error: Maximum size exceeded"); - } - - m_State = STATE_GETBODY; - return true; - } - return false; -} - -bool DBus::MessageProtocol::getBody(OctetBuffer& buffer) -{ - if (buffer.size() >= m_bodySize) { - std::string body((const char*)buffer.data(), m_bodySize); - buffer.remove_prefix(m_bodySize); - onBodyComplete(body); - return true; + if (state == ReadState::Peek) { + buffer.resize(Message::Header::MinimumSize); } - return false; -} - -void DBus::MessageProtocol::processData(OctetBuffer& buffer) -{ - while (buffer.size()) { - if (m_State == STATE_GETHEADERSIZE) { - if (!getHeaderSize(buffer)) { - return; - } - } - - if (m_State == STATE_UNMARSHALLHEADER) { - if (!unmarshallHeader(buffer)) { - return; - } - } - - if (m_State == STATE_GETBODY) { - if (!getBody(buffer)) { - return; - } - } + else { // ReadState::Receive + OctetBuffer header = { buffer.data(), buffer.size() }; + buffer.resize(Message::Header::getMessageSize(header)); } + return { buffer.data(), buffer.size() }; } -void DBus::MessageProtocol::onReceiveData(OctetBuffer& buffer) +void DBus::MessageProtocol::asyncReadMessage(ReadState state) { - // If a whole message is contained in the buffer - // it can be processed without copying but only if - // there is no data already cached - if (m_octetCache.empty()) { - processData(buffer); - } - - // Append any remaining data to the data cache - m_octetCache.append(buffer.data(), buffer.size()); - buffer.remove_prefix(buffer.size()); - - // If the data cache is not empty process it - if (!m_octetCache.empty()) { - OctetBuffer cachedBuffer(m_octetCache.data(), m_octetCache.size()); - processData(cachedBuffer); - m_octetCache.erase(0, m_octetCache.size() - cachedBuffer.size()); + if (state == ReadState::Peek) { + m_transport->asyncPeek( + makeReadBuffer(state, m_messageBuffer), + [self = shared_from_this()] + (const error_code& error, std::size_t bytes_read) + { + if (error) + return self->invokeErrorHandler(0, error); + if (bytes_read == 0) + return self->releasePendingHandlers(); + if (bytes_read < Message::Header::MinimumSize) + return self->invokeErrorHandler(0, { + "short read peeking header", + "message protocol :: read message" }); + + self->asyncReadMessage(ReadState::Receive); + }); + } else { + m_transport->asyncRead( + makeReadBuffer(state, m_messageBuffer), + m_unixFdBuffer, + [self = shared_from_this()] + (error_code error, std::size_t bytes_read) mutable + { + if (error) + return self->invokeErrorHandler(0, error); + if (bytes_read == 0) + return self->releasePendingHandlers(); + + MessageBuffer& msg = self->m_messageBuffer; + UnixFdBuffer& fds = self->m_unixFdBuffer; + OctetBuffer message(msg.data(), msg.size(), fds); + + if (bytes_read < Message::Header::MinimumSize || + bytes_read < Message::Header::getMessageSize(message)) + return self->invokeErrorHandler(0, { + "short read receiving message", + "message protocol :: read message" }); + + Log::write(Log::TRACE, "\nDBus :: Receive : Message Data : %ld bytes, %ld FDs\n", + msg.size(), fds.size()); + Log::writeHex(Log::TRACE, " ", msg.data(), msg.size()); + self->dispatchMessage(message); + + msg.clear(); + fds.clear(); + self->asyncReadMessage(ReadState::Peek); + }); } } -void DBus::MessageProtocol::onBodyComplete(const std::string& body) +void DBus::MessageProtocol::dispatchMessage(OctetBuffer& message) { - Log::write(Log::TRACE, "DBus :: Unmarshall : Body complete.\n"); - - try - { - uint8_t type = Type::asByte(m_HeaderStruct[1]); - std::lock_guard lock(m_CallbackMutex); - switch (type) { - case TYPE_METHOD: - m_MethodCallCallback(Message::MethodCall(m_HeaderStruct, body)); - break; - - case TYPE_METHOD_RETURN: - m_MethodReturnCallback(Message::MethodReturn(m_HeaderStruct, body)); - break; - - case TYPE_ERROR: - m_ErrorCallback(Message::Error(m_HeaderStruct, body)); - break; - - case TYPE_SIGNAL: - m_SignalCallback(Message::Signal(m_HeaderStruct, body)); - break; - - default: - Log::write(Log::WARNING, "DBus :: Unknown message type %d received\n", - type); - } - Log::write(Log::TRACE, - "DBus :: Message type %d dispatched : new message initialized\n", - type); + m_stats.bytes_recv += message.size(); + const Message::Header header(message); + Log::write(Log::TRACE, "DBus :: Recv : dispatching %s message\n", + Message::typeString(header.type).c_str()); + + switch (header.type) { + case Message::Type::MethodCall: + ++m_stats.count_recv_methodcalls; + invokeMethodCallHandler(header.getFullName(), Message::MethodCall(header, message)); + break; + + case Message::Type::MethodReturn: + ++m_stats.count_recv_methodreturns; + invokeMethodReturnHandler(header.replySerial, Message::MethodReturn(header, message)); + break; + + case Message::Type::Signal: + ++m_stats.count_recv_signals; + invokeSignalHandler(header.getFullName(), Message::Signal(header, message)); + break; + + case Message::Type::Error: + ++m_stats.count_recv_errors; + invokeErrorHandler(header.replySerial, Message::Error(header, message)); + break; + + default: + Log::write(Log::WARNING, "DBus :: Ignoring unknown message type %d\n", + static_cast>(header.type)); } - catch(const std::exception & e) - { - // Catch exceptions from std::function such as bad_function_call. No action required. - Log::write(Log::INFO, "DBus :: Exception caught in onBodyComplete: %s\n", e.what()); - } - - startMessage(); } diff --git a/src/dbus_messageprotocol.h b/src/dbus_messageprotocol.h index fea8128..25c73c1 100644 --- a/src/dbus_messageprotocol.h +++ b/src/dbus_messageprotocol.h @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -15,71 +16,373 @@ // file named COPYING. If you do not have this file see // . -#ifndef DBUS_MESSAGEPROTOCOL -#define DBUS_MESSAGEPROTOCOL +#pragma once -#include +#include "dbus_asio.h" +#include "dbus_error.h" #include "dbus_message.h" -#include "dbus_octetbuffer.h" +#include "dbus_messageostream.h" #include "dbus_type_struct.h" +#include "dbus_transport.h" + +#include +#include namespace DBus { -class MessageProtocol { +using MessageBuffer = std::vector; + +class MessageProtocol : public std::enable_shared_from_this { public: - MessageProtocol(); - - void reset(); - void - setMethodCallHandler(const Message::CallbackFunctionMethodCall& callback); - void - setMethodReturnHandler(const Message::CallbackFunctionMethodReturn& callback); - void setErrorHandler(const Message::CallbackFunctionError& callback); - void setSignalHandler(const Message::CallbackFunctionSignal& callback); - - void onReceiveData(OctetBuffer& buffer); - void onBodyComplete(const std::string& body); - -private: - enum { STATE_GETHEADERSIZE, - STATE_UNMARSHALLHEADER, - STATE_GETBODY }; - - enum { - TYPE_METHOD = 1, - TYPE_METHOD_RETURN = 2, - TYPE_ERROR = 3, - TYPE_SIGNAL = 4 + using Ptr = std::shared_ptr; + + struct Statistics { + std::size_t count_send_errors; + std::size_t count_send_signals; + std::size_t count_send_methodcalls; + std::size_t count_send_methodreturns; + std::size_t count_recv_errors; + std::size_t count_recv_signals; + std::size_t count_recv_methodcalls; + std::size_t count_recv_methodreturns; + std::size_t bytes_send; + std::size_t bytes_recv; }; - size_t m_State; - size_t m_headerSize; - size_t m_bodySize; - // TODO:?? Move the handler into a separate message class? We can't get 2 - // interspersed message so it's a 1:1 relationship between protocol handler - // and its message, but it might be useful elsewhere. - DBus::Type::Struct m_HeaderStruct; - std::basic_string m_octetCache; - - // - std::recursive_mutex m_CallbackMutex; - Message::CallbackFunctionMethodCall m_MethodCallCallback; - Message::CallbackFunctionMethodReturn m_MethodReturnCallback; - Message::CallbackFunctionError m_ErrorCallback; - Message::CallbackFunctionSignal m_SignalCallback; - - void startMessage(); - - bool getHeaderSize(OctetBuffer& buffer); - bool unmarshallHeader(OctetBuffer& buffer); - bool getBody(OctetBuffer& buffer); - void processData(OctetBuffer& buffer); - - void onReceiveMethodCall(const DBus::Message::MethodCall& result) {} - void onReceiveMethodReturn(const DBus::Message::MethodReturn& result) {} - void onReceiveError(const DBus::Message::Error& result) {} - void onReceiveSignal(const DBus::Message::Signal& result) {} + static Ptr start(asio::io_context& ioContext, Transport::Ptr transport); + + void stop() + { + stopping = true; + m_transport->disconnect(); + } + + template + auto sendMethodCall( + std::uint32_t serial, + const Message::MethodCall& methodCall, + CompletionToken&& token) + { + Log::write(Log::TRACE, "DBus :: Send : preparing METHOD_CALL message\n"); + auto replySerial = methodCall.isReplyExpected() ? serial : 0; + + return asio::async_initiate< + CompletionToken, void(const Error&, const Message::MethodReturn&)>( + [this](auto&& handler, MessageOStream&& data, uint32_t replySerial) mutable + { + ++m_stats.count_send_methodcalls; + m_stats.bytes_send += data.size(); + auto payload = std::make_shared(std::move(data)); + + // Store handler if there is a reply expected + if (replySerial) { + storeMethodReturnHandler(replySerial, std::move(handler)); + m_transport->asyncWrite( + payload, + [self = shared_from_this(), replySerial, payload] + (const error_code& error, std::size_t) mutable -> void + { + if (error) + self->invokeErrorHandler(replySerial, error); + }); + } else { + m_transport->asyncWrite( + payload, + [self = shared_from_this(), payload, handler = std::move(handler)] + (const error_code& error, std::size_t) mutable -> void + { + handler(error, Message::MethodReturn()); + }); + } + }, + token, methodCall.marshall(serial), replySerial); + } + + template + auto sendMethodReturn( + std::uint32_t serial, + const Message::MethodReturn& methodReturn, + CompletionToken&& token) + { + Log::write(Log::TRACE, "DBus :: Send : preparing METHOD_RETURN message\n"); + return asio::async_initiate< + CompletionToken, void(const Error&)>( + [this](auto&& handler, MessageOStream&& data) { + ++m_stats.count_send_methodreturns; + m_stats.bytes_send += data.size(); + auto payload = std::make_shared(std::move(data)); + m_transport->asyncWrite( + payload, + [self = shared_from_this(), payload, handler = std::move(handler)] + (const error_code& error, std::size_t) mutable -> void + { + handler(error); + }); + }, token, methodReturn.marshall(serial)); + } + + template + auto sendSignal( + std::uint32_t serial, + const Message::Signal& signal, + CompletionToken&& token) + { + Log::write(Log::TRACE, "DBus :: Send : preparing SIGNAL message\n"); + return asio::async_initiate< + CompletionToken, void(const Error&)>( + [this](auto&& handler, MessageOStream&& data) { + ++m_stats.count_send_signals; + m_stats.bytes_send += data.size(); + auto payload = std::make_shared(std::move(data)); + m_transport->asyncWrite( + payload, + [self = shared_from_this(), payload, handler = std::move(handler)] + (const error_code& error, std::size_t) mutable -> void + { + handler(error); + }); + }, token, signal.marshall(serial)); + } + + template + auto sendError( + std::uint32_t serial, + const Message::Error& error, + CompletionToken&& token) + { + Log::write(Log::TRACE, "DBus :: Send : preparing ERROR message\n"); + return asio::async_initiate< + CompletionToken, void(const Error&)>( + [this](auto&& handler, MessageOStream&& data) { + ++m_stats.count_send_errors; + m_stats.bytes_send += data.size(); + auto payload = std::make_shared(std::move(data)); + m_transport->asyncWrite( + payload, + [self = shared_from_this(), payload, handler = std::move(handler)] + (const error_code& error, std::size_t) mutable -> void + { + handler(error); + }); + }, token, error.marshall(serial)); + } + + + template + auto receiveMethodCall( + const std::string& interface, + CompletionToken&& token) + { + return asio::async_initiate< + CompletionToken, void(const Message::MethodCall&)>( + [this](auto&& handler, const std::string& interface) { + this->storeMethodCallHandler(interface, std::forward(handler)); + }, token, interface); + } + + template + auto receiveSignal( + const std::string& signal, + CompletionToken&& token) + { + return asio::async_initiate< + CompletionToken, void(const Message::Signal&)>( + [this](auto&& handler, const std::string& signal) { + this->storeSignalHandler(signal, std::forward(handler)); + }, token, signal); + } + + template + auto receiveError(CompletionToken&& token) + { + return asio::async_initiate< + CompletionToken, void(const Error&)>( + [this](auto&& handler) { + m_errorHandler = StoredErrorHandler::create( + m_ioContext, std::move(handler)); + }, token); + } + + struct Statistics getStats() { return m_stats; } + +protected: + MessageProtocol(asio::io_context& ioContext, Transport::Ptr transport); + + template + bool storeMethodReturnHandler(uint32_t serial, Handler&& handler) + { + const bool ok = m_methodReturnHandlers.emplace( + std::make_pair( + serial, + StoredMethodReturnHandler::create( + m_ioContext, + std::move(handler)))).second; + if (!ok) + Log::write(Log::ERROR, "DBus :: METHOD RETURN : could not store handler\n"); + return ok; + } + + template + bool storeMethodCallHandler(const std::string& interface, Handler&& handler) + { + const bool ok = m_methodCallHandlers.emplace( + std::make_pair( + interface, + StoredMethodCallHandler::create( + m_ioContext, + std::move(handler)))).second; + if (!ok) + Log::write(Log::ERROR, "DBus :: METHOD CALL : could not store handler\n"); + return ok; + } + + template + bool storeSignalHandler(const std::string& signal, Handler&& handler) + { + const bool ok = m_signalHandlers.emplace( + std::make_pair( + signal, + StoredSignalHandler::create( + m_ioContext, + std::move(handler)))).second; + if (!ok) + Log::write(Log::ERROR, "DBus :: SIGNAL : could not store handler\n"); + return ok; + } + + void invokeMethodReturnHandler( + uint32_t replySerial, + const Message::MethodReturn& methodReturn) + { + const auto it = m_methodReturnHandlers.find(replySerial); + if (it != m_methodReturnHandlers.end()) { + it->second->invoke({}, methodReturn); + m_methodReturnHandlers.erase(it); + return; + } + + Log::write(Log::WARNING, "DBus :: METHOD RETURN : unexpected : " \ + "reply to #%u\n", replySerial); + } + + void invokeMethodCallHandler( + const std::string& interface, + const Message::MethodCall& methodCall) + { + // Invoke handler for this exact interface + auto it = m_methodCallHandlers.find(interface); + if (it != m_methodCallHandlers.end()) { + it->second->invoke(methodCall); + m_methodCallHandlers.erase(it); + return; + } + + // Invoke handler for any method of this interface + const std::string any_method(interface, 0, interface.rfind('.')); + it = m_methodCallHandlers.find(any_method); + if (it != m_methodCallHandlers.end()) { + it->second->invoke(methodCall); + m_methodCallHandlers.erase(it); + return; + } + + // Invoke handler for any interface (catch all) + it = m_methodCallHandlers.find(""); + if (it != m_methodCallHandlers.end()) { + it->second->invoke(methodCall); + m_methodCallHandlers.erase(it); + return; + } + + Log::write(Log::INFO, "DBus :: METHOD CALL : unhandled : %s\n", + interface.c_str()); + } + + void invokeSignalHandler( + const std::string& name, + const Message::Signal& signal) + { + // Invoke handler for this exact signal + auto it = m_signalHandlers.find(name); + if (it != m_signalHandlers.end()) { + it->second->invoke(signal); + m_signalHandlers.erase(it); + return; + } + + // Invoke handler for any signal (catch all) + it = m_signalHandlers.find(""); + if (it != m_signalHandlers.end()) { + it->second->invoke(signal); + m_signalHandlers.erase(it); + return; + } + + Log::write(Log::INFO, "DBus :: SIGNAL : %s unhandled\n", + name.c_str()); + } + + void invokeErrorHandler( + uint32_t replySerial, + const Error& error) + { + if (replySerial) { + const auto it = m_methodReturnHandlers.find(replySerial); + if (it != m_methodReturnHandlers.end()) { + it->second->invoke(error, {}); + m_methodReturnHandlers.erase(it); + return; + } + } + + if (m_errorHandler) { + m_errorHandler->invoke(error); + m_errorHandler.reset(); + return; + } + + Log::write(Log::WARNING, "DBus :: ERROR : unhandled : %s " \ + "(reply to #%u)\n", error.message.c_str(), replySerial); + } + + void releasePendingHandlers() + { + for (auto& elem : m_methodReturnHandlers) + elem.second->invoke({}, {}); + for (auto& elem : m_methodCallHandlers) + elem.second->invoke({}); + for (auto& elem : m_signalHandlers) + elem.second->invoke({}); + if (m_errorHandler) + m_errorHandler->invoke({}); + } + + using StoredMethodReturnHandler = StoredToken; + using StoredMethodCallHandler = StoredToken; + using StoredSignalHandler = StoredToken; + using StoredErrorHandler = StoredToken; + + std::map m_methodReturnHandlers; + std::map m_methodCallHandlers; + std::map m_signalHandlers; + StoredErrorHandler::Ptr m_errorHandler; + + using ReadBuffer = asio::mutable_buffer; + + enum class ReadState { Peek, Receive }; + ReadBuffer makeReadBuffer(ReadState state, MessageBuffer& buffer); + + void asyncReadMessage(ReadState state); + void dispatchMessage(OctetBuffer& message); + + bool stopping = false; + MessageBuffer m_messageBuffer; + UnixFdBuffer m_unixFdBuffer; + asio::io_context& m_ioContext; + Transport::Ptr m_transport; + + struct Statistics m_stats = {}; }; + } // namespace DBus -#endif // DBUS_MESSAGEPROTOCOL diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5a07d91..5d52fe6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -117,7 +117,7 @@ target_include_directories(dbus_octetbuffer_unittest PRIVATE ../extern) target_link_libraries(dbus_octetbuffer_unittest LINK_PRIVATE dbus-asio) add_test(NAME run_dbus_octetbuffer_unittest COMMAND dbus_octetbuffer_unittest) -add_executable(dbus_messageprotocol_unittest dbus_messageprotocol_unittest.cpp test_main.cpp) -target_include_directories(dbus_messageprotocol_unittest PRIVATE ../extern) -target_link_libraries(dbus_messageprotocol_unittest LINK_PRIVATE dbus-asio) -add_test(NAME run_messageprotocol_unittest COMMAND dbus_messageprotocol_unittest) +#add_executable(dbus_messageprotocol_unittest dbus_messageprotocol_unittest.cpp test_main.cpp) +#target_include_directories(dbus_messageprotocol_unittest PRIVATE ../extern) +#target_link_libraries(dbus_messageprotocol_unittest LINK_PRIVATE dbus-asio) +#add_test(NAME run_messageprotocol_unittest COMMAND dbus_messageprotocol_unittest) From 4d13147fb052428851ffa9745a5709df31d029e5 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Thu, 14 Apr 2022 01:56:43 +0200 Subject: [PATCH 15/24] add DBus::Connection This is the class to tie all things together. It replaces DBus::Native with pretty much comparable functionality. Just think the name suits better. Offers an interface to connect to DBus and deal with messages on a low level. There are also helper functions for DBus interfaces and messages. Signed-off-by: Heiko Hund --- CMakeLists.txt | 4 +- src/dbus.h | 2 +- src/dbus_connection.cpp | 35 +++ src/dbus_connection.h | 412 +++++++++++++++++++++++++++++++++++ src/dbus_native.cpp | 267 ----------------------- src/dbus_native.h | 173 --------------- src/dbus_native_messages.cpp | 182 ---------------- 7 files changed, 449 insertions(+), 626 deletions(-) create mode 100644 src/dbus_connection.cpp create mode 100644 src/dbus_connection.h delete mode 100644 src/dbus_native.cpp delete mode 100644 src/dbus_native.h delete mode 100644 src/dbus_native_messages.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b93c79..a8db509 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=gold") set(HEADER_FILES src/dbus_asio.h src/dbus_auth.h + src/dbus_connection.h src/dbus_error.h src/dbus.h src/dbus_introspectable.h @@ -17,7 +18,6 @@ set(HEADER_FILES src/dbus_messageprotocol.h src/dbus_messageostream.h src/dbus_messageistream.h - src/dbus_native.h src/dbus_names.h src/dbus_octetbuffer.h src/dbus_platform.h @@ -52,8 +52,6 @@ add_library(dbus-asio SHARED src/dbus_message.cpp src/dbus_messageistream.cpp src/dbus_messageprotocol.cpp - src/dbus_native.cpp - src/dbus_native_messages.cpp src/dbus_names.cpp src/dbus_octetbuffer.cpp src/dbus_platform.cpp diff --git a/src/dbus.h b/src/dbus.h index d10f469..f67d7c1 100644 --- a/src/dbus.h +++ b/src/dbus.h @@ -54,8 +54,8 @@ #include "dbus_message.h" #include "dbus_messageostream.h" #include "dbus_messageprotocol.h" -#include "dbus_native.h" #include "dbus_transport.h" +#include "dbus_connection.h" // Introspective library #include "dbus_introspectable.h" diff --git a/src/dbus_connection.cpp b/src/dbus_connection.cpp new file mode 100644 index 0000000..d848c15 --- /dev/null +++ b/src/dbus_connection.cpp @@ -0,0 +1,35 @@ +// This file is part of dbus-asio +// Copyright 2022 OpenVPN Inc. +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, version 3, or at your +// option any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The GNU Lesser General Public License version 3 is included in the +// file named COPYING. If you do not have this file see +// . + +#include "dbus_connection.h" + +DBus::Connection::Ptr +DBus::Connection::create(DBus::asio::io_context& ioContext) +{ + struct ShareableConnection : public Connection { + ShareableConnection(DBus::asio::io_context& ioContext) + : Connection(ioContext) {}; + }; + return std::make_shared(ioContext); +} + +DBus::Connection::Connection(DBus::asio::io_context& ioContext) + : m_ioContext(ioContext) + , m_transport(DBus::Transport::create(ioContext)) +{ +} + diff --git a/src/dbus_connection.h b/src/dbus_connection.h new file mode 100644 index 0000000..686e7b2 --- /dev/null +++ b/src/dbus_connection.h @@ -0,0 +1,412 @@ +// This file is part of dbus-asio +// Copyright 2022 OpenVPN Inc. +// +// This library is free software: you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License +// as published by the Free Software Foundation, version 3, or at your +// option any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// The GNU Lesser General Public License version 3 is included in the +// file named COPYING. If you do not have this file see +// . + +#pragma once + +#include "dbus_asio.h" +#include "dbus_auth.h" +#include "dbus_messageprotocol.h" +#include "dbus_transport.h" + +#include +#include + +namespace DBus { + +static constexpr char const *Name = "org.freedesktop.DBus"; +static constexpr char const *Object = "/org/freedesktop/DBus"; +static constexpr char const *Interface = "org.freedesktop.DBus"; + +enum RequestNameFlags { + None = 0, + AllowReplacement = 1, + ReplaceExisting = 2, + DoNotQueue = 4 +}; + +enum RequestNameReply { + PrimaryOwner = 1, + InQueue = 2, + Exists = 3, + AlreadyOwner = 4 +}; + +enum ReleaseNameReply { + Released = 1, + NonExistent = 2, + NotOwner = 3 +}; + +class Connection : public std::enable_shared_from_this { +public: + using Ptr = std::shared_ptr; + + static Ptr create(asio::io_context& ioContext) + { + struct ShareableConnection : public Connection { + ShareableConnection(asio::io_context& ioContext) + : Connection(ioContext) {}; + }; + return std::make_shared(ioContext); + } + + template + auto connect( + const std::string& busPath, + AuthenticationProtocol::Ptr authProto, + CompletionToken&& token) + { + enum { starting, connecting, authenticating, requestingName }; + return asio::async_compose< + CompletionToken, void(const Error&, const std::string&, const std::string&)>( + [self = shared_from_this(), state = starting, &busPath, authProto] + (auto& composition, const Error& error = {}, const std::string& data = {}) mutable + { + switch (state) { + case starting: + state = connecting; + self->m_transport->asyncConnect(busPath, std::move(composition)); + return; + + case connecting: + if (error) + break; + state = authenticating; + authProto->asyncAuthenticate(self->m_transport, std::move(composition)); + return; + + case authenticating: + if (error) + break; + state = requestingName; + self->m_authStats = authProto->getStats(); + self->m_serverGuid = data; + self->m_msgProto = MessageProtocol::start(self->m_ioContext, self->m_transport); + self->hello(std::move(composition)); + return; + + case requestingName: + self->m_uniqueName = data; + break; + } + composition.complete(error, self->m_uniqueName, self->m_serverGuid); + }, token); + } + + void disconnect() + { + m_nextSerial = 1; + m_msgProto->stop(); + } + + template + auto sendMethodCall( + const Message::MethodCall& methodCall, + CompletionToken&& token) + { + return m_msgProto->sendMethodCall( + m_nextSerial++, methodCall, std::forward(token)); + } + + template + auto sendMethodReturn( + const Message::MethodReturn& methodReturn, + CompletionToken&& token) + { + return m_msgProto->sendMethodReturn( + m_nextSerial++, methodReturn, std::forward(token)); + } + + template + auto sendSignal( + const Message::Signal& signal, + CompletionToken&& token) + { + return m_msgProto->sendSignal( + m_nextSerial++, signal, std::forward(token)); + } + + template + auto sendError( + const Message::Error& error, + CompletionToken&& token) + { + return m_msgProto->sendError( + m_nextSerial++, error, std::forward(token)); + } + + template + auto receiveMethodCall( + const std::string& interface, + CompletionToken&& token) + { + return m_msgProto->receiveMethodCall( + interface, std::forward(token)); + } + + template + auto receiveSignal( + const std::string& signal, + CompletionToken&& token) + { + return m_msgProto->receiveSignal( + signal, std::forward(token)); + } + + template + auto receiveError( + CompletionToken&& token) + { + return m_msgProto->receiveError( + std::forward(token)); + } + + + ////////////////////////////////////////////// + // Methods for the standard DBus interfaces // + ////////////////////////////////////////////// + + template + auto getAllProperties( + const BusName& destination, + const ObjectPath& path, + const InterfaceName& interface, + CompletionToken&& token) + { + return sendMethodCall({ destination, + {path, "org.freedesktop.DBus.Properties", "GetAll"}, {interface} }, + std::forward(token)); + } + + template + auto getProperty( + const BusName& destination, + const ObjectPath& path, + const InterfaceName& interface, + const MemberName& property, + CompletionToken&& token) + { + return sendMethodCall({ destination, + {path, "org.freedesktop.DBus.Properties", "Get"}, {interface, property} }, + std::forward(token)); + } + + template + auto setProperty( + const BusName& destination, + const ObjectPath& path, + const InterfaceName& interface, + const MemberName& property, + CompletionToken&& token) + { + return sendMethodCall({ destination, + {path, "org.freedesktop.DBus.Properties", "Set"}, {interface, property} }, + std::forward(token)); + } + + + ////////////////////////////////////////// + // Methods for the Message Bus Messages // + ////////////////////////////////////////// + + template + auto requestName( + const WellKnownName& name, + std::uint32_t flags, + CompletionToken&& token) + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "RequestName"}, {name, flags} }, + std::forward(token)); + } + + template + auto releaseName( + const WellKnownName& name, + CompletionToken&& token) + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "ReleaseName"}, {name} }, + std::forward(token)); + } + + template + auto listQueuedOwners( + const WellKnownName& name, + CompletionToken&& token) + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "ListQueuedOwners"}, {name} }, + std::forward(token)); + } + + template + auto listNames(CompletionToken&& token) + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "ListNames"} }, + std::forward(token)); + } + + template + auto listActivatableNames(CompletionToken&& token) + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "ListActivatableNames"} }, + std::forward(token)); + } + + template + auto nameHasOwner( + const WellKnownName& name, + CompletionToken&& token) + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "NameHasOwner"}, {name} }, + std::forward(token)); + } + + template + auto getNameOwner( + const WellKnownName& name, + CompletionToken&& token) + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "GetNameOwner"}, {name} }, + std::forward(token)); + } + + template + auto getConnectionUnixUser( + const BusName& name, + CompletionToken&& token) + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "GetConnectionUnixUser"}, {name} }, + std::forward(token)); + } + + template + auto getConnectionUnixProcessID( + const BusName& name, + CompletionToken&& token) + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "GetConnectionUnixProcessID"}, {name} }, + std::forward(token)); + } + + template + auto addMatch(const MatchRule& rule, CompletionToken&& token) const + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "AddMatch"}, {rule.str()} }, + std::forward(token)); + } + + template + auto removeMatch(const MatchRule& rule, CompletionToken&& token) const + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "RemoveMatch"}, {rule.str()} }, + std::forward(token)); + } + + template + auto getId(CompletionToken&& token) + { + return sendMethodCall({ DBus::Name, + {DBus::Object, DBus::Interface, "GetId"} }, + std::forward(token)); + } + + template + auto becomeMonitor( + const std::vector& rules, + CompletionToken&& token) + { + DBus::Type::Array rulesArray("as"); + for (const auto& rule : rules) + rulesArray.add(rule.str()); + + return sendMethodCall({DBus::Name, + {DBus::Object, DBus::Interface, "BecomeMonitor"}, {rulesArray, Type::Uint32(0)} }, + std::forward(token)); + } + + std::string getStats() const + { + std::ostringstream stats; + MessageProtocol::Statistics msgStats = m_msgProto->getStats(); + + stats << "Connection stats:" << '\n'; + stats << " count_send_auth_commands: " << m_authStats.count_send << '\n'; + stats << " count_receive_auth_commands: " << m_authStats.count_recv << '\n'; + stats << " count_send_methodcalls: " << msgStats.count_send_methodcalls << '\n'; + stats << " count_receive_methodcalls: " << msgStats.count_recv_methodcalls << '\n'; + stats << " count_send_methodreturns: " << msgStats.count_send_methodreturns << '\n'; + stats << " count_receive_methodreturns: " << msgStats.count_recv_methodreturns << '\n'; + stats << " count_send_errors: " << msgStats.count_send_errors << '\n'; + stats << " count_receive_errors: " << msgStats.count_recv_errors << '\n'; + stats << " count_send_signals: " << msgStats.count_send_signals << '\n'; + stats << " count_receive_signals: " << msgStats.count_recv_signals << '\n'; + stats << " bytes_send_auth: " << m_authStats.bytes_send << '\n'; + stats << " bytes_receive_auth: " << m_authStats.bytes_recv << '\n'; + stats << " bytes_send_message: " << msgStats.bytes_send << '\n'; + stats << " bytes_receive_message: " << msgStats.bytes_recv << '\n'; + + return stats.str(); + } + +protected: + Connection(asio::io_context& ioContext) + : m_ioContext(ioContext) + , m_transport(Transport::create(ioContext)) + {} + + template + auto hello(CompletionToken&& token) + { + return asio::async_initiate< + CompletionToken, void(const Error&, const std::string&)>( + [this](auto&& handler) mutable + { + sendMethodCall({ DBus::Name, {DBus::Object, DBus::Interface, "Hello"} }, + [self = shared_from_this(), handler = std::forward(handler)] + (const Error& error, const Message::MethodReturn& methodReturn) mutable + { + if (error) + handler(error, ""); + else + handler(error, DBus::Type::asString(methodReturn.getParameter(0))); + }); + }, token); + } + + struct AuthenticationProtocol::Statistics m_authStats = {}; + + asio::io_context& m_ioContext; + Transport::Ptr m_transport; + MessageProtocol::Ptr m_msgProto; + + std::string m_serverGuid; + std::string m_uniqueName; + + std::uint32_t m_nextSerial = 1; +}; + +} diff --git a/src/dbus_native.cpp b/src/dbus_native.cpp deleted file mode 100644 index acca103..0000000 --- a/src/dbus_native.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . - -#include "dbus_native.h" -#include "dbus_auth.h" -#include "dbus_log.h" -#include "dbus_matchrule.h" -#include "dbus_message.h" -#include "dbus_messageprotocol.h" - -std::string DBus::Native::DBusDaemon("org.freedesktop.DBus"); - -DBus::Native::Native(const std::string& busname) - : m_MessageProtocol(new DBus::MessageProtocol()) -{ - m_Transport.reset(new DBus::Transport(busname)); - m_AuthenticationProtocol.reset(new DBus::AuthenticationProtocol(m_Transport)); - m_Transport->setDataHandler( - std::bind(&Native::onReceiveAuthData, this, std::placeholders::_1)); - m_MessageProtocol->setMethodCallHandler( - std::bind(&Native::onReceiveMethodCall, this, std::placeholders::_1)); - m_MessageProtocol->setMethodReturnHandler( - std::bind(&Native::onReceiveMethodReturn, this, std::placeholders::_1)); - m_MessageProtocol->setErrorHandler( - std::bind(&Native::onReceiveError, this, std::placeholders::_1)); - m_MessageProtocol->setSignalHandler( - std::bind(&Native::onReceiveSignal, this, std::placeholders::_1)); -} - -DBus::Native::~Native() -{ - m_Transport->setDataHandler([](DBus::OctetBuffer&) {}); - m_MessageProtocol->setMethodCallHandler( - [](const DBus::Message::MethodCall& method) {}); - m_MessageProtocol->setMethodReturnHandler( - [](const DBus::Message::MethodReturn& method) {}); - m_MessageProtocol->setErrorHandler([](const DBus::Message::Error& method) {}); - m_MessageProtocol->setSignalHandler( - [](const DBus::Message::Signal& method) {}); -} - -void DBus::Native::BeginAuth(AuthenticationProtocol::AuthRequired type) -{ - m_AuthenticationProtocol->sendAuth(type); -} - -void DBus::Native::onReceiveAuthData(OctetBuffer& buffer) -{ - try { - const size_t bufferSize = buffer.size(); - if (m_AuthenticationProtocol->onReceiveData(buffer)) { - m_Stats.bytes_auth = bufferSize - buffer.size(); - onReceiveMessageData(buffer); - m_Transport->setDataHandler(std::bind(&Native::onReceiveMessageData, this, - std::placeholders::_1)); - } - } catch (const std::exception& e) { - DBus::Log::write( - DBus::Log::ERROR, - "DBus :: Native : onReceiveAuthData has thrown an exception : %s\n", - e.what()); - m_AuthenticationProtocol->reset(); - } -} - -void DBus::Native::onReceiveMessageData(OctetBuffer& buffer) -{ - try { - // AFAIK, we can ever leave the message protocol, once auth'd, so the return - // value can be safely ignored. (To exit the communication we just close the - // socket.) - m_Stats.bytes_message += buffer.size(); - m_MessageProtocol->onReceiveData(buffer); - } catch (const std::exception& e) { - DBus::Log::write( - DBus::Log::ERROR, - "DBus :: Native : onReceiveMessageData has thrown an exception : %s\n", - e.what()); - m_MessageProtocol->reset(); - } -} - -void DBus::Native::registerMethodCallHandler( - const std::string& name, - const DBus::Message::CallbackFunctionMethodCall& handler) -{ - boost::recursive_mutex::scoped_lock guard(m_MethodCallMapMutex); - - m_MethodCallMap.emplace(name, handler); -} - -void DBus::Native::registerSignalHandler( - const std::string& name, - const DBus::Message::CallbackFunctionSignal& handler) -{ - boost::recursive_mutex::scoped_lock guard(m_SignalMapMutex); - - m_SignalMap.emplace(name, handler); -} - -void DBus::Native::unRegisterMethodCallHandler( - const std::string& name) -{ - boost::recursive_mutex::scoped_lock guard(m_MethodCallMapMutex); - - m_MethodCallMap.erase(name); -} - -void DBus::Native::unRegisterSignalHandler( - const std::string& name) -{ - boost::recursive_mutex::scoped_lock guard(m_SignalMapMutex); - - m_SignalMap.erase(name); -} - -// -// Send messages out -// -void DBus::Native::sendMethodCall( - const std::string& destination, const DBus::Message::MethodCall& method, - const DBus::Message::CallbackFunctionMethodReturn& success, - const DBus::Message::CallbackFunctionError& failure) -{ - ++m_Stats.count_sent_methodcalls; - if (method.isReplyExpected()) { - boost::recursive_mutex::scoped_lock guard(m_MessageQueueMutex); - - m_MessageQueue.emplace(method.getSerial(), - Native::CallbackPair(success, failure)); - } - m_Transport->sendString(method.marshall(destination)); -} - -void DBus::Native::sendMethodReturn(const std::string& destination, - const DBus::Message::MethodReturn& result) -{ - ++m_Stats.count_sent_methodreturns; - m_Transport->sendString(result.marshall(destination)); -} - -void DBus::Native::sendError(const std::string& destination, - const DBus::Message::Error& err) -{ - ++m_Stats.count_sent_errors; - m_Transport->sendString(err.marshall(destination)); -} - -void DBus::Native::sendSignal(const std::string& destination, - const DBus::Message::Signal& signal) -{ - ++m_Stats.count_sent_signals; - m_Transport->sendString(signal.marshall(destination)); -} - -void DBus::Native::broadcastSignal(const DBus::Message::Signal& signal) -{ - ++m_Stats.count_sent_signals; - m_Transport->sendString(signal.marshall("")); -} - -// -// Handle incoming messages -// -void DBus::Native::onReceiveMethodCall( - const DBus::Message::MethodCall& method) -{ - DBus::Log::write(DBus::Log::INFO, "DBus :: onReceiveMethodCall : %s\n", - method.getFullName().c_str()); - ++m_Stats.count_receive_methodcalls; - - boost::recursive_mutex::scoped_lock guard(m_MethodCallMapMutex); - auto it = m_MethodCallMap.find(method.getFullName()); - - if (it != m_MethodCallMap.end()) { - it->second(method); - } -} - -void DBus::Native::onReceiveMethodReturn( - const DBus::Message::MethodReturn& result) -{ - boost::recursive_mutex::scoped_lock guard(m_MessageQueueMutex); - auto it = m_MessageQueue.find(result.m_SerialReplyingTo); - ++m_Stats.count_receive_methodreturns; - - if (it != m_MessageQueue.end()) { - it->second.success(result); - m_MessageQueue.erase(it); - } -} - -void DBus::Native::onReceiveError(const DBus::Message::Error& error) -{ - boost::recursive_mutex::scoped_lock guard(m_MessageQueueMutex); - auto it = m_MessageQueue.find(error.getSerialOfReply()); - - if (it != m_MessageQueue.end()) { - it->second.failure(error); - m_MessageQueue.erase(it); - } - DBus::Log::write(DBus::Log::WARNING, "DBus :: Error : %s\n", - error.getMessage().c_str()); -} - -void DBus::Native::onReceiveSignal(const DBus::Message::Signal& signal) -{ - boost::recursive_mutex::scoped_lock guard(m_SignalMapMutex); - ++m_Stats.count_receive_signals; - - std::string fullname = signal.getHeaderInterface() + "." + signal.getHeaderMember(); - auto it = m_SignalMap.find(fullname); - - if (it != m_SignalMap.end()) { - it->second(signal); - } - - { - boost::recursive_mutex::scoped_lock guard(m_RulesMapMutex); - for (auto it : m_RulesMap) { - if (it.second.isMatched(signal)) { - it.second.invoke(signal); - } - } - } -} - -std::string DBus::Native::getStats() const -{ - std::stringstream ss; - - ss << "Native stats:" << std::endl; - ss << " count_sent_methodcalls: " << m_Stats.count_sent_methodcalls - << std::endl; - ss << " count_sent_methodreturns: " << m_Stats.count_sent_methodreturns - << std::endl; - ss << " count_sent_errors: " << m_Stats.count_sent_errors << std::endl; - ss << " count_sent_signals: " << m_Stats.count_sent_signals << std::endl; - - ss << " count_receive_methodcalls: " << m_Stats.count_receive_methodcalls - << std::endl; - ss << " count_receive_methodreturns: " << m_Stats.count_receive_methodreturns - << std::endl; - ss << " count_receive_errors: " << m_Stats.count_receive_errors << std::endl; - ss << " count_receive_signals: " << m_Stats.count_receive_signals - << std::endl; - - ss << " bytes_auth: " << m_Stats.bytes_auth << std::endl; - ss << " bytes_message: " << m_Stats.bytes_message << std::endl; - - ss << m_Transport->getStats(); - return ss.str(); -} diff --git a/src/dbus_native.h b/src/dbus_native.h deleted file mode 100644 index 61cdb8f..0000000 --- a/src/dbus_native.h +++ /dev/null @@ -1,173 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . - -#ifndef DBUS_NATIVE -#define DBUS_NATIVE - -#include "dbus_auth.h" -#include "dbus_matchrule.h" -#include "dbus_message.h" -#include "dbus_messageprotocol.h" -#include "dbus_transport.h" - -namespace DBus { - -class Native { -public: - Native(const std::string& busname); - ~Native(); - - void BeginAuth(AuthenticationProtocol::AuthRequired type); - void onReceiveAuthData(OctetBuffer& buffer); - void onReceiveMessageData(OctetBuffer& buffer); - - void - registerMethodCallHandler(const std::string& name, - const Message::CallbackFunctionMethodCall& handler); - void - unRegisterMethodCallHandler(const std::string& name); - // registerSignalHandler checks only for matching interfaces, and is a - // convenience method. It is preferably to use callAddMatch in most cases. - void registerSignalHandler(const std::string& name, - const Message::CallbackFunctionSignal& handler); - void unRegisterSignalHandler(const std::string& name); - - void - sendMethodCall(const std::string& destination, - const DBus::Message::MethodCall& msg, - const DBus::Message::CallbackFunctionMethodReturn& success = - [](const DBus::Message::MethodReturn& msg) {}, - const DBus::Message::CallbackFunctionError& failure = - [](const DBus::Message::Error& msg) {}); - void sendMethodReturn(const std::string& destination, - const DBus::Message::MethodReturn& result); - void sendError(const std::string& destination, - const DBus::Message::Error& err); - void sendSignal(const std::string& destination, - const DBus::Message::Signal& signal); - void broadcastSignal(const DBus::Message::Signal& signal); - - void onReceiveMethodCall(const DBus::Message::MethodCall& method); - void onReceiveMethodReturn(const Message::MethodReturn& reply); - void onReceiveError(const Message::Error& error); - void onReceiveSignal(const Message::Signal& signal); - - // Message Bus Messages (see dbus_native_messages.cpp for implementation) - // Q. Move elsewhere? (because of the myriad flag definitions these need) - void callHello(const Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure); - void callGetUnixProcessId( - const std::string& name, - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure); - void callGetConnectionUnixUser( - const std::string& name, - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure); - void callRequestName(const std::string& name, uint32_t flags, - const Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure); - void callReleaseName(const std::string& name, - const Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure); - void - callListQueuedOwners(const std::string& bus_name, - const Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure); - void callListNames(const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure); - void callListActivatableNames( - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure); - void callNameHasOwner(const std::string& name, - const Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure); - void callAddMatch(const std::string& rule, - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure, - const Message::CallbackFunctionSignal& handler); - void callRemoveMatch(const std::string& rule); - - std::string getStats() const; - - enum { - DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 0x01, - DBUS_NAME_FLAG_REPLACE_EXISTING = 0x02, - DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04 - } RequestNameFlagsIn; - - enum { - DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 0x01, - DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 0x02, - DBUS_REQUEST_NAME_REPLY_EXISTS = 0x03, - DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 0x04 - } RequestNameReplyOut; - - enum { - DBUS_RELEASE_NAME_REPLY_RELEASED = 0x01, - DBUS_RELEASE_NAME_REPLY_NON_EXISTENT = 0x02, - DBUS_RELEASE_NAME_REPLY_NOT_OWNER = 0x03 - } ReleaseNameReplyOut; - -private: - struct CallbackPair { - CallbackPair(const Message::CallbackFunctionMethodReturn& s, - const Message::CallbackFunctionError& f) - : success(s) - , failure(f) - { - } - - Message::CallbackFunctionMethodReturn success; - Message::CallbackFunctionError failure; - }; - - struct Stats { - size_t bytes_auth = 0; - size_t bytes_message = 0; - size_t count_sent_methodcalls = 0; - size_t count_sent_methodreturns = 0; - size_t count_sent_errors = 0; - size_t count_sent_signals = 0; - size_t count_receive_methodcalls = 0; - size_t count_receive_methodreturns = 0; - size_t count_receive_errors = 0; - size_t count_receive_signals = 0; - } m_Stats; - - mutable boost::recursive_mutex m_MessageQueueMutex; - std::map m_MessageQueue; - - mutable boost::recursive_mutex m_MethodCallMapMutex; - std::map m_MethodCallMap; - - mutable boost::recursive_mutex m_SignalMapMutex; - std::map m_SignalMap; - - mutable boost::recursive_mutex m_RulesMapMutex; - std::map m_RulesMap; - - std::unique_ptr m_MessageProtocol; - std::unique_ptr m_AuthenticationProtocol; - std::shared_ptr m_Transport; - -public: - static std::string DBusDaemon; -}; -} // namespace DBus - -#endif diff --git a/src/dbus_native_messages.cpp b/src/dbus_native_messages.cpp deleted file mode 100644 index 4ee46ef..0000000 --- a/src/dbus_native_messages.cpp +++ /dev/null @@ -1,182 +0,0 @@ -// This file is part of dbus-asio -// Copyright 2018 Brightsign LLC -// -// This library is free software: you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public License -// as published by the Free Software Foundation, version 3, or at your -// option any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// The GNU Lesser General Public License version 3 is included in the -// file named COPYING. If you do not have this file see -// . - -#include "dbus_log.h" - -#include "dbus_type.h" -#include "dbus_type_base.h" -#include "dbus_type_struct.h" - -#include "dbus_auth.h" -#include "dbus_matchrule.h" -#include "dbus_message.h" -#include "dbus_messageprotocol.h" -#include "dbus_native.h" - -#define MESSAGE_BUS_OBJECT "/org/freedesktop/DBus" -#define MESSAGE_BUS_INTERFACE "org.freedesktop.DBus" - -// This file contains only the implementations of the primary "Message Bus -// Messages" They're included primarily as a convenience to application -// developers, as the implementation is trivial. -void DBus::Native::callHello( - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure) -{ - DBus::Message::MethodCall method(DBus::Message::MethodCallIdentifier( - MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, "Hello")); - - sendMethodCall(DBus::Native::DBusDaemon, method, success, failure); -} - -void DBus::Native::callGetUnixProcessId( - const std::string& name, - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure) -{ - Message::MethodCallIdentifier id(MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, - "GetConnectionUnixProcessID"); - Message::MethodCallParametersIn inparams(name); - Message::MethodCall method(id, inparams); - - sendMethodCall(DBus::Native::DBusDaemon, method, success, failure); -} - -void DBus::Native::callGetConnectionUnixUser( - const std::string& name, - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure) -{ - Message::MethodCallIdentifier id(MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, - "GetConnectionUnixUser"); - Message::MethodCallParametersIn inparams(name); - Message::MethodCall method(id, inparams); - - sendMethodCall(DBus::Native::DBusDaemon, method, success, failure); -} - -void DBus::Native::callRequestName( - const std::string& name, uint32_t flags, - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure) -{ - Message::MethodCallIdentifier id(MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, - "RequestName"); - Message::MethodCallParametersIn inparams(name, flags); - Message::MethodCall method(id, inparams); - - sendMethodCall(DBus::Native::DBusDaemon, method, success, failure); -} - -void DBus::Native::callReleaseName( - const std::string& name, - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure) -{ - Message::MethodCallIdentifier id(MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, - "ReleaseName"); - Message::MethodCallParametersIn inparams(name); - Message::MethodCall method(id, inparams); - - sendMethodCall(DBus::Native::DBusDaemon, method, success, failure); -} - -void DBus::Native::callListQueuedOwners( - const std::string& bus_name, - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure) -{ - Message::MethodCallIdentifier id(MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, - "ListQueuedOwners"); - Message::MethodCallParametersIn inparams(bus_name); - Message::MethodCall method(id, inparams); - - sendMethodCall(DBus::Native::DBusDaemon, method, success, failure); -} - -void DBus::Native::callListNames( - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure) -{ - DBus::Message::MethodCall method(Message::MethodCallIdentifier( - MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, "ListNames")); - - sendMethodCall(DBus::Native::DBusDaemon, method, success, failure); -} - -void DBus::Native::callListActivatableNames( - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure) -{ - DBus::Message::MethodCall method(Message::MethodCallIdentifier( - MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, "ListActivatableNames")); - - sendMethodCall(DBus::Native::DBusDaemon, method, success, failure); -} - -void DBus::Native::callNameHasOwner( - const std::string& name, - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure) -{ - Message::MethodCallIdentifier id(MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, - "NameHasOwner"); - Message::MethodCallParametersIn inparams(name); - Message::MethodCall method(id, inparams); - - sendMethodCall(DBus::Native::DBusDaemon, method, success, failure); -} - -void DBus::Native::callAddMatch( - const std::string& rulestring, - const DBus::Message::CallbackFunctionMethodReturn& success, - const Message::CallbackFunctionError& failure, - const Message::CallbackFunctionSignal& handler) -{ - // Note: Rules are specified as a string of comma separated key/value pairs - // Possible keys you can match on are type, sender, interface, member, path, - // destination and numbered keys to match message args (keys are 'arg0', - // 'arg1', etc.) - Message::MethodCallIdentifier id(MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, - "AddMatch"); - Message::MethodCallParametersIn inparams(rulestring); - Message::MethodCall method(id, inparams); - - { - boost::recursive_mutex::scoped_lock guard(m_RulesMapMutex); - DBus::MatchRule newrule(rulestring, handler); - m_RulesMap.emplace(rulestring, newrule); - } - - sendMethodCall(DBus::Native::DBusDaemon, method, success, failure); -} - -void DBus::Native::callRemoveMatch(const std::string& rule) -{ - Message::MethodCallIdentifier id(MESSAGE_BUS_OBJECT, MESSAGE_BUS_INTERFACE, - "RemoveMatch"); - Message::MethodCallParametersIn inparams(rule); - Message::MethodCall method(id, inparams, - DBus::Message::Header::FLAGS_NO_REPLY_EXPECTED); - - sendMethodCall(DBus::Native::DBusDaemon, method); - - { - boost::recursive_mutex::scoped_lock guard(m_RulesMapMutex); - m_RulesMap.erase(rule); - } -} From ad41751a571cd353941965b76978e9070b60e6ec Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Thu, 14 Apr 2022 02:10:51 +0200 Subject: [PATCH 16/24] convert example programs to use the new API They have been converted 1:1 even if the functionality doesn't make much sense anymore. To be extended at a later time. Signed-off-by: Heiko Hund --- example/dbus-test.cpp | 427 ++++++++++++++++++-------------- example/tests/pummel-client.cpp | 129 +++++----- example/tests/pummel-server.cpp | 237 +++++++++--------- tests/dbus_asio_test.cpp | 28 ++- 4 files changed, 436 insertions(+), 385 deletions(-) diff --git a/example/dbus-test.cpp b/example/dbus-test.cpp index 6d1ba83..8d3ebf3 100644 --- a/example/dbus-test.cpp +++ b/example/dbus-test.cpp @@ -26,236 +26,279 @@ About unix_fd */ #include "dbus.h" +#include -void test1() +void handleNameAcquired(DBus::Connection::Ptr dbus) { - DBus::Log::setLevel(DBus::Log::WARNING); - - DBus::Log::write(DBus::Log::INFO, "System bus: %s\n", - DBus::Platform::getSystemBus().c_str()); - DBus::Log::write(DBus::Log::INFO, "Session bus: %s\n", - DBus::Platform::getSessionBus().c_str()); - // DBus::Native native(DBus::Platform::getSystemBus()); - DBus::Native native(DBus::Platform::getSessionBus()); - sleep(1); - - native.BeginAuth( - DBus::AuthenticationProtocol::AUTH_BASIC); // AUTH_BASIC or - // AUTH_NEGOTIATE_UNIX_FD - - // org.bluez.obex - use / and objectmanager - -#if 1 - DBus::Introspectable::Introspection introspection; - DBus::Introspectable::Interface iface("biz.brightsign.TestInterface"); - iface.addMethod(DBus::Introspectable::Method("Ping", "", "ss")); - iface.addMethod(DBus::Introspectable::Method("Echo2", "ss", "s")); - iface.addProperty(DBus::Introspectable::Property("p1", "s")); - iface.addProperty(DBus::Introspectable::Property("p2", "s")); - iface.addSignal(DBus::Introspectable::Signal("BroadcastStuff", "s")); - - introspection.addInterface(iface); - - std::string xml_introspection(introspection.serialize()); -#endif - - // Also, support methods etc - // native.SendAuthListMethods(); - - // Hello() is necessary before any communications take place - printf("Send hello message...\n"); - - DBus::Log::setLevel(DBus::Log::TRACE); - native.registerSignalHandler( + dbus->receiveSignal( "org.freedesktop.DBus.NameAcquired", - [&](const DBus::Message::Signal& signal) { + [dbus](const DBus::Message::Signal& signal) { + if (!signal) return; // Knowing the type is 's' we can be safe in assuming asString will work - std::string signalName = DBus::Type::asString(signal.getParameter(0)); - printf("RCV signalName : NameAcquired : %s\n", signalName.c_str()); + const auto name = DBus::Type::asString(signal.getParameter(0)); + std::cout << "RCV signal : NameAcquired : " << name << std::endl; + handleNameAcquired(dbus); }); +} - native.registerSignalHandler( +void handleNameOwnerChanged(DBus::Connection::Ptr dbus) +{ + dbus->receiveSignal( "org.freedesktop.DBus.NameOwnerChanged", - [&](const DBus::Message::Signal& signal) { - std::string signalName = DBus::Type::asString(signal.getParameter(0)); - printf("RCV signalName : NameOwnerChanged : %s\n", signalName.c_str()); + [dbus](const DBus::Message::Signal& signal) { + if (!signal) return; + const auto name = DBus::Type::asString(signal.getParameter(0)); + std::cout << "RCV signal : NameOwnerChanged : " << name << std::endl; + handleNameOwnerChanged(dbus); }); +} - native.callHello( - [](const DBus::Message::MethodReturn& msg) { - std::string signalName = DBus::Type::asString(msg.getParameter(0)); - printf("REPLY FROM HELLO : This is our unique name : %s\n", - signalName.c_str()); - }, - [](const DBus::Message::Error& msg) { - printf("ERROR FROM HELLO : %s\n", msg.getMessage().c_str()); - }); - // DBus::Log::setLevel(DBus::Log::WARNING); - // TODO: This should get removed, but _something_ i not buffering the method - // calls below - // TODO: Am I deadlocking? - // sleep(3); - -#if 0 - DBus::Message::MethodCallIdentifier test_ping("/org/example/TestObject", "org.example.TestInterface", "Ping"); - native.sendMethodCall(test_ping, - [] (const DBus::Message::MethodReturn &msg) { - printf("REPLY FROM ping : %s\n", DBus::Type::asString(msg.m_Body).c_str()); - }); -#endif - - native.registerMethodCallHandler( +void servePropertiesGet(DBus::Connection::Ptr dbus) +{ + dbus->receiveMethodCall( "org.freedesktop.DBus.Properties.Get", - [&](const DBus::Message::MethodCall& method) { - DBus::Message::MethodReturn result(method.getSerial()); - // TODO: return Serial should be in ctor for error and return - and - // FIRST PARAMETER - std::string interface = DBus::Type::asString(method.getParameter(0)); - std::string propertyName = DBus::Type::asString(method.getParameter(1)); - - // result.m_Parameters.add(DBus::Type::String("The result of " + - // interface + " of " + propertyName + " is 42. Always 42!")); - result.addParameter(DBus::Type::String("The result of " + interface + " of " + propertyName + " is 42. Always 42!")); - - if (propertyName == "p1") { - native.sendMethodReturn(method.getHeaderSender(), result); + [dbus](const DBus::Message::MethodCall& call) { + if (!call) return; + const auto interface = call.getParameter(0).asString(); + const auto property = call.getParameter(1).asString(); + + auto errorHandler = [](const DBus::Error& error){ + if (error) + std::cerr << "error while sending Get reply:" << error.message << std::endl; + }; + + if (property == "Answer") { + DBus::Message::MethodReturn reply(call.getSender(), call.getSerial()); + reply.addParameter(DBus::Type::Uint16(42)); + dbus->sendMethodReturn(reply, errorHandler); + } else if (property == "Poetry") { + DBus::Message::MethodReturn reply(call.getSender(), call.getSerial()); + reply.addParameter(DBus::Type::String("The dead swans lay in the stagnant pool.")); + dbus->sendMethodReturn(reply, errorHandler); } else { - DBus::Message::Error err(method.getSerial(), + DBus::Message::Error err(call.getSender(), call.getSerial(), "biz.brightsign.Error.InvalidParameters", - "Parameter is not p1"); - native.sendError(method.getHeaderSender(), err); + "Requested property is unknown: " + property); + dbus->sendError(err, errorHandler); } + servePropertiesGet(dbus); }); +} -#if 1 - native.registerMethodCallHandler( - "org.freedesktop.DBus.Introspectable.Introspect", - [&](const DBus::Message::MethodCall& method) { - DBus::Message::MethodReturn result(method.getSerial()); - result.addParameter(DBus::Type::String(xml_introspection)); - native.sendMethodReturn(method.getHeaderSender(), result); - }); -#endif - -// TODO: finish org.freedesktop.DBus.Properties.GetAll to return the correct -// data as name:value pair looks wrong, but it doesn't crash. Whereas the a(sv) -// is right, but causes the sender to terminate the connection -#if 1 - // It is recommend you implement this method - even if you only return an - // empty string. Some apps (like d-feet) will wait for a reply, making them - // appear slow. - native.registerMethodCallHandler( +void servePropertiesGetAll(DBus::Connection::Ptr dbus) +{ + // TODO: finish org.freedesktop.DBus.Properties.GetAll to return the correct + // data as name:value pair looks wrong, but it doesn't crash. Whereas the a(sv) + // is right, but causes the sender to terminate the connection + dbus->receiveMethodCall( "org.freedesktop.DBus.Properties.GetAll", - [&](const DBus::Message::MethodCall& method) { + [dbus](const DBus::Message::MethodCall& method) { + if (!method) return; printf("GetAll\nCALLBACK METHOD : serial %.4x \n", method.getSerial()); printf("CALLBACK METHOD : sender %s \n", - method.getHeaderSender().c_str()); + method.getSender().c_str()); printf("CALLBACK METHOD : destination %s \n", - method.getHeaderDestination().c_str()); + method.getDestination().c_str()); printf("CALLBACK METHOD : serial %d \n", method.getSerial()); - DBus::Type::Array propertyList; - - DBus::Type::Struct s1; - s1.add(DBus::Type::String("p1")); - s1.add(DBus::Type::Variant(DBus::Type::String("s"))); - - propertyList.add(s1); - // native.sendMethodReturn(method.getSerial(), header.destination, - // header.sender, propertyList); - - // std::string result("p1:123,p2:44"); - std::string result(""); - DBus::Type::Generic body = DBus::Type::String(result); + DBus::Type::Struct p1; + p1.add(DBus::Type::String("Answer")); + p1.add(DBus::Type::Variant(DBus::Type::Uint16(42))); + + DBus::Type::Struct p2; + p2.add(DBus::Type::String("Poetry")); + p2.add(DBus::Type::Variant(DBus::Type::String("The dead swans lay in the stagnant pool."))); + + DBus::Type::Array properties; + properties.add(p1); + + DBus::Message::MethodReturn reply(method.getSender(), method.getSerial()); + reply.addParameter(properties); + dbus->sendMethodReturn(reply, + [](const DBus::Error& error){ + if (error) + std::cerr << "error while sending GetAll reply:" << error.message << std::endl; + }); + servePropertiesGetAll(dbus); + }); +} - // REM: The destination for the incoming message is us, the sender, in - // this context. +void serveIntrospect(DBus::Connection::Ptr dbus) +{ + static std::string xml; + if (xml.empty()) + { + DBus::Introspectable::Interface iface("biz.brightsign.TestInterface"); + iface.addMethod(DBus::Introspectable::Method("Ping", "", "ss")); + iface.addMethod(DBus::Introspectable::Method("Echo2", "ss", "s")); + iface.addProperty(DBus::Introspectable::Property("Answer", "q")); + iface.addProperty(DBus::Introspectable::Property("Poetry", "s")); + iface.addSignal(DBus::Introspectable::Signal("BroadcastStuff", "s")); + + DBus::Introspectable::Introspection introspection; + introspection.addInterface(iface); + xml = introspection.serialize(); + } + + dbus->receiveMethodCall( + "org.freedesktop.DBus.Introspectable.Introspect", + [dbus](const DBus::Message::MethodCall& call) { + if (!call) return; + DBus::Message::MethodReturn reply(call.getSender(), call.getSerial()); + reply.addParameter(DBus::Type::String(xml)); + dbus->sendMethodReturn(reply, + [](const DBus::Error& error){ + if (error) + std::cerr << "error serving introspection: " + << error.message << std::endl; + }); + serveIntrospect(dbus); + }); +} - // TODO - DBus::Message::MethodReturn result2(method.getSerial()); - result2.addParameter(body); - native.sendMethodReturn(method.getHeaderSender(), result2); - // native.sendMethodReturn(method.getSerial(), - //header.destination, header.sender, body); +void servePing(DBus::Connection::Ptr dbus) +{ + dbus->receiveMethodCall( + "biz.brightsign.TestInterface.Ping", + [dbus](const DBus::Message::MethodCall& call) { + if (!call) return; + DBus::Message::MethodReturn reply(call.getSender(), call.getSerial()); + reply.addParameter(DBus::Type::String("Ping??")); + reply.addParameter(DBus::Type::String("Pong!!")); + dbus->sendMethodReturn(reply, + [](const DBus::Error& error) { + if (error) + std::cerr << "error while sending Ping reply:" << error.message << std::endl; + }); + servePing(dbus); }); -#endif +} -#if 1 - native.registerMethodCallHandler( +void serveEcho2(DBus::Connection::Ptr dbus) +{ + dbus->receiveMethodCall( "biz.brightsign.TestInterface.Echo2", - [&](const DBus::Message::MethodCall& method) { - if (!method.isReplyExpected()) { - // NOP - } else if (method.getParameterCount() != 2) { - DBus::Message::Error err(method.getSerial(), - "biz.brightsign.Error.InvalidParameters", - "This needs 2 params."); - native.sendError(method.getHeaderSender(), err); - - } else { - std::string input1(DBus::Type::asString(method.getParameter(0))); - std::string input2(DBus::Type::asString(method.getParameter(1))); - DBus::Type::Generic body = DBus::Type::String("Echo of : " + input1 + " and " + input2); - // TODO - DBus::Message::MethodReturn result(method.getSerial()); - result.addParameter(body); - native.sendMethodReturn(method.getHeaderSender(), result); + [dbus](const DBus::Message::MethodCall& call) { + if (!call) return; + if (call.isReplyExpected()) { + auto errorHandler = [](const DBus::Error& error) { + if (error) + std::cerr << "error while sending Echo2 reply: " << error.message << std::endl; + }; + if (call.getParameterCount() != 2) { + DBus::Message::Error err(call.getSender(), call.getSerial(), + "biz.brightsign.Error.InvalidParameters", + "This needs 2 params."); + dbus->sendError(err, errorHandler); + } else { + const auto input1(call.getParameter(0).asString()); + const auto input2(call.getParameter(1).asString()); + DBus::Message::MethodReturn reply(call.getSender(), call.getSerial()); + reply.addParameter(DBus::Type::String("Echo of : " + input1 + " and " + input2)); + dbus->sendMethodReturn(reply, errorHandler); + } } + serveEcho2(dbus); }); +} - native.registerMethodCallHandler( - "biz.brightsign.TestInterface.Ping", - [&](const DBus::Message::MethodCall& method) { - DBus::Type::Array propertyList; - - propertyList.add(DBus::Type::DictEntry(DBus::Type::String("p1"), - DBus::Type::String("two"))); - - DBus::Message::MethodReturn result(method.getSerial()); - result.addParameter(DBus::Type::String("pong!!")); - native.sendMethodReturn(method.getHeaderSender(), result); +void serveOpenFile(DBus::Connection::Ptr dbus) +{ + dbus->receiveMethodCall( + "biz.brightsign.TestInterface.OpenFile", + [dbus](const DBus::Message::MethodCall& call) { + if (!call) return; + if (call.isReplyExpected()) { + auto errorHandler = [](const DBus::Error& error) { + if (error) + std::cerr << "error while sending OpenFile reply: " << error.message << std::endl; + }; + if (call.getParameterCount() != 1) { + DBus::Message::Error err(call.getSender(), call.getSerial(), + "biz.brightsign.Error.InvalidParameters", + "This needs 2 params."); + dbus->sendError(err, errorHandler); + } else { + const auto input1 = DBus::Type::asString(call.getParameter(0)); + DBus::Message::MethodReturn reply(call.getSender(), call.getSerial()); + int fd = 0; + reply.addParameter(DBus::Type::UnixFd(fd)); + dbus->sendMethodReturn(reply, errorHandler); + } + } + serveOpenFile(dbus); }); +} -#endif - - // Run a "server" - native.callRequestName( - "test.steev", 0, - [](const DBus::Message::MethodReturn& msg) { - uint32_t result = DBus::Type::asUint32(msg.getParameter(0)); +void test1() +{ + DBus::Log::setLevel(DBus::Log::TRACE); - if (result != DBus::Native::DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - printf("Not Primary Owner\n"); - } - }, - [](const DBus::Message::Error& msg) { - printf("ERROR FROM callRequestName : %s\n", msg.getMessage().c_str()); - }); + DBus::Log::write(DBus::Log::INFO, "System bus: %s\n", + DBus::Platform::getSystemBus().c_str()); + DBus::Log::write(DBus::Log::INFO, "Session bus: %s\n", + DBus::Platform::getSessionBus().c_str()); - printf("Try and call ourselves...\n"); - DBus::Message::MethodCallParametersIn params; - params.add(std::string("one")); - params.add(std::string("two")); - DBus::Message::MethodCall concat( - DBus::Message::MethodCallIdentifier("/", "biz.brightsign.TestInterface", - "Echo2"), - params); - native.sendMethodCall( - "test.steev", concat, - [](const DBus::Message::MethodReturn& msg) { - std::string result = DBus::Type::asString(msg.getParameter(0)); - printf("REPLY FROM echo2 : This is our id : %s\n", result.c_str()); - }, - [](const DBus::Message::Error& msg) { - printf("ERROR FROM echo2 : %s\n", msg.getMessage().c_str()); + DBus::asio::io_context ioc; + DBus::Connection::Ptr dbus = DBus::Connection::create(ioc); + if (!dbus) + return; + + dbus->connect( + DBus::Platform::getSessionBus(), + DBus::AuthenticationProtocol::create(), + [dbus](const DBus::Error& error, const std::string&, const std::string&) { + if (error) + return; + + handleNameAcquired(dbus); + handleNameOwnerChanged(dbus); + + servePropertiesGetAll(dbus); + servePropertiesGet(dbus); + serveIntrospect(dbus); + + servePing(dbus); + serveEcho2(dbus); + serveOpenFile(dbus); + + dbus->requestName( + "test.steev", DBus::RequestNameFlags::None, + [dbus](const DBus::Error& error, const DBus::Message::MethodReturn& msg) { + if (error) + return dbus->disconnect(); + + uint32_t result = DBus::Type::asUint32(msg.getParameter(0)); + if (result != DBus::RequestNameReply::PrimaryOwner) + std::cerr << "Not Primary Owner" << std::endl; + + std::cout << "Try and call ourselves..." << std::endl; + dbus->sendMethodCall( + { "test.steev", + { "/", "biz.brightsign.TestInterface", "Echo2" }, {"one", "two"} }, + [](const DBus::Error& error, const DBus::Message::MethodReturn& reply) { + const auto result = reply.getParameter(0).asString(); + std::cout << "REPLY FROM Echo2 : " << result << std::endl; + }); + + dbus->sendMethodCall( + { "test.steev", + { "/", "biz.brightsign.TestInterface", "OpenFile" }, {"/etc/passwd"} }, + [dbus](const DBus::Error& error, const DBus::Message::MethodReturn& reply) { + const auto result = DBus::Type::asUnixFd(reply.getParameter(0)); + std::cout << "REPLY FROM OpenFile : " << result << std::endl; + dbus->disconnect(); + + std::cout << '\n' << dbus->getStats(); + }); + }); }); - sleep(200); + ioc.run(); } -int main(int argc, const char* argv[]) +int main() { test1(); - return 0; } diff --git a/example/tests/pummel-client.cpp b/example/tests/pummel-client.cpp index 7e9bc85..3a73fea 100644 --- a/example/tests/pummel-client.cpp +++ b/example/tests/pummel-client.cpp @@ -11,26 +11,15 @@ using namespace std::chrono; size_t gTotalSuccesses = 0; std::mutex gMutexOutput; std::mutex gMutexSuccessCount; -std::mutex gMutexReplyErrorCount; -std::mutex gMutexReplyResultCount; bool testClient(const std::string& stubname, size_t iterations) { DBus::Log::setLevel(DBus::Log::INFO); - DBus::Native native(DBus::Platform::getSessionBus()); - - native.BeginAuth(DBus::AuthenticationProtocol::AUTH_BASIC); - - native.callHello( - [](const DBus::Message::MethodReturn& msg) { - std::cerr << "Ready. Client on " - << DBus::Type::asString(msg.getParameter(0)) << std::endl; - }, - [](const DBus::Message::Error& msg) { - std::cerr << "Failed to get hello message. Aborting early" << std::endl; - return -1; - }); + DBus::asio::io_context ioc; + DBus::Connection::Ptr dbus = DBus::Connection::create(ioc); + if (!dbus) + return false; milliseconds ms_start = duration_cast(system_clock::now().time_since_epoch()); @@ -38,80 +27,76 @@ bool testClient(const std::string& stubname, size_t iterations) size_t correct = 0; size_t errors = 0; size_t sent = 0; - for (; sent < iterations; ++sent) { - std::string expected(stubname); - std::string number(std::to_string(sent)); - expected += " "; - expected += number; - - DBus::Message::MethodCallParametersIn params; - params.add(stubname); - params.add(number); - - DBus::Message::MethodCall concat(DBus::Message::MethodCallIdentifier( - "/", "biz.brightsign.test", "concat"), - params); - native.sendMethodCall( - "biz.brightsign", concat, - [&replies, &correct, expected](const DBus::Message::MethodReturn& msg) { - std::lock_guard guard(gMutexReplyResultCount); - std::string result = DBus::Type::asString(msg.getParameter(0)); - ++replies; - if (result == expected) { - ++correct; - } - }, - [&errors](const DBus::Message::Error& msg) { - std::lock_guard guard(gMutexReplyErrorCount); - ++errors; - }); - - // If we have a short delay (e.g. 1ms) between messages, everything is happy - // for 10-10-100 If this is 10ms, then the timeouts below happen a lot more - // frequently. ATM, I can't tell if this is coincidence, or intentional. - // std::this_thread::sleep_for(message_sleep); - } - // We don't get a callback upon timeout. We use a separate loop to check for - // that posibility, so we don't hang in this method. libdbus (as used by the - // daemon) will timeout a request after 25 seconds. So, if there's anything - // left as that time, we're not going to get a reply, so timeout that and - // everything else. milliseconds timeout = std::chrono::seconds(45); - milliseconds ms_timeout_starts = duration_cast(system_clock::now().time_since_epoch()); - milliseconds ms_end; + DBus::asio::steady_timer timer(ioc); + timer.expires_after(timeout); + timer.async_wait( + [dbus] + (const DBus::error_code& error) + { + if (!error) + dbus->disconnect(); + }); - do { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + dbus->connect( + DBus::Platform::getSessionBus(), + DBus::AuthenticationProtocol::create(), + [dbus, &timer, &errors, &replies, &correct, &sent, &stubname, iterations] + (const DBus::Error& error, const std::string&, const std::string&) { + if (error) + return; + + while (sent < iterations) { + const std::string number(std::to_string(sent++)); + const std::string expected(stubname + ' ' + number); + + dbus->sendMethodCall( + { "biz.brightsign", + {"/", "biz.brightsign.test", "concat"}, {stubname, number} }, + [dbus, &timer, &errors, &replies, &correct, expected, iterations] + (const DBus::Error& error, const DBus::Message::MethodReturn& reply) { + if (error) + ++errors; + else { + ++replies; + const auto result = reply.getParameter(0).asString(); + if (result == expected) + ++correct; + } + + if (errors + replies == iterations) { + timer.cancel(); + dbus->disconnect(); + } + }); + + // If we have a short delay (e.g. 1ms) between messages, everything is happy + // for 10-10-100 If this is 10ms, then the timeouts below happen a lot more + // frequently. ATM, I can't tell if this is coincidence, or intentional. + // std::this_thread::sleep_for(message_sleep); + } + }); - ms_end = duration_cast(system_clock::now().time_since_epoch()); + ioc.run(); - if (ms_end - ms_timeout_starts > timeout) { - std::cerr << "Timing out..." << std::endl; - break; - } - } while (sent != replies + errors); + milliseconds ms_end = duration_cast(system_clock::now().time_since_epoch()); std::lock_guard guard(gMutexOutput); - std::cout << (correct == sent ? "SUCCESS" : "FAILURE"); std::cout << " " << stubname << ":: Results ::"; std::cout << " Sent: " << sent; std::cout << " Replies: " << replies; std::cout << " Correct: " << correct; std::cout << " Errors: " << errors; - std::cout << " Duration: " << std::to_string((ms_end - ms_start).count()) - << "ms"; + std::cout << " Duration: " << (ms_end - ms_start).count() << "ms"; std::cout << std::endl; std::cout << " Parameters: " << std::endl; - std::cout << " Timeout: " << std::to_string(timeout.count()).c_str() - << "ms" << std::endl; - std::cout << " Iterations: " << iterations; - std::cout << std::endl; + std::cout << " Timeout: " << timeout.count() << "ms" << std::endl; + std::cout << " Iterations: " << iterations << std::endl; - std::cout << native.getStats(); - std::cout << std::endl; + std::cout << dbus->getStats() << std::endl; return correct == sent; } diff --git a/example/tests/pummel-server.cpp b/example/tests/pummel-server.cpp index e6fe816..801dd8c 100644 --- a/example/tests/pummel-server.cpp +++ b/example/tests/pummel-server.cpp @@ -1,147 +1,152 @@ #include "dbus.h" #include -bool gInterrupted = false; +void serveIntrospect(DBus::Connection::Ptr dbus, const std::string& xml) +{ + dbus->receiveMethodCall( + "org.freedesktop.DBus.Introspectable.Introspect", + [dbus, &xml](const DBus::Message::MethodCall& call) { + DBus::Message::MethodReturn reply(call.getSender(), call.getSerial()); + reply.addParameter(DBus::Type::String(xml)); + dbus->sendMethodReturn(reply, + [dbus, &xml](const DBus::Error& error) { + if (!error) + serveIntrospect(dbus, xml); + }); + }); -void s_signal_handler(int signal_value) { gInterrupted = true; } +} -void s_catch_signals(void) +// It is recommend you implement org.freedesktop.DBus.Properties.GetAll, +// even if you only return an empty string. Some apps (like d-feet) will +// issue a GetAll before the standard method call you've requested, and +// then wait for a reply, making them appear slow. +void serveGetAll(DBus::Connection::Ptr dbus) { - struct sigaction action; - action.sa_handler = s_signal_handler; - action.sa_flags = 0; - sigemptyset(&action.sa_mask); - sigaction(SIGINT, &action, NULL); - sigaction(SIGTERM, &action, NULL); + dbus->receiveMethodCall( + "org.freedesktop.DBus.Properties.GetAll", + [dbus](const DBus::Message::MethodCall& call) { + DBus::Message::MethodReturn reply(call.getSender(), call.getSerial()); + reply.addParameter(DBus::Type::String("")); + dbus->sendMethodReturn(reply, + [dbus](const DBus::Error& error) { + if (!error) + serveGetAll(dbus); + }); + }); } -void testServer() +void serveConcat(DBus::Connection::Ptr dbus) { - DBus::Log::setLevel(DBus::Log::WARNING); + dbus->receiveMethodCall( + "biz.brightsign.test.concat", + [dbus](const DBus::Message::MethodCall& call) { + if (call.isReplyExpected()) { + if (call.getParameterCount() != 2) { + DBus::Message::Error error( + call.getSender(), call.getSerial(), + "biz.brightsign.Error.InvalidParameters", + "This needs 2 params."); + + dbus->sendError( + error, + [dbus](const DBus::Error& error){ + if (error) { + std::cerr << "sendError failed: " << error.message << std::endl; + dbus->disconnect(); + } + }); + } else { + const auto input1 = call.getParameter(0).asString(); + const auto input2 = call.getParameter(1).asString(); + DBus::Message::MethodReturn reply(call.getSender(), call.getSerial()); + reply.addParameter(DBus::Type::String(input1 + " " + input2)); + + dbus->sendMethodReturn(reply, + [dbus](const DBus::Error& error) { + if (error) { + std::cerr << "concat failed: " << error.message << std::endl; + dbus->disconnect(); + } + }); + } + } + serveConcat(dbus); + }); +} - DBus::Native native(DBus::Platform::getSessionBus()); +int main() +{ + DBus::Log::setLevel(DBus::Log::WARNING); - native.BeginAuth(DBus::AuthenticationProtocol::AUTH_BASIC); + DBus::asio::io_context ioc; + auto dbus = DBus::Connection::create(ioc); + if (!dbus) + return 1; -#if 1 - DBus::Introspectable::Introspection introspection; DBus::Introspectable::Interface iface("biz.brightsign.test"); iface.addMethod(DBus::Introspectable::Method("concat", "ss", "s")); - + DBus::Introspectable::Introspection introspection; introspection.addInterface(iface); - std::string xml_introspection(introspection.serialize()); -#endif - - native.callHello( - [](const DBus::Message::MethodReturn& msg) { - std::cout << "Ready. Server on " - << DBus::Type::asString(msg.getParameter(0)) << std::endl; - }, - [](const DBus::Message::Error& msg) { - std::cout << "ERROR : Server did not say 'hello' correct " - << msg.getMessage() << std::endl; + dbus->connect( + DBus::Platform::getSessionBus(), + DBus::AuthenticationProtocol::create(), + [dbus, &introspection] + (const DBus::Error& error, const std::string&, const std::string&) { + if (error) + return; + + serveIntrospect(dbus, introspection.serialize()); + serveGetAll(dbus); + serveConcat(dbus); + + dbus->requestName( + "biz.brightsign", DBus::RequestNameFlags::None, + [dbus](const DBus::Error& error, const DBus::Message::MethodReturn& reply) { + if (error) { + std::cerr << "RequestName error: " << error.message << std::endl; + dbus->disconnect(); + return; + } + + auto result = DBus::Type::asUint32(reply.getParameter(0)); + std::cout << "Now running as biz.brightsign (" << result << ")" << std::endl; + if (result != DBus::RequestNameReply::PrimaryOwner) + std::cout << "Not Primary Owner." << std::endl; + }); }); - native.registerMethodCallHandler( - "org.freedesktop.DBus.Introspectable.Introspect", - [&](const DBus::Message::MethodCall& method) { - DBus::Message::MethodReturn result(method.getSerial()); - result.addParameter(DBus::Type::String(xml_introspection)); - native.sendMethodReturn(method.getHeaderSender(), result); + DBus::asio::signal_set signals(ioc, SIGINT, SIGTERM); + signals.async_wait( + [dbus] + (const DBus::error_code& error, int /*signal_number*/) { + if (!error) + dbus->disconnect(); }); - // It is recommend you implement this method - even if you only return an - // empty string. Some apps (like d-feet) will issue a GetAll before the - // standard method call you've requested, and then wait for a reply, making - // them appear slow. - native.registerMethodCallHandler( - "org.freedesktop.DBus.Properties.GetAll", - [&](const DBus::Message::MethodCall& method) { - // DBus::Type::Generic body = DBus::Type::String(""); - DBus::Message::MethodReturn result2(method.getSerial()); - result2.addParameter(DBus::Type::String("")); - native.sendMethodReturn(method.getHeaderSender(), result2); - }); - - native.registerMethodCallHandler( - "biz.brightsign.test.concat", - [&](const DBus::Message::MethodCall& method) { - if (!method.isReplyExpected()) { - // NOP - } else if (method.getParameterCount() != 2) { - DBus::Message::Error err(method.getSerial(), - "biz.brightsign.Error.InvalidParameters", - "This needs 2 params."); - native.sendError(method.getHeaderSender(), err); - - } else { - std::string input1(DBus::Type::asString(method.getParameter(0))); - std::string input2(DBus::Type::asString(method.getParameter(1))); - DBus::Type::Generic body = DBus::Type::String(input1 + " " + input2); - DBus::Message::MethodReturn result(method.getSerial()); - result.addParameter(body); - native.sendMethodReturn(method.getHeaderSender(), result); - } - }); - - native.callRequestName( - "biz.brightsign", 0, - [](const DBus::Message::MethodReturn& msg) { - uint32_t result = DBus::Type::asUint32(msg.getParameter(0)); - - std::cout << "Now running as biz.brightsign (" << result << ")" - << std::endl; - - if (result != DBus::Native::DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - std::cout << "Not Primary Owner." << std::endl; - } - }, - [](const DBus::Message::Error& msg) { - std::cout << "ERROR FROM callRequestName : " << msg.getMessage() - << std::endl; - }); - - bool bRunLoop = true; - // Now do a mini-self test - DBus::Message::MethodCallParametersIn params; - params.add(std::string("one")); - params.add(std::string("two")); - DBus::Message::MethodCall concat( - DBus::Message::MethodCallIdentifier("/", "biz.brightsign.test", "concat"), - params); - native.sendMethodCall( - "biz.brightsign", concat, - [&bRunLoop](const DBus::Message::MethodReturn& msg) { - std::string result = DBus::Type::asString(msg.getParameter(0)); + dbus->sendMethodCall( + { "biz.brightsign", {"/", "biz.brightsign.test", "concat"}, {"one", "two"} }, + [dbus](const DBus::Error& error, const DBus::Message::MethodReturn& reply) { + if (error) { + std::cout + << "Terminating because concat has returned an error" + << std::endl; + dbus->disconnect(); + } + const auto result = reply.getParameter(0).asString(); if (result != "one two") { std::cout - << "Terminating because concat is returning incorrect base data." + << "Terminating because concat is returning incorrect base data" << std::endl; - bRunLoop = false; + dbus->disconnect(); } - }, - [&bRunLoop](const DBus::Message::Error& msg) { - std::cout << "Terminating because concat has returned an error." - << std::endl; - bRunLoop = false; }); - // Spin! - while (bRunLoop) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - if (gInterrupted) { - break; - } - } - std::cout << native.getStats(); -} + ioc.run(); -int main(int argc, const char* argv[]) -{ - s_catch_signals(); - testServer(); +//TODO std::cout << native.getStats(); return 0; } diff --git a/tests/dbus_asio_test.cpp b/tests/dbus_asio_test.cpp index 94573a8..b893418 100644 --- a/tests/dbus_asio_test.cpp +++ b/tests/dbus_asio_test.cpp @@ -1,5 +1,6 @@ // This file is part of dbus-asio // Copyright 2018 Brightsign LLC +// Copyright 2022 OpenVPN Inc. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public License @@ -26,11 +27,28 @@ int main() DBus::Platform::getSystemBus().c_str()); DBus::Log::write(DBus::Log::INFO, "Session bus: %s\n", DBus::Platform::getSessionBus().c_str()); - DBus::Native native(DBus::Platform::getSessionBus()); - sleep(1); - native.BeginAuth( - DBus::AuthenticationProtocol::AUTH_BASIC); // AUTH_BASIC or - // AUTH_NEGOTIATE_UNIX_FD + DBus::asio::io_context ioc; + auto conn = DBus::Connection::create(ioc); + + conn->connect( + DBus::Platform::getSessionBus(), + DBus::AuthenticationProtocol::create(), + [conn](const DBus::Error& e, const std::string& guid, const std::string& name) + { + if (e) + DBus::Log::write( + DBus::Log::ERROR, + "error: %s (%s)\n", e.message.c_str(), e.category.c_str()); + + DBus::Log::write( + DBus::Log::INFO, + ">>> server guid: %s my bus name: %s\n", guid.c_str(), name.c_str()); + + conn->disconnect(); + }); + + ioc.run(); + return 0; } From 17b8560f5fc6ea14c42d24832b60ed65df885cb7 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Thu, 14 Apr 2022 02:12:50 +0200 Subject: [PATCH 17/24] remove no longer needed dependencies Get rid of boost and pthreads. Signed-off-by: Heiko Hund --- CMakeLists.txt | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a8db509..a7d76c5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,15 +78,6 @@ add_library(dbus-asio SHARED src/dbus_utils.cpp ) -# DBUS DEPENDENCIES -find_package(Boost COMPONENTS chrono system thread REQUIRED) -include_directories(${Boost_INCLUDE_DIR}) -set (PRIVATE_LIBS ${Boost_LIBRARIES}) -link_directories(${Boost_LIBRARY_DIRS}) - -# PTHREAD DEPENDENCIES -find_package (Threads REQUIRED) - # ASIO option(DBUS_ASIO_STANDALONE "Use ASIO standalone library (non-boost)" OFF) if(DBUS_ASIO_STANDALONE) @@ -102,28 +93,21 @@ else() set(DBUS_ERROR_CODE_NAMESPACE "::boost::system") set(DBUS_ERROR_CODE_ERRC "::boost::system::errc::errc_t") set(DBUS_MAKE_ERROR_CODE "::boost::asio::error::make_error_code") + + find_package (Threads REQUIRED) + target_link_libraries(dbus-asio Threads::Threads) endif() set(DBUS_ASIO_H ${CMAKE_CURRENT_SOURCE_DIR}/src/dbus_asio.h) configure_file(${DBUS_ASIO_H}.in ${DBUS_ASIO_H}) unset(DBUS_ASIO_H) -target_link_libraries (dbus-asio ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) - set_target_properties(dbus-asio PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION 1 PUBLIC_HEADER "${HEADER_FILES}") -foreach(LIB ${PRIVATE_LIBS}) - get_filename_component(BARE_LIB ${LIB} NAME) - string(REGEX REPLACE ".so$" "" BARE_LIB "${BARE_LIB}") - string(REGEX REPLACE "^lib" "-l" BARE_LIB "${BARE_LIB}") - set(PLATFORM_LIBS "${PLATFORM_LIBS} ${BARE_LIB}") -endforeach() - -set(PLATFORM_LIBS "${PLATFORM_LIBS} -ldbus-asio") +set(PLATFORM_LIBS "-ldbus-asio") -message(${PLATFORM_LIBS}) configure_file(dbus-asio.pc.in dbus-asio.pc @ONLY) target_include_directories(dbus-asio PUBLIC src) From ac36dfa33f52c02d05c2779390d2cee423ac0d8c Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Sat, 23 Apr 2022 11:12:33 +0200 Subject: [PATCH 18/24] also build asio standalone in github action Signed-off-by: Heiko Hund --- .github/workflows/build-and-test.yml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 73a590d..de0c500 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -8,13 +8,18 @@ on: jobs: build: - name: ${{matrix.compiler.compiler}} ${{matrix.build_type}} + name: ${{matrix.compiler.name}} ${{matrix.asio.name}} ${{matrix.build_type}} runs-on: ubuntu-latest +# run in docker until jammy is ubuntu-latest + container: ubuntu:jammy strategy: matrix: compiler: - - { compiler: GCC, CC: gcc, CXX: g++ } - - { compiler: Clang, CC: clang, CXX: clang++ } + - { name: GCC, CC: gcc, CXX: g++ } + - { name: Clang, CC: clang, CXX: clang++ } + asio: + - { name: asio-boost, pkg: libboost-dev, standalone: OFF } + - { name: asio-standalone, pkg: libasio-dev, standalone: ON } build_type: [ Debug, Release ] steps: - uses: actions/checkout@v2 @@ -23,21 +28,25 @@ jobs: env: DEBIAN_FRONTEND: noninteractive run: | - sudo apt update && sudo apt install -yq libboost-all-dev ninja-build + apt update && apt install --no-install-recommends -yq ${{matrix.asio.pkg}} cmake g++ clang ninja-build +# sudo apt update && sudo apt install --no-install-recommends -yq ${{matrix.asio.pkg}} ninja-build - name: Configure CMake env: CC: ${{matrix.compiler.CC}} CXX: ${{matrix.compiler.CXX}} # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{matrix.build_type}} + run: | + cmake -G Ninja -B ${{github.workspace}}/build \ + -DDBUS_ASIO_STANDALONE=${{matrix.asio.standalone}} \ + -DCMAKE_BUILD_TYPE=${{matrix.build_type}} - name: Build # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build --config ${{matrix.build_type}} - name: Test - working-directory: ${{github.workspace}}/build # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail - run: ctest -C ${{matrix.build_type}} + run: | + cd ${{github.workspace}}/build; ctest -C ${{matrix.build_type}} From a04eed3e1a7c067f6ffa234f28ece28007f25c9a Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Fri, 13 May 2022 17:22:03 +0200 Subject: [PATCH 19/24] add default c'tor to some dbus names BusName, InterfaceName and ObjectPath are not needed in all messages and thus can be left undefined for those. This can be achieved by default constructing them. Added an operator bool() to test for this. Also support ostream'ing names via operator const char*(). Signed-off-by: Heiko Hund --- src/dbus_message.cpp | 12 ++++++++++++ src/dbus_message.h | 2 ++ src/dbus_names.cpp | 9 +++++++-- src/dbus_names.h | 8 +++++--- src/dbus_type_objectpath.cpp | 2 ++ src/dbus_type_objectpath.h | 2 -- tests/dbus_names_unittest.cpp | 17 ++++++++++++++++- 7 files changed, 44 insertions(+), 8 deletions(-) diff --git a/src/dbus_message.cpp b/src/dbus_message.cpp index 89bd564..ee8e589 100644 --- a/src/dbus_message.cpp +++ b/src/dbus_message.cpp @@ -39,6 +39,18 @@ DBus::Message::Header::Fields::Fields(const Header& header) : DBus::Type::Array("a(yv)") { + // Check the required fields that could have been passed in empty + if (header.type != Message::Type::Signal && header.destination.empty()) + throw InvalidMessage("Message without destination"); + if (header.type == Message::Type::Signal) { + if (header.path.empty()) + throw InvalidMessage("Signal without ObjectPath"); + if (header.interface.empty()) + throw InvalidMessage("Signal without Interface"); + } + else if (header.type == Message::Type::MethodCall && header.path.empty()) + throw InvalidMessage("MethodCall without ObjectPath"); + if (!header.destination.empty()) add(Header::Field::Destination, header.destination); if (!header.path.empty()) diff --git a/src/dbus_message.h b/src/dbus_message.h index 989da6f..13566ea 100644 --- a/src/dbus_message.h +++ b/src/dbus_message.h @@ -34,6 +34,8 @@ struct MemberName; struct InterfaceName; class MessageOStream; +using InvalidMessage = std::runtime_error; + class Message { public: static constexpr std::size_t MaximumSize = 134217728; /*128 MiB*/ diff --git a/src/dbus_names.cpp b/src/dbus_names.cpp index 24171e0..0509c1b 100644 --- a/src/dbus_names.cpp +++ b/src/dbus_names.cpp @@ -27,9 +27,14 @@ static constexpr std::array nameStr = { "member name" }; -DBus::Name::operator std::string() const +DBus::Name::operator const char*() const { - return m_name; + return m_name.c_str(); +} + +DBus::Name::operator bool() const +{ + return !m_name.empty(); } // Restrictions that apply to names in general: diff --git a/src/dbus_names.h b/src/dbus_names.h index 9830478..e0b0ee3 100644 --- a/src/dbus_names.h +++ b/src/dbus_names.h @@ -30,7 +30,8 @@ namespace DBus { class Name { public: static constexpr std::size_t MaximumSize = 255; - operator std::string() const; + operator const char*() const; + explicit operator bool() const; protected: enum Type { @@ -42,12 +43,11 @@ namespace DBus { }; struct BusName : public Name { + BusName() = default; BusName(BusName&&) = default; BusName(const BusName&) = default; BusName(const char* name); BusName(const std::string& name); - protected: - BusName() = default; }; struct UniqueName : public BusName { @@ -65,6 +65,7 @@ namespace DBus { }; struct InterfaceName : public Name { + InterfaceName() = default; InterfaceName(InterfaceName&&) = default; InterfaceName(const InterfaceName&) = default; InterfaceName(const char* name); @@ -93,6 +94,7 @@ namespace DBus { }; struct ObjectPath : public Name { + ObjectPath() = default; ObjectPath(ObjectPath&&) = default; ObjectPath(const ObjectPath&) = default; ObjectPath(const char* path); diff --git a/src/dbus_type_objectpath.cpp b/src/dbus_type_objectpath.cpp index 981ef6f..22d05c3 100644 --- a/src/dbus_type_objectpath.cpp +++ b/src/dbus_type_objectpath.cpp @@ -34,5 +34,7 @@ DBus::Type::ObjectPath::ObjectPath(const DBus::ObjectPath& v) void DBus::Type::ObjectPath::marshall(MessageOStream& stream) const { + if (m_Value.empty()) + throw InvalidObjectPath("Cannot marshall empty ObjectPath"); Type::String::marshall(stream); } diff --git a/src/dbus_type_objectpath.h b/src/dbus_type_objectpath.h index 29db86c..117fefb 100644 --- a/src/dbus_type_objectpath.h +++ b/src/dbus_type_objectpath.h @@ -20,8 +20,6 @@ #include "dbus_type_string.h" -#include - namespace DBus { struct ObjectPath; diff --git a/tests/dbus_names_unittest.cpp b/tests/dbus_names_unittest.cpp index 2c3d601..2b746b5 100644 --- a/tests/dbus_names_unittest.cpp +++ b/tests/dbus_names_unittest.cpp @@ -1,6 +1,6 @@ #include "dbus_names.h" #include -#include +#include namespace DBus { namespace test { @@ -233,5 +233,20 @@ TEST_CASE("ObjectPath Validation") REQUIRE_THROWS_WITH(ObjectPath("/foo//bar"), "/foo//bar has // sequence"); } +TEST_CASE("DBus::Name operators") +{ + REQUIRE(static_cast(BusName("a.b"))); + REQUIRE(static_cast(InterfaceName("a.b"))); + REQUIRE(static_cast(ObjectPath("/a/b"))); + + REQUIRE_FALSE(BusName()); + REQUIRE_FALSE(InterfaceName()); + REQUIRE_FALSE(ObjectPath()); + + std::ostringstream test; + test << UniqueName(":1.2"); + REQUIRE(test.str() == ":1.2"); +} + } // namespace test } // namespace DBus From 2008378f7aad7bf86552ef522fd6e9eabb3f6613 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Fri, 13 May 2022 19:20:09 +0200 Subject: [PATCH 20/24] use singular for enum RequestNameFlag Since each represents one flag value, it's a bit easier on the eye. Signed-off-by: Heiko Hund --- example/dbus-test.cpp | 2 +- example/tests/pummel-server.cpp | 2 +- src/dbus_connection.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/example/dbus-test.cpp b/example/dbus-test.cpp index 8d3ebf3..0afb2f1 100644 --- a/example/dbus-test.cpp +++ b/example/dbus-test.cpp @@ -263,7 +263,7 @@ void test1() serveOpenFile(dbus); dbus->requestName( - "test.steev", DBus::RequestNameFlags::None, + "test.steev", DBus::RequestNameFlag::None, [dbus](const DBus::Error& error, const DBus::Message::MethodReturn& msg) { if (error) return dbus->disconnect(); diff --git a/example/tests/pummel-server.cpp b/example/tests/pummel-server.cpp index 801dd8c..d292d09 100644 --- a/example/tests/pummel-server.cpp +++ b/example/tests/pummel-server.cpp @@ -102,7 +102,7 @@ int main() serveConcat(dbus); dbus->requestName( - "biz.brightsign", DBus::RequestNameFlags::None, + "biz.brightsign", DBus::RequestNameFlag::None, [dbus](const DBus::Error& error, const DBus::Message::MethodReturn& reply) { if (error) { std::cerr << "RequestName error: " << error.message << std::endl; diff --git a/src/dbus_connection.h b/src/dbus_connection.h index 686e7b2..15bf21d 100644 --- a/src/dbus_connection.h +++ b/src/dbus_connection.h @@ -31,7 +31,7 @@ static constexpr char const *Name = "org.freedesktop.DBus"; static constexpr char const *Object = "/org/freedesktop/DBus"; static constexpr char const *Interface = "org.freedesktop.DBus"; -enum RequestNameFlags { +enum RequestNameFlag { None = 0, AllowReplacement = 1, ReplaceExisting = 2, From 2c9c335a3b0efbaf8f3ae71cfc0fc7e1fc7d6d77 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Fri, 13 May 2022 19:51:43 +0200 Subject: [PATCH 21/24] add convenience connect API and connected() method Signed-off-by: Heiko Hund --- example/dbus-test.cpp | 4 +--- src/dbus_connection.h | 28 ++++++++++++++++++++++++++++ src/dbus_transport.h | 5 +++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/example/dbus-test.cpp b/example/dbus-test.cpp index 0afb2f1..db4c136 100644 --- a/example/dbus-test.cpp +++ b/example/dbus-test.cpp @@ -244,9 +244,7 @@ void test1() if (!dbus) return; - dbus->connect( - DBus::Platform::getSessionBus(), - DBus::AuthenticationProtocol::create(), + dbus->connectSessionBus( [dbus](const DBus::Error& error, const std::string&, const std::string&) { if (error) return; diff --git a/src/dbus_connection.h b/src/dbus_connection.h index 15bf21d..722a45a 100644 --- a/src/dbus_connection.h +++ b/src/dbus_connection.h @@ -64,6 +64,29 @@ class Connection : public std::enable_shared_from_this { return std::make_shared(ioContext); } + template + auto connectSystemBus(CompletionToken&& token) + { + return connect(Platform::getSystemBus(), std::move(token)); + } + + template + auto connectSessionBus(CompletionToken&& token) + { + return connect(Platform::getSessionBus(), std::move(token)); + } + + template + auto connect( + const std::string& busPath, + CompletionToken&& token) + { + return connect( + busPath, + AuthenticationProtocol::create(), + std::move(token)); + } + template auto connect( const std::string& busPath, @@ -113,6 +136,11 @@ class Connection : public std::enable_shared_from_this { m_msgProto->stop(); } + bool connected() + { + return m_transport->connected(); + } + template auto sendMethodCall( const Message::MethodCall& methodCall, diff --git a/src/dbus_transport.h b/src/dbus_transport.h index 051b0cf..23dff8c 100644 --- a/src/dbus_transport.h +++ b/src/dbus_transport.h @@ -54,6 +54,11 @@ class Transport : public std::enable_shared_from_this { m_socket.close(); } + bool connected() + { + return m_socket.is_open(); + } + template void asyncPeek(const MutableBuffer& buffer, Handler&& handler) { From 91345d23fda301a7879bf1cfb142a4cb09888564 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Fri, 13 May 2022 19:54:49 +0200 Subject: [PATCH 22/24] add method to cancel receiving signal(s) Signed-off-by: Heiko Hund --- src/dbus_connection.h | 5 +++++ src/dbus_messageprotocol.h | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/dbus_connection.h b/src/dbus_connection.h index 722a45a..c0f6dcf 100644 --- a/src/dbus_connection.h +++ b/src/dbus_connection.h @@ -195,6 +195,11 @@ class Connection : public std::enable_shared_from_this { signal, std::forward(token)); } + auto cancelReceiveSignal(const std::string& signal) + { + return m_msgProto->cancelReceiveSignal(signal); + } + template auto receiveError( CompletionToken&& token) diff --git a/src/dbus_messageprotocol.h b/src/dbus_messageprotocol.h index 25c73c1..40d69b9 100644 --- a/src/dbus_messageprotocol.h +++ b/src/dbus_messageprotocol.h @@ -192,6 +192,17 @@ class MessageProtocol : public std::enable_shared_from_this { }, token, signal); } + bool cancelReceiveSignal(const std::string& signal) + { + // Invoke handler for this exact signal + auto it = m_signalHandlers.find(signal); + if (it != m_signalHandlers.end()) { + m_signalHandlers.erase(it); + return true; + } + return false; + } + template auto receiveError(CompletionToken&& token) { From 01120f14fde9cc8aedfe914232d0323d9cde8f75 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Fri, 13 May 2022 20:04:56 +0200 Subject: [PATCH 23/24] add variadic template, copy and move c'tors to Parameters Signed-off-by: Heiko Hund --- src/dbus_message.cpp | 10 ---------- src/dbus_message.h | 12 ++++++++++-- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/dbus_message.cpp b/src/dbus_message.cpp index ee8e589..494c44b 100644 --- a/src/dbus_message.cpp +++ b/src/dbus_message.cpp @@ -315,16 +315,6 @@ DBus::Message::Parameters::toString(const std::string& prefix) const return oss.str(); } -DBus::Message::Parameters::Parameters( - const DBus::Type::Any& v) - : m_parameters({v}) -{} - -DBus::Message::Parameters::Parameters( - const DBus::Type::Any& v1, const DBus::Type::Any& v2) - : m_parameters({v1, v2}) -{} - void DBus::Message::Parameters::add(const DBus::Type::Any& value) { m_parameters.push_back(value); diff --git a/src/dbus_message.h b/src/dbus_message.h index 13566ea..ec8aeff 100644 --- a/src/dbus_message.h +++ b/src/dbus_message.h @@ -192,8 +192,16 @@ class Message { // inline, with Parameters("param") etc. Everyone else will need // to use a temporary variable and add() Parameters() = default; - Parameters(const DBus::Type::Any& v); - Parameters(const DBus::Type::Any& v1, const DBus::Type::Any& v2); + Parameters(Parameters&&) = default; + Parameters(const Parameters&) = default; + + template + Parameters(const Param&... param) + : m_parameters({ DBus::Type::Any(param)... }) + {} + + Parameters& operator=(Parameters&&) = default; + Parameters& operator=(const Parameters&) = default; void add(const DBus::Type::Any& value); From 2391617ca90701d196d1d001e039bdbf26d320c1 Mon Sep 17 00:00:00 2001 From: Heiko Hund Date: Mon, 16 May 2022 15:29:24 +0200 Subject: [PATCH 24/24] move more initialization into Message c'tor Signed-off-by: Heiko Hund --- src/dbus_message.cpp | 32 +++++++++++++++++++------------- src/dbus_message.h | 14 ++++++++++---- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/dbus_message.cpp b/src/dbus_message.cpp index 494c44b..55f82b0 100644 --- a/src/dbus_message.cpp +++ b/src/dbus_message.cpp @@ -325,6 +325,14 @@ void DBus::Message::Parameters::add(const DBus::Type::Any& value) // // Message // +DBus::Message::Message(const Identifier& id, const Parameters& params) + : m_Parameters(params) +{ + m_Header.path = id.path; + m_Header.interface = id.interface; + m_Header.member = id.member; +} + DBus::Message::Message(const Header& header, OctetBuffer& body) : m_Header(header) { @@ -399,9 +407,10 @@ DBus::Message::MethodCall::MethodCall(const Message::Header& header, DBus::Message::MethodCall::MethodCall( const BusName& destination, - const Identifier& name, + const Identifier& id, const Parameters& params, uint32_t flags) + : Message(id, params) { if (flags & Message::Flag::AllowInteractiveAuthorization) { @@ -415,10 +424,6 @@ DBus::Message::MethodCall::MethodCall( m_Header.flags = flags; m_Header.type = Message::Type::MethodCall; m_Header.destination = destination; - m_Header.path = name.path; - m_Header.interface = name.interface; - m_Header.member = name.member; - m_Parameters = params; } @@ -427,13 +432,14 @@ DBus::Message::MethodCall::MethodCall( // Message::MethodReturn // DBus::Message::MethodReturn::MethodReturn( - const BusName& destination, - uint32_t replySerial) + const BusName& destination, uint32_t replySerial, + const Message::Parameters& params) { m_Header.flags = Flag::NoReplyExpected; m_Header.type = Message::Type::MethodReturn; m_Header.destination = destination; m_Header.replySerial = replySerial; + m_Parameters = params; } DBus::Message::MethodReturn::MethodReturn(const Message::Header& header, @@ -487,19 +493,19 @@ std::string DBus::Message::Error::getMessage() const // Message::Signal // DBus::Message::Signal::Signal( - const Message::Identifier& name) + const Message::Identifier& id, + const Message::Parameters& params) + : Message(id, params) { m_Header.flags = Flag::NoReplyExpected; m_Header.type = Message::Type::Signal; - m_Header.path = name.path; - m_Header.interface = name.interface; - m_Header.member = name.member; } DBus::Message::Signal::Signal( const BusName& destination, - const Message::Identifier& name) - : Signal(name) + const Message::Identifier& id, + const Message::Parameters& params) + : Signal(id, params) { m_Header.destination = destination; } diff --git a/src/dbus_message.h b/src/dbus_message.h index ec8aeff..7e7ea9a 100644 --- a/src/dbus_message.h +++ b/src/dbus_message.h @@ -218,6 +218,8 @@ class Message { }; Message() = default; + Message(const Message::Identifier& id, + const Message::Parameters& params = {}); Message(const Header& header, OctetBuffer& body); explicit operator bool() const { return m_Header.type != Type::Invalid; } @@ -271,7 +273,7 @@ class Message::MethodCall : public Message { MethodCall() = default; // This is for outgoing method calls MethodCall( - const BusName& destination, const Identifier& name, + const BusName& destination, const Identifier& id, const Parameters& params = Parameters(), uint32_t flags = 0); // This is for receiving method calls MethodCall(const Header& header, OctetBuffer& body); @@ -285,7 +287,8 @@ class Message::MethodReturn : public Message { public: MethodReturn() = default; // This is for sending outgoing replies - MethodReturn(const BusName& destination, uint32_t replySerial); + MethodReturn(const BusName& destination, uint32_t replySerial, + const Parameters& params = {}); // This is for receiving method returns MethodReturn(const Header& header, OctetBuffer& body); }; @@ -307,9 +310,12 @@ class Message::Signal : public Message { public: Signal() = default; // This is for outgoing broadcast signals - Signal(const Identifier& name); + Signal(const Identifier& id, + const Parameters& params = {}); // This is for outgoing unicast signals - Signal(const BusName& destination, const Identifier& name); + Signal(const BusName& destination, + const Identifier& id, + const Parameters& params = {}); // This is for receiving signals Signal(const Header& header, OctetBuffer& body); };