From 13e13a5759ad795a8726bdcdad30232d454fafd2 Mon Sep 17 00:00:00 2001 From: YannGarcia Date: Fri, 23 Sep 2022 08:48:02 +0200 Subject: [PATCH 1/9] Create bases of the framework --- README.md | 9 + ccsrc/Framework/include/base_time.hh | 73 ++ ccsrc/Framework/include/codec_factory.hh | 45 + ccsrc/Framework/include/codec_gen.hh | 61 ++ .../Framework/include/codec_stack_builder.hh | 75 ++ ccsrc/Framework/include/converter.hh | 387 +++++++++ ccsrc/Framework/include/layer.hh | 143 ++++ ccsrc/Framework/include/layer_factory.hh | 121 +++ .../Framework/include/layer_stack_builder.hh | 66 ++ ccsrc/Framework/include/oer_codec.hh | 48 ++ ccsrc/Framework/include/params.hh | 93 +++ ccsrc/Framework/include/per_codec.hh | 50 ++ ccsrc/Framework/include/per_codec.t.hh | 26 + ccsrc/Framework/include/registration.hh | 58 ++ ccsrc/Framework/include/t_layer.hh | 70 ++ ccsrc/Framework/module.mk | 7 + ccsrc/Framework/src/base_time.cc | 13 + ccsrc/Framework/src/codec_stack_builder.cc | 4 + ccsrc/Framework/src/converter.cc | 195 +++++ ccsrc/Framework/src/layer_factory.cc | 61 ++ ccsrc/Framework/src/params.cc | 97 +++ ccsrc/Helpers/module.mk | 0 ccsrc/Protocols/ETH/ethernet_layer.cc | 82 ++ ccsrc/Protocols/ETH/ethernet_layer.hh | 48 ++ ccsrc/Protocols/ETH/ethernet_layer_factory.hh | 42 + ccsrc/Protocols/ETH/module.mk | 3 + ccsrc/Protocols/Http/http_codec.cc | 766 ++++++++++++++++++ ccsrc/Protocols/Http/http_codec.hh | 72 ++ ccsrc/Protocols/Http/http_encdec.cc | 28 + ccsrc/Protocols/Http/http_layer.cc | 128 +++ ccsrc/Protocols/Http/http_layer.hh | 83 ++ ccsrc/Protocols/Http/http_layer_factory.hh | 42 + ccsrc/Protocols/Http/module.mk | 3 + ccsrc/Protocols/Pcap/module.mk | 3 + ccsrc/Protocols/Pcap/pcap_cygwin_layer.cc | 265 ++++++ ccsrc/Protocols/Pcap/pcap_cygwin_layer.hh | 79 ++ ccsrc/Protocols/Pcap/pcap_layer.cc | 276 +++++++ ccsrc/Protocols/Pcap/pcap_layer.hh | 5 + ccsrc/Protocols/Pcap/pcap_layer_factory.hh | 42 + ccsrc/Protocols/Pcap/pcap_linux_layer.cc | 198 +++++ ccsrc/Protocols/Pcap/pcap_linux_layer.hh | 62 ++ ccsrc/Protocols/Pcap/pcap_offline_layer.cc | 227 ++++++ ccsrc/Protocols/Pcap/pcap_offline_layer.hh | 80 ++ .../Pcap/pcap_offline_layer_factory.hh | 42 + ccsrc/Protocols/Tcp/module.mk | 3 + ccsrc/Protocols/Tcp/tcp_layer.cc | 259 ++++++ ccsrc/Protocols/Tcp/tcp_layer.hh | 108 +++ ccsrc/Protocols/Tcp/tcp_layer_factory.hh | 41 + ccsrc/Protocols/UDP/module.mk | 4 + ccsrc/Protocols/UDP/udp_layer.cc | 196 +++++ ccsrc/Protocols/UDP/udp_layer.hh | 86 ++ ccsrc/Protocols/UDP/udp_layer_factory.hh | 42 + ccsrc/loggers/loggers.cc | 4 + ccsrc/loggers/loggers.hh | 273 +++++++ ccsrc/loggers/module.mk | 3 + ttcn/LibHttp/LibHelpers/module.mk | 0 ttcn/LibHttp/module.mk | 20 + .../ttcn/LibHttp_BinaryMessageBodyTypes.ttcn | 28 + .../LibHttp/ttcn/LibHttp_BinaryTemplates.ttcn | 18 + ttcn/LibHttp/ttcn/LibHttp_BinaryTypes.ttcn | 9 + .../ttcn/LibHttp_EncdecDeclarations.ttcn | 11 + ttcn/LibHttp/ttcn/LibHttp_Functions.ttcn | 326 ++++++++ ttcn/LibHttp/ttcn/LibHttp_JSONTypes.ttcn | 9 + .../ttcn/LibHttp_JsonMessageBodyTypes.ttcn | 18 + ttcn/LibHttp/ttcn/LibHttp_JsonTemplates.ttcn | 31 + .../ttcn/LibHttp_MessageBodyTypes.ttcn | 24 + ttcn/LibHttp/ttcn/LibHttp_Pics.ttcn | 36 + ttcn/LibHttp/ttcn/LibHttp_Pixits.ttcn | 9 + ttcn/LibHttp/ttcn/LibHttp_Templates.ttcn | 406 ++++++++++ ttcn/LibHttp/ttcn/LibHttp_TestSystem.ttcn | 40 + ttcn/LibHttp/ttcn/LibHttp_TypesAndValues.ttcn | 73 ++ ttcn/LibHttp/ttcn/LibHttp_XMLTypes.ttcn | 11 + .../ttcn/LibHttp_XmlMessageBodyTypes.ttcn | 18 + ttcn/LibHttp/ttcn/LibHttp_XmlTemplates.ttcn | 33 + 74 files changed, 6417 insertions(+) create mode 100644 README.md create mode 100644 ccsrc/Framework/include/base_time.hh create mode 100644 ccsrc/Framework/include/codec_factory.hh create mode 100644 ccsrc/Framework/include/codec_gen.hh create mode 100644 ccsrc/Framework/include/codec_stack_builder.hh create mode 100644 ccsrc/Framework/include/converter.hh create mode 100644 ccsrc/Framework/include/layer.hh create mode 100644 ccsrc/Framework/include/layer_factory.hh create mode 100644 ccsrc/Framework/include/layer_stack_builder.hh create mode 100644 ccsrc/Framework/include/oer_codec.hh create mode 100644 ccsrc/Framework/include/params.hh create mode 100644 ccsrc/Framework/include/per_codec.hh create mode 100644 ccsrc/Framework/include/per_codec.t.hh create mode 100644 ccsrc/Framework/include/registration.hh create mode 100644 ccsrc/Framework/include/t_layer.hh create mode 100644 ccsrc/Framework/module.mk create mode 100644 ccsrc/Framework/src/base_time.cc create mode 100644 ccsrc/Framework/src/codec_stack_builder.cc create mode 100644 ccsrc/Framework/src/converter.cc create mode 100644 ccsrc/Framework/src/layer_factory.cc create mode 100644 ccsrc/Framework/src/params.cc create mode 100644 ccsrc/Helpers/module.mk create mode 100644 ccsrc/Protocols/ETH/ethernet_layer.cc create mode 100644 ccsrc/Protocols/ETH/ethernet_layer.hh create mode 100644 ccsrc/Protocols/ETH/ethernet_layer_factory.hh create mode 100644 ccsrc/Protocols/ETH/module.mk create mode 100644 ccsrc/Protocols/Http/http_codec.cc create mode 100644 ccsrc/Protocols/Http/http_codec.hh create mode 100644 ccsrc/Protocols/Http/http_encdec.cc create mode 100644 ccsrc/Protocols/Http/http_layer.cc create mode 100644 ccsrc/Protocols/Http/http_layer.hh create mode 100644 ccsrc/Protocols/Http/http_layer_factory.hh create mode 100644 ccsrc/Protocols/Http/module.mk create mode 100644 ccsrc/Protocols/Pcap/module.mk create mode 100644 ccsrc/Protocols/Pcap/pcap_cygwin_layer.cc create mode 100644 ccsrc/Protocols/Pcap/pcap_cygwin_layer.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_layer.cc create mode 100644 ccsrc/Protocols/Pcap/pcap_layer.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_layer_factory.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_linux_layer.cc create mode 100644 ccsrc/Protocols/Pcap/pcap_linux_layer.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_offline_layer.cc create mode 100644 ccsrc/Protocols/Pcap/pcap_offline_layer.hh create mode 100644 ccsrc/Protocols/Pcap/pcap_offline_layer_factory.hh create mode 100644 ccsrc/Protocols/Tcp/module.mk create mode 100644 ccsrc/Protocols/Tcp/tcp_layer.cc create mode 100644 ccsrc/Protocols/Tcp/tcp_layer.hh create mode 100644 ccsrc/Protocols/Tcp/tcp_layer_factory.hh create mode 100644 ccsrc/Protocols/UDP/module.mk create mode 100644 ccsrc/Protocols/UDP/udp_layer.cc create mode 100644 ccsrc/Protocols/UDP/udp_layer.hh create mode 100644 ccsrc/Protocols/UDP/udp_layer_factory.hh create mode 100644 ccsrc/loggers/loggers.cc create mode 100644 ccsrc/loggers/loggers.hh create mode 100644 ccsrc/loggers/module.mk create mode 100644 ttcn/LibHttp/LibHelpers/module.mk create mode 100644 ttcn/LibHttp/module.mk create mode 100644 ttcn/LibHttp/ttcn/LibHttp_BinaryMessageBodyTypes.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_BinaryTemplates.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_BinaryTypes.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_EncdecDeclarations.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_Functions.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_JSONTypes.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_JsonMessageBodyTypes.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_JsonTemplates.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_MessageBodyTypes.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_Pics.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_Pixits.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_Templates.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_TestSystem.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_TypesAndValues.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_XMLTypes.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_XmlMessageBodyTypes.ttcn create mode 100644 ttcn/LibHttp/ttcn/LibHttp_XmlTemplates.ttcn diff --git a/README.md b/README.md new file mode 100644 index 0000000..a34e5d3 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# TITAN Test System Framework + + +This module provides the bases to start the development of any Conformance Testing project or Interoperability Testing project. +It contains: +- A set C/C++ modules such as logging, codec support, +- A set of TTCN-3 modules such as LibHttp + + diff --git a/ccsrc/Framework/include/base_time.hh b/ccsrc/Framework/include/base_time.hh new file mode 100644 index 0000000..03fa8f6 --- /dev/null +++ b/ccsrc/Framework/include/base_time.hh @@ -0,0 +1,73 @@ +/*! + * \file base_time.hh + * \brief Header file for the control port base_time functionality. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include + +/** + * \class base_time + * \brief This class provides time tools such as getting current time + */ +class base_time { + const unsigned long long its_base_time_ms = 1072915200000L; //! Base time 01/01/2004 12:00am in millseconds + + unsigned long long leap_delay; + + static base_time *_instance; + +private: + base_time() : leap_delay{0} {}; //! Can not be created manually +public: + static inline base_time &get_instance(); + + virtual ~base_time() { + if (_instance != nullptr) + delete _instance; + }; + +public: + inline const unsigned long long get_current_time_ms() const; + inline const unsigned long long get_its_base_time_ms() const; + inline const unsigned long long get_its_current_time_ms() const; + inline const unsigned long long get_its_current_time_us() const; + inline const unsigned long long get_its_current_time_mod_ms() const; + inline void set_leap_delay_us(const unsigned long long p_leap_delay); + inline const unsigned long long get_leap_delay_us() const; +}; // End of class base_time + +// static functions +base_time &base_time::get_instance() { return (_instance != nullptr) ? *_instance : *(_instance = new base_time()); } + +const unsigned long long base_time::get_current_time_ms() const { + return (leap_delay / 1000) + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); +} + +const unsigned long long base_time::get_its_base_time_ms() const { return base_time::its_base_time_ms; } + +const unsigned long long base_time::get_its_current_time_ms() const { + return (leap_delay / 1000) + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() - + base_time::its_base_time_ms; +} + +const unsigned long long base_time::get_its_current_time_us() const { + return leap_delay + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() - + base_time::its_base_time_ms * 1000; +} + +const unsigned long long base_time::get_its_current_time_mod_ms() const { + return ((leap_delay / 1000) + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() - + base_time::its_base_time_ms) % + 65536; +} + +void base_time::set_leap_delay_us(const unsigned long long p_leap_delay) { leap_delay = p_leap_delay; } + +inline const unsigned long long base_time::get_leap_delay_us() const { return leap_delay; } diff --git a/ccsrc/Framework/include/codec_factory.hh b/ccsrc/Framework/include/codec_factory.hh new file mode 100644 index 0000000..9d09b73 --- /dev/null +++ b/ccsrc/Framework/include/codec_factory.hh @@ -0,0 +1,45 @@ +/*! + * \file codec_factory.hh + * \brief Header file for ITS abstract protocol codec definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include +#include + +#include "codec_gen.hh" + +class Record_Type; //! TITAN forward declaration + +/*! + * \class codec_factory + * \brief This class provides a factory class to create codec class instances + * \abstract + */ +class codec_factory { +public: //! \publicsection + /*! + * \fn codec_factory(); + * \brief Default constructor + */ + codec_factory(){}; + /*! + * \fn codec* create_codec(const std::string & type, const std::string & param); + * \brief Create the codecs stack based on the provided codecs stack description (cf. remark) + * \param[in] p_type The provided codecs stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \remark The description below introduces codecs stack in case of ITS project: + * HTTP(codecs=xml:held_codec;html:html_codec,json:json_codec)/TCP(debug=1,server=httpbin.org,port=80,use_ssl=0) + * \pure + */ + virtual codec_gen *create_codec() = 0; +}; // End of class codec_factory diff --git a/ccsrc/Framework/include/codec_gen.hh b/ccsrc/Framework/include/codec_gen.hh new file mode 100644 index 0000000..82cfc8b --- /dev/null +++ b/ccsrc/Framework/include/codec_gen.hh @@ -0,0 +1,61 @@ +/*! + * \file codec.hh + * \brief Header file for ITS abstract codec definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "params.hh" + +class OCTETSTRING; //! Declare TITAN class +class CHARSTRING; //! Declare TITAN class +class BITSTRING; //! Declare TITAN class + +/*! + * \class codec + * \brief This class provides the interface for all ITS codecs, include UT and AC codecs + * \abstract + */ +template class codec_gen { +protected: + params *_params; //! Reference to params stack + // \todo Use smart pointer std::unique_ptr + +public: //! \publicsection + /*! + * \fn codec_gen(); + * \brief Default constructor + * \todo Remove logs + */ + explicit codec_gen() : _params(nullptr){}; + /*! + * \fn ~codec_gen(); + * \brief Default destructor + * \virtual + * \todo Remove logs + */ + virtual ~codec_gen(){}; + /*! + * \fn int encode(const TPDUEnc& msg, OCTETSTRING& data); + * \brief Encode typed message into an octet string + * \param[in] p_message The typed message to be encoded + * \param[out] p_data The encoding result + * \return 0 on success, -1 otherwise + * \pure + */ + virtual int encode(const TPDUEnc &p_message, OCTETSTRING &p_data) = 0; + /*! + * \fn int decode(const OCTETSTRING& p_, TPDUDec& p_message, params_its* p_params = NULL); + * \brief Encode typed message into an octet string format + * \param[in] p_data The message in its octet string + * \param[out] p_message The decoded typed message + * \return 0 on success, -1 otherwise + * \pure + */ + virtual int decode(const OCTETSTRING &p_, TPDUDec &p_message, params *p_params = NULL) = 0; +}; // End of class codec_gen diff --git a/ccsrc/Framework/include/codec_stack_builder.hh b/ccsrc/Framework/include/codec_stack_builder.hh new file mode 100644 index 0000000..ca1be9f --- /dev/null +++ b/ccsrc/Framework/include/codec_stack_builder.hh @@ -0,0 +1,75 @@ +/*! + * \file codec_stack_builder.hh + * \brief Header file for ITS protocol stack builder. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "codec_factory.hh" + +class Record_Type; //! TITAN forward declaration + +/*! + * \class codec_stack_builder + * \brief This class provides a factory class to create Codec class instances + */ +class codec_stack_builder { +private: //! \privatesection + static codec_stack_builder * _instance; //! Smart pointer to the unique instance of the logger framework + std::map _codecs; //! The list of the registered \see t_codec factories + + /*! + * \brief Default constructor + * Create a new instance of the codec_stack_builder class + * \private + */ + codec_stack_builder(){}; // can not be created manually +public: //! \publicsection + /*! + * \fn codec_stack_builder* get_instance(); + * \brief Accessor for the unique instance of the logger framework + * \static + */ + static codec_stack_builder *get_instance() { return _instance ? _instance : _instance = new codec_stack_builder(); }; + + /*! + * \fn void register_codec_factory(const std::string & p_type, codec_factory* p_codec_factory); + * \brief Add a new codec factory + * \param[in] p_type The codec identifier (e.g. GN for the GeoNetworking codec...) + * \param[in] p_codec_factory A reference to the \see codec_factory + * \static + */ + static void register_codec_factory(const std::string &p_type, codec_factory *p_codec_factory) { + codec_stack_builder::get_instance()->_register_codec_factory(p_type, p_codec_factory); + }; + +private: //! \privatesection + /*! + * \fn void _register_codec_factory(const std::string & p_type, codec_factory* p_codec_factory); + * \brief Add a new codec factory + * \param[in] p_type The codec identifier (e.g. GN for the GeoNetworking codec...) + * \param[in] p_codec_factory A reference to the \see codec_factory + */ + void _register_codec_factory(const std::string &p_type, codec_factory *p_codec_factory) { _codecs[p_type] = p_codec_factory; }; + +public: //! \publicsection + /*! + * \fn codec* get_codec(const char* p_codec_name); + * \brief Retrieve the specified codec name from the list of the registered codecs + * \param[in] p_codec_name The codec indentifier + * \return The pointer to the codec object on success, nullptr otherwise + */ + inline codec_gen *get_codec(const char *p_codec_name) { // NOTE A virtual method cannot not be a template ==> polymorphism required here + typename std::map::const_iterator it = _codecs.find(p_codec_name); + if (it != _codecs.cend()) { + return it->second->create_codec(); + } + + return nullptr; + } +}; // End of class codec_stack_builder diff --git a/ccsrc/Framework/include/converter.hh b/ccsrc/Framework/include/converter.hh new file mode 100644 index 0000000..6070f2d --- /dev/null +++ b/ccsrc/Framework/include/converter.hh @@ -0,0 +1,387 @@ +/*! + * \file converter.hh + * \brief Helper class for types converter. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include + +#include +#include +#include + +#include +#include // LONG_MAX, LLONG_MAX +#include +#include // time_t, struct tm, difftime, time, mktime + +/*! + * \class converter + * \brief This class provide a set of methods for types conversions + * \remark Singleton pattern + */ +class converter { + + /*! + * \brief Unique static object reference of this class + */ + static converter *instance; + + /*! + * \brief Default private ctor + */ + converter(){}; + /*! + * \brief Default private dtor + */ + ~converter() { + if (instance != NULL) { + delete instance; + instance = NULL; + } + }; + +public: /*! \publicsection */ + /*! + * \brief Public accessor to the single object reference + */ + inline static converter &get_instance() { + if (instance == NULL) + instance = new converter(); + return *instance; + }; + +public: + /*! + * \enum endian_t + * \brief Endianess style + */ + typedef enum { big_endian, little_endian } endian_t; + +public: + /*! + * \brief Convert a Binary Coded Decimal value into a binary value + * \param[in] p_value The BDC value + * \return The binary value + * \inline + */ + inline uint8_t bcd_to_bin(const uint8_t p_value) { return ((p_value / 16 * 10) + (p_value % 16)); }; + + /*! + * \brief Convert a binary value into a Binary Coded Decimal value + * \param[in] p_value The binary value + * \return The BCD value + * \inline + */ + inline uint8_t bin_to_bcd(const uint8_t p_value) { return ((p_value / 10 * 16) + (p_value % 10)); }; + + /*! + * \brief Swap two bytes length value (e.g. 0xCAFE becomes 0xFECA) + * \param[in] p_value The value to swap + * \return The swapped value + * \inline + */ + uint16_t swap(const uint16_t p_value); + inline int16_t swap(const int16_t p_value) { return static_cast(swap(static_cast(p_value))); }; + /*! + * \brief Swap four bytes length value (used for littel endian / big endian) + * \param[in] p_value The value to swap + * \return The swapped value + */ + uint32_t swap(const uint32_t p_value); + inline int32_t swap(const int32_t p_value) { return static_cast(swap(static_cast(p_value))); }; + + /*! + * \brief Convert a string into an hexadecimal string + * \param[in] p_value The string value + * \return The hexadecimal value + */ + std::string string_to_hexa(const std::string &p_value, const bool p_uppercase = false); + /*! + * \brief Convert a bytes array int32_t an hexadecimal string + * \param[in] p_value The bytes array value + * \return The hexadecimal value + */ + std::string bytes_to_hexa(const std::vector &p_value, const bool p_uppercase = false); + /*! + * \brief Convert an hexadecimal string into a bytes array + * \param[in] p_value The hexadecimal value + * \return The bytes array value + */ + std::vector hexa_to_bytes(const std::string &p_value); + + /*! + * \brief Convert a time in time_t format into a string formated according to RFC 822, 1036, 1123, 2822 + * \param[in] p_time The time to convert in time_t format + * \return The time string formated + * \see http://www.unixtimestamp.com/ + * @code + * std::string result = time_to_string(1489755780); + * result.compare("Fri, 17 Mar 2017 13:03:00 +0000") == 0 // When time zone is set to UTC + * @endcode + * \remark Use commands 1) timedatectl to change your machine timezone (e.g. sudo timedatectl set-timezone UTC to change machine timezone to UTC, 2) + * timedatectl list-timezones to get the list of the timezones) + */ + std::string time_to_string(const time_t p_time); + /*! + * \brief Convert a time in struct tm format into a string formated according to RFC 822, 1036, 1123, 2822 + * \param[in] p_time The time to convert in struct tm format + * \return The time string formated + * \see http://www.unixtimestamp.com/ + */ + std::string time_to_string(const struct tm &p_time); + + /*! + * \brief Convert a 16-bits integer (int16_t) into a bytes array + * \param[in] p_value The 16-bits integer value + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The bytes array value + */ + inline std::vector short_to_bytes(const int16_t p_value, const endian_t p_endianess = big_endian) const { + std::vector result(sizeof(short), 0x00); + for (int i = sizeof(short) - 1; i >= 0; i--) { + int offset = (sizeof(short) - 1 - i) * 8; + result[i] = static_cast((p_value >> offset) & 0xFF); + } // End of 'for' statement + return result; + }; // End of short_to_bytes + + /*! + * \brief Convert a bytes array into a 16-bits integer (int16_t) + * \param[in] p_value The bytes array + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The 16-bits integer on success, SHRT_MAX on error (wrong bytes array size) + */ + inline int16_t bytes_to_short(const std::vector &p_value, const endian_t p_endianess = big_endian) const { + // Sanity check + if (p_value.size() > sizeof(short)) { + return SHRT_MAX; + } + int16_t value = 0; + for (size_t i = 0; i < p_value.size(); i++) { + value = (value << 8) + (p_value[i] & 0xff); + } // End of 'for' statement + return value; + }; // End of bytes_to_short + + /*! + * \brief Convert a 32-bits integer (int32_t) into a bytes array + * \param[in] p_value The 32-bits integer value + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The bytes array value + */ + inline std::vector int_to_bytes(const int32_t p_value, const endian_t p_endianess = big_endian) const { + /*uint8_t bytes[sizeof(p_value)]; + std::copy( + static_cast(static_cast(&p_value)), + static_cast(static_cast(&p_value)) + sizeof(p_value), + bytes + ); + std::vector result(bytes, bytes + sizeof(bytes) / sizeof(uint8_t));*/ + std::vector result(sizeof(int), 0x00); + for (int i = sizeof(int) - 1; i >= 0; i--) { + int offset = (sizeof(int) - 1 - i) * 8; + result[i] = static_cast((p_value >> offset) & 0xFF); + } // End of 'for' statement + return result; + }; // End of int_to_bytes + + /*! + * \brief Convert a bytes array into a 32-bits integer (int32_t) + * \param[in] p_value The bytes array + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The 32-bits integer on success, LONG_MAX on error (wrong bytes array size) + */ + inline int32_t bytes_to_int(const std::vector &p_value, const endian_t p_endianess = big_endian) const { + // Sanity check + if (p_value.size() > sizeof(int)) { + return INT_MAX; + } + int32_t value = 0; + for (size_t i = 0; i < p_value.size(); i++) { + value = (value << 8) + (p_value[i] & 0xff); + } // End of 'for' statement + return value; + // return *((int *)(&p_value[0])); + }; // End of bytes_to_int + + /*! + * \brief Convert a 64-bits integer (int64_t) into a bytes array + * \param[in] p_value The 64-bits integer value + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The bytes array value + */ + inline std::vector long_to_bytes(const int64_t p_value, const endian_t p_endianess = big_endian) const { + /*uint8_t bytes[sizeof(p_value)]; + std::copy( + static_cast(static_cast(&p_value)), + static_cast(static_cast(&p_value)) + sizeof(p_value), + bytes + ); + std::vector result(bytes, bytes + sizeof(bytes) / sizeof(uint8_t));*/ + std::vector result(sizeof(int64_t), 0x00); + for (int i = sizeof(int64_t) - 1; i >= 0; i--) { + int offset = (sizeof(int64_t) - 1 - i) * 8; + result[i] = static_cast((p_value >> offset) & 0xFF); + } // End of 'for' statement + return result; + }; // End of long_to_bytes + + /*! + * \brief Convert a bytes array into a 64-bits integer (int64_t) + * \param[in] p_value The bytes array + * \param[in] p_endianess Endianess style. Default: big_endian + * \return The 64-bits integer on success, LLONG_MAX on error (wrong bytes array size) + */ + inline int64_t bytes_to_long(const std::vector &p_value, const endian_t p_endianess = big_endian) const { + // Sanity check + if (p_value.size() > sizeof(int64_t)) { + return LLONG_MAX; + } + int64_t value = 0; + for (size_t i = 0; i < p_value.size(); i++) { + value = (value << 8) + (p_value[i] & 0xff); + } // End of 'for' statement + return value; + // return *((long *)(&p_value[0])); + }; // End of bytes_to_long + + /*! + * \brief Convert a float value into a bytes array + * \param[in] p_value The float value + * \return The bytes array value + */ + inline std::vector float_to_bytes(const float p_value) const { + uint8_t bytes[sizeof(p_value)]; + std::copy(static_cast(static_cast(&p_value)), + static_cast(static_cast(&p_value)) + sizeof(p_value), bytes); + std::vector result(bytes, bytes + sizeof(bytes) / sizeof(uint8_t)); + return result; + }; // End of float_to_long + + /*! + * \brief Convert a bytes array into a float + * \param[in] p_value The bytes array + * \return The float value + */ + inline float bytes_to_float(const std::vector &p_value) const { return *((float *)(&p_value[0])); }; // End of bytes_to_float + + /*! + * \brief Convert a string into a bytes array + * \param[in] p_value The string value + * \return The bytes array value + */ + inline std::vector string_to_bytes(const std::string &p_value) const { + return std::vector(p_value.begin(), p_value.end()); + }; // End of string_to_bytes + + /*! + * \brief Convert a bytes array into a string + * \param[in] p_value The bytes array value + * \return The string value + */ + inline std::string bytes_to_string(const std::vector &p_value) const { + return std::string(p_value.begin(), p_value.end()); + }; // End of bytes_to_string + +public: + /*! + * \brief Convert a string into an integer + * \param[in] p_value The string value + * \return The integer value + */ + inline int32_t string_to_int(const std::string &p_value) const { + return std::stoi(p_value); + // return atoi(p_value.c_str()); + }; // End of string_to_int + + /*! + * \brief Convert an integer into a string + * \param[in] p_value The integer value + * \return The string value + */ + inline std::string int_to_string(const int32_t &p_value) const { + std::ostringstream ss; + ss << p_value; + return ss.str(); + }; // End of string_to_bytes + + /*! + * \brief Convert a string in to lower case + * \param[in/out] p_value The string value to convert + */ + inline void to_lower(std::string &p_value) { std::transform(p_value.begin(), p_value.end(), p_value.begin(), ::tolower); } + + /*! + * \brief Convert a string in to upper case + * \param[in/out] p_value The string value to convert + */ + inline void to_upper(std::string &p_value) { std::transform(p_value.begin(), p_value.end(), p_value.begin(), ::toupper); } + +public: + /*! + * \brief Returns a copy of the string, with leading and trailing special characters omitted + * \param[in] p_value The string value + * \param[in] p_trim_chars The special characters to be omitted. Default: ' ' and TAB + * \return The new string value + */ + std::string trim(const std::string &p_value, const std::string &p_trim_chars = " \t"); + + /*! + * \brief Convert the provided string into a list of arguments + * \param[in] p_value The string value + * \param[in] p_separator The separator sequence to use for the spliting process + * \return The item list + * \code{.cc} + * std::string str = "This is a test for spliting a string with a white spave"; + * std::vector tokens = converter::get_instance().split(str, " "); + * std::clog << "Tokens: " << std::endl; + * for (auto it = tokens.begin(); it != tokens.end(); ++it) { + * std::clog << " " << *it << std::endl; + * } + * \endcode + */ + std::vector split(const std::string &p_value, const std::string &p_separator); + + /*! + * \brief Convert the provided string into a list of arguments + * \param[in] p_value The string value + * \return The arguments list + * \code{.cc} + * std::string str = "--host localhost --port 12345 --duration -1"; + * std::vector tokens = converter::get_instance().split_arguments_line(str); + * std::clog << "Tokens: " << std::endl; + * for (auto it = tokens.begin(); it != tokens.end(); ++it) { + * std::clog << " " << *it << std::endl; + * } + * \endcode + */ + std::vector split_arguments_line(const std::string &p_value); + + /*! + * \brief Convert the provided buffer into a Base64 + * \param[in] p_value The buffer value + * \return The Base64 encoded buffert + */ + std::vector buffer_to_base64(const std::vector &p_value); + + /*! + * \brief Convert the provided Base64 buffer + * \param[in] p_value The buffer value + * \return The Base64 encoded buffert + */ + std::vector base64_to_buffer(const std::vector &p_value); + + static const std::string lut_u; + static const std::string lut_l; + static const std::string base64_enc_map; + +}; // End of class converter diff --git a/ccsrc/Framework/include/layer.hh b/ccsrc/Framework/include/layer.hh new file mode 100644 index 0000000..66377a1 --- /dev/null +++ b/ccsrc/Framework/include/layer.hh @@ -0,0 +1,143 @@ +/*! + * \file layer.hh + * \brief Header file for ITS abstract protocol layer definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include +#include + +#include "params.hh" + +class OCTETSTRING; //! Forward declaration of TITAN class +class BITSTRING; //! Forward declaration of TITAN class +class CHARSTRING; //! Forward declaration of TITAN class +class INTEGER; //! Forward declaration of TITAN class + +/*! + * \class layer + * \brief This class provides basic description of an ITS protocol layer + */ +class layer { + std::vector upperLayers; //! List of the upper protocol layers + std::vector lowerLayers; //! List of the lower protocol layers + +protected: + std::string type; //! Type description, it indicates the protocol type (e.g. CAM, DENM, GN, ETH, PCAP...) + +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the layer class + */ + explicit layer() : upperLayers(), lowerLayers(), type(std::string("")){}; + + /*! + * \brief Specialized constructor + * Create a new instance of the layer class with its type description + * \param[in] p_type The port type name (e.g. GN for the GeoNetworking layer) + * \remark This constructor is called by the layer factory + * \see layer_factory + */ + explicit layer(const std::string &p_type) : upperLayers(), lowerLayers(), type(std::string(p_type.begin(), p_type.end())){}; + + /*! + * \brief Default destructor + * \todo Remove logs + */ + virtual ~layer() { + // Double linked list, only remove layers in lowerLayers from the lowest one + std::for_each(lowerLayers.rbegin(), lowerLayers.rend(), [](layer *it) { delete it; }); + lowerLayers.clear(); + upperLayers.clear(); + }; + + /*! + * \fn void delete_layer(); + * \brief Delete this layer + * \todo To be implemented + */ + void delete_layer(){}; + +public: //! \publicsection + /*! + * \inline + * \fn void add_upper_layer(layer* p_layer); + * \brief Add a new layer in the list of the upper layer + * \param[in] p_layer The layer protocol to be removed + */ + inline void add_upper_layer(layer *p_layer) { + if (p_layer != NULL) { + upperLayers.push_back(p_layer); + p_layer->lowerLayers.push_back(this); + }; + }; + + /*! + * \fn void remove_upper_layer(layer* p_layer); + * \brief Remove the specified upper layer protocol from the list of the upper layer + * \param[in] p_layer The layer protocol to be removed + * \todo To be implemented + */ + void remove_upper_layer(layer *p_layer){}; + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + * \todo Remove the logs + * \virtual + */ + virtual void send_data(OCTETSTRING &p_data, params &p_params){}; + + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params_its& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + * \todo Remove the logs + * \virtual + */ + virtual void receive_data(OCTETSTRING &p_data, params &p_params) {} + + /*! + * \inline + * \fn const std::string& to_string(); + * \brief Remove the specified upper layer protocol from the list of the upper layer + * \param[in] The layer protocol to be removed + */ + inline const std::string &to_string() const { return type; }; + +protected: //! \protectedsection + inline void to_all_layers(std::vector &layers, OCTETSTRING &data, params ¶ms) { + for (std::vector::const_iterator it = layers.cbegin(); it != layers.cend(); ++it) { + layer *p = *it; + p->receive_data(data, params); // FIXME BUG I + } // End of 'for' statement + }; + + inline void receive_to_all_layers(OCTETSTRING &data, params ¶ms) { + for (std::vector::const_iterator it = upperLayers.cbegin(); it != upperLayers.cend(); ++it) { + layer *p = *it; + p->receive_data(data, params); + } // End of 'for' statement + }; + + inline void send_to_all_layers(OCTETSTRING &data, params ¶ms) { + for (std::vector::const_iterator it = lowerLayers.cbegin(); it != lowerLayers.cend(); ++it) { + layer *p = *it; + p->send_data(data, params); + } // End of 'for' statement + }; +}; // End of class layer diff --git a/ccsrc/Framework/include/layer_factory.hh b/ccsrc/Framework/include/layer_factory.hh new file mode 100644 index 0000000..7b8ac35 --- /dev/null +++ b/ccsrc/Framework/include/layer_factory.hh @@ -0,0 +1,121 @@ +/*! + * \file layer_factory.hh + * \brief Header file for ITS abstract protocol layer definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include +#include + +#include "layer.hh" + +/*! + * \class layer_factory + * \brief This class provides a factory class to create layer class instances + * \abstract + */ +class layer_factory { +public: //! \publicsection + /*! + * \fn codec(); + * \brief Default constructor + */ + layer_factory(){}; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description (cf. remark) + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \remark The description below introduces layers stack in case of ITS project: + * CAM layer + * next_header : btpA|btpB (overwrite BTP.type) + * header_type : tsb|gbc + * header_sub_type : sh (single hop) + * DENM layer + * next_header : btpA|btpB (overwrite BTP.type) + * header_type : tsb|gbc + * BTP layer + * type : btpA|btpB + * destination port: dst_port + * source port : src_port + * device_mode : Set to 1 if the layer shall encapsulate upper layer PDU + * GN layer + * its_aid : ITS AID as defined by ETSI TS 102 965 V1.2.1. Default: 141 + * ll_address : GeoNetworking address of the Test System + * latitude : latitude of the Test System + * longitude : longitude of the Test System + * beaconing : Set to 1 if GnLayer shall start beaconing + * Beaconing timer expiry : expiry (ms) + * device_mode : Set to 1 if the layer shall encapsulate upper layer PDU + * secured_mode : Set to 1 if message exchanges shall be signed + * encrypted_mode : Set to 1 if message exchanges shall be encrypted + * NOTE: For signed & encrypted message exchanges, both secured_mode and encrypted_mode shall be set to 1 + * certificate : Certificate identifier the Test Adapter shall use + * secure_db_path : Path to the certificates and keys storage location + * hash : Hash algorithm to be used when secured mode is set + * Authorized values are SHA-256 or SHA-384 + * Default: SHA-256 + * signature : Signature algorithm to be used when secured mode is set + * Authorized values are NISTP-256, BP-256 and BP-384 + * Default: NISTP-256 + * cypher : Cyphering algorithm to be used when secured mode is set + * Ethernet layer + * mac_src :Source MAC address + * mac_bc :Broadcast address + * eth_type : Ethernet type + * Commsignia layer + * mac_src : Device MAC address, used to discard packets + * To indicate no filering, use the value 000000000000 + * mac_bc : Broadcast address + * eth_type : Ethernet type, used to discard packets + * target_host : Device address + * target_port : Device port + * source_port : Test System port + * interface_id: Interface id, used to discard packets + * tx_power : TX power (dB) + * UDP layer (IP/UDP based on Pcap) + * dst_ip : destination IPv4 address (aa.bb.cc.dd) + * dst_port: destination port + * src_ip : source IPv4 address (aa.bb.cc.dd) + * src_port: source port + * Pcap layer + * mac_src : Source MAC address, used to exclude from capture the acket sent by the Test System + * filter : Pcap filter (compliant with tcpdump syntax) + * E.g. filter=and ether src 04e548000001 + * Online mode: + * nic: Local NIC + * If set, online mode is used + * Offline mode (nic is present but not set): + * file : File to read + * frame_offset: Frame offset, used to skip packets with frame number < frame_offset + * time_offset : Time offset, used to skip packets with time offset < time_offset + * save_mode : 1 to save sent packet, 0 otherwise + * Here are some examples: + * GeoNetworking multiple component case: + * NodeB.geoNetworkingPort.params := +"GN(ll_address=04e548000001,latitude=43551050,longitude=10298730,beaconing=0,expiry=1000,its_aid=141)/COMMSIGNIA(mac_src=04e548000001,mac_bc=FFFFFFFFFFFF,eth_type=8947,target_host=10.200.1.101,target_port=7942,source_port=7943,its_aid=141,interface_id=2,tx_power=-32)/UDP(dst_ip=192.168.56.1,dst_port=12346,src_ip=192.168.156.4,src_port=12345)/ETH(mac_src=04e548000001,mac_dst=0A0027000011,eth_type=0800)/PCAP(mac_src=04e548000001,file=/home/vagrant/TriesAndDelete/etsi_its/testdata/TC_AUTO_IOT_DENM_RWW_BV_01_short.pcap,filter=and +(udp port 30000 or udp port 7943))" NodeC.geoNetworkingPort.params := +"GN(ll_address=70b3d5791b48,latitude=43551050,longitude=10298730,beaconing=0,expiry=1000,its_aid=141)/COMMSIGNIA(mac_src=70b3d5791b48,mac_bc=FFFFFFFFFFFF,eth_type=8947,target_host=10.200.1.101,target_port=7942,source_port=7943,its_aid=141,interface_id=2,tx_power=-32)/UDP(dst_ip=192.168.56.1,dst_port=12346,src_ip=192.168.156.4,src_port=12345)/ETH(mac_src=70b3d5791b48,mac_dst=0A0027000011,eth_type=0800)/PCAP(mac_src=70b3d5791b48,file=/home/vagrant/TriesAndDelete/etsi_its/testdata/TC_AUTO_IOT_DENM_RWW_BV_01_short.pcap,filter=and +(udp port 30000 or udp port 7943))" + * NodeB.geoNetworkingPort.params := +"GN(ll_address=04e548000001,latitude=43551050,longitude=10298730,beaconing=0,expiry=1000,its_aid=141)/ETH(mac_src=04e548000001,mac_dst=0A0027000011,eth_type=0800)/PCAP(mac_src=04e548000001,file=/home/vagrant/TriesAndDelete/etsi_its/testdata/TC_AUTO_IOT_DENM_RWW_BV_01.pcap,filter=and +ether src 04e548000001)" #NodeC.geoNetworkingPort.params := +"GN(ll_address=70b3d5791b48,latitude=43551050,longitude=10298730,beaconing=0,expiry=1000,its_aid=141)/ETH(mac_src=70b3d5791b48,mac_dst=0A0027000011,eth_type=0800)/PCAP(mac_src=70b3d5791b48,file=/home/vagrant/TriesAndDelete/etsi_its/testdata/TC_AUTO_IOT_DENM_RWW_BV_01.pcap,filter=and +ether src 70b3d5791b48)" + * UpperTester port based on UDP + * system.utPort.params := +"UT_GN/UDP(dst_ip=192.168.1.1,dst_port=12346,src_ip=192.168.156.4,src_port=12345)/ETH(mac_src=026f8338c1e5,mac_dst=0A0027000011,eth_type=0800)/PCAP(mac_src=0800275c4959,nic=enp0s8,filter=and +udp port 12346)" + * \pure + */ + virtual layer *create_layer(const std::string &p_type, const std::string &p_params) = 0; +}; // End of class layer_factory diff --git a/ccsrc/Framework/include/layer_stack_builder.hh b/ccsrc/Framework/include/layer_stack_builder.hh new file mode 100644 index 0000000..2d1b25a --- /dev/null +++ b/ccsrc/Framework/include/layer_stack_builder.hh @@ -0,0 +1,66 @@ +/*! + * \file layer_stack_builder.hh + * \brief Header file for ITS protocol stack builder. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_factory.hh" + +/*! + * \class layer_stack_builder + * \brief This class provides a factory class to create Layer class instances + */ +class layer_stack_builder { +private: //! \privatesection + typedef std::map LayerFactoryMap; + + static layer_stack_builder * _instance; //! Smart pointer to the unique instance of the logger framework + std::map _layer_factories; //! The list of the registered \see t_layer factories + + /*! + * \brief Default constructor + * Create a new instance of the layer_stack_builder class + * \private + */ + layer_stack_builder(); // can not be created manually +public: //! \publicsection + /*! + * \fn layer_stack_builder* get_instance(); + * \brief Accessor for the unique instance of the logger framework + * \static + */ + static layer_stack_builder *get_instance(); + + /*! + * \fn void register_layer_factory(const std::string & p_type, layer_factory* p_layer_factory); + * \brief Add a new layer factory + * \param[in] p_type The layer identifier (e.g. GN for the GeoNetworking layer...) + * \param[in] p_layer_factory A reference to the \see layer_factory + * \static + */ + static void register_layer_factory(const std::string &p_type, layer_factory *p_layer_factory); + +private: //! \privatesection + /*! + * \fn void _register_layer_factory(const std::string & p_type, layer_factory* p_layer_factory); + * \brief Add a new layer factory + * \param[in] p_type The layer identifier (e.g. GN for the GeoNetworking layer...) + * \param[in] p_layer_factory A reference to the \see layer_factory + */ + void _register_layer_factory(const std::string &p_type, layer_factory *p_layer_factory); + +public: //! \publicsection + /*! + * \fn layer* create_layer_stack(const char* p_layer_stack_description); + * \brief Add a new layer factory + * \param[in] p_layer_stack_description A textual description of the layer to create + * \return The created layer object on success, nullptr otherwise + */ + layer *create_layer_stack(const char *p_layer_stack_description); +}; // End of class layer_stack_builder diff --git a/ccsrc/Framework/include/oer_codec.hh b/ccsrc/Framework/include/oer_codec.hh new file mode 100644 index 0000000..51a32cd --- /dev/null +++ b/ccsrc/Framework/include/oer_codec.hh @@ -0,0 +1,48 @@ +#pragma once + +#include "params.hh" + +class OCTETSTRING; +class CHARSTRING; +class BITSTRING; + +struct asn_TYPE_descriptor_s; +class asn1_recode_oer { +protected: + int xer2oer(const asn_TYPE_descriptor_s &td, TTCN_Buffer &buf); + int oer2xer(const asn_TYPE_descriptor_s &td, TTCN_Buffer &buf); + int recode(const asn_TYPE_descriptor_s &td, int from, int to, TTCN_Buffer &buf); +}; + +template class oer_codec : public asn1_recode_oer { +public: + virtual int encode(const TPDU &msg, BITSTRING &bits) = 0; + virtual int decode(const BITSTRING &bits, TPDU &msg) = 0; + +protected: + inline int _decode(const TTCN_Typedescriptor_t &ttcn, const asn_TYPE_descriptor_s &td, const BITSTRING &p_data, TPDU &msg) { + TTCN_Buffer buf(bit2oct(p_data)); + TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING); + int rc = oer2xer(td, buf); + if (rc > 0) { + msg.decode(ttcn, buf, TTCN_EncDec::CT_BER, BER_ACCEPT_ALL); + rc = buf.get_len(); + } + return rc; + } + inline int _encode(const TTCN_Typedescriptor_t &ttcn, const asn_TYPE_descriptor_s &td, const TPDU &msg, BITSTRING &p_data) { + int rc = -1; + TTCN_Buffer buf; + TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING); + msg.encode(ttcn, buf, TTCN_EncDec::CT_BER, BER_ENCODE_DER); + if (buf.get_len() > 0) { + rc = xer2oer(td, buf); + if (rc > 0) { + p_data = oct2bit(OCTETSTRING(buf.get_len(), buf.get_data())); + } + } + return rc; + } +}; + +#endif diff --git a/ccsrc/Framework/include/params.hh b/ccsrc/Framework/include/params.hh new file mode 100644 index 0000000..99e81d1 --- /dev/null +++ b/ccsrc/Framework/include/params.hh @@ -0,0 +1,93 @@ +/*! + * \file params.hh + * \brief Header file for the parameter dictionary. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include + +/*! + * \class params + * \brief This class provides basic functionalities for an ITS dictionary + * \implements std::map + */ +class params : public std::map { +public: //! \publicsection + // TODO Use static constexpr (see commsignia_layer.hh) + static const std::string& debug; //! Set to 1 to enable the debug mode + + static const std::string& loopback; + + static const std::string& timestamp; //! Packet reception timestamp + static const std::string& mac_src; //! Source MAC address parameter name + static const std::string& mac_dst; //! Destination MAC address parameter name + static const std::string& mac_bc; //! Broadcast MAC address parameter name + static const std::string& eth_type; //! Ethernet type parameter name + static const std::string& filter; //! Additinal PCAP filter + + static const std::string& nic; //! Network Interface Card parameter name + + static const std::string& server; //! HTTP server address (e.g. www.etsi.org) + static const std::string& port; //! HTTP server port. Default: 80 + static const std::string& use_ssl; //! Set to 1 to use SSL to communicate with the HTTP server. Default: false + static const std::string& mutual_auth; //! Set to 1 to use mutual authentication. Default: false + static const std::string& trusted_ca_list; //! List of trusted CA certificates + static const std::string& server_mode; //! Does the test sytem acting as a server. Default: 0 + static const std::string& local_port; //! Local listener port. Default: 80 + + static const std::string& method; //! HTTP method type. Default: POST + static const std::string& uri; //! HTTP URI value. Default: / + static const std::string& host; //! HTTP Host value. Default: 127.0.0.1 + static const std::string& content_type; //! HTTP Content-type value. Default: application/text + + static const std::string& codecs; //! List of codecs to use for HTTP application layers + + /*! + * \brief Default constructor + * Create a new instance of the params class + */ + params() : std::map(){}; + /*! + * \brief Copy constructor + * Clone an existing instance of a params object + * \param[in] p_params An existing instance of a params object + */ + explicit params(const params &p_params) : std::map(p_params.begin(), p_params.end()){}; + + /*! + * \brief Default destructor + */ + virtual ~params(){}; + + /*! + * \fn void log() const; + * \brief Provides a dump of the content of this instance + */ + void log() const; + /*! + * \fn void log() const; + * \brief Provides a dump of the content of this instance + */ + void log(); + /*! + * \fn void reset(); + * \brief Reset the content of this instance + */ + void reset(); + + /*! + * \static + * \fn void convert(params& p_param, const std::string p_parameters); + * \brief Create a new instance of a params object by converting a list of ITS parameters in string format (t1=v1,T2=(v0,v1v2)...) + * \return a new instance of a params object + */ + static void convert(params &p_param, const std::string p_parameters); +}; // End of class params diff --git a/ccsrc/Framework/include/per_codec.hh b/ccsrc/Framework/include/per_codec.hh new file mode 100644 index 0000000..077d74a --- /dev/null +++ b/ccsrc/Framework/include/per_codec.hh @@ -0,0 +1,50 @@ +/*! + * \file per_codec.hh + * \brief Header file for TITAN message to ASN.1 PER message codec. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "asn1_recode_per.hh" +#include "params.hh" + +class BITSTRING; //! Forward declaration of TITAN class +class TTCN_Typedescriptor_t; //! Forward declaration of TITAN class + +struct asn_TYPE_descriptor_s; //! Declare asn1c class + +/*! + * \class per_codec + * \brief This class provides the interface for all ASN.1 PER codecs. + * \remark This class uses asn1c external tool + */ +template class per_codec : public asn1_recode_per { +public: //! \publicsection + /*! + * \fn int encode(const TPDU& p_message, BITSTRING& p_bitstring); + * \brief Encode TITAN message into ASN.1 PER message + * \param[in] p_message The PDU message to encode + * \param[out] p_bitstring The encoded PDU message in bit string format + * \pure + */ + virtual int encode(const TPDU &p_message, BITSTRING &p_bitstring) = 0; + /*! + * \fn int decode(const BITSTRING& p_bitstring, TPDU& p_message); + * \brief Decode ASN.1 PER message into TITAN message + * \param[in] p_bitstring The network message in bit string format to decode + * \param[out] p_message The PDU message + * \pure + */ + virtual int decode(const BITSTRING &p_bitstring, TPDU &p_message) = 0; + +protected: //! \protectedsection + int _decode(const TTCN_Typedescriptor_t &ttcn, const asn_TYPE_descriptor_s &td, const BITSTRING &p_data, TPDU &msg); + int _encode(const TTCN_Typedescriptor_t &ttcn, const asn_TYPE_descriptor_s &td, const TPDU &msg, BITSTRING &p_data); +}; // End of class per_codec + +#include "per_codec.t.hh" diff --git a/ccsrc/Framework/include/per_codec.t.hh b/ccsrc/Framework/include/per_codec.t.hh new file mode 100644 index 0000000..6973dc6 --- /dev/null +++ b/ccsrc/Framework/include/per_codec.t.hh @@ -0,0 +1,26 @@ +#include + +template int per_codec::_decode(const TTCN_Typedescriptor_t &ttcn, const asn_TYPE_descriptor_s &td, const BITSTRING &p_data, TPDU &msg) { + TTCN_Buffer buf(bit2oct(p_data)); + TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING); + int rc = per2ber(td, buf); + if (rc > 0) { + msg.decode(ttcn, buf, TTCN_EncDec::CT_BER, BER_ACCEPT_ALL); + rc = buf.get_len(); + } + return rc; +} + +template int per_codec::_encode(const TTCN_Typedescriptor_t &ttcn, const asn_TYPE_descriptor_s &td, const TPDU &msg, BITSTRING &p_data) { + int rc = -1; + TTCN_Buffer buf; + TTCN_EncDec::set_error_behavior(TTCN_EncDec::ET_ALL, TTCN_EncDec::EB_WARNING); + msg.encode(ttcn, buf, TTCN_EncDec::CT_BER, BER_ENCODE_DER); + if (buf.get_len() > 0) { + rc = ber2per(td, buf); + if (rc > 0) { + p_data = oct2bit(OCTETSTRING(buf.get_len(), buf.get_data())); + } + } + return rc; +} diff --git a/ccsrc/Framework/include/registration.hh b/ccsrc/Framework/include/registration.hh new file mode 100644 index 0000000..df24ea1 --- /dev/null +++ b/ccsrc/Framework/include/registration.hh @@ -0,0 +1,58 @@ +/*! + * \file registration.hh + * \brief Header file for the control port registration functionality. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include + +/** + * \class registration + * \brief This class provides a way to register items and its object reference + * The registered items are accessible from any object (singleton pattern) + * \remark There is one limitation: each item is uniquely defined in the process + */ +template class registration { // TODO Refine the naming, derive from std::map + std::map _items; + + static registration *_instance; + +private: + registration() : _items(){}; // can not be created manually +public: + static registration &get_instance(); + + virtual ~registration() { + if (_instance != nullptr) + delete _instance; + }; + +public: + void add_item(const std::string &type, TItem *f); + TItem *get_item(const std::string &type); +}; // End of class registration + +template registration *registration::_instance = nullptr; + +// static functions +template registration ®istration::get_instance() { + return (_instance != nullptr) ? *_instance : *(_instance = new registration()); +} + +template void registration::add_item(const std::string &type, TItem *f) { _items[type] = f; } + +template TItem *registration::get_item(const std::string &type) { + typename std::map::const_iterator it = _items.find(type); + if (it == _items.cend()) { + return nullptr; + } + + return it->second; +} // End of class registration diff --git a/ccsrc/Framework/include/t_layer.hh b/ccsrc/Framework/include/t_layer.hh new file mode 100644 index 0000000..286bd31 --- /dev/null +++ b/ccsrc/Framework/include/t_layer.hh @@ -0,0 +1,70 @@ +/*! + * \file t_layer.hh + * \brief Header file for ITS abstract protocol layer definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer.hh" + +/*! + * \class t_layer + * \brief This class provides basic description of an ITS port protocol layer. + * A port protocol layer is the final layer which provides the access to the physical communication channel + * A port protocol layer derives from both a layer class and a template port class + */ +template class t_layer : public layer { + typedef std::vector TPortList; + typedef typename std::vector::iterator TPortListIterator; + + TPortList upperPorts; //! The list of the upper ports + +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the t_layer class + * \todo Remove logs + */ + explicit t_layer() : layer(), upperPorts(){}; + + /*! + * \brief Specialized constructor + * Create a new instance of the layer class with its type description + * \param[in] p_type The port type name (e.g. TCP for the TCP sockect based layer) + * \remark This constructor is called by the layer factory + * \see layer_factory + */ + explicit t_layer(const std::string &p_type) : layer(p_type), upperPorts(){}; + /*! + * \inline + * \fn void add_upper_port(TPort * p_port); + * \brief Add a new upper port layer + * \todo To be done + */ + inline void add_upper_port(TPort *p_port) { upperPorts.push_back(p_port); }; + /*! + * \fn void remove_upper_port(TPort*); + * \brief Remove the specified upper layer port protocol from the list of the upper layers + * \param[in] p_layer The layer protocol to be removed + */ + void remove_upper_port(TPort *); + +protected: //! \protectedsection + /*! + * \inline + * \fn void to_all_upper_ports(const TMessage& m, const params& param); + * \brief Forward the message to all available upper port layers + * \param[in] p_message The message to be forwarded + * \param[in] p_params Some lower layers parameters values when data was received + */ + template inline void to_all_upper_ports(const TMessage &p_message, const params& p_params) { + for (TPortListIterator it = upperPorts.begin(); it < upperPorts.end(); ++it) { + (*it)->receiveMsg(p_message, p_params); + } + } +}; // End of class t_layer diff --git a/ccsrc/Framework/module.mk b/ccsrc/Framework/module.mk new file mode 100644 index 0000000..ba3e9f2 --- /dev/null +++ b/ccsrc/Framework/module.mk @@ -0,0 +1,7 @@ +sources := src/base_time.cc \ + src/codec_stack_builder.cc \ + src/converter.cc \ + src/layer_factory.cc \ + src/params.cc + +includes += ./include diff --git a/ccsrc/Framework/src/base_time.cc b/ccsrc/Framework/src/base_time.cc new file mode 100644 index 0000000..3833119 --- /dev/null +++ b/ccsrc/Framework/src/base_time.cc @@ -0,0 +1,13 @@ +/*! + * \file base_time.cc + * \brief Source file for the control port base_time functionality. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#include "base_time.hh" + +base_time* base_time::_instance = nullptr; diff --git a/ccsrc/Framework/src/codec_stack_builder.cc b/ccsrc/Framework/src/codec_stack_builder.cc new file mode 100644 index 0000000..bb5a706 --- /dev/null +++ b/ccsrc/Framework/src/codec_stack_builder.cc @@ -0,0 +1,4 @@ +#include "codec_stack_builder.hh" + +codec_stack_builder* codec_stack_builder::_instance = NULL; + diff --git a/ccsrc/Framework/src/converter.cc b/ccsrc/Framework/src/converter.cc new file mode 100644 index 0000000..e9ad4d1 --- /dev/null +++ b/ccsrc/Framework/src/converter.cc @@ -0,0 +1,195 @@ +#include "converter.hh" +#include +converter *converter::instance = NULL; + +uint16_t converter::swap(const uint16_t p_value) { + uint8_t *ptr = (uint8_t *)&p_value; + return (ptr[0] << 8) | ptr[1]; +} + +uint32_t converter::swap(const uint32_t p_value) { + uint8_t *ptr = (uint8_t *)&p_value; + return (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; +} + +const std::string converter::lut_u = "0123456789ABCDEF"; +const std::string converter::lut_l = "0123456789abcdef"; +std::string converter::string_to_hexa(const std::string &p_value, const bool p_uppercase) { + + std::string input(p_value); + std::for_each(input.begin(), input.end(), [](char &c) { c = std::toupper(c); }); + + std::string output; + uint32_t length = p_value.length(); + output.reserve(2 * length); + if (p_uppercase) { // TODO Use pointer to reduce code size + for (uint32_t i = 0; i < length; ++i) { + const uint8_t c = input[i]; + output.push_back(lut_u[c >> 4]); + output.push_back(lut_u[c & 15]); + } // End of 'for' statement + } else { + for (uint32_t i = 0; i < length; ++i) { + const uint8_t c = input[i]; + output.push_back(lut_l[c >> 4]); + output.push_back(lut_l[c & 15]); + } // End of 'for' statement + } + + return output; +} + +std::string converter::bytes_to_hexa(const std::vector &p_value, const bool p_uppercase) { + std::string ret; + ret.assign(p_value.size() * 2, ' '); + if (p_uppercase) { // TODO Use pointer to reduce code size + for (size_t i = 0; i < p_value.size(); i++) { + uint8_t c = p_value[i]; + ret[i * 2] = lut_u[c >> 4]; + ret[i * 2 + 1] = lut_u[c & 0xF]; + } + } else { + for (size_t i = 0; i < p_value.size(); i++) { + uint8_t c = p_value[i]; + ret[i * 2] = lut_l[c >> 4]; + ret[i * 2 + 1] = lut_l[c & 0xF]; + } + } + return ret; +} + +inline uint8_t char2byte(const char p_ch) { + size_t s = converter::lut_l.find(p_ch); + if (s == std::string::npos) { + if ((s = converter::lut_u.find(p_ch)) == std::string::npos) { + throw(std::length_error("")); + } + } + return s; +} + +std::vector converter::hexa_to_bytes(const std::string &p_value) { + // Sanity check + std::vector output; + size_t i = 0, idx = 0, outlen = (p_value.length() + 1) / 2; + + output.assign(outlen, 0x00); + try { + if (p_value.length() & 1) + output[idx++] = char2byte(p_value[i++]); + for (; idx < outlen; idx++) { + uint8_t b0 = char2byte(p_value[i++]); + uint8_t b1 = char2byte(p_value[i++]); + output[idx] = (b0 << 4) | b1; + } + } catch (const std::length_error &le) { + output.clear(); + } + return output; +} + +std::string converter::time_to_string(const time_t p_time) { + struct tm *t = std::localtime(&p_time); + return time_to_string(*t); +} + +std::string converter::time_to_string(const struct tm &p_time) { + char buffer[64] = {0}; + // Format: RFC 822, 1036, 1123, 2822 + std::strftime(buffer, 64, "%a, %d %b %Y %H:%M:%S %z", &p_time); + return std::string(buffer); +} + +std::string converter::trim(const std::string &str, const std::string &whitespace) { + size_t strBegin = str.find_first_not_of(whitespace); + if (strBegin == std::string::npos) + return ""; // no content + + size_t strEnd = str.find_last_not_of(whitespace); + size_t strRange = strEnd - strBegin + 1; + + return str.substr(strBegin, strRange); +} + +std::vector converter::split(const std::string &p_value, const std::string &p_separator) { + std::vector output; + std::size_t current, previous = 0; + current = p_value.find(p_separator); + while (current != std::string::npos) { + output.push_back(p_value.substr(previous, current - previous)); + previous = current + 1; + current = p_value.find(p_separator, previous); + } + output.push_back(p_value.substr(previous, current - previous)); + + return output; +} + +std::vector converter::split_arguments_line(const std::string &p_value) { + std::vector output; + std::string line = trim(p_value); + if (!line.empty() && (line[0] == '-')) { // Valid command line + size_t current = 0; + size_t next = (size_t)-1; + size_t pos = 0; + do { + if (line[pos + 1] == '-') { // -- + current = pos + 2; + } else { + current = pos + 1; + } + next = line.find("-", current); + std::string str(line.substr(pos, next - pos)); + output.push_back(str); + pos = next; + } while (next != std::string::npos); + } // else, invalid command line + return output; +} + +const std::string converter::base64_enc_map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +std::vector converter::buffer_to_base64(const std::vector &p_value) { + std::vector out; + + int val = 0, valb = -6; + for (unsigned char c : p_value) { + val = (val << 8) + c; + valb += 8; + while (valb >= 0) { + out.push_back(converter::base64_enc_map[(val >> valb) & 0x3F]); + valb -= 6; + } // End of 'while' statement + } // End of 'for' statement + if (valb > -6) { + out.push_back(converter::base64_enc_map[((val << 8) >> (valb + 8)) & 0x3F]); + } + while (out.size() % 4) { + out.push_back('='); + } // End of 'while' statement + + return out; +} + +std::vector converter::base64_to_buffer(const std::vector &p_value) { + std::vector out; + + std::vector T(256, -1); + for (int i = 0; i < 64; i++) { + T[converter::base64_enc_map[i]] = i; + } + + int val = 0, valb = -8; + for (unsigned char c : p_value) { + if (T[c] == -1) { + break; + } + val = (val << 6) + T[c]; + valb += 6; + if (valb >= 0) { + out.push_back((unsigned char)char((val >> valb) & 0xFF)); + valb -= 8; + } + } // End of 'for' statement + return out; +} diff --git a/ccsrc/Framework/src/layer_factory.cc b/ccsrc/Framework/src/layer_factory.cc new file mode 100644 index 0000000..ef3ad21 --- /dev/null +++ b/ccsrc/Framework/src/layer_factory.cc @@ -0,0 +1,61 @@ +#include +#include + +#include "layer_stack_builder.hh" + +#include "loggers.hh" + +layer_stack_builder *layer_stack_builder::_instance = NULL; + +// static functions +layer_stack_builder *layer_stack_builder::get_instance() { return _instance ? _instance : _instance = new layer_stack_builder(); } + +void layer_stack_builder::register_layer_factory(const std::string &p_type, layer_factory *p_layer_factory) { + layer_stack_builder::get_instance()->_register_layer_factory(p_type, p_layer_factory); +} + +// member functions +layer_stack_builder::layer_stack_builder() {} + +void layer_stack_builder::_register_layer_factory(const std::string &p_type, layer_factory *p_layer_factory) { _layer_factories[p_type] = p_layer_factory; } + +layer *layer_stack_builder::create_layer_stack(const char *p_layer_stack_description) { + loggers::get_instance().log(">>> layer_stack_builder::create_layer_stack: %s", p_layer_stack_description); + + layer *entry = NULL; // Initial layer (the first declared) + layer *up = NULL; // Upper layer + // Parse the layer description + try { + std::regex rgx("(\\w+)(\\((.*?)\\))?(\\/|$)"); + std::string str = p_layer_stack_description; + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::sregex_iterator end = std::sregex_iterator(); + for (std::sregex_iterator it = begin; it != end; ++it) { + std::smatch m = *it; + loggers::get_instance().log("layer_stack_builder::create_layer_stack: %d - %s - %s - %s - %s", m.size(), m[0].str().c_str(), m[1].str().c_str(), + m[2].str().c_str(), m[3].str().c_str()); + LayerFactoryMap::iterator i = _layer_factories.find(m[1].str()); + if (i == _layer_factories.end()) { + loggers::get_instance().error("layer_stack_builder::create_layer_stack: %s: Unknown layer type", m[1].str().c_str()); + } + loggers::get_instance().log("layer_stack_builder::create_layer_stack: Create layer %s, %s", m[1].str().c_str(), m[3].str().c_str()); + layer *l = i->second->create_layer(m[1].str(), m[3].str()); + if (NULL == l) { + loggers::get_instance().error("layer_stack_builder::create_layer_stack: %s: Layer creation error", m[1].str().c_str()); + } + + loggers::get_instance().log("layer_stack_builder::create_layer_stack: Setup layers for %s", l->to_string().c_str()); + l->add_upper_layer(up); + if (entry == NULL) { // Set the first declared layer + entry = l; + } + up = l; // Build the linked list of layers + } // End of 'for' statement + } catch (const std::logic_error &e) { + if (up) { // FIXME To be reviewed + up->delete_layer(); + up = NULL; + } + } + return entry; +} diff --git a/ccsrc/Framework/src/params.cc b/ccsrc/Framework/src/params.cc new file mode 100644 index 0000000..5d7a44d --- /dev/null +++ b/ccsrc/Framework/src/params.cc @@ -0,0 +1,97 @@ +/*! + * \file params.cc + * \brief Source file for the parameter dictionary. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#include +#include + +#include "loggers.hh" +#include "params.hh" + +const std::string& params::debug = std::string("debug"); + +const std::string& params::loopback = std::string("loopback"); + +const std::string& params::timestamp = std::string("timestamp"); + +const std::string& params::mac_src = std::string("mac_src"); +const std::string& params::mac_dst = std::string("mac_dst"); +const std::string& params::mac_bc = std::string("mac_bc"); +const std::string& params::eth_type = std::string("eth_type"); +const std::string& params::filter = std::string("filter"); + +const std::string& params::nic = std::string("nic"); + +const std::string& params::server = std::string("server"); +const std::string& params::port = std::string("port"); +const std::string& params::use_ssl = std::string("use_ssl"); +const std::string& params::mutual_auth = std::string("mutual_auth"); +const std::string& params::trusted_ca_list = std::string("trusted_ca_list"); + +const std::string& params::server_mode = std::string("server_mode"); +const std::string& params::local_port = std::string("local_port"); + +const std::string& params::method = std::string("method"); +const std::string& params::uri = std::string("uri"); +const std::string& params::host = std::string("host"); +const std::string& params::content_type = std::string("content_type"); + +const std::string& params::codecs = std::string("codecs"); + +void params::convert(params &p_param, const std::string p_parameters) { + // Sanity checks + if (p_parameters.length() == 0) { + return; + } + loggers::get_instance().log(">>> params::convert: %s", p_parameters.c_str()); + // Extract parameters + try { + std::regex rgx("(\\w+)=(.*?)(,|$)"); + std::sregex_iterator begin(p_parameters.cbegin(), p_parameters.cend(), rgx); + std::sregex_iterator end = std::sregex_iterator(); + for (std::sregex_iterator it = begin; it != end; ++it) { + std::smatch m = *it; + loggers::get_instance().log("params::convert: %d - %s - %s - %s - %s", m.size(), m[0].str().c_str(), m[1].str().c_str(), m[2].str().c_str(), + m[3].str().c_str()); + p_param.insert(std::pair(m[1].str(), m[2].str())); + } // End of 'for' statement + } catch (const std::logic_error &e) { + p_param.clear(); + } + loggers::get_instance().log("<<< params::convert"); +} + +void params::log() const { + loggers::get_instance().log("params::log"); + if (size() == 0) { + loggers::get_instance().log("\tEmpty"); + } else { + for (const_iterator it = cbegin(); it != cend(); ++it) { + loggers::get_instance().log("\t(%s, %s)", it->first.c_str(), it->second.c_str()); + } // End of 'for' statement + } +} + +void params::log() { + loggers::get_instance().log("params::log"); + if (size() == 0) { + loggers::get_instance().log("\tEmpty"); + } else { + for (const_iterator it = cbegin(); it != cend(); ++it) { + loggers::get_instance().log("\t(%s, %s)", it->first.c_str(), it->second.c_str()); + } // End of 'for' statement + } +} + +void params::reset() { + loggers::get_instance().log("params::reset"); + for (iterator it = begin(); it != end(); ++it) { + it->second.clear(); + } // End of 'for' statement +} diff --git a/ccsrc/Helpers/module.mk b/ccsrc/Helpers/module.mk new file mode 100644 index 0000000..e69de29 diff --git a/ccsrc/Protocols/ETH/ethernet_layer.cc b/ccsrc/Protocols/ETH/ethernet_layer.cc new file mode 100644 index 0000000..134d4d7 --- /dev/null +++ b/ccsrc/Protocols/ETH/ethernet_layer.cc @@ -0,0 +1,82 @@ +#include "ethernet_layer_factory.hh" + +#include "loggers.hh" + +ethernet_layer::ethernet_layer(const std::string & p_type, const std::string & p_param) : layer(p_type), _params() { + loggers::get_instance().log(">>> ethernet_layer::ethernet_layer: %s, %s", to_string().c_str(), p_param.c_str()); + // Setup parameters + params::convert(_params, p_param); + params::const_iterator it = _params.find("mac_src"); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("mac_src"), "000000000000")); + } + it = _params.find("mac_bc"); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("mac_bc"), "FFFFFFFFFFFF")); + } + it = _params.find("eth_type"); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("eth_type"), "8947")); + } + //_params.log(); +} + +void ethernet_layer::send_data(OCTETSTRING& p_data, params& p_params) { + loggers::get_instance().log_msg(">>> ethernet_layer::send_data: ", p_data); + + OCTETSTRING eth; + // Destination MAC address + params::const_iterator it = p_params.find(params::mac_dst); // Find in provided parameters, params + if (it != p_params.cend()) { + eth = str2oct(CHARSTRING(it->second.c_str())); + } else { + it = _params.find(params::mac_dst); + if (it != _params.cend()) { + eth = str2oct(CHARSTRING(it->second.c_str())); + } else { + eth = str2oct(CHARSTRING(_params[params::mac_bc].c_str())); + } + } + // Source MAC address + it = p_params.find(params::mac_src); // Find in provided parameters, params + if (it != p_params.cend()) { + eth += str2oct(CHARSTRING(it->second.c_str())); + } else { + eth += str2oct(CHARSTRING(_params[params::mac_src].c_str())); + } + // Ethernet type + it = p_params.find(params::eth_type); // Find in layer parameters + if (it != p_params.cend()) { + eth += str2oct(CHARSTRING(it->second.c_str())); + } else { + eth += str2oct(CHARSTRING(_params[params::eth_type].c_str())); + } + + eth += p_data; + send_to_all_layers(eth, static_cast(p_params)); +} + +void ethernet_layer::receive_data(OCTETSTRING& p_data, params& p_params) { + loggers::get_instance().log_msg(">>> ethernet_layer::receive_data: ", p_data); + + // Extract dest MAC Address + OCTETSTRING dst = OCTETSTRING(6, static_cast(p_data)); + //loggers::get_instance().log_msg("ethernet_layer::receive_data: dst: ", dst); + // Extract source MAC Address + OCTETSTRING src = OCTETSTRING(6, 6 + static_cast(p_data)); + //loggers::get_instance().log_msg("ethernet_layer::receive_data: src: ", src); + // Extract ethertype + OCTETSTRING proto = OCTETSTRING(2, 2 + static_cast(p_data)); + //loggers::get_instance().log_msg("ethernet_layer::receive_data: proto: ", proto); + OCTETSTRING data = OCTETSTRING(data.lengthof() - 14, 14 + static_cast(p_data)); + // Update params + CHARSTRING s = oct2str(dst); + p_params.insert(std::pair(params::mac_dst, std::string(static_cast(s)))); + s = oct2str(src); + p_params.insert(std::pair(params::mac_src, std::string(static_cast(s)))); + //loggers::get_instance().log_msg("ethernet_layer::receive_data: payload for upper layer:", data); + + receive_to_all_layers(data, static_cast(p_params)); +} + +ethernet_layer_factory ethernet_layer_factory::_f; diff --git a/ccsrc/Protocols/ETH/ethernet_layer.hh b/ccsrc/Protocols/ETH/ethernet_layer.hh new file mode 100644 index 0000000..f510350 --- /dev/null +++ b/ccsrc/Protocols/ETH/ethernet_layer.hh @@ -0,0 +1,48 @@ +/*! + * \file udp_layer.hh + * \brief Header file for ITS UDP/IP protocol layer definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "params.hh" +#include "t_layer.hh" + +class ethernet_layer : public layer { + params _params; //! Layer parameters + +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the ethernet_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + ethernet_layer(const std::string &p_type, const std::string &p_param); + /*! + * \brief Default destructor + */ + virtual ~ethernet_layer(){}; + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& p_params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING& p_data, params& p_params); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& p_params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING& p_data, params& p_params); +}; // End of class ethernet_layer diff --git a/ccsrc/Protocols/ETH/ethernet_layer_factory.hh b/ccsrc/Protocols/ETH/ethernet_layer_factory.hh new file mode 100644 index 0000000..7adf9f4 --- /dev/null +++ b/ccsrc/Protocols/ETH/ethernet_layer_factory.hh @@ -0,0 +1,42 @@ +/*! + * \file ethernet_layer_factory.hh + * \brief Header file for ITS Ethernet protocol layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "ethernet_layer.hh" + +/*! + * \class ethernet_layer_factory + * \brief This class provides a factory class to create an ethernet_layer class instance + */ +class ethernet_layer_factory : public layer_factory { + static ethernet_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the ethernet_layer_factory class + * \remark The ETH layer identifier is ETH + */ + ethernet_layer_factory() { + // register factory + layer_stack_builder::register_layer_factory("ETH", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \inline + */ + inline virtual layer *create_layer(const std::string &p_type, const std::string &p_param) { return new ethernet_layer(p_type, p_param); }; +}; // End of class ethernet_layer_factory diff --git a/ccsrc/Protocols/ETH/module.mk b/ccsrc/Protocols/ETH/module.mk new file mode 100644 index 0000000..eb2f4a8 --- /dev/null +++ b/ccsrc/Protocols/ETH/module.mk @@ -0,0 +1,3 @@ +sources := ethernet_layer.cc +includes := . + diff --git a/ccsrc/Protocols/Http/http_codec.cc b/ccsrc/Protocols/Http/http_codec.cc new file mode 100644 index 0000000..8433b8b --- /dev/null +++ b/ccsrc/Protocols/Http/http_codec.cc @@ -0,0 +1,766 @@ +#include +#include +#include + +#include "codec_stack_builder.hh" + +#include "http_codec.hh" + +#include "loggers.hh" + +#include "LibHttp_MessageBodyTypes.hh" +#include "LibHttp_TypesAndValues.hh" +#include "LibHttp_XmlMessageBodyTypes.hh" + +#include "http_etsi_ieee1609dot2_codec.hh" + +int http_codec::encode(const LibHttp__TypesAndValues::HttpMessage &msg, OCTETSTRING &data) { + loggers::get_instance().log_msg(">>> http_codec::encode: ", (const Base_Type &)msg); + loggers::get_instance().log(">>> http_codec::encode: %p", this); + + TTCN_EncDec::clear_error(); + TTCN_Buffer encoding_buffer; + + _ec.reset(); + + int result; + if (msg.ischosen(LibHttp__TypesAndValues::HttpMessage::ALT_request)) { + result = encode_request(msg.request(), encoding_buffer); + } else if (msg.ischosen(LibHttp__TypesAndValues::HttpMessage::ALT_response)) { + result = encode_response(msg.response(), encoding_buffer); + } else { + loggers::get_instance().warning("http_codec::encode: Unbound HttpMessage"); + return -1; + } + + data = OCTETSTRING(encoding_buffer.get_len(), encoding_buffer.get_data()); + + loggers::get_instance().log_msg("<<< http_codec::encode: data=", data); + return result; +} + +int http_codec::decode(const OCTETSTRING &data, LibHttp__TypesAndValues::HttpMessage &msg, params *params) { + loggers::get_instance().log_msg(">>> http_codec::decode: data=", data); + + // Sanity checks + if ((data[0].get_octet() & 0x80) == 0x80) { + loggers::get_instance().warning("http_codec::decode: Unicode format not supported"); + return -1; + } + + TTCN_EncDec::clear_error(); + TTCN_Buffer decoding_buffer(data); + loggers::get_instance().log_to_hexa("http_codec::decode: decoding_buffer=", decoding_buffer); + + _dc.reset(); + + _params = params; + + // Get the first line (e.g. HTTP/1.1 302 Found or POST / HTTP/1.1) + CHARSTRING message_id; + if (get_line(decoding_buffer, message_id) == -1) { + loggers::get_instance().warning("http_codec::decode: get_line failed, errno: %d", errno); + return -1; + } + loggers::get_instance().log_msg("http_codec::decode: message_id: ", message_id); + // Extract parameters + try { + std::string str(static_cast(message_id)); + std::regex rgx("\\s*(\\w+)/"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + loggers::get_instance().log("http_codec::decode: %d - %s", m.size(), m[0].str().c_str()); + if (m[0].str().compare("HTTP/") == 0) { // HTTP response + LibHttp__TypesAndValues::Response response; + std::regex rgx("\\s*HTTP/(\\d+)\\.(\\d+)\\s+(\\d+)\\s+([\\w\\s\\t\\v\\f]+)*"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + loggers::get_instance().log("http_codec::decode: Process response: %d", m.size()); + if (m.size() != 5) { + loggers::get_instance().warning("http_codec::decode: Unsupported tag"); + return -1; + } + response.version__major() = std::stoi(m[1].str().c_str()); + response.version__minor() = std::stoi(m[2].str().c_str()); + response.statuscode() = std::stoi(m[3].str().c_str()); + response.statustext() = CHARSTRING(m[4].str().c_str()); + LibHttp__TypesAndValues::Headers headers; + std::string content_type; + decode_headers(decoding_buffer, headers, content_type); + response.header() = headers; + loggers::get_instance().log_to_hexa("Before decoding Body: ", decoding_buffer); + LibHttp__MessageBodyTypes::HttpMessageBody body; + if (decode_body(decoding_buffer, body, content_type) == -1) { + response.body().set_to_omit(); + } else { + response.body() = OPTIONAL(body); + } + msg.response() = response; + } else { // HTTP request + LibHttp__TypesAndValues::Request request; + std::regex rgx("\\s*(\\w+)\\s+(.+)\\s+HTTP/(\\d)\\.(\\d)"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + if (m.size() != 5) { + loggers::get_instance().error("http_codec::decode: Unsupported tag"); + return -1; + } + request.method() = CHARSTRING(m[1].str().c_str()); + request.uri() = CHARSTRING(m[2].str().c_str()); + request.version__major() = std::stoi(m[3].str().c_str()); + request.version__minor() = std::stoi(m[4].str().c_str()); + LibHttp__TypesAndValues::Headers headers; + std::string content_type; + decode_headers(decoding_buffer, headers, content_type); + request.header() = headers; + OPTIONAL body; + body.set_to_omit(); + if (decode_body(decoding_buffer, body, content_type) == -1) { + request.body().set_to_omit(); + } else { + request.body() = body; + } + msg.request() = request; + } + + loggers::get_instance().log_msg("<<< http_codec::decode: ", (const Base_Type &)msg); + return 0; + } catch (const std::logic_error &e) { + return -1; + } +} + +int http_codec::encode_request(const LibHttp__TypesAndValues::Request &p_request, TTCN_Buffer &p_encoding_buffer) { + loggers::get_instance().log_msg(">>> http_codec::encode_request: ", (const Base_Type &)p_request); + + // Encode generic part + p_encoding_buffer.put_cs(p_request.method()); + p_encoding_buffer.put_c(' '); + p_encoding_buffer.put_cs(p_request.uri()); + p_encoding_buffer.put_cs(" HTTP/"); + p_encoding_buffer.put_cs(int2str(p_request.version__major())); + p_encoding_buffer.put_c('.'); + p_encoding_buffer.put_cs(int2str(p_request.version__minor())); + p_encoding_buffer.put_cs("\r\n"); + + // Encode headers excepeted the Content-Length + const LibHttp__TypesAndValues::Headers &headers = p_request.header(); + std::string content_type; + for (int i = 0; i < headers.size_of(); i++) { + const LibHttp__TypesAndValues::Header &header = headers[i]; + loggers::get_instance().log_msg("http_codec::encode_request: Processing header ", header.header__name()); + if (std::string(static_cast(header.header__name())).compare("Content-Length") == 0) { // Skip it, processed later + loggers::get_instance().log("http_codec::encode_request: Skip it"); + continue; + } else { + p_encoding_buffer.put_cs(header.header__name()); + p_encoding_buffer.put_cs(": "); + const OPTIONAL &o = header.header__value(); + if (o.ispresent()) { + const LibHttp__TypesAndValues::charstring__list &v = dynamic_cast &>(o); + if (v.size_of() > 0) { + loggers::get_instance().log_msg("http_codec::encode_request: Processing value ", v[0]); + if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { // Store it for HTTP body payload encoding + loggers::get_instance().log("http_codec::encode_request: Storing Content-Type"); + int j = 0; + while (j < v.size_of()) { + content_type += v[j++]; + } // End of 'while' statement + } + p_encoding_buffer.put_cs(v[0]); + int j = 1; + while (j < v.size_of()) { + p_encoding_buffer.put_cs(", "); + loggers::get_instance().log_msg("http_codec::encode_request: Processing value ", v[j]); + p_encoding_buffer.put_cs(v[j++]); + } // End of 'while' statement + } + } // else, do not include it + } + p_encoding_buffer.put_cs("\r\n"); + } // End of 'for' statement + + // Encode message body + const OPTIONAL &v = p_request.body(); + OCTETSTRING os; + if (v.ispresent()) { + const LibHttp__MessageBodyTypes::HttpMessageBody &body = static_cast(*v.get_opt_value()); + loggers::get_instance().log_msg("http_codec::encode_request: body: ", body); + if (encode_body(body, os, content_type) == -1) { + loggers::get_instance().warning("http_codec::encode_request: Failed to encode HTTP body"); + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } else { + _ec.length = os.lengthof(); + _ec.is_content_length_present = 0x01; + } + loggers::get_instance().log("http_codec::encode_request: length=%d", _ec.length); + } else { + loggers::get_instance().log("http_codec::encode_request: HTTP body field not present"); + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } + + // Encode Content-Length header + p_encoding_buffer.put_cs("Content-Length: "); + if (_ec.length != 0) { + loggers::get_instance().log("http_codec::encode_request: Content-Length: %s", + static_cast(int2str(_ec.length + 2 /*Stand for the last CRLF*/))); + p_encoding_buffer.put_cs(static_cast(int2str(_ec.length))); + _ec.is_content_length_present = 0x01; + } else { + p_encoding_buffer.put_cs("0"); + _ec.is_content_length_present = 0x00; + } + loggers::get_instance().log("http_codec::encode_request: Content-Length: %d - %x", _ec.length, _ec.is_content_length_present); + p_encoding_buffer.put_cs("\r\n"); + + // Add message body + p_encoding_buffer.put_cs("\r\n"); + if (_ec.is_content_length_present == 0x01) { + loggers::get_instance().log_msg("http_codec::encode_request: Add body ", os); + p_encoding_buffer.put_os(os); + // p_encoding_buffer.put_cs("\r\n"); + } + + loggers::get_instance().log_to_hexa("<<< http_codec::encode_request: ", p_encoding_buffer); + return 0; +} + +int http_codec::encode_response(const LibHttp__TypesAndValues::Response &p_response, TTCN_Buffer &p_encoding_buffer) { + loggers::get_instance().log_msg(">>> http_codec::encode_response: ", (const Base_Type &)p_response); + + // Encode generic part + p_encoding_buffer.put_cs("HTTP/"); + p_encoding_buffer.put_cs(int2str(p_response.version__major())); + p_encoding_buffer.put_c('.'); + p_encoding_buffer.put_cs(int2str(p_response.version__minor())); + p_encoding_buffer.put_cs(" "); + p_encoding_buffer.put_cs(int2str(p_response.statuscode())); + p_encoding_buffer.put_cs(" "); + if (p_response.statustext().lengthof() != 0) { + p_encoding_buffer.put_cs(p_response.statustext()); + } + p_encoding_buffer.put_cs("\r\n"); + + // Encode headers excepeted the Content-Length + const LibHttp__TypesAndValues::Headers &headers = p_response.header(); + std::string content_type; + for (int i = 0; i < headers.size_of(); i++) { + const LibHttp__TypesAndValues::Header &header = headers[i]; + loggers::get_instance().log_msg("http_codec::encode_response: Processing header ", header.header__name()); + if (std::string(static_cast(header.header__name())).compare("Content-Length") == 0) { + continue; + } else { + p_encoding_buffer.put_cs(header.header__name()); + p_encoding_buffer.put_cs(": "); + const OPTIONAL &o = header.header__value(); + if (o.ispresent()) { + const LibHttp__TypesAndValues::charstring__list &v = dynamic_cast &>(o); + if (v.size_of() > 0) { + loggers::get_instance().log_msg("http_codec::encode_response: Processing value ", v[0]); + if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { // Store it for HTTP body payload encoding + loggers::get_instance().log("http_codec::encode_response: Storing Content-Type"); + int j = 0; + while (j < v.size_of()) { + content_type += v[j++]; + } // End of 'while' statement + } + p_encoding_buffer.put_cs(v[0]); + int j = 1; + while (j < v.size_of()) { + p_encoding_buffer.put_cs(", "); + loggers::get_instance().log_msg("http_codec::encode_response: Processing value ", v[j]); + p_encoding_buffer.put_cs(v[j++]); + j += 1; + } // End of 'while' statement + } + } // else, do not include it + } + p_encoding_buffer.put_cs("\r\n"); + } // End of 'for' statement + + // Encode message body + const OPTIONAL &v = p_response.body(); + OCTETSTRING os; + if (v.ispresent()) { + const LibHttp__MessageBodyTypes::HttpMessageBody &body = static_cast(*v.get_opt_value()); + loggers::get_instance().log_msg("http_codec::encode_response: body: ", body); + if (encode_body(body, os, content_type) == -1) { + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } else { + _ec.length = os.lengthof(); + _ec.is_content_length_present = 0x01; + } + loggers::get_instance().log("http_codec::encode_response: length=%d", _ec.length); + } else { + loggers::get_instance().log("http_codec::encode_response: HTTP body field not present"); + _ec.length = 0; + _ec.is_content_length_present = 0x00; + } + + // Encode Content-Length header + p_encoding_buffer.put_cs("Content-Length: "); + if (_ec.length != 0) { + loggers::get_instance().log("http_codec::encode_request: Content-Length: %s", + static_cast(int2str(_ec.length + 2 /*Stand for the last CRLF*/))); + p_encoding_buffer.put_cs(static_cast(int2str(_ec.length))); + _ec.is_content_length_present = 0x01; + } else { + p_encoding_buffer.put_cs("0"); + _ec.is_content_length_present = 0x00; + } + loggers::get_instance().log("http_codec::encode_request: Content-Length: %d - %x", _ec.length, _ec.is_content_length_present); + p_encoding_buffer.put_cs("\r\n"); + + // Add message body + p_encoding_buffer.put_cs("\r\n"); + if (_ec.is_content_length_present == 0x01) { + loggers::get_instance().log_msg("http_codec::encode_request: Add body ", os); + p_encoding_buffer.put_os(os); + // p_encoding_buffer.put_cs("\r\n"); + } + + loggers::get_instance().log_to_hexa("<<< http_codec::encode_response: ", p_encoding_buffer); + return 0; +} + +int http_codec::decode_headers(TTCN_Buffer &decoding_buffer, LibHttp__TypesAndValues::Headers &headers, std::string &p_content_type) { + loggers::get_instance().log(">>> http_codec::decode_headers"); + loggers::get_instance().log_to_hexa("http_codec::decode_headers: ", decoding_buffer); + + CHARSTRING cstr; + int i = 0; + while (true) { + switch (get_line(decoding_buffer, cstr, true)) { + case 0: { + loggers::get_instance().log_msg("http_codec::decode_headers: ", cstr); + LibHttp__TypesAndValues::Header header; + if (decode_header(cstr, header) == -1) { + loggers::get_instance().warning("http_codec::decode_headers: Failed to decode header %s", static_cast(cstr)); + return -1; + } + headers[i++] = header; + if (std::string(static_cast(header.header__name())).compare("Content-Type") == 0) { + if (header.header__value().is_present() != 0) { + const PreGenRecordOf::PREGEN__RECORD__OF__CHARSTRING &l = + static_cast(*header.header__value().get_opt_value()); + p_content_type = static_cast(l[0]); + } else { + p_content_type = ""; + } + } + } break; + case 1: + if (headers.is_bound()) { + loggers::get_instance().log_msg("<<< http_codec::decode_headers: ", headers); + return 0; + } else { + loggers::get_instance().warning("http_codec::decode_headers: Failed to decode headers"); + return -1; + } + case -1: + loggers::get_instance().warning("http_codec::decode_headers: Failed to decode headers"); + return -1; + } // End of 'switch' statement + } // End of 'while' statement +} + +int http_codec::decode_header(CHARSTRING &header_line, LibHttp__TypesAndValues::Header &header) { + loggers::get_instance().log_msg(">>> http_codec::decode_header", header_line); + + try { + std::string str(static_cast(header_line)); + std::regex rgx("([0-9a-zA-Z-]+)\\:\\s+(.+)(,(.+))*"); + std::sregex_iterator begin(str.cbegin(), str.cend(), rgx); + std::smatch m = *begin; + if (m.size() < 5) { + loggers::get_instance().warning("http_codec::decode_header: Failed to decode header %s", str.c_str()); + return -1; + } + loggers::get_instance().log("http_codec::decode_header: %d", m.size()); + header.header__name() = CHARSTRING(m[1].str().c_str()); + LibHttp__TypesAndValues::charstring__list v; + for (unsigned int j = 0; j < m.size(); j++) { + if (m[j + 2].str().length() == 0) { + break; + } + v[j] = CHARSTRING(m[j + 2].str().c_str()); + } // End of 'for' statement + header.header__value() = OPTIONAL(v); + + if (m[1].str().compare("Content-Length") == 0) { + // Save the the body length + loggers::get_instance().log("http_codec::decode_header: decoded Content-Length %s", m[2].str().c_str()); + _dc.length = std::stoi(m[2].str()); + } else if (m[1].str().compare("Transfer-Encoding") == 0) { + if (m[2].str().find("chunked") != std::string::npos) { + _dc.chunked = true; + loggers::get_instance().log("http_codec::decode_header: decoded Transfer-Encoding %x", _dc.chunked); + } + } + + return 0; + } catch (const std::logic_error &e) { + return -1; + } +} + +int http_codec::encode_body(const LibHttp__MessageBodyTypes::HttpMessageBody &p_message_body, OCTETSTRING &p_encoding_buffer, + const std::string &p_content_type) { + loggers::get_instance().log_msg(">>> http_codec::encode_body: ", (const Base_Type &)p_message_body); + + // Sanity check + if (p_content_type.empty()) { + loggers::get_instance().warning("http_codec::encode_body: Failed to select a codec for HTTP body payload"); + return -1; + } + + if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_binary__body)) { + const LibHttp__BinaryMessageBodyTypes::BinaryBody &binary_body = p_message_body.binary__body(); + if (binary_body.ischosen(LibHttp__BinaryMessageBodyTypes::BinaryBody::ALT_raw)) { + p_encoding_buffer = OCTETSTRING(binary_body.raw().lengthof(), (unsigned char *)static_cast(binary_body.raw())); + } else { + std::map>>::const_iterator it; + bool processed = false; + if (p_content_type.find("x-its") != std::string::npos) { + loggers::get_instance().log("http_codec::encode_body: Find x-its"); + it = _codecs.find("http_its"); // TODO Use params + if (it != _codecs.cend()) { + loggers::get_instance().log("http_codec::encode_body: Call '%s'", it->first.c_str()); + if (binary_body.ischosen(LibHttp__BinaryMessageBodyTypes::BinaryBody::ALT_ieee1609dot2__data)) { + _codecs["http_its"]->encode((Record_Type &)binary_body.ieee1609dot2__data(), p_encoding_buffer); // TODO Use params + processed = true; + } else if (binary_body.ischosen(LibHttp__BinaryMessageBodyTypes::BinaryBody::ALT_ieee1609dot2__certificate)) { + _codecs["http_its"]->encode((Record_Type &)binary_body.ieee1609dot2__certificate(), p_encoding_buffer); // TODO Use params + processed = true; + } else { + loggers::get_instance().warning("http_codec::encode_body: Unsupported variant"); + } + } + } // TODO Add new HTTP message codec here + if (!processed) { + loggers::get_instance().warning("http_codec::encode_body: Unsupported HTTP codec, use raw field as default"); + p_encoding_buffer = OCTETSTRING(0, nullptr); + } + } + } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_html__body)) { + p_encoding_buffer = OCTETSTRING(p_message_body.html__body().lengthof(), (unsigned char *)static_cast(p_message_body.html__body())); + } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_text__body)) { + p_encoding_buffer = OCTETSTRING(p_message_body.text__body().lengthof(), (unsigned char *)static_cast(p_message_body.text__body())); + } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_xml__body)) { + const LibHttp__XmlMessageBodyTypes::XmlBody &xml_body = p_message_body.xml__body(); + if (xml_body.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_raw)) { + p_encoding_buffer = OCTETSTRING(xml_body.raw().lengthof(), (unsigned char *)static_cast(xml_body.raw())); + } else { + std::map>>::const_iterator it; + bool processed = false; + loggers::get_instance().log("http_codec::encode_body: Content-Type:'%s'", p_content_type.c_str()); + if (p_content_type.find("held") != std::string::npos) { + it = _codecs.find("held"); // TODO Use params + if (it != _codecs.cend()) { + loggers::get_instance().log("http_codec::encode_body: Call 'held_codec'"); + _codecs["held"]->encode((Record_Type &)xml_body, p_encoding_buffer); // TODO Use params + processed = true; + } + } else if (p_content_type.find("lost") != std::string::npos) { + it = _codecs.find("lost"); // TODO Use params + if (it != _codecs.cend()) { + loggers::get_instance().log("http_codec::encode_body: Call 'lost_codec'"); + _codecs["lost"]->encode((Record_Type &)xml_body, p_encoding_buffer); // TODO Use params + processed = true; + } + } // TODO Add new HTTP message codec here + if (!processed) { + loggers::get_instance().warning("http_codec::encode_body: Unsupported HTTP codec, use raw field as default"); + p_encoding_buffer = OCTETSTRING(0, nullptr); + } + } + } else { + loggers::get_instance().warning("http_codec::encode_body: Failed to encode HTTP message body"); + return -1; + } + loggers::get_instance().log_msg("http_codec::encode_body: HTTP message ", p_encoding_buffer); + _ec.length = p_encoding_buffer.lengthof(); + loggers::get_instance().log("http_codec::encode_body: HTTP message length: %d", _ec.length); + + return 0; +} + +int http_codec::decode_body(TTCN_Buffer &decoding_buffer, LibHttp__MessageBodyTypes::HttpMessageBody &message_body, const std::string &p_content_type) { + loggers::get_instance().log(">>> http_codec::decode_body"); + loggers::get_instance().log_to_hexa("http_codec::decode_body", decoding_buffer); + loggers::get_instance().log("http_codec::decode_body: # of codecs=%d - %p", _codecs.size(), this); + loggers::get_instance().log("http_codec::decode_body: Content-Type=%s", p_content_type.c_str()); + + // Sanity checks + if (decoding_buffer.get_len() - decoding_buffer.get_pos() <= 0) { + return -1; + } + /* TODO Uncommentif (p_content_type.empty()) { + loggers::get_instance().warning("http_codec::encode_body: Failed to select a codec for HTTP body payload"); + return -1; + }*/ + + OCTETSTRING s(decoding_buffer.get_len() - decoding_buffer.get_pos(), decoding_buffer.get_data() + decoding_buffer.get_pos()); + loggers::get_instance().log_msg("http_codec::decode_body: raw body=", s); + + // Align the payload length with the specified Content-Lenght value + loggers::get_instance().log("http_codec::decode_body: _dc.length=%d - body length=%d", _dc.length, s.lengthof()); + OCTETSTRING body; + if (_dc.length != 0) { + const unsigned char *p = static_cast(s); + if ((unsigned int)s.lengthof() <= _dc.length) { + body = OCTETSTRING(s.lengthof(), p); + } else { + body = OCTETSTRING(_dc.length, p); + } + } else { + loggers::get_instance().warning("http_codec::decode_body: No Conten-Length header, process all remaining bytes"); + body = s; + } + loggers::get_instance().log_msg("http_codec::decode_body: Aligned body=", body); + loggers::get_instance().log("http_codec::decode_body: body length=%d", body.lengthof()); + /* TODO To be removed + // Remove CRLF if any + int counter = 0; + if ((body[body.lengthof() - 1].get_octet() == 0x0d) || (body[body.lengthof() - 1].get_octet() == 0x0a)) { + counter += 1; + if ((body[body.lengthof() - 2].get_octet() == 0x0d) || (body[body.lengthof() - 2].get_octet() == 0x0a)) { + counter += 1; + } + } + loggers::get_instance().log("http_codec::decode_body: counter=%d", counter); + body = OCTETSTRING(body.lengthof() - counter, static_cast(body)); + */ + if (_dc.chunked) { + int counter = 0; + int prev = 0; + OCTETSTRING os(0, nullptr); + do { + while (counter < body.lengthof()) { // Extract the size of the chunk \r[\n] + if ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n')) { + break; + } + counter += 1; + } // End of 'while' statement + loggers::get_instance().log("http_codec::decode_body: Chunked(0): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + if (counter < body.lengthof()) { + int idx = counter - prev; + OCTETSTRING trunk(idx, static_cast(body)); + loggers::get_instance().log_msg("http_codec::decode_body: trunk: ", trunk); + std::string str((const char *)static_cast(trunk), idx); + loggers::get_instance().log("http_codec::decode_body: str: '%s'", str.c_str()); + int len = std::stoi(str, nullptr, 16); // converter::get_instance().string_to_int(str); + loggers::get_instance().log("http_codec::decode_body: Chunk len: %d", len); + while (counter < body.lengthof() && ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n'))) { // Skip additional \n + counter += 1; + } // End of 'while' statement + if (counter < body.lengthof()) { + loggers::get_instance().log("http_codec::decode_body: Chunked (1): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + os += OCTETSTRING(len, counter + static_cast(body)); + loggers::get_instance().log_msg("http_codec::decode_body: os=", os); + counter += len; + loggers::get_instance().log("http_codec::decode_body: Chunked: %02x %02x %02x", body[counter].get_octet(), body[counter + 1].get_octet(), + body[counter + 2].get_octet()); + loggers::get_instance().log("http_codec::decode_body: Chunked (2): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + while (counter < body.lengthof() && ((body[counter].get_octet() == '\r') || (body[counter].get_octet() == '\n'))) { // Skip additional \n + counter += 1; + } // End of 'while' statement + prev = counter; + loggers::get_instance().log("http_codec::decode_body: Chunked (3): prev = %d, counter=%d / %d", prev, counter, body.lengthof()); + } + } + } while (counter < body.lengthof()); // Process next chunk if any + body = os; + loggers::get_instance().log_msg("http_codec::decode_body: Finalised body=", body); + } + // Check if HTTP message body contains binary characters + for (int i = 0; i < body.lengthof(); i++) { + unsigned char c = body[i].get_octet(); + if (!std::isprint(c) && !std::isspace(c) && !std::ispunct(c)) { + loggers::get_instance().log("http_codec::decode_body: Byte #%d is not printable: 0x%02x", i, body[i].get_octet()); + _dc.is_binary = 0x01; + break; + } + } // End of 'for' statement + loggers::get_instance().log("http_codec::decode_body: Binary mode: %x", _dc.is_binary); + LibHttp__MessageBodyTypes::HttpMessageBody v; + if (_dc.is_binary == 0x01) { + LibHttp__BinaryMessageBodyTypes::BinaryBody binary_body; + std::map>>::const_iterator it; + bool processed = false; + // TODO To be refined adding a string identifier to check which codec to use. E.g. held_code.id() returns "xmlns=\"urn:ietf:params:xml:ns:geopriv:held\">" + if ((p_content_type.find("x-its") != std::string::npos) || (p_content_type.find("application/octet-stream") != std::string::npos)) { + loggers::get_instance().log("http_codec::decode_body: Find 'x-its'"); + it = _codecs.cbegin(); //_codecs.find("http_its"); + if (it != _codecs.cend()) { + /*** + FIXME: + This code generate a codedump, I don't undertsand the reason. + The same code works file for Ng112 HELD & LOST codec. Ununderstandable!!!! + ==> Use a patch + if (_codecs["http_its"].get() != nullptr) { + loggers::get_instance().log("http_codec::decode_body: Call 'http_etsi_ieee1609dot2_codec'"); + if (_codecs["http_its"]->decode(body, (Record_Type&)binary_body) == 0) { + processed = true; + } + }*/ + loggers::get_instance().log("http_codec::decode_body: Call '%s'", it->first.c_str()); + http_etsi_ieee1609dot2_codec *codec = new http_etsi_ieee1609dot2_codec(); + if (body[0].get_octet() != 0x80) { + if (codec->decode(body, binary_body.ieee1609dot2__data()) == 0) { + message_body.binary__body() = binary_body; + processed = true; + } + } else { + if (codec->decode(body, binary_body.ieee1609dot2__certificate()) == 0) { + message_body.binary__body() = binary_body; + processed = true; + } + } + delete codec; + } + } // TODO Add new HTTP message codec here + if (!processed) { + loggers::get_instance().warning("http_codec::decode_body: Unsupported HTTP codec, use raw field as default"); + binary_body.raw() = body; + message_body.binary__body() = binary_body; + } + } else { + // Convert into string + params p; + p["decode_str"] = std::string(static_cast(body), body.lengthof() + static_cast(body)); + loggers::get_instance().log("http_codec::decode_body: decode_str: %s", p["decode_str"].c_str()); + // Try to identify xml + if (p["decode_str"].find("" + if ((p["decode_str"].find("=\"urn:ietf:params:xml:ns:geopriv:held\"") != std::string::npos) || + (p["decode_str"].find("=\"urn:ietf:params:xml:ns:pidf\"") != std::string::npos)) { + loggers::get_instance().log("http_codec::decode_body: Find 'urn:ietf:params:xml:ns:geopriv:held'"); + if (_codecs["held"].get() != nullptr) { + loggers::get_instance().log("http_codec::decode_body: Call 'held_codec'"); + if (_codecs["held"]->decode(body, (Record_Type &)xml_body, &p) == -1) { + loggers::get_instance().warning("http_codec::decode_body: Failed to decode HELD message"); + xml_body.raw() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); + } else { + loggers::get_instance().log_msg("http_codec::decode_body: Decoded message:", xml_body); + message_body.xml__body() = xml_body; + } + } else { + loggers::get_instance().warning("http_codec::decode_body: No codec for HELD"); + xml_body.raw() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); + } + message_body.xml__body() = xml_body; + } else if (p["decode_str"].find("=\"urn:ietf:params:xml:ns:lost1\"") != std::string::npos) { + loggers::get_instance().log("http_codec::decode_body: Find 'urn:ietf:params:xml:ns:lost1'"); + if (_codecs["lost"].get() != nullptr) { + loggers::get_instance().log("http_codec::decode_body: Call 'lost_codec'"); + if (_codecs["lost"]->decode(body, (Record_Type &)xml_body, &p) == -1) { + loggers::get_instance().warning("http_codec::decode_body: Failed to decode LOST message"); + xml_body.raw() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); + } else { + loggers::get_instance().log_msg("http_codec::decode_body: Decoded message:", xml_body); + message_body.xml__body() = xml_body; + } + } else { + loggers::get_instance().warning("http_codec::decode_body: No codec for LOST"); + xml_body.raw() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); + } + message_body.xml__body() = xml_body; + } else { + loggers::get_instance().warning("http_codec::decode_body: No XML codec found"); + xml_body.raw() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); + message_body.xml__body() = xml_body; + } + } else if (p["decode_str"].find("") != std::string::npos) { // Try to identify HTML + loggers::get_instance().log("http_codec::decode_body: Find html message"); + LibHttp__MessageBodyTypes::HtmlBody html_body; + message_body.html__body() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); + } else { + loggers::get_instance().log("http_codec::decode_body: Use textBdy as default"); + LibHttp__MessageBodyTypes::TextBody text_body; + message_body.text__body() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); + } + } + + return 0; +} + +int http_codec::get_line(TTCN_Buffer &buffer, CHARSTRING &to, const bool concatenate_header_lines) { + unsigned int i = 0; + const unsigned char *cc_to = buffer.get_read_data(); + + // Sanity checks + if (buffer.get_read_len() == 0) { + return -1; + } + + while (true) { + // Skip spaces, and empty lines + for (; i < buffer.get_read_len() && cc_to[i] != '\0' && cc_to[i] != '\r' && cc_to[i] != '\n'; i++) + ; + if (i >= buffer.get_read_len()) { // No more characters to process + to = CHARSTRING(""); + return -1; + } else if (cc_to[i] == '\n') { // New line found, we don't care is '\r' is missing + if ((i > 0) && ((i + 1) < buffer.get_read_len()) && concatenate_header_lines && ((cc_to[i + 1] == ' ') || (cc_to[i + 1] == '\t'))) { + i += 1; // Skip it + } else { + to = CHARSTRING(i, (const char *)cc_to); + buffer.set_pos(buffer.get_pos() + i + 1); + return i == 0 ? 1 : 0; + } + } else { + if ((i + 1) < buffer.get_read_len() && cc_to[i + 1] != '\n') { + return -1; + } else if (i > 0 && (i + 2) < buffer.get_read_len() && concatenate_header_lines && (cc_to[i + 2] == ' ' || cc_to[i + 2] == '\t')) { + i += 2; + } else { + to = CHARSTRING(i, (const char *)cc_to); + buffer.set_pos(buffer.get_pos() + i + 2); + return i == 0 ? 1 : 0; + } + } + } // End of 'while' statement +} + +void http_codec::set_payload_codecs(const std::string &p_codecs) { + loggers::get_instance().log(">>> http_codec::set_payload_codecs: %s", p_codecs.c_str()); + + // Sanity check + if (p_codecs.length() == 0) { + return; + } + + // Extract codecs + try { + std::regex rgx("(\\w+):(\\w+)(;(\\w+):(\\w+))*"); + std::sregex_iterator begin(p_codecs.cbegin(), p_codecs.cend(), rgx); + std::sregex_iterator end = std::sregex_iterator(); + // E.g. 9 - xml - :held_codec - held_codec - ;html:html_codec - html:html_codec - html - :html_codec - html_codec + for (std::sregex_iterator it = begin; it != end; ++it) { + std::smatch m = *it; + loggers::get_instance().log("http_codec::set_payload_codecs: %d - %s - %s - %s - %s - %s - %s - %s - %s", m.size(), m[1].str().c_str(), + m[2].str().c_str(), m[3].str().c_str(), m[4].str().c_str(), m[5].str().c_str(), m[6].str().c_str(), m[7].str().c_str(), + m[8].str().c_str()); + for (unsigned int j = 1; j < m.size() - 1; j += 3) { // Exclude m[0] + loggers::get_instance().log("http_codec::set_payload_codecs: insert (%s, %s), j = %d", m[j].str().c_str(), m[j + 1].str().c_str(), j); + if (m[j].str().empty()) { + break; + } + std::string key(m[j].str()); + loggers::get_instance().log("http_codec::set_payload_codecs: Add codec %s", key.c_str()); + _codecs.insert( + std::make_pair(key, std::unique_ptr>(codec_stack_builder::get_instance()->get_codec(m[j + 1].str().c_str())))); + } // End of 'for' statement + } // End of 'for' statement + loggers::get_instance().log("http_codec::set_payload_codecs: _codecs length=%d - %p", _codecs.size(), this); + } catch (const std::logic_error &e) { + loggers::get_instance().warning("http_codec::set_payload_codecs: std::logic_error: %s", e.what()); + _codecs.clear(); + } +} diff --git a/ccsrc/Protocols/Http/http_codec.hh b/ccsrc/Protocols/Http/http_codec.hh new file mode 100644 index 0000000..0a1ff1f --- /dev/null +++ b/ccsrc/Protocols/Http/http_codec.hh @@ -0,0 +1,72 @@ +#pragma once + +#include + +#include "codec_gen.hh" +#include "params.hh" + +class Base_Type; +class Record_Type; +class TTCN_Typedescriptor_t; +class TTCN_Buffer; + +namespace LibHttp__TypesAndValues { + class HttpMessage; + class Request; + class Response; + class Headers; + class Header; +} // namespace LibHttp__TypesAndValues +namespace LibHttp__MessageBodyTypes { + class HttpMessageBody; +} + +struct encoding_context { + unsigned int length; + unsigned char is_content_length_present; + + encoding_context() { reset(); }; + void reset() { + length = -1; + is_content_length_present = 0x00; + }; +}; + +struct decoding_context { + unsigned int length; + unsigned char is_binary; + bool chunked; + + decoding_context() { reset(); }; + void reset() { + length = -1; + is_binary = 0x00; + chunked = false; + }; +}; + +class http_codec : public codec_gen { + encoding_context _ec; + decoding_context _dc; + std::map>> _codecs; + +public: + explicit http_codec() : codec_gen(), _ec(), _dc(), _codecs(){}; + virtual ~http_codec(){}; + + virtual int encode(const LibHttp__TypesAndValues::HttpMessage &, OCTETSTRING &data); + virtual int decode(const OCTETSTRING &data, LibHttp__TypesAndValues::HttpMessage &, params *params = NULL); + + void set_payload_codecs(const std::string &p_codecs); + +private: + int encode_request(const LibHttp__TypesAndValues::Request &p_request, TTCN_Buffer &p_encoding_buffer); + int encode_response(const LibHttp__TypesAndValues::Response &p_response, TTCN_Buffer &p_encoding_buffer); + int encode_body(const LibHttp__MessageBodyTypes::HttpMessageBody &p_message_body, OCTETSTRING &p_encoding_buffer, const std::string &p_content_type); + + int decode_headers(TTCN_Buffer &decoding_buffer, LibHttp__TypesAndValues::Headers &headers, std::string &p_content_type); + int decode_header(CHARSTRING &header_line, LibHttp__TypesAndValues::Header &header); + int decode_body(TTCN_Buffer &decoding_buffer, LibHttp__MessageBodyTypes::HttpMessageBody &message_body, const std::string &p_content_type); + int get_line(TTCN_Buffer &buffer, CHARSTRING &to, const bool concatenate_header_lines = false); + +}; // End of class http_codec diff --git a/ccsrc/Protocols/Http/http_encdec.cc b/ccsrc/Protocols/Http/http_encdec.cc new file mode 100644 index 0000000..f947e63 --- /dev/null +++ b/ccsrc/Protocols/Http/http_encdec.cc @@ -0,0 +1,28 @@ + +#include "LibHttp_MessageBodyTypes.hh" + +#include "http_codec.hh" + +#include "loggers.hh" + +namespace LibHttp__EncdecDeclarations { + + BITSTRING fx__enc__http__message(const LibHttp__TypesAndValues::HttpMessage& p) { + loggers::get_instance().log_msg(">>> fx__enc__http__message: ", (const Base_Type&)p); + + OCTETSTRING os; + http_codec codec; + codec.encode(p, os); + + return oct2bit(os); + } + INTEGER fx__dec__http__message(BITSTRING& pdu, LibHttp__TypesAndValues::HttpMessage& p) { + loggers::get_instance().log_msg(">>> fx__dec__http__message: ", pdu); + + OCTETSTRING os = bit2oct(pdu); + http_codec codec; + codec.decode(os, p); + + return 0; + } +} // End of namespace LibHttp__EncdecDeclarations diff --git a/ccsrc/Protocols/Http/http_layer.cc b/ccsrc/Protocols/Http/http_layer.cc new file mode 100644 index 0000000..11fcee1 --- /dev/null +++ b/ccsrc/Protocols/Http/http_layer.cc @@ -0,0 +1,128 @@ +#include "LibHttp_TypesAndValues.hh" + +#include "codec_stack_builder.hh" +#include "http_layer_factory.hh" + +#include "loggers.hh" + +#include "converter.hh" + +using namespace std; // Required for isnan() +#include "LibHttp_TestSystem.hh" +#include "LibHttp_TypesAndValues.hh" + +http_layer::http_layer(const std::string &p_type, const std::string ¶m) + : t_layer(p_type), _params(), _device_mode{false} { + loggers::get_instance().log(">>> http_layer::http_layer: %s, %s", to_string().c_str(), param.c_str()); + // Setup parameters + params::convert(_params, param); + + params::const_iterator it = _params.find(params::codecs); + if (it != _params.cend()) { + _codec.set_payload_codecs(it->second); + } + // it = _params.find(params::device_mode); + // if (it != _params.cend()) { + // _device_mode = (1 == converter::get_instance().string_to_int(it->second)); + // } + it = _params.find(params::method); + if (it == _params.cend()) { + _params[params::method] = "POST"; + } + it = _params.find(params::uri); + if (it == _params.cend()) { + _params[params::uri] = "/"; + } + it = _params.find(params::host); + if (it == _params.cend()) { + _params[params::host] = "127.0.0.1"; + } + it = _params.find(params::content_type); + if (it == _params.cend()) { + _params[params::content_type] = "application/text"; + } +} + +void http_layer::sendMsg(const LibHttp__TypesAndValues::HttpMessage &p_http_message, params &p_param) { + loggers::get_instance().log_msg(">>> http_layer::sendMsg: ", p_http_message); + + // Encode HttpMessage + OCTETSTRING data; + _codec.encode(p_http_message, data); + send_data(data, _params); +} + +void http_layer::send_data(OCTETSTRING &data, params ¶ms) { + loggers::get_instance().log_msg(">>> http_layer::send_data: ", data); + + if (_device_mode) { // Need to build an HTTP packet + loggers::get_instance().log("http_layer::send_data: Build http layer"); + TTCN_Buffer buffer; + buffer.put_cs(_params[params::method].c_str()); + buffer.put_c(' '); + buffer.put_cs(_params[params::uri].c_str()); + buffer.put_cs(" HTTP/1.1\r\n"); + buffer.put_cs("Host: "); + buffer.put_cs(_params[params::host].c_str()); + buffer.put_cs("\r\n"); + buffer.put_cs("Content-type: "); + buffer.put_cs(_params[params::content_type].c_str()); + buffer.put_cs("\r\n"); + buffer.put_cs("Content-length: "); + buffer.put_cs(static_cast(int2str(data.lengthof() + 2 /*Stand for the last CRLF*/))); + buffer.put_cs("\r\n\r\n"); + buffer.put_os(data); + buffer.put_cs("\r\n"); + data = OCTETSTRING(buffer.get_len(), buffer.get_data()); + } + + loggers::get_instance().log_msg("http_layer::send_data: ", data); + send_to_all_layers(data, params); +} + +void http_layer::receive_data(OCTETSTRING &data, params ¶ms) { + loggers::get_instance().log_msg(">>> http_layer::receive_data: ", data); + + // Decode HTTP message + LibHttp__TypesAndValues::HttpMessage http_message; + if (_codec.decode(data, http_message) == -1) { + loggers::get_instance().warning("http_layer::receive_data: Failed to decode data"); + return; + } + if (_device_mode) { + OCTETSTRING os; + if (http_message.ischosen(LibHttp__TypesAndValues::HttpMessage::ALT_response)) { + if (http_message.response().body().ispresent()) { + LibHttp__MessageBodyTypes::HttpMessageBody &body = + static_cast(*http_message.response().body().get_opt_value()); + if (body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_binary__body)) { + LibHttp__BinaryMessageBodyTypes::BinaryBody &binary = body.binary__body(); + if (binary.ischosen(LibHttp__BinaryMessageBodyTypes::BinaryBody::ALT_raw)) { + os = binary.raw(); + } else { + loggers::get_instance().warning("http_layer::receive_data: A raw binary payload is expected"); + } + } else if (body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_html__body)) { + // TODO To be done + loggers::get_instance().warning("http_layer::receive_data: Not implemented yet"); + } else if (body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_xml__body)) { + // TODO To be done + loggers::get_instance().warning("http_layer::receive_data: Not implemented yet"); + } else if (body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_text__body)) { + // TODO To be done + loggers::get_instance().warning("http_layer::receive_data: Not implemented yet"); + } + receive_to_all_layers(os, params); + } else { + loggers::get_instance().warning("http_layer::receive_data: No body present"); + } + } else { + loggers::get_instance().warning("http_layer::receive_data: An HTTP response is expected"); + } + } else { + // Pass it to the ports + to_all_upper_ports(http_message, params); + } +} + +http_layer_factory http_layer_factory::_f; diff --git a/ccsrc/Protocols/Http/http_layer.hh b/ccsrc/Protocols/Http/http_layer.hh new file mode 100644 index 0000000..bfc47b3 --- /dev/null +++ b/ccsrc/Protocols/Http/http_layer.hh @@ -0,0 +1,83 @@ +/*! + * \file http_layer.hh + * \brief Header file for ITS HTTP protocol layer. + * \author ETSI STF549 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include + +#include "t_layer.hh" + +#include "http_codec.hh" + +namespace LibHttp__TestSystem { + class HttpPort; +} + +namespace LibHttp__TypesAndValues { + class HttpMessage; //! Forward declaration of TITAN class +} + +class OCTETSTRING; //! Forward declaration of TITAN class + +/*! + * \class http_layer + * \brief This class provides a factory class to create an tcp_layer class instance + */ +class http_layer : public t_layer { + params _params; + http_codec _codec; + bool _device_mode; + +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the http_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + http_layer() : t_layer(), _params(), _device_mode{false} { }; + /*! + * \brief Specialised constructor + * Create a new instance of the http_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + http_layer(const std::string& p_type, const std::string& p_param); + /*! + * \brief Default destructor + */ + virtual ~http_layer() { }; + + /*! + * \fn void sendMsg(const LibHttp__TypesAndValues::HttpMessage& p_http_message, params& p_param); + * \brief Send HTTP message to the lower layers + * \param[in] p_http_message The GeoNetworking message to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + void sendMsg(const LibHttp__TypesAndValues::HttpMessage& p_http_message, params& p_param); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING& data, params& params); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING& data, params& info); +}; // End of class http_layer + diff --git a/ccsrc/Protocols/Http/http_layer_factory.hh b/ccsrc/Protocols/Http/http_layer_factory.hh new file mode 100644 index 0000000..5e4e71a --- /dev/null +++ b/ccsrc/Protocols/Http/http_layer_factory.hh @@ -0,0 +1,42 @@ +/*! + * \file http_layer_factory.hh + * \brief Header file for ITS Http protocol layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "http_layer.hh" + +/*! + * \class http_layer_factory + * \brief This class provides a factory class to create an http_layer class instance + */ +class http_layer_factory : public layer_factory { + static http_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the http_layer_factory class + * \remark The HTTP layer identifier is HTTP + */ + http_layer_factory() { + // Register factory + layer_stack_builder::register_layer_factory("HTTP", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \inline + */ + inline virtual layer *create_layer(const std::string &p_type, const std::string &p_param) { return new http_layer(p_type, p_param); }; +}; // End of class http_layer_factory diff --git a/ccsrc/Protocols/Http/module.mk b/ccsrc/Protocols/Http/module.mk new file mode 100644 index 0000000..2c92703 --- /dev/null +++ b/ccsrc/Protocols/Http/module.mk @@ -0,0 +1,3 @@ +sources := http_codec.cc http_layer.cc +includes := . + diff --git a/ccsrc/Protocols/Pcap/module.mk b/ccsrc/Protocols/Pcap/module.mk new file mode 100644 index 0000000..0bb7431 --- /dev/null +++ b/ccsrc/Protocols/Pcap/module.mk @@ -0,0 +1,3 @@ +sources := pcap_cygwin_layer.cc pcap_layer.cc pcap_linux_layer.cc pcap_offline_layer.cc +includes := . + diff --git a/ccsrc/Protocols/Pcap/pcap_cygwin_layer.cc b/ccsrc/Protocols/Pcap/pcap_cygwin_layer.cc new file mode 100644 index 0000000..56f9bbc --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_cygwin_layer.cc @@ -0,0 +1,265 @@ +#if defined(__CYGWIN__) + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pcap_layer_factory.hh" + +#include "loggers.hh" + +#include + +typedef struct { + bpf_int32 tv_sec; /* seconds */ + bpf_int32 tv_usec; /* microseconds */ +} pcap_o_timeval; + +typedef struct pcap_o_pkthdr { + pcap_o_timeval ts; /* time stamp */ + bpf_u_int32 caplen; /* length of portion present */ + bpf_u_int32 len; /* length this packet (off wire) */ +} pcap_o_pkthdr; + +extern "C" int pcap_oid_get_request(pcap_t *p, bpf_u_int32 oid, void *data, size_t *lenp); + +static const char *_hexDigits = "0123456789ABCDEF"; +static char * _bin2hex(char *hex, size_t hlen, const char *bin, size_t blen) { + const unsigned char *b, *e; + char * s; + + // sanity check + if (hlen >= 0 && hlen < blen * 2) + return NULL; + + b = (const unsigned char *)bin; + e = b + blen - 1; + s = hex + blen * 2; + if (s < hex + hlen) + *s = 0; + for (; b <= e; e--) { + *(--s) = _hexDigits[(*e) & 0xF]; + *(--s) = _hexDigits[(*e) >> 4]; + } + return hex + blen * 2; +} + +pcap_layer::pcap_layer(const std::string &p_type, const std::string ¶m) + : layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _running(FALSE), _time_key("pcap_layer::Handle_Fd_Event_Readable") { + loggers::get_instance().log(">>> pcap_layer::pcap_layer: %s, %s", p_type.c_str(), param.c_str()); + params::convert(_params, param); + + char error_buffer[PCAP_ERRBUF_SIZE]; + params::const_iterator it; + std::string nic; // network interface name + bpf_u_int32 mask; // subnet mask + bpf_u_int32 net; // ip address + + it = _params.find(params::nic); + if ((it == _params.end()) || it->second.empty()) { + loggers::get_instance().error("pcap_layer::pcap_layer: NIC must be specified"); + return; + } + + nic = std::string("\\Device\\NPF_{") + it->second + "}"; + + if (pcap_lookupnet(nic.c_str(), &net, &mask, error_buffer) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: pcap_layer::pcap_layer: Failed to fetch newtork address for device %s", nic.c_str()); + } + loggers::get_instance().log("pcap_layer::pcap_layer: Device %s Network address: %d", nic.c_str(), net); + + // Open the device + _device = pcap_open_live(nic.c_str(), 65536 /*64*1024*/, 1, 100, error_buffer); + if (_device == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open device %s", nic.c_str()); + return; + } + + // Setup filter + std::string filter = ""; + std::string mac_src; + it = _params.find(params::mac_src); + if (it != _params.end() && !it->second.empty()) { + mac_src = it->second; + } else { + // Not found + // determine it automatically +#ifndef OID_802_3_CURRENT_ADDRESS +#define OID_802_3_CURRENT_ADDRESS 0x01010102 +#endif + char mac[6] = {0}; + size_t l = sizeof(mac); + pcap_oid_get_request(_device, OID_802_3_CURRENT_ADDRESS, mac, &l); + char buf[13]; + *_bin2hex(buf, sizeof(buf), mac, 6) = 0; + mac_src = buf; + loggers::get_instance().user("pcap_layer::pcap_layer: local MAC is %s", mac_src.c_str()); + _params[params::mac_src] = mac_src; + } + + std::string mac_bc; + it = _params.find(params::mac_bc); + if (it != _params.end() && !it->second.empty()) + mac_bc = it->second; + else + mac_bc = "ffffffffffff"; + + if (mac_bc == mac_src || mac_src.empty()) + filter = "ether dst " + mac_bc; + else + filter = "( ether dst " + mac_bc + " or ether dst " + mac_src + " )"; + + if (!mac_src.empty()) + // Reject ITS messages sent by this component + filter += " and not ether src " + mac_src; + + // Add user defined filter + it = _params.find(params::filter); + if ((it != _params.end()) && !it->second.empty()) { + filter += std::string(" ") + it->second; + } + + // Log final PCAP filter + loggers::get_instance().user("pcap_layer::pcap_layer: Filter: %s", filter.c_str()); + + // setup filter + { + struct bpf_program f = {0}; + if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to compile PCAP filter"); + } else { + if (pcap_setfilter(_device, &f) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set PCAP filter"); + } + } + pcap_freecode(&f); + } + + _o_params.insert(std::pair(std::string("timestamp"), std::string())); + + // create pipe and run thread + if (pipe2(_fd, O_NONBLOCK) == -1) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to create a pipe: %s", ::strerror(errno)); + } + // Pass the pipe handler to the polling procedure + loggers::get_instance().log("pcap_layer::pcap_layer: Call handler with descriptor %d", _fd[0]); + Handler_Add_Fd_Read(_fd[0]); + + // Create the reader thread + _thread = new std::thread(&pcap_layer::run, (void *)this); + if (_thread == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to start offline thread"); + } + while (_running == FALSE) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + // Thread was started + loggers::get_instance().log("<<< pcap_layer::pcap_layer"); +} // End of ctor + +pcap_layer::~pcap_layer() { + loggers::get_instance().log(">>> pcap_layer::~pcap_layer"); + + if (_device != NULL) { + if (_thread != NULL) { + _running = FALSE; + // Wait for the working thread to terminate + _thread->join(); + loggers::get_instance().log("pcap_layer::~pcap_layer: Thread were stops"); + // Cleanup + delete _thread; + close(_fd[0]); + close(_fd[1]); + } + pcap_close(_device); + } +} // End of dtor + +void *pcap_layer::run(void *p_this) { + pcap_layer &p = *static_cast(p_this); + return p.thread(); +} + +void *pcap_layer::thread() { + pcap_o_pkthdr *pkt_header; + const u_char * pkt_data; + unsigned char pkt_count = 0; + + // loggers::get_instance().log(">>> pcap_layer::run"); + + _running = TRUE; + + // wait a bit before sending first packet + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + + while (_running) { // Loop while _running flag is up + // get next frame + int result = pcap_next_ex(_device, (struct pcap_pkthdr **)&pkt_header, &pkt_data); + if (result == 0) { + continue; + } + if (result < 0) { + // loggers::get_instance().log("<<< pcap_layer::run: error %s", pcap_geterr(_device)); + break; + } + + while (_running && !_resume.try_lock()) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +#if 0 + { + char buf[128]; + std::time_t t = pkt_header->ts.tv_sec; + std::tm * pt = std::localtime( &t ); + t = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", pt); + std::sprintf(buf+t, "%ld", pkt_header->ts.tv_sec * 1000 + static_cast(pkt_header->ts.tv_usec / 1000)); // Use milliseconds + _o_params[params::timestamp] = std::string(buf); + } +#else + _o_params[params::timestamp] = std::to_string(pkt_header->ts.tv_sec * 1000 + static_cast(pkt_header->ts.tv_usec / 1000)); // Use milliseconds +#endif + _o_data = OCTETSTRING(pkt_header->len, pkt_data); + write(_fd[1], &pkt_count, 1); + pkt_count++; + } + _running = FALSE; + // loggers::get_instance().log("<<< pcap_layer::run"); + return NULL; +} + +void pcap_layer::send_data(OCTETSTRING &data, params ¶ms) { + loggers::get_instance().log_msg(">>> pcap_layer::send_data: ", data); + if (pcap_sendpacket(_device, static_cast(data), data.lengthof()) == -1) { + loggers::get_instance().error("pcap_layer::send_data: Failed to send packet: %s", pcap_geterr(_device)); + } +} + +void pcap_layer::receive_data(OCTETSTRING &data, params ¶ms) { + loggers::get_instance().log(">>> pcap_layer::receive_data: Received %d bytes", data.lengthof()); + loggers::get_instance().log_to_hexa("Packet dump", data); + + // Pass the packet to the upper layers + receive_to_all_layers(data, params); +} + +void pcap_layer::Handle_Fd_Event_Readable(int fd) { + char c[2]; + float duration; + loggers::get_instance().set_start_time(_time_key); + this->receive_data(_o_data, _o_params); + loggers::get_instance().set_stop_time(_time_key, duration); + read(_fd[0], &c, 1); + _resume.unlock(); +} + +pcap_layer_factory pcap_layer_factory::_f; + +#endif //__CYGWIN__ diff --git a/ccsrc/Protocols/Pcap/pcap_cygwin_layer.hh b/ccsrc/Protocols/Pcap/pcap_cygwin_layer.hh new file mode 100644 index 0000000..d7d477a --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_cygwin_layer.hh @@ -0,0 +1,79 @@ +/*! + * \file pcap_layer.hh + * \brief Header file for ITS Offline Pcap port layer. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include + +#include "params.hh" +#include "t_layer.hh" + +#include + +class PORT; //! Forward declaration of TITAN class + +typedef struct pcap pcap_t; + +/*! + * \class pcap_layer + * \brief This class provides description of ITS PCAP port protocol layer + */ +class pcap_layer : public layer, public PORT { + + params _params; //! Layer parameters + pcap_t * _device; //! Device handle + std::thread *_thread; //! Thread handle, used to read PCAP file instead of NIC, used in file mode + std::mutex _resume; + bool _running; //! Set to true when the thread is running, used in file mode + int _fd[2]; //! pipe to signal to Titan + + OCTETSTRING _o_data; + params _o_params; + + std::string _time_key; + + static void *run(void *p_this); + +public: + void *thread(void); + +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the pcap_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + pcap_layer(const std::string &p_type, const std::string ¶m); + /*! + * \brief Default destructor + */ + virtual ~pcap_layer(); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING &data, params ¶ms); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING &data, params &info); + + void Handle_Fd_Event_Readable(int fd); +}; diff --git a/ccsrc/Protocols/Pcap/pcap_layer.cc b/ccsrc/Protocols/Pcap/pcap_layer.cc new file mode 100644 index 0000000..8313c9f --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_layer.cc @@ -0,0 +1,276 @@ +#if 0 +#include +#include +#include +#include +#include + +#include + +#include "pcap_layer_factory.hh" + +typedef struct pcap_pkthdr pcap_o_pkthdr; +typedef struct timeval pcap_o_timeval; + +#include "loggers.hh" + +pcap_layer::pcap_layer(const std::string& p_type, const std::string& param) : layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _pcap_h(-1), _thread(NULL), _running(FALSE), _resume(), _sent_file(NULL), _time_key("pcap_layer::Handle_Fd_Event_Readable") { + bool online = false; + loggers::get_instance().log(">>> pcap_layer::pcap_layer: %s, %s", to_string().c_str(), param.c_str()); + _fd[0] = -1; _fd[1] = -1; + // Setup parameters + params::convert(_params, param); + //_params.log(); + // Prepare capture processing + char error_buffer[PCAP_ERRBUF_SIZE]; + params::const_iterator it = _params.find(params::nic); + if ((it != _params.end()) && !it->second.empty()) { // Use online capture + // Fetch the network address and network mask + bpf_u_int32 mask; // subnet mask + bpf_u_int32 net; // ip address + std::string nic; + online = true; + nic = _params[params::nic]; + if (pcap_lookupnet(nic.c_str(), &net, &mask, error_buffer) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: pcap_layer::pcap_layer: Failed to fetch newtork address for device %s", nic.c_str()); + } + loggers::get_instance().log("pcap_layer::pcap_layer: Device %s Network address: %d", nic.c_str(), net); + // Open the device + _device = pcap_open_live(nic.c_str(), 65536, 1, 1000, error_buffer); // TODO Replace hard coded values by pcap_layer:: + if (_device == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open device %s", nic.c_str()); + } // else, continue + // Set non-blocking flag for the polling procedure + if (pcap_setnonblock(_device, 1, error_buffer) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set blocking mode: %s", error_buffer); + } + // Retrieve the device file handler + _pcap_h = pcap_get_selectable_fd(_device); + if (_pcap_h == -1) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to get device handler"); + } + } else { + // Check file name + it = _params.find(std::string("file")); + if ((it != _params.cend()) && !it->second.empty()) { // Use offline capture + struct stat s = {0}; + if ((stat(_params["file"].c_str(), &s) != 0) || !S_ISREG(s.st_mode)) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to acces PCAP file %s", _params["file"].c_str()); + } + // File exist, open it + _device = pcap_open_offline(_params["file"].c_str(), error_buffer); + if (_device == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open PCAP file %s", error_buffer); + } // else, continue + // Create the dump file for the sent packet based on the openned file name and the current time in milliseconds + it = _params.find("save_mode"); + if ((it != _params.cend()) && (it->second.compare("1") == 0)) { + unsigned long ms = std::chrono::system_clock::now().time_since_epoch() / std::chrono::milliseconds(1); + std::string ext("_" + std::to_string(ms)); + int i = _params["file"].find(".pcap"); + if (i > 0) { + std::string f(_params["file"].substr(0, i) + ext + ".pcap"); + loggers::get_instance().log("pcap_layer::pcap_layer: Save file name: %s", f.c_str()); + if ((_sent_file = pcap_dump_open(_device, f.c_str())) == NULL) { + loggers::get_instance().warning("pcap_layer::pcap_layer: Failed to open save file %s", f.c_str()); + } + } + } // else, nothing to do + } else { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open PCAP file %s", error_buffer); + } + } + + // Setup filter + std::string filter = ""; + it = _params.find(params::mac_src); + if (it == _params.end()) { // Not found + loggers::get_instance().error("pcap_layer::pcap_layer: mac_src parameter not found, cannot continue"); + } else { + // Reject ITS messages sent by this component + filter = "not ether src " + _params[params::mac_src]; + // Accept ITS broadcasted to this componenet + filter += " and (ether dst " + _params[params::mac_src]; + // Accept ITS broadcasted messages + it = _params.find(params::mac_bc); + if ((it != _params.end()) && !it->second.empty()) { + filter += " or ether dst " + it->second + ")"; + } else { + filter += " or ether dst ffffffffffff) "; + } + // Add user defined filter + it = _params.find(params::filter); + if ((it != _params.end()) && !it->second.empty()) { + filter += _params["filter"]; + } // else nothing to do + } + // Log final PCAP filter + loggers::get_instance().user("pcap_layer::pcap_layer: Filter: %s", filter.c_str()); + if (!filter.empty()) { + struct bpf_program f = {0}; + if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to compile PCAP filter"); + } + if (pcap_setfilter(_device, &f) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set PCAP filter"); + } + pcap_freecode(&f); + } + + // Pass the device file handler to the polling procedure + if (_pcap_h != -1) { // Live capture + Handler_Add_Fd_Read(_pcap_h); + } else { // Offline capture or cygwin + // Create a pipe + if (pipe2(_fd, O_NONBLOCK) == -1) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to create a pipe: %s", ::strerror(errno)); + } + if(online){ + _pcap_h = _fd[0]; + } + // Pass the pipe handler to the polling procedure + loggers::get_instance().log("pcap_layer::pcap_layer: Call handler with descriptor %d", _fd[0]); + Handler_Add_Fd_Read(_fd[0]); + // Create the offline reader thread + _thread = new std::thread(&pcap_layer::run, (void *)this); + if (_thread == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to start offline thread"); + } + // Start it to dispatch packet to a pipe + while (_running == FALSE) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + // Thread was started + loggers::get_instance().log("<<< pcap_layer::pcap_layer"); + } +} // End of ctor + +pcap_layer::~pcap_layer() { + loggers::get_instance().log(">>> pcap_layer::~pcap_layer"); + + if (_device != NULL) { + if (_thread != NULL) { + _running = FALSE; + // Wait for the working thread to terminate + _thread->join(); + loggers::get_instance().log("pcap_layer::~pcap_layer: Thread were stops"); + // Cleanup + delete _thread; + close(_fd[0]); + close(_fd[1]); + } + if (_sent_file != NULL) { + pcap_dump_close(_sent_file); + } + pcap_close(_device); + } +} // End of dtor + +void* pcap_layer::run(void* p_this) { + pcap_layer& p = *static_cast(p_this); + return p.thread(); +} + +void* pcap_layer::thread() { + loggers::get_instance().log(">>> pcap_layer::run"); + + // Wait a little bit before to start sending packet + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + params::const_iterator it = _params.find("frame_offset"); + if ((it != _params.cend()) && (it->second.compare("0") != 0)) { + // TODO Try t use PCAP filter to start directly to the correct frame offset + /*try { + unsigned int offset = std::stoul(str_dec, &s); + // Skip frames + struct pcap_pkthdr *pkt_header; + const u_char *pkt_data; + int result = pcap_next_ex(_device, &pkt_header, &pkt_data); + if (result == 1) { // Succeed + } + } + catch (invalid_argument& i) { + } + catch (out_of_range& o) { + }*/ + } + // Let's go + _running = TRUE; + while (_running) { // Loop while _running flag is up + if (_resume.try_lock() == TRUE) { // Previous packet was consumed, lock for the next one + write(_fd[1], "\n", 1); // Any character will do the job + } else { // not ready yet + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + + loggers::get_instance().log("<<< pcap_layer::run"); + return NULL; +} + +void pcap_layer::send_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> pcap_layer::send_data: ", data); + + if (_pcap_h != -1) { // Check if offline mode is used + if (pcap_sendpacket(_device, static_cast(data), data.lengthof()) == -1) { + loggers::get_instance().error("pcap_layer::send_data: Failed to send packet: %s", pcap_geterr(_device)); + } + } else if (_sent_file != NULL) { + struct pcap_pkthdr hdr; + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + std::chrono::milliseconds ms = std::chrono::duration_cast(now.time_since_epoch()); + hdr.ts.tv_sec = ms.count() / 1000; + hdr.ts.tv_usec = (ms.count() % 1000) * 1000; + hdr.caplen = data.lengthof(); + hdr.len = hdr.caplen; + pcap_dump((u_char *)_sent_file, &hdr, static_cast(data)); + } else { + loggers::get_instance().log("pcap_layer::send_data: Offline mode, operation was skipped"); + } +} + +void pcap_layer::receive_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log(">>> pcap_layer::receive_data: Received %d bytes", data.lengthof()); + loggers::get_instance().log_to_hexa("Packet dump", data); + + // Pass the packet to the upper layers + receive_to_all_layers(data, params); +} + +void pcap_layer::Handle_Fd_Event_Readable(int fd) { + //loggers::get_instance().log(">>> pcap_layer::Handle_Fd_Event_Readable: %d", fd); + + pcap_o_pkthdr *pkt_header; + const u_char *pkt_data; + int result = pcap_next_ex(_device, (struct pcap_pkthdr**)&pkt_header, &pkt_data); + if (result == 1) { // Succeed + if (pkt_header->caplen > 14) { // Reject too small packet + //loggers::get_instance().log("pcap_layer::Handle_Fd_Event_Readable: %ld - % ld - %d", pkt_header->ts.tv_sec, pkt_header->ts.tv_usec, pkt_header->len); + // Fill parameters from PCAP layer + params params; + params.insert(std::pair(params::timestamp, std::to_string(pkt_header->ts.tv_sec * 1000 + static_cast(pkt_header->ts.tv_usec / 1000)))); // Use milliseconds + // Process the packet at this layer + OCTETSTRING os(pkt_header->caplen, pkt_data); + //loggers::get_instance().log_to_hexa("pcap_layer::Handle_Fd_Event_Readable: ", os); + // TODO Case of caplen != len !!! + float duration; + loggers::get_instance().set_start_time(_time_key); + this->receive_data(os, params); // TODO Check execution time for decoding operation + loggers::get_instance().set_stop_time(_time_key, duration); + } + } // else, skip the packet + // Specific to offline mode + if (_fd[0] != -1) { // Check if offline mode is used + //loggers::get_instance().log("pcap_layer::Handle_Fd_Event_Readable: Read pipe"); + char c[2]; + read(_fd[0], &c, 1); + if (result == -2) { // End of file, therminate worker thread + _running = FALSE; + } + //loggers::get_instance().log("pcap_layer::Handle_Fd_Event_Readable: pcap_next_ex failed: result=%d", result); + _resume.unlock(); + } // else, nothing to do +} + +pcap_layer_factory pcap_layer_factory::_f; + +#endif // !CYGWIN diff --git a/ccsrc/Protocols/Pcap/pcap_layer.hh b/ccsrc/Protocols/Pcap/pcap_layer.hh new file mode 100644 index 0000000..151eab8 --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_layer.hh @@ -0,0 +1,5 @@ +#if defined (__CYGWIN__) + #include "pcap_cygwin_layer.hh" +#else + #include "pcap_linux_layer.hh" +#endif diff --git a/ccsrc/Protocols/Pcap/pcap_layer_factory.hh b/ccsrc/Protocols/Pcap/pcap_layer_factory.hh new file mode 100644 index 0000000..0125e19 --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_layer_factory.hh @@ -0,0 +1,42 @@ +/*! + * \file pcap_layer_factory.hh + * \brief Header file for Pcap layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "pcap_layer.hh" + +/*! + * \class pcap_layer_factory + * \brief This class provides a factory class to create an pcap_layer class instance + */ +class pcap_layer_factory : public layer_factory { + static pcap_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the udp_layer_factory class + * \remark The PCAP layer identifier is PCAP + */ + pcap_layer_factory() { + // register factory + layer_stack_builder::register_layer_factory("PCAP", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \inline + */ + inline virtual layer *create_layer(const std::string &p_type, const std::string &p_param) { return new pcap_layer(p_type, p_param); }; +}; // End of class pcap_layer_factory diff --git a/ccsrc/Protocols/Pcap/pcap_linux_layer.cc b/ccsrc/Protocols/Pcap/pcap_linux_layer.cc new file mode 100644 index 0000000..fecb2b3 --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_linux_layer.cc @@ -0,0 +1,198 @@ +#if !defined(__CYGWIN__) +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pcap_layer_factory.hh" + +#include "loggers.hh" + +static const char *_hexDigits = "0123456789ABCDEF"; +static char * _bin2hex(char *hex, size_t hlen, const char *bin, size_t blen) { + const unsigned char *b, *e; + char * s; + + // sanity check + if (hlen >= 0 && hlen < blen * 2) + return NULL; + + b = (const unsigned char *)bin; + e = b + blen - 1; + s = hex + blen * 2; + if (s < hex + hlen) + *s = 0; + for (; b <= e; e--) { + *(--s) = _hexDigits[(*e) & 0xF]; + *(--s) = _hexDigits[(*e) >> 4]; + } + return hex + blen * 2; +} + +pcap_layer::pcap_layer(const std::string &p_type, const std::string ¶m) + : layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _pcap_h(-1), _time_key("pcap_layer::Handle_Fd_Event_Readable") { + char error_buffer[PCAP_ERRBUF_SIZE]; + params::const_iterator it; + std::string nic; + + loggers::get_instance().log(">>> pcap_layer::pcap_layer: %s, %s", to_string().c_str(), param.c_str()); + // Setup parameters + params::convert(_params, param); + // Prepare capture processing + it = _params.find(params::nic); + if ((it == _params.end()) || it->second.empty()) { // Use online capture + loggers::get_instance().error("pcap_layer::pcap_layer: NIC name must be specified"); + return; + } + + nic = _params[params::nic]; + { + bpf_u_int32 net, mask; // ip address and subnet mask + if (pcap_lookupnet(nic.c_str(), &net, &mask, error_buffer) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to fetch newtork address for device %s", nic.c_str()); + } else { + loggers::get_instance().log("pcap_layer::pcap_layer: Device %s Network address: %d", nic.c_str(), net); + } + } + // Open the device in promiscuous mode + _device = pcap_open_live(nic.c_str(), 65536 /*64*1024*/, 1, 100, error_buffer); // TODO Replace hard coded values by pcap_layer:: + if (_device == NULL) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to open device %s", nic.c_str()); + return; + } // else, continue + // Set non-blocking flag for the polling procedure + if (pcap_setnonblock(_device, 1, error_buffer) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set blocking mode: %s", error_buffer); + } + // Retrieve the device file handler + _pcap_h = pcap_get_selectable_fd(_device); + if (_pcap_h == -1) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to get device handler"); + } + + // Setup filter + std::string filter = ""; + std::string mac_src; + it = _params.find(params::mac_src); + if (it != _params.end()) { // Use online capture + mac_src = it->second; + } else { + // Detect MAC address of NIC + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + nic.copy(ifr.ifr_name, sizeof(ifr.ifr_name)); + if (ioctl(_pcap_h, SIOCGIFHWADDR, &ifr) == -1) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to get device MAC address"); + } else { + char buf[13]; + *_bin2hex(buf, sizeof(buf), ifr.ifr_hwaddr.sa_data, 6) = 0; + mac_src = buf; + loggers::get_instance().user("pcap_layer::pcap_layer: local MAC is %s", mac_src.c_str()); + _params[params::mac_src] = mac_src; + } + } + + // Accept ITS broadcasted messages + std::string mac_bc; + it = _params.find(params::mac_bc); + if (it != _params.end() && !it->second.empty()) { + mac_bc = it->second; + } else { + mac_bc = "ffffffffffff"; + } + + if ((mac_bc == mac_src) || mac_src.empty()) { + filter = "ether dst " + mac_bc; + } else { + filter = "( ether dst " + mac_bc + " or ether dst " + mac_src + " )"; + } + + if (!mac_src.empty()) { + // Reject ITS messages sent by this component + filter += " and not ether src " + mac_src; + } + + // Add user defined filter + it = _params.find(params::filter); + if ((it != _params.end()) && !it->second.empty()) { + filter += std::string(" ") + it->second; + } + // Log final PCAP filter + loggers::get_instance().user("pcap_layer::pcap_layer: Filter: %s", filter.c_str()); + + { + struct bpf_program f = {0}; + if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to compile PCAP filter"); + } else { + if (pcap_setfilter(_device, &f) != 0) { + loggers::get_instance().error("pcap_layer::pcap_layer: Failed to set PCAP filter"); + } + } + pcap_freecode(&f); + } + + // Pass the device file handler to the polling procedure + Handler_Add_Fd_Read(_pcap_h); +} // End of ctor + +pcap_layer::~pcap_layer() { + loggers::get_instance().log(">>> pcap_layer::~pcap_layer"); + + if (_device != NULL) { + pcap_close(_device); + } +} // End of dtor + +void pcap_layer::send_data(OCTETSTRING &data, params ¶ms) { + loggers::get_instance().log_msg(">>> pcap_layer::send_data: ", data); + + if (pcap_sendpacket(_device, static_cast(data), data.lengthof()) == -1) { + loggers::get_instance().error("pcap_layer::send_data: Failed to send packet: %s", pcap_geterr(_device)); + } +} + +void pcap_layer::receive_data(OCTETSTRING &data, params ¶ms) { + loggers::get_instance().log(">>> pcap_layer::receive_data: Received %d bytes", data.lengthof()); + loggers::get_instance().log_to_hexa("Packet dump", data); + + // Pass the packet to the upper layers + receive_to_all_layers(data, params); +} + +void pcap_layer::Handle_Fd_Event_Readable(int fd) { + // loggers::get_instance().log(">>> pcap_layer::Handle_Fd_Event_Readable: %d", fd); + + pcap_pkthdr * pkt_header; + const u_char *pkt_data; + int result = pcap_next_ex(_device, &pkt_header, &pkt_data); + if (result == 1) { // Succeed + if (pkt_header->caplen > 14) { // Reject too small packet + loggers::get_instance().log("pcap_layer::Handle_Fd_Event_Readable: %ld - %ld - %ld - %d", pkt_header->ts.tv_sec, pkt_header->ts.tv_usec, + pkt_header->ts.tv_sec * 1000 + static_cast(pkt_header->ts.tv_usec / 1000), pkt_header->len); + // Fill parameters from PCAP layer + params params; + params.insert(std::pair( + params::timestamp, std::to_string(pkt_header->ts.tv_sec * 1000 + static_cast(pkt_header->ts.tv_usec / 1000)))); // Use milliseconds + // Process the packet at this layer + OCTETSTRING os(pkt_header->caplen, pkt_data); + // loggers::get_instance().log_to_hexa("pcap_layer::Handle_Fd_Event_Readable: ", os); + // TODO Case of caplen != len !!! + float duration; + loggers::get_instance().set_start_time(_time_key); + this->receive_data(os, params); // TODO Check execution time for decoding operation + loggers::get_instance().set_stop_time(_time_key, duration); + } + } // else, skip the packet +} + +pcap_layer_factory pcap_layer_factory::_f; + +#endif // !CYGWIN diff --git a/ccsrc/Protocols/Pcap/pcap_linux_layer.hh b/ccsrc/Protocols/Pcap/pcap_linux_layer.hh new file mode 100644 index 0000000..82efc4b --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_linux_layer.hh @@ -0,0 +1,62 @@ +/*! + * \file pcap_layer.hh + * \brief Header file for ITS Pcap port layer. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include + +#include "params.hh" +#include "t_layer.hh" + +class PORT; //! Forward declaration of TITAN class + +/*! + * \class pcap_layer + * \brief This class provides description of ITS PCAP port protocol layer + */ +class pcap_layer : public layer, public PORT { + params _params; //! Layer parameters + pcap_t * _device; //! Device handle + int _pcap_h; //! PCAP instance handle + pcap_dumper_t *_sent_file; //! Write file handle to save sent packet, used in file mode + std::string _time_key; //! \todo + +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the pcap_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + pcap_layer(const std::string &p_type, const std::string ¶m); + /*! + * \brief Default destructor + */ + virtual ~pcap_layer(); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING &data, params ¶ms); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING &data, params &info); + + void Handle_Fd_Event_Readable(int fd); +}; diff --git a/ccsrc/Protocols/Pcap/pcap_offline_layer.cc b/ccsrc/Protocols/Pcap/pcap_offline_layer.cc new file mode 100644 index 0000000..d8afc3a --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_offline_layer.cc @@ -0,0 +1,227 @@ +#if defined(__CYGWIN__) +#define _GNU_SOURCE +#endif +#include +#include +#include +#include +#include + +#include + +#include "pcap_offline_layer_factory.hh" + +#include "loggers.hh" + +#include + +#ifdef __CYGWIN__ +typedef struct { + bpf_int32 tv_sec; /* seconds */ + bpf_int32 tv_usec; /* microseconds */ +} pcap_o_timeval; + +typedef struct pcap_o_pkthdr { + pcap_o_timeval ts; /* time stamp */ + bpf_u_int32 caplen; /* length of portion present */ + bpf_u_int32 len; /* length this packet (off wire) */ +} pcap_o_pkthdr; +#else +typedef struct pcap_pkthdr pcap_o_pkthdr; +typedef struct timeval pcap_o_timeval; +#endif + +pcap_offline_layer::pcap_offline_layer(const std::string &p_type, const std::string ¶m) + : layer(p_type), PORT(p_type.c_str()), _params(), _device(NULL), _running(FALSE), _time_key("pcap_offline_layer::Handle_Fd_Event_Readable") { + loggers::get_instance().log(">>> pcap_offline_layer::pcap_offline_layer: %s, %s", p_type.c_str(), param.c_str()); + params::convert(_params, param); + + _o_params.insert(std::pair(std::string("timestamp"), std::string())); + + char error_buffer[PCAP_ERRBUF_SIZE]; + params::const_iterator it; + + it = _params.find(std::string("realtime")); + _realtime = ((it != _params.end()) && !it->second.empty()); + + it = _params.find(std::string("loop")); + _loop = ((it != _params.end()) && !it->second.empty()); + + it = _params.find(std::string("file")); + if ((it != _params.end()) && !it->second.empty()) { + const std::string &file = it->second; + _device = pcap_open_offline(file.c_str(), error_buffer); + if (_device) { + + // Add user defined filter + it = _params.find(params::filter); + if ((it != _params.end()) && !it->second.empty()) { + const std::string &filter = it->second; + // Log final PCAP filter + loggers::get_instance().user("pcap_offline_layer::pcap_offline_layer: Filter: %s", filter.c_str()); + struct bpf_program f = {0}; + if (pcap_compile(_device, &f, filter.c_str(), 1, PCAP_NETMASK_UNKNOWN) != 0) { + loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to compile PCAP filter"); + } else { + if (pcap_setfilter(_device, &f) != 0) { + loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to set PCAP filter"); + } + } + pcap_freecode(&f); + } + + // create pipe and run thread + if (pipe2(_fd, O_NONBLOCK) == -1) { + loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to create a pipe: %s", ::strerror(errno)); + } + // Pass the pipe handler to the polling procedure + loggers::get_instance().log("pcap_offline_layer::pcap_offline_layer: Call handler with descriptor %d", _fd[0]); + Handler_Add_Fd_Read(_fd[0]); + // Create the offline reader thread + _thread = new std::thread(&pcap_offline_layer::run, (void *)this); + if (_thread == NULL) { + loggers::get_instance().error("pcap_offline_layer::pcap_offline_layer: Failed to start offline thread"); + } + while (_running == FALSE) { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + // Thread was started + loggers::get_instance().log("<<< pcap_offline_layer::pcap_offline_layer"); + } + } +} // End of ctor + +pcap_offline_layer::~pcap_offline_layer() { + loggers::get_instance().log(">>> pcap_offline_layer::~pcap_offline_layer"); + + if (_device != NULL) { + if (_thread != NULL) { + _running = FALSE; + // Wait for the working thread to terminate + _thread->join(); + loggers::get_instance().log("pcap_offline_layer::~pcap_offline_layer: Thread were stops"); + // Cleanup + delete _thread; + close(_fd[0]); + close(_fd[1]); + } + pcap_close(_device); + } +} // End of dtor + +void *pcap_offline_layer::run(void *p_this) { + pcap_offline_layer &p = *static_cast(p_this); + return p.thread(); +} + +static long timeval_diff(const pcap_o_timeval &x, const pcap_o_timeval &y) { + pcap_o_timeval z = y; + /* Perform the carry for the later subtraction by updating y. */ + if (x.tv_usec < y.tv_usec) { + int nsec = (y.tv_usec - x.tv_usec) / 1000000 + 1; + z.tv_usec -= 1000000 * nsec; + z.tv_sec += nsec; + } + if (x.tv_usec - z.tv_usec > 1000000) { + int nsec = (x.tv_usec - z.tv_usec) / 1000000; + z.tv_usec += 1000000 * nsec; + z.tv_sec -= nsec; + } + + return (x.tv_sec - z.tv_sec) * 1000 + ((x.tv_usec - z.tv_usec) / 1000); +} + +void *pcap_offline_layer::thread() { + pcap_o_pkthdr *pkt_header; + pcap_o_pkthdr lh; + const u_char * pkt_data; + unsigned char pkt_count = 0; + + // loggers::get_instance().log(">>> pcap_offline_layer::run"); + + memset(&lh, 0, sizeof(lh)); + + _running = TRUE; + + int delay = 1000; + params::const_iterator it; + it = _params.find(std::string("delay")); + if (it != _params.cend()) { + delay = std::stoi(it->second); + } + + // wait a bit before sending first packet + std::this_thread::sleep_for(std::chrono::milliseconds(delay)); + + while (_running) { // Loop while _running flag is up + // get next frame + int result = pcap_next_ex(_device, (struct pcap_pkthdr **)&pkt_header, &pkt_data); + if (result == 2) { + if (_loop) { + + } else { + _running = FALSE; + return NULL; + } + } + if (_realtime) { + // wait for next packet timestamp + if (lh.ts.tv_sec | lh.ts.tv_usec) { + long diff = timeval_diff(pkt_header->ts, lh.ts); + if (diff > 0) { + loggers::get_instance().log("<<< pcap_offline_layer::run: Wait %d msec", diff); + std::this_thread::sleep_for(std::chrono::milliseconds(diff)); + loggers::get_instance().log("<<< pcap_offline_layer::run: Wait done"); + } + } + } + while (_running && !_resume.try_lock()) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + lh = *pkt_header; +#if 0 + { + char buf[128]; + std::time_t t = pkt_header->ts.tv_sec; + std::tm * pt = std::localtime( &t ); + t = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", pt); + std::sprintf(buf+t, ".%06ld", pkt_header->ts.tv_usec); + _o_params["timestamp"] = std::string(buf); + } +#else + _o_params["timestamp"] = std::to_string(pkt_header->ts.tv_usec); +#endif + _o_data = OCTETSTRING(pkt_header->len, pkt_data); + write(_fd[1], &pkt_count, 1); + pkt_count++; + } + + // loggers::get_instance().log("<<< pcap_offline_layer::run"); + return NULL; +} + +void pcap_offline_layer::send_data(OCTETSTRING &data, params ¶ms) { + loggers::get_instance().log("pcap_offline_layer::send_data: Offline mode, operation was skipped"); +} + +void pcap_offline_layer::receive_data(OCTETSTRING &data, params ¶ms) { + loggers::get_instance().log(">>> pcap_offline_layer::receive_data: Received %d bytes", data.lengthof()); + loggers::get_instance().log_to_hexa("Packet dump", data); + + // Pass the packet to the upper layers + receive_to_all_layers(data, params); +} + +void pcap_offline_layer::Handle_Fd_Event_Readable(int fd) { + // loggers::get_instance().log(">>> pcap_offline_layer::Handle_Fd_Event_Readable: %d", fd); + char c[2]; + float duration; + // Process the packet at this layer + loggers::get_instance().set_start_time(_time_key); + this->receive_data(_o_data, _o_params); + loggers::get_instance().set_stop_time(_time_key, duration); + read(_fd[0], &c, 1); + _resume.unlock(); +} + +pcap_offline_layer_factory pcap_offline_layer_factory::_f; diff --git a/ccsrc/Protocols/Pcap/pcap_offline_layer.hh b/ccsrc/Protocols/Pcap/pcap_offline_layer.hh new file mode 100644 index 0000000..0c1993e --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_offline_layer.hh @@ -0,0 +1,80 @@ +/*! + * \file pcap_offline_layer.hh + * \brief Header file for ITS Offline Pcap port layer. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include + +#include "params.hh" +#include "t_layer.hh" + +#include + +class PORT; //! Forward declaration of TITAN class + +typedef struct pcap pcap_t; + +/*! + * \class pcap_layer + * \brief This class provides description of ITS PCAP port protocol layer + */ +class pcap_offline_layer : public layer, public PORT { + params _params; //! Layer parameters + pcap_t * _device; //! Device handle + std::thread *_thread; //! Thread handle, used to read PCAP file instead of NIC, used in file mode + std::mutex _resume; + bool _running; //! Set to true when the thread is running, used in file mode + bool _realtime; //! Set to true if realtime delay shall be added between packets + bool _loop; //! Set to true if playback shall be looped + int _fd[2]; //! pipe to signal to Titan + + params _o_params; + OCTETSTRING _o_data; + + std::string _time_key; + + static void *run(void *p_this); + +public: + void *thread(void); + +public: //! \publicsection + /*! + * \brief Specialised constructor + * Create a new instance of the pcap_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + pcap_offline_layer(const std::string &p_type, const std::string ¶m); + /*! + * \brief Default destructor + */ + virtual ~pcap_offline_layer(); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING &data, params ¶ms); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING &data, params &info); + + void Handle_Fd_Event_Readable(int fd); +}; diff --git a/ccsrc/Protocols/Pcap/pcap_offline_layer_factory.hh b/ccsrc/Protocols/Pcap/pcap_offline_layer_factory.hh new file mode 100644 index 0000000..23cd1d0 --- /dev/null +++ b/ccsrc/Protocols/Pcap/pcap_offline_layer_factory.hh @@ -0,0 +1,42 @@ +/*! + * \file pcap_offline_layer_factory.hh + * \brief Header file for Pcap layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "pcap_offline_layer.hh" + +/*! + * \class pcap_offline_layer_factory + * \brief This class provides a factory class to create an pcap_offline_layer class instance + */ +class pcap_offline_layer_factory : public layer_factory { + static pcap_offline_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the udp_layer_factory class + * \remark The PCAP layer identifier is PCAP + */ + pcap_offline_layer_factory() { + // register factory + layer_stack_builder::register_layer_factory("PCAP_FILE", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \inline + */ + inline virtual layer *create_layer(const std::string &p_type, const std::string &p_param) { return new pcap_offline_layer(p_type, p_param); }; +}; // End of class pcap_offline_layer_factory diff --git a/ccsrc/Protocols/Tcp/module.mk b/ccsrc/Protocols/Tcp/module.mk new file mode 100644 index 0000000..44d6c56 --- /dev/null +++ b/ccsrc/Protocols/Tcp/module.mk @@ -0,0 +1,3 @@ +sources := tcp_layer.cc +includes := . + diff --git a/ccsrc/Protocols/Tcp/tcp_layer.cc b/ccsrc/Protocols/Tcp/tcp_layer.cc new file mode 100644 index 0000000..d216f9c --- /dev/null +++ b/ccsrc/Protocols/Tcp/tcp_layer.cc @@ -0,0 +1,259 @@ +#include +#include +#include +#include +#include + +#include "tcp_layer_factory.hh" + +#include "loggers.hh" + +tcp_layer::tcp_layer(const std::string & p_type, const std::string & param) : layer(p_type), SSL_Socket(), PORT(p_type.c_str()), _params(), _client_id{-1}, _time_key("tcp_layer::Handle_Fd_Event_Readable"), _reconnect_on_send{false} { + loggers::get_instance().log(">>> tcp_layer::tcp_layer (1): %s, %s", to_string().c_str(), param.c_str()); + // Setup parameters + params::convert(_params, param); + _params.log(); + + init(); } + +tcp_layer::tcp_layer(const std::string & p_type, const params & param) : layer(p_type), SSL_Socket(), PORT(p_type.c_str()), _params(), _client_id{-1}, _time_key("tcp_layer::Handle_Fd_Event_Readable"), _reconnect_on_send{false} { + loggers::get_instance().log(">>> tcp_layer::tcp_layer (2): %s", to_string().c_str()); + // Setup parameters + _params = param; + + init(); +} + +void tcp_layer::init() { + loggers::get_instance().log(">>> tcp_layer::init"); + + set_socket_debugging(false); + params::const_iterator it = _params.find(params::debug); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("debug"), "0")); + } else if (it->second.compare("1") == 0) { + set_socket_debugging(true); + } + it = _params.find(std::string("tcp_fragmented")); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("tcp_fragmented"), "0")); + } + bool server_mode = false; + it = _params.find(params::server_mode); + if (it != _params.cend()) { + server_mode = (1 == std::stoi(it->second)); + } else { + _params.insert(std::pair(std::string("server_mode"), "0")); + } + it = _params.find(params::server); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("server"), "127.0.0.1")); // TODO Try using params::server instead of std::string("server") + } + if (!parameter_set(params::server.c_str(), _params[params::server].c_str())) { + loggers::get_instance().warning("tcp_layer::set_parameter: Unprocessed parameter: %s", params::server.c_str()); + } + bool ssl_mode = false; + it = _params.find(params::use_ssl); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("use_ssl"), "0")); + } else if (it->second.compare("1") == 0) { + _params.insert(std::pair(std::string("use_ssl"), "1")); + ssl_mode = true; + } + set_ssl_use_ssl(ssl_mode); + it = _params.find(params::port); + if (it == _params.cend()) { + if (_params[params::use_ssl].compare("0") == 0) { // Use standard HTTP port + _params.insert(std::pair(std::string("port"), "80")); + } else { // Use standard HTTPS port + _params.insert(std::pair(std::string("port"), "443")); + } + } + if (!parameter_set(remote_port_name(), _params[params::port].c_str())) { + loggers::get_instance().warning("tcp_layer::set_parameter: Unprocessed parameter: %s", params::port.c_str()); + } + it = _params.find(params::local_port); + if (it == _params.cend()) { + if (_params[params::use_ssl].compare("0") == 0) { // Use standard HTTP local_port + _params.insert(std::pair(std::string("local_port"), "80")); + } else { // Use standard HTTPS local_port + _params.insert(std::pair(std::string("local_port"), "443")); + } + } + if (!parameter_set(local_port_name(), _params[params::local_port].c_str())) { + loggers::get_instance().warning("tcp_layer::set_parameter: Unprocessed parameter: %s", params::local_port.c_str()); + } + + parameter_set(use_connection_ASPs_name(), (!server_mode) ? "yes" : "no"); + loggers::get_instance().warning("tcp_layer::set_parameter: Limit to one simultanneous accepted connection (server_backlog == 1"); + parameter_set(server_backlog_name(), "1"); // Limit to one simultanneous accepted connection + loggers::get_instance().log("tcp_layer::init: server_mode=%x", server_mode); + set_server_mode(server_mode); + if (server_mode) { + parameter_set("serverPort", _params[params::local_port].c_str()); + } + if (ssl_mode) { // Add certificate bundle + // Check mutual authentication param + _params.insert(std::pair(std::string("mutual_tls"), "0")); + parameter_set(ssl_verifycertificate_name(), "no"); + it = _params.find(params::mutual_auth); + if (it == _params.cend()) { + if (_params[params::mutual_auth].compare("1") == 0) { // Use mutual authentication + parameter_set(ssl_verifycertificate_name(), "yes"); + _params.insert(std::pair(std::string("mutual_tls"), "1")); + } + } + // Set trusted CA file + it = _params.find(params::mutual_auth); + if (it == _params.cend()) { + parameter_set(ssl_trustedCAlist_file_name(), it->second.c_str()); + _params.insert(std::pair(std::string("mutual_tls"), it->second)); + } else { + parameter_set(ssl_trustedCAlist_file_name(), "/usr/share/ca-certificates/mozilla/Amazon_Root_CA_1.crt"); + } + // Set additional certificates + //parameter_set(ssl_private_key_file_name(), "../certificates/out/privates/e5e11abad8003766e4a7b721afb175a189b5f4cc7046af9b0d8eaebb86f28c40_server_dsa.key.pem"); + //parameter_set(ssl_certificate_file_name(), "../certificates/out/certs/e5e11abad8003766e4a7b721afb175a189b5f4cc7046af9b0d8eaebb86f28c40_server_dsa.cert.pem"); + } + set_ttcn_buffer_usercontrol(false); + set_handle_half_close(true); + + map_user(); + + parameter_set(client_TCP_reconnect_name(), "yes"); + + if (server_mode == 0) { + loggers::get_instance().log("tcp_layer::init: Establish connection: %s/%s", _params[params::server].c_str(), _params[params::port].c_str()); + open_client_connection(_params[params::server].c_str(), _params[params::port].c_str(), NULL, NULL); + } +} + +tcp_layer::~tcp_layer() { + loggers::get_instance().log(">>> tcp_layer::~tcp_layer: %d", _client_id); + if (_client_id != -1) { + remove_client(_client_id); + } + + unmap_user(); +} + +void tcp_layer::Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable, boolean is_error) +{ + loggers::get_instance().log(">>> tcp_layer::Handle_Fd_Event: %d - _client_id: %d", fd, _client_id); + Handle_Socket_Event(fd, is_readable, is_writable, is_error); + log_debug("<<< tcp_layer::Handle_Fd_Event"); +} + +void tcp_layer::Handle_Timeout(double time_since_last_call) +{ + loggers::get_instance().log(">>> tcp_layer::Handle_Timeout: %f", time_since_last_call); + Handle_Timeout_Event(time_since_last_call); + loggers::get_instance().log("<<< tcp_layer::Handle_Timeout"); +} + +void tcp_layer::send_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> tcp_layer::send_data: ", data); + + loggers::get_instance().log("tcp_layer::send_data: SSL mode: %x", get_ssl_use_ssl()); + loggers::get_instance().log("tcp_layer::send_data: server_mode: %s", _params[params::server_mode].c_str()); + loggers::get_instance().log("tcp_layer::send_data: peer_list_get_nr_of_peers: %d", peer_list_get_nr_of_peers()); + + if ((_params[params::server_mode].compare("0") == 0) && (peer_list_get_nr_of_peers() == 0)) { + // Reconnect (e.g. HTTP connection lost + loggers::get_instance().log("tcp_layer::send_data: Re-establish connection: %s/%s", _params[params::server].c_str(), _params[params::port].c_str()); + open_client_connection(_params[params::server].c_str(), _params[params::port].c_str(), NULL, NULL); + } + send_outgoing(static_cast(data), data.lengthof(), _client_id); +} + +void tcp_layer::receive_data(OCTETSTRING& data, params& params) { + loggers::get_instance().log_msg(">>> tcp_layer::receive_data: ", data); + + receive_to_all_layers(data, params); +} + +void tcp_layer::message_incoming(const unsigned char* message_buffer, int length, int client_id) { + loggers::get_instance().log(">>> tcp_layer::message_incoming"); + loggers::get_instance().log_to_hexa("tcp_layer::message_incoming: ", message_buffer, length); + + float duration; + loggers::get_instance().set_start_time(_time_key); + OCTETSTRING data(length, message_buffer); + params params; + params.insert(std::pair( + std::string("timestamp"), + std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()))); + this->receive_data(data, params); // TODO Check execution time for decoding operation + loggers::get_instance().set_stop_time(_time_key, duration); +} + +void tcp_layer::client_connection_opened(int p_client_id) +{ + loggers::get_instance().log(">>> tcp_layer::client_connection_opened - _client_id: %d: %d", p_client_id, _client_id); +} + +bool tcp_layer::add_user_data(int p_client_id) +{ + loggers::get_instance().log(">>> tcp_layer::add_user_data: %d - _client_id: %d", p_client_id, _client_id); + _client_id = p_client_id; + if (_params[params::use_ssl].compare("0") == 0) { + loggers::get_instance().log("tcp_layer::add_user_data: Non secured mode"); + return Abstract_Socket::add_user_data(p_client_id); + } + loggers::get_instance().log("tcp_layer::add_user_data: SSL mode"); + return SSL_Socket::add_user_data(p_client_id); +} + +int tcp_layer::send_message_on_fd(int p_client_id, const unsigned char * message_buffer, int length_of_message) +{ + loggers::get_instance().log(">>> tcp_layer::send_message_on_fd: %d", p_client_id); + + if(get_user_data(p_client_id)) { + loggers::get_instance().log("tcp_layer::send_message_on_fd: SSL mode"); + return SSL_Socket::send_message_on_fd(p_client_id, message_buffer, length_of_message); + } + + loggers::get_instance().log("tcp_layer::send_message_on_fd: Non secured mode"); + return Abstract_Socket::send_message_on_fd(p_client_id, message_buffer, length_of_message); +} + +int tcp_layer::send_message_on_nonblocking_fd(int p_client_id, const unsigned char * message_buffer, int length_of_message) +{ + loggers::get_instance().log(">>> tcp_layer::send_message_on_nonblocking_fd: %d", p_client_id); + + if(get_user_data(p_client_id)) { + loggers::get_instance().log("tcp_layer::send_message_on_nonblocking_fd: SSL mode"); + return SSL_Socket::send_message_on_nonblocking_fd(p_client_id, message_buffer, length_of_message); + } + + loggers::get_instance().log("tcp_layer::send_message_on_nonblocking_fd: Non secured mode"); + return Abstract_Socket::send_message_on_nonblocking_fd(p_client_id, message_buffer, length_of_message); +} + +int tcp_layer::receive_message_on_fd(int p_client_id) +{ + loggers::get_instance().log(">>> tcp_layer::receive_message_on_fd: %d", p_client_id); + + if(get_user_data(p_client_id)) { + // INFO: it is assumed that only SSL_Socket assigns user data to each peer + loggers::get_instance().log("tcp_layer::receive_message_on_fd: SSL mode"); + return SSL_Socket::receive_message_on_fd(p_client_id); + } + + loggers::get_instance().log("tcp_layer::receive_message_on_fd: Non secured mode"); + if (_params[std::string("tcp_fragmented")].compare("1") == 0) { + sleep(5); // FIXME When HTTP paquet is fragmented into several TCP packets, a timer is required. This is a Q&D solution + } + return Abstract_Socket::receive_message_on_fd(p_client_id); +} + +void tcp_layer::peer_disconnected(int p_client_id) +{ + loggers::get_instance().log(">>> tcp_layer::peer_disconnected: %d", p_client_id); + + Abstract_Socket::peer_disconnected(p_client_id); + _client_id = -1; +} + +tcp_layer_factory tcp_layer_factory::_f; + diff --git a/ccsrc/Protocols/Tcp/tcp_layer.hh b/ccsrc/Protocols/Tcp/tcp_layer.hh new file mode 100644 index 0000000..3dbcba7 --- /dev/null +++ b/ccsrc/Protocols/Tcp/tcp_layer.hh @@ -0,0 +1,108 @@ +/*! + * \file tcp_layer.hh + * \brief Header file for ITS TCP socket based protocol port layer. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer.hh" + +using namespace std; // Required for isnan() +#include "Abstract_Socket.hh" + +class PORT; //! Forward declaration of TITAN class + +/*! + * \class tcp_layer + * \brief This class provides description of ITS TCP port protocol layer + */ +class tcp_layer : public layer, public SSL_Socket, public PORT { + params _params; //! Layer parameters + int _client_id; //! Connection identifier + std::string _time_key; //! \todo + bool _reconnect_on_send; //! Set to true if connection shall be done when sending data. Otherwise, connection is established by the \see constructor + +public: //! \publicsection + //tcp_layer(): PORT("TCP") {}; + /*! + * \brief Specialised constructor + * Create a new instance of the tcp_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + tcp_layer(const std::string &p_type, const std::string &p_param); + /*! + * \brief Specialised constructor + * Create a new instance of the tcp_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + tcp_layer(const std::string &p_type, const params &p_param); + /*! + * \brief Default destructor + * \remark If \see _reconnect_on_send is set to false, the disconnection is done by the destructor + */ + virtual ~tcp_layer(); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + * \virtual + */ + virtual void send_data(OCTETSTRING &data, params ¶ms); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING &data, params &info); + + /*! + * \virtual + * \fn void message_incoming(const unsigned char* message_buffer, int length, int client_id = -1); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_buffer The bytes formated data received + * \param[in] p_length The number of bytes received + * \param[in] p_client_id The connection identifier.Default: -1 + */ + virtual void message_incoming(const unsigned char *p_buffer, int p_length, int p_client_id = -1); + +protected: //! \protectedsection + void init(); + + void Add_Fd_Read_Handler(int fd) { Handler_Add_Fd_Read(fd); }; + void Add_Fd_Write_Handler(int fd) { Handler_Add_Fd_Write(fd); }; + void Remove_Fd_Read_Handler(int fd) { Handler_Remove_Fd_Read(fd); }; + void Remove_Fd_Write_Handler(int fd) { Handler_Remove_Fd_Write(fd); }; + void Remove_Fd_All_Handlers(int fd) { Handler_Remove_Fd(fd); }; + void Handler_Uninstall() { Uninstall_Handler(); } + void Timer_Set_Handler(double call_interval, boolean is_timeout = TRUE, boolean call_anyway = TRUE, boolean is_periodic = TRUE) { + Handler_Set_Timer(call_interval, is_timeout, call_anyway, is_periodic); + }; + + const char *remote_address_name() { return params::server.c_str(); }; + const char *remote_port_name() { return params::port.c_str(); }; + const char *socket_debugging_name() { return params::debug.c_str(); }; + const char *ssl_use_ssl_name() { return params::use_ssl.c_str(); }; + + void client_connection_opened(int p_client_id); + bool add_user_data(int p_client_id); + int send_message_on_fd(int p_client_id, const unsigned char *message_buffer, int length_of_message); + int send_message_on_nonblocking_fd(int client_id, const unsigned char *message_buffer, int length_of_message); + int receive_message_on_fd(int p_client_id); + void peer_disconnected(int p_client_id); + +private: //! \privatesection + void Handle_Fd_Event(int fd, boolean is_readable, boolean is_writable, boolean is_error); + void Handle_Timeout(double time_since_last_call); +}; // End of class tcp_layer diff --git a/ccsrc/Protocols/Tcp/tcp_layer_factory.hh b/ccsrc/Protocols/Tcp/tcp_layer_factory.hh new file mode 100644 index 0000000..4fe2009 --- /dev/null +++ b/ccsrc/Protocols/Tcp/tcp_layer_factory.hh @@ -0,0 +1,41 @@ +/*! + * \file tcp_layer_factory.hh + * \brief Header file for ITS TCP socket based protocol layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "tcp_layer.hh" + +/*! + * \class tcp_layer_factory + * \brief This class provides a factory class to create an tcp_layer class instance + */ +class tcp_layer_factory : public layer_factory { + static tcp_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the tcp_layer class + * \remark The TCP socket based layer identifier is TCP + */ + tcp_layer_factory() { + // Register factory + layer_stack_builder::register_layer_factory("TCP", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + */ + inline virtual layer *create_layer(const std::string &p_type, const std::string &p_param) { return new tcp_layer(p_type, p_param); }; +}; // End of class tcp_layer_factory diff --git a/ccsrc/Protocols/UDP/module.mk b/ccsrc/Protocols/UDP/module.mk new file mode 100644 index 0000000..d19e9d9 --- /dev/null +++ b/ccsrc/Protocols/UDP/module.mk @@ -0,0 +1,4 @@ +#sources := udp_layer.cc udp_pcap_layer.cc +sources := udp_layer.cc +includes := . + diff --git a/ccsrc/Protocols/UDP/udp_layer.cc b/ccsrc/Protocols/UDP/udp_layer.cc new file mode 100644 index 0000000..e98c078 --- /dev/null +++ b/ccsrc/Protocols/UDP/udp_layer.cc @@ -0,0 +1,196 @@ +#include +#include +#include + +#include + +#include "loggers.hh" + +#include "udp_layer_factory.hh" + +#include +#include + +udp_layer::udp_layer(const std::string &p_type, const std::string ¶m) + : layer(p_type), PORT(p_type.c_str()), _params(), _saddr{0}, _daddr{0}, _reuse_incoming_source_adddress(false), _fd(-1), + _time_key("udp_layer::Handle_Fd_Event_Readable") { + loggers::get_instance().log(">>> udp_layer::udp_layer (1): %s, %s", to_string().c_str(), param.c_str()); + + // Setup parameters + params::convert(_params, param); + + init(); +} + +udp_layer::udp_layer(const std::string &p_type, const params_its ¶m) + : layer(p_type), PORT(p_type.c_str()), _params(), _saddr{0}, _daddr{0}, _reuse_incoming_source_adddress(false), _fd(-1), + _time_key("udp_layer::Handle_Fd_Event_Readable") { + loggers::get_instance().log(">>> udp_layer::udp_layer (2): %s", to_string().c_str()); + + // Setup parameters + _params = param; + + init(); +} + +void udp_layer::init() { + loggers::get_instance().log(">>> udp_layer::init"); + + params::const_iterator it = _params.find("src_ip"); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("src_ip"), "127.0.0.1")); + } + it = _params.find("src_port"); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("src_port"), "0")); // Dynamic binding requested + } + it = _params.find("dst_ip"); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("dst_ip"), "127.0.0.1")); + } + it = _params.find("dst_port"); + if (it == _params.cend()) { + _params.insert(std::pair(std::string("dst_port"), "12345")); + } + it = _params.find("reuse_incoming_source_adddress"); + if (it != _params.cend()) { + _reuse_incoming_source_adddress = (boolean)(it->second.compare("1") == 0); + } + loggers::get_instance().log("udp_layer::init: _reuse_incoming_source_adddress: %d", _reuse_incoming_source_adddress); + + // Initialize the socket + _saddr.sin_family = AF_INET; + _saddr.sin_addr.s_addr = htonl(INADDR_ANY); + loggers::get_instance().log("udp_layer::init: Port to listen=%d", std::atoi(_params["src_port"].c_str())); + _saddr.sin_port = htons(std::atoi(_params["src_port"].c_str())); + // Create socket + _fd = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (_fd == -1) { + loggers::get_instance().error("udp_layer::init: Failed to create socket"); + } + loggers::get_instance().log("udp_layer::init: socket id: %d", _fd); + int reuse = 1; + if (::setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) { + loggers::get_instance().warning("udp_layer::init: Failed to set SO_REUSEADDR"); + } + // Bind it + /*struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth1"); + if (setsockopt(_fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr.ifr_name, strlen(ifr.ifr_name)) < 0) { + close(); + loggers::get_instance().error("udp_layer::init: Failed to bind socket to %s", ifr.ifr_name); + } + loggers::get_instance().log("udp_layer::init: Bound to device %s", ifr.ifr_name);*/ + if (::bind(_fd, (struct sockaddr *)&_saddr, sizeof(_saddr)) < 0) { + close(); + loggers::get_instance().error("udp_layer::init: Failed to bind socket"); + } + loggers::get_instance().log("udp_layer::init: Bound on port %s", _params["src_port"].c_str()); + // Pass the device file handler to the polling procedure + Handler_Add_Fd_Read(_fd); + + _daddr.sin_family = AF_INET; + _daddr.sin_addr.s_addr = htonl(get_host_id(_params["dst_ip"])); + _daddr.sin_port = htons(std::atoi(_params["dst_port"].c_str())); +} + +udp_layer::~udp_layer() { + loggers::get_instance().log(">>> udp_layer::~udp_layer"); + + close(); +} + +void udp_layer::close() { + loggers::get_instance().log(">>> udp_layer::close: %d", _fd); + + if (_fd != -1) { + ::close(_fd); + _fd = -1; + } +} + +void udp_layer::send_data(OCTETSTRING &data, params_its ¶ms) { + loggers::get_instance().log(">>> udp_layer::send_data: %d", _fd); + loggers::get_instance().log_msg(">>> udp_layer::send_data: ", data); + + int result = ::sendto(_fd, (const char *)static_cast(data), data.lengthof(), 0, (struct sockaddr *)&_daddr, sizeof(_daddr)); + loggers::get_instance().log("udp_layer::send_data: #bytes sent: %d to %s:%d", result, ::inet_ntoa(_daddr.sin_addr), ntohs(_daddr.sin_port)); +} + +void udp_layer::receive_data(OCTETSTRING& p_data, params& p_params) { + loggers::get_instance().log_msg(">>> udp_layer::receive_data: ", p_data); + + receive_to_all_layers(p_data, p_params); +} + +void udp_layer::Handle_Fd_Event_Readable(int fd) { + loggers::get_instance().log(">>> udp_layer::Handle_Fd_Event_Readable: %d", fd); + + unsigned char buffer[3072] = {0}; + struct sockaddr_in from = {0}; + socklen_t len = sizeof(struct sockaddr_in); // Length of sender's address + params_its params; + std::vector acc; + int result = ::recvfrom(fd, buffer, 3072, 0, (struct sockaddr *)&from, &len); + loggers::get_instance().log("udp_layer::Handle_Fd_Event_Readable: src_port = %s:%d, payload length = %d, errno = %d", ::inet_ntoa(from.sin_addr), + ntohs(from.sin_port), result, errno); + while ((result == 3072) && (errno == 0)) { + std::copy((unsigned char *)buffer, (unsigned char *)((unsigned char *)buffer + result), std::back_inserter(acc)); + result = ::recvfrom(fd, buffer, 3072, 0, (struct sockaddr *)&from, &len); + loggers::get_instance().log("udp_layer::Handle_Fd_Event_Readable: src_port = %s:%d, payload length = %d, errno = %d", ::inet_ntoa(from.sin_addr), + ntohs(from.sin_port), result, errno); + } // End of 'while' statement + if (errno < 0) { + loggers::get_instance().warning("udp_layer::Handle_Fd_Event_Readable: Failed to read data, discard them: errno=%d", errno); + return; + } else { + std::copy((unsigned char *)buffer, (unsigned char *)((unsigned char *)buffer + result), std::back_inserter(acc)); + if (_reuse_incoming_source_adddress) { // Reuse the incoming address/port for sending + memcpy((void *)&_daddr, (const void *)&from, sizeof(struct sockaddr_in)); + loggers::get_instance().log("udp_layer::Handle_Fd_Event_Readable: New _daddr: %s:%d", ::inet_ntoa(_daddr.sin_addr), ntohs(_daddr.sin_port)); + } + } + params.insert(std::pair( + std::string("timestamp"), + std::to_string(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()))); + + float duration; + loggers::get_instance().set_start_time(_time_key); + OCTETSTRING os(acc.size(), acc.data()); + receive_data(os, params); // TODO Check execution time for decoding operation + loggers::get_instance().set_stop_time(_time_key, duration); +} + +unsigned long udp_layer::get_host_id(const std::string &p_host_name) { + loggers::get_instance().log(">>> udp_layer::get_host_id"); + + if (p_host_name.empty()) { + loggers::get_instance().warning("udp_layer::get_host_id: Wrong parameter"); + return INADDR_ANY; + } + + unsigned long ip_addr = 0; + if (p_host_name.compare("255.255.255.255") == 0) { + loggers::get_instance().warning("udp_layer::get_host_id: Host ip is 255.255.255.255"); + ip_addr = 0xffffffff; + } else { + in_addr_t addr = ::inet_addr(p_host_name.c_str()); + if (addr != (in_addr_t)-1) { // host name in XX:XX:XX:XX form + ip_addr = addr; + } else { // host name in domain.com form + struct hostent *hptr; + if ((hptr = ::gethostbyname(p_host_name.c_str())) == 0) { + close(); + loggers::get_instance().error("udp_layer::get_host_id: Invalid host name: %s", p_host_name.c_str()); + } + ip_addr = *((unsigned long *)hptr->h_addr_list[0]); + } + } + + loggers::get_instance().log("udp_layer::get_host_id: Host name: %s, Host address: %u", p_host_name.c_str(), ip_addr); + + return htonl(ip_addr); +} + +udp_layer_factory udp_layer_factory::_f; diff --git a/ccsrc/Protocols/UDP/udp_layer.hh b/ccsrc/Protocols/UDP/udp_layer.hh new file mode 100644 index 0000000..6ac61f4 --- /dev/null +++ b/ccsrc/Protocols/UDP/udp_layer.hh @@ -0,0 +1,86 @@ +/*! + * \file udp_layer.hh + * \brief Header file for ITS UDP/IP protocol layer definition. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include +#include + +#include "layer.hh" + +#include "params_its.hh" + +using namespace std; // Required for isnan() +#include "Abstract_Socket.hh" + +//class PORT; //! Forward declaration of TITAN class + +/*! + * \class udp_layer + * \brief This class provides description of ITS UDP/IP protocol layer + */ +class udp_layer : public layer, public PORT { + params_its _params; //! Layer parameters + struct sockaddr_in _saddr; //! Source socket address description + struct sockaddr_in _daddr; //! Destination socket address description + bool _reuse_incoming_source_adddress; + //! This flag must be set to true if the UpperTester/UDP layer must act as an IUT, not as a Test System. Default value: Test System + int _fd; // Socket file descriptor + std::string _time_key; //! \todo + +public: //! \publicsection + //udp_layer(): PORT("UDP") {}; + /*! + * \brief Specialised constructor + * Create a new instance of the udp_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + udp_layer(const std::string &p_type, const std::string &p_param); + /*! + * \brief Specialised constructor + * Create a new instance of the udp_layer class + * \param[in] p_type \todo + * \param[in] p_param \todo + */ + udp_layer(const std::string &p_type, const params_its &p_param); + /*! + * \brief Default destructor + */ + virtual ~udp_layer(); + + /*! + * \virtual + * \fn void send_data(OCTETSTRING& data, params_its& params); + * \brief Send bytes formated data to the lower layers + * \param[in] p_data The data to be sent + * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters + */ + virtual void send_data(OCTETSTRING &data, params_its ¶ms); + /*! + * \virtual + * \fn void receive_data(OCTETSTRING& data, params_its& params); + * \brief Receive bytes formated data from the lower layers + * \param[in] p_data The bytes formated data received + * \param[in] p_params Some lower layers parameters values when data was received + */ + virtual void receive_data(OCTETSTRING& p_data, params& p_params); + + void Handle_Fd_Event_Readable(int fd); + +protected: + void init(); + +private: + unsigned long get_host_id(const std::string &p_host_name); + void close(); +}; // End of class udp_layer diff --git a/ccsrc/Protocols/UDP/udp_layer_factory.hh b/ccsrc/Protocols/UDP/udp_layer_factory.hh new file mode 100644 index 0000000..d08c503 --- /dev/null +++ b/ccsrc/Protocols/UDP/udp_layer_factory.hh @@ -0,0 +1,42 @@ +/*! + * \file udp_layer_factory.hh + * \brief Header file for ITS UDP/IP protocol layer factory. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include "layer_stack_builder.hh" + +#include "udp_layer.hh" + +/*! + * \class udp_layer_factory + * \brief This class provides a factory class to create an udp_layer class instance + */ +class udp_layer_factory : public layer_factory { + static udp_layer_factory _f; //! Reference to the unique instance of this class +public: //! \publicsection + /*! + * \brief Default constructor + * Create a new instance of the udp_layer_factory class + * \remark The UDP/IP layer identifier is UDP + */ + udp_layer_factory() { + // register factory + layer_stack_builder::register_layer_factory("UDP", this); + }; + /*! + * \fn layer* create_layer(const std::string & type, const std::string & param); + * \brief Create the layers stack based on the provided layers stack description + * \param[in] p_type The provided layers stack description + * \param[in] p_params Optional parameters + * \return 0 on success, -1 otherwise + * \inline + */ + inline virtual layer *create_layer(const std::string &p_type, const std::string &p_param) { return new udp_layer(p_type, p_param); }; +}; // End of class udp_layer_factory diff --git a/ccsrc/loggers/loggers.cc b/ccsrc/loggers/loggers.cc new file mode 100644 index 0000000..be5285a --- /dev/null +++ b/ccsrc/loggers/loggers.cc @@ -0,0 +1,4 @@ +#include "loggers.hh" + +std::unique_ptr loggers::_instance = static_cast>(new loggers); + diff --git a/ccsrc/loggers/loggers.hh b/ccsrc/loggers/loggers.hh new file mode 100644 index 0000000..36f7344 --- /dev/null +++ b/ccsrc/loggers/loggers.hh @@ -0,0 +1,273 @@ +/*! + * \file loogers.hh + * \brief Header file for the logger framework. + * \author ETSI STF525 + * \copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * \version 0.1 + */ +#pragma once + +#include +#include +#include +#include +#include + +/** +class Base_Type; +class OCTETSTRING; +class TTCN_Buffer; +class TTCN_Logger; +enum TTCN_Logger::Severity; +extern void TTCN_error(const char *err_msg, ...) __attribute__ ((__format__ (__printf__, 1, 2), __noreturn__)); +extern void TTCN_error_begin(const char *err_msg, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +extern void TTCN_error_end() __attribute__ ((__noreturn__)); +void TTCN_warning(const char *warning_msg, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +extern void TTCN_warning_begin(const char *warning_msg, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +extern void TTCN_warning_end(); +**/ + +using namespace std; // Required for isnan() +#include + +/*! + * \class loggers + * \brief This class provides basic functionalities for an ITS dictionary + * \implements Singleton pattern + * \todo Remove reference to TTCN3.hh + */ +class loggers { +private: //! \privatesection + static std::unique_ptr _instance; //! Smart pointer to the unique instance of the logger framework + std::map + _times; //! Timer used to measure execution time between calls to methods \loggers::set_start_time and \loggers::set_stop_time + + /*! + * \brief Default constructor + * Create a new instance of the loggers class + * \private + */ + explicit loggers() : _times(){}; + + inline void log_time_exec(const char *p_fmt, ...); + +public: //! \publicsection + /*! + * \brief Default destructor + */ + virtual ~loggers(){}; + + /*! + * \fn static loggers& get_instance(); + * \brief Accessor for the unique instance of the logger framework + * \inline + */ + static inline loggers &get_instance() { return *_instance.get(); }; + + /*! + * \fn void log_to_hexa(const char *p_prompt, const TTCN_Buffer& buffer); + * \brief Hexa dump of the \see TTCN_Buffer buffer + * \param[in] p_prompt Label of the log to be produced + * \param[in] buffer The TTCN_Buffer buffer to dump + * \inline + */ + inline void log_to_hexa(const char *p_prompt, const TTCN_Buffer &buffer); + /*! + * \fn void log_to_hexa(const char *p_prompt, const OCTETSTRING& msg); + * \brief Hexa dump of the \see OCTETSTRING buffer + * \param[in] p_prompt Label of the log to be produced + * \param[in] msg The OCTETSTRING buffer to dump + * \inline + */ + inline void log_to_hexa(const char *p_prompt, const OCTETSTRING &msg); + /*! + * \fn void log_to_hexa(const char *p_prompt, const unsigned char* msg, const size_t msg_size); + * \brief Hexa dump of the provided buffer + * \param[in] p_prompt Label of the log to be produced + * \param[in] msg The buffer to dump + * \inline + */ + inline void log_to_hexa(const char *p_prompt, const unsigned char *msg, const size_t msg_size); + /*! + * \fn void log_msg(const char *p_prompt, const Base_Type& p_type); + * \brief Debug log of TITAN data structures + * \param[in] p_prompt Label of the log to be produced + * \param[in] msg The TITAN data structure to log + * \inline + */ + inline void log_msg(const char *p_prompt, const Base_Type &p_type); + /*! + * \fn void log(const char *p_fmt, ...); + * \brief Debug message based on printf-compliant formatting message + * \param[in] p_fmt The printf-compliant format of the message to log + * \param[in] ... The arguments + * \inline + */ + inline void log(const char *p_fmt, ...); + + /*! + * \fn void user_msg(const char *p_prompt, const Base_Type& p_type); + * \brief User message of TITAN data structures + * \param[in] p_prompt Label of the log to be produced + * \param[in] msg The TITAN data structure to log + * \inline + */ + inline void user_msg(const char *p_prompt, const Base_Type &p_type); + /*! + * \fn void user(const char *p_fmt, ...); + * \brief User message based on printf-compliant formatting message + * \param[in] p_fmt The printf-compliant format of the message to log + * \param[in] ... The arguments + * \inline + */ + inline void user(const char *p_fmt, ...); + + /*! + * \fn void user_msg(const char *p_prompt, const Base_Type& p_type); + * \brief Warning message of TITAN data structures + * \param[in] p_prompt Label of the log to be produced + * \param[in] msg The TITAN data structure to log + * \inline + */ + inline void warning_msg(const char *p_prompt, const Base_Type &p_type); + /*! + * \fn void user(const char *p_fmt, ...); + * \brief Warning message based on printf-compliant formatting message + * \param[in] p_fmt The printf-compliant format of the message to log + * \param[in] ... The arguments + * \inline + */ + inline void warning(const char *p_fmt, ...); + + /*! + * \fn void user(const char *p_fmt, ...); + * \brief Error message based on printf-compliant formatting message + * \param[in] p_fmt The printf-compliant format of the message to log + * \param[in] ... The arguments + * \inline + */ + inline void error(const char *p_fmt, ...); + + /*! + * \fn void set_start_time(std::string& p_time_key); + * \brief Start execution time measurement + * \param[in] p_time_key A timer identifier (any string) + * \inline + */ + inline void set_start_time(std::string &p_time_key); + /*! + * \fn void set_stop_time(std::string& p_time_key, float& p_time); + * \brief Stop execution time measurement + * \param[in] p_time_key The timer identifier provided while calling \see loggers::set_start_time method + * \param[out] p_time The execution time measured in milliseconds + * \inline + */ + inline void set_stop_time(std::string &p_time_key, float &p_time); +}; // End of class loggers + +void loggers::log_to_hexa(const char *p_prompt, const TTCN_Buffer &buffer) { + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + buffer.log(); + TTCN_Logger::end_event(); +} + +void loggers::log_to_hexa(const char *p_prompt, const OCTETSTRING &msg) { + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + TTCN_Logger::log_event("Size: %d,\nMsg: ", msg.lengthof()); + + for (int i = 0; i < msg.lengthof(); i++) { + TTCN_Logger::log_event(" %02x", ((const unsigned char *)msg)[i]); + } + TTCN_Logger::log_event("\n"); + TTCN_Logger::end_event(); +} + +void loggers::log_to_hexa(const char *p_prompt, const unsigned char *msg, const size_t msg_size) { + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + for (size_t i = 0; i < msg_size; i++) { + TTCN_Logger::log_event(" %02x", *(msg + i)); + } + TTCN_Logger::log_event("\n"); + TTCN_Logger::end_event(); +} + +void loggers::log_msg(const char *p_prompt, const Base_Type &p_type) { + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + p_type.log(); + TTCN_Logger::end_event(); +} + +void loggers::log(const char *p_fmt, ...) { + TTCN_Logger::begin_event(TTCN_Logger::DEBUG_UNQUALIFIED); + va_list args; + va_start(args, p_fmt); + TTCN_Logger::log_event_va_list(p_fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} + +void loggers::user_msg(const char *p_prompt, const Base_Type &p_type) { + TTCN_Logger::begin_event(TTCN_Logger::USER_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + p_type.log(); + TTCN_Logger::end_event(); +} + +void loggers::user(const char *p_fmt, ...) { + TTCN_Logger::begin_event(TTCN_Logger::USER_UNQUALIFIED); + va_list args; + va_start(args, p_fmt); + TTCN_Logger::log_event_va_list(p_fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} + +void loggers::warning(const char *p_fmt, ...) { + TTCN_Logger::begin_event(TTCN_Logger::WARNING_UNQUALIFIED); + va_list args; + va_start(args, p_fmt); + TTCN_Logger::log_event_va_list(p_fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} + +void loggers::warning_msg(const char *p_prompt, const Base_Type &p_type) { + TTCN_Logger::begin_event(TTCN_Logger::WARNING_UNQUALIFIED); + TTCN_Logger::log_event_str(p_prompt); + p_type.log(); + TTCN_Logger::end_event(); +} + +void loggers::error(const char *p_fmt, ...) { + va_list args; + va_start(args, p_fmt); + TTCN_error(p_fmt, args); + va_end(args); +} + +void loggers::set_start_time(std::string &p_time_key) { _times[p_time_key] = std::clock(); } + +void loggers::set_stop_time(std::string &p_time_key, float &p_time) { + std::map::iterator it = _times.find(p_time_key); + if (it != loggers::_times.end()) { + p_time = (std::clock() - _times[p_time_key]) * 1000.0 / CLOCKS_PER_SEC; // in milliseconds + _times.erase(it); + loggers::get_instance().log_time_exec("%s: Execution duration: %f ms", p_time_key.c_str(), p_time); + } +} + +void loggers::log_time_exec(const char *p_fmt, ...) { + TTCN_Logger::begin_event(TTCN_Logger::EXECUTOR_RUNTIME); + va_list args; + va_start(args, p_fmt); + TTCN_Logger::log_event_va_list(p_fmt, args); + va_end(args); + TTCN_Logger::end_event(); +} diff --git a/ccsrc/loggers/module.mk b/ccsrc/loggers/module.mk new file mode 100644 index 0000000..36b4b2c --- /dev/null +++ b/ccsrc/loggers/module.mk @@ -0,0 +1,3 @@ +sources := loggers.cc +includes := . + diff --git a/ttcn/LibHttp/LibHelpers/module.mk b/ttcn/LibHttp/LibHelpers/module.mk new file mode 100644 index 0000000..e69de29 diff --git a/ttcn/LibHttp/module.mk b/ttcn/LibHttp/module.mk new file mode 100644 index 0000000..d962d1e --- /dev/null +++ b/ttcn/LibHttp/module.mk @@ -0,0 +1,20 @@ +sources := \ + ttcn/LibHttp_BinaryTypes.ttcn \ + ttcn/LibHttp_EncdecDeclarations.ttcn \ + ttcn/LibHttp_Functions.ttcn \ + ttcn/LibHttp_JSONTypes.ttcn \ + ttcn/LibHttp_JsonMessageBodyTypes.ttcn \ + ttcn/LibHttp_JsonTemplates.ttcn \ + ttcn/LibHttp_MessageBodyTypes.ttcn \ + ttcn/LibHttp_Pics.ttcn \ + ttcn/LibHttp_Pixits.ttcn \ + ttcn/LibHttp_Templates.ttcn \ + ttcn/LibHttp_TestSystem.ttcn \ + ttcn/LibHttp_TypesAndValues.ttcn \ + ttcn/LibHttp_XMLTypes.ttcn \ + ttcn/LibHttp_XmlMessageBodyTypes.ttcn \ + ttcn/LibHttp_XmlTemplates.ttcn + +# Please, move and comment the module you need to overwrite tofit your project +# ttcn/LibHttp_BinaryTemplates.ttcn \ +# ttcn/LibHttp_BinaryMessageBodyTypes.ttcn \ diff --git a/ttcn/LibHttp/ttcn/LibHttp_BinaryMessageBodyTypes.ttcn b/ttcn/LibHttp/ttcn/LibHttp_BinaryMessageBodyTypes.ttcn new file mode 100644 index 0000000..6fbc5fc --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_BinaryMessageBodyTypes.ttcn @@ -0,0 +1,28 @@ +/** + * @author ETSI / STF545 + * @version $URL$ + * $ID:$ + * @desc This module provides the custom binary types for ITS HTTP based protocols. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + */ +module LibHttp_BinaryMessageBodyTypes { + + /** + * This file volontary contains a trivial declaration of the type BinaryBodu. + * In accordance with your TTCN-3 module LibHttp_BinaryTypes, you have to change the BinaryBody typing. + */ + // TODO Add here your custom binary import + + type union BinaryBody { + // TODO Add here your custom variants + charstring raw + } with { + variant "" + } + +} with { + variant "" +} // End of LibHttp_BinaryMessageBodyTypes diff --git a/ttcn/LibHttp/ttcn/LibHttp_BinaryTemplates.ttcn b/ttcn/LibHttp/ttcn/LibHttp_BinaryTemplates.ttcn new file mode 100644 index 0000000..8234a87 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_BinaryTemplates.ttcn @@ -0,0 +1,18 @@ +/** + * @author ETSI / STF545 + * @version $URL$ + * $ID:$ + * @desc This module provides the custom templates for ITS HTTP based protocols. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + */ +module LibHttp_BinaryTemplates { + + // LibHttp + import from LibHttp_BinaryMessageBodyTypes all; + + // TODO Add here your custom binary import + +} // End of module LibHttp_BinaryTemplates diff --git a/ttcn/LibHttp/ttcn/LibHttp_BinaryTypes.ttcn b/ttcn/LibHttp/ttcn/LibHttp_BinaryTypes.ttcn new file mode 100644 index 0000000..2606857 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_BinaryTypes.ttcn @@ -0,0 +1,9 @@ +module LibHttp_BinaryTypes { // FIXME To be removed + + /** + * This file is volontary empry. You have to declare all XSD files required by your project + * In addition, the TTCN-3 module LibHttp_XmlMessageBodyTypes have to be updated too. + */ + // TODO Add here your custom binary import + +} // End of module LibHttp_BinaryTypes diff --git a/ttcn/LibHttp/ttcn/LibHttp_EncdecDeclarations.ttcn b/ttcn/LibHttp/ttcn/LibHttp_EncdecDeclarations.ttcn new file mode 100644 index 0000000..e7eb077 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_EncdecDeclarations.ttcn @@ -0,0 +1,11 @@ +module LibHttp_EncdecDeclarations { + // LibHttp + import from LibHttp_TypesAndValues all; + + external function fx_enc_http_message (HttpMessage p) return bitstring + with {extension "prototype(convert) encode(HttpCodec)"} + + external function fx_dec_http_message (inout bitstring b, out HttpMessage p) return integer + with {extension "prototype(sliding) decode(HttpCodec)"} + +} // End of module LibHttp_EncdecDeclarations diff --git a/ttcn/LibHttp/ttcn/LibHttp_Functions.ttcn b/ttcn/LibHttp/ttcn/LibHttp_Functions.ttcn new file mode 100644 index 0000000..3a4e457 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_Functions.ttcn @@ -0,0 +1,326 @@ +module LibHttp_Functions { + + // LibCommon + import from LibCommon_Time all; + import from LibCommon_VerdictControl all; + import from LibCommon_Sync all; + + // LibHttp + import from LibHttp_TypesAndValues all; + import from LibHttp_Templates all; + import from LibHttp_Pics all; + import from LibHttp_Pixits all; + import from LibHttp_TestSystem all; + + group preambles { + + /** + * @desc Setup HTTP pprotocol port + */ + function f_cf_01_http_up() runs on HttpComponent { + + // Map ports + map(self:httpPort, system:httpPort); + + // Connect + f_connect4SelfOrClientSync(); + + activate(a_cf_01_http_down()); + activate(a_default_requests()); + activate(a_default_responses()); + + } // End of function f_cf_01_http_up + + /** + * @desc Setup HTTP pprotocol port + */ + function f_cf_01_http_notif_up() runs on HttpComponent { + + // Map ports + map(self:httpPort, system:httpPort); + map(self:httpPort_notif, system:httpPort_notif); + + // Connect + f_connect4SelfOrClientSync(); + + activate(a_cf_01_http_notif_down()); + activate(a_default_requests()); + activate(a_default_responses()); + + } // End of function f_cf_01_http_notif_up + + } // End of group preambles + + group postambles { + + /** + * @desc Shutdown HTTP pprotocol port + */ + function f_cf_01_http_down() runs on HttpComponent { + + // Unmap ports + unmap(self:httpPort, system:httpPort); + + // Disconnect ports + f_disconnect4SelfOrClientSync(); + + deactivate; + } // End of function f_cf_01_http_down + + /** + * @desc Shutdown HTTP pprotocol port + */ + function f_cf_01_http_notif_down() runs on HttpComponent { + + // Unmap ports + unmap(self:httpPort, system:httpPort); + unmap(self:httpPort_notif, system:httpPort_notif); + + // Disconnect ports + f_disconnect4SelfOrClientSync(); + + deactivate; + } // End of function f_cf_01_http_notif_down + + /** + * @desc Default handling cf01 de-initialisation. + */ + altstep a_cf_01_http_down() runs on HttpComponent { + [] a_shutdown() { + f_cf_01_http_down(); + log("*** a_cf_01_http_down: INFO: TEST COMPONENT NOW STOPPING ITSELF! ***"); + stop; + } + } // End of altstep a_cf_01_http_down + + /** + * @desc Default handling cf01 de-initialisation. + */ + altstep a_cf_01_http_notif_down() runs on HttpComponent { + [] a_shutdown() { + f_cf_01_http_notif_down(); + log("*** a_cf_01_http_notif_down: INFO: TEST COMPONENT NOW STOPPING ITSELF! ***"); + stop; + } + } // End of altstep a_cf_01_http_notif_down + + } // End of group postambles + + group http_headers { + + function f_init_default_headers_list( + in charstring p_header_content_type := PICS_HEADER_CONTENT_TYPE, + in charstring p_header_content_text := "", + out Headers p_headers + ) { + var integer v_i := 0; + + p_headers[v_i] := { c_header_host, { PICS_HEADER_HOST } }; + v_i := v_i + 1; + p_headers[v_i] := { c_header_content_type, { p_header_content_type } }; + v_i := v_i + 1; + if (p_header_content_text != "") { + p_headers[v_i] := { c_header_content_text, { p_header_content_text } }; + v_i := v_i + 1; + } + p_headers[v_i] := { c_header_content_length, { "0" } }; + v_i := v_i + 1; + p_headers[v_i] := { c_header_connection, { "keep-alive" } }; + v_i := v_i + 1; + p_headers[v_i] := { c_header_pragma, { "no-cache" } }; + v_i := v_i + 1; + p_headers[v_i] := { c_header_cache_control, { "no-cache" } }; + v_i := v_i + 1; + if (PICS_USE_TOKEN_HEADER) { + p_headers[v_i] := { c_header_authorization, { PICS_TOKEN_HEADER } }; // aladdin:opensesame + v_i := v_i + 1; + } + //p_headers[v_i] := { c_header_accept, { "application/x-its-response" } }; + if (lengthof(PX_ADDITIONAL_HTTP_HEADERS_KEYS) != 0) { + f_set_headers_list(PX_ADDITIONAL_HTTP_HEADERS_KEYS, PX_ADDITIONAL_HTTP_HEADERS_VALUES, p_headers); + } + } // End of function f_init_default_headers_list + + function f_set_headers_list( + in charstring_list p_headers_to_set, + in charstring_list p_headers_value, + inout Headers p_headers + ) { + // Sanity checks + if (lengthof(p_headers_to_set) == 0) { + return; + } else if (lengthof(p_headers) == 0) { + return; + } + + for (var integer v_idx := 0; v_idx < lengthof(p_headers_to_set); v_idx := v_idx + 1) { + var integer v_jdx; + for (v_jdx := 0; v_jdx < lengthof(p_headers); v_jdx := v_jdx + 1) { + if (p_headers[v_jdx].header_name == p_headers_to_set[v_idx]) { + p_headers[v_jdx].header_value := { p_headers_value[v_idx] }; // NOTE Codec won't encode it + break; + } + } // End of 'for' statement + if (v_jdx == lengthof(p_headers)) { + p_headers[v_jdx].header_name := p_headers_to_set[v_idx]; + p_headers[v_jdx].header_value := { p_headers_value[v_idx] }; + } + } // End of 'for' statement + } // End of function f_set_headers_list + + function f_remove_headers_list( + in charstring_list p_headers_to_remove, + inout Headers p_headers + ) { + // Sanity checks + if (lengthof(p_headers_to_remove) == 0) { + return; + } else if (lengthof(p_headers) == 0) { + return; + } + + for (var integer v_idx := 0; v_idx < lengthof(p_headers_to_remove); v_idx := v_idx + 1) { + for (var integer v_jdx := 0; v_jdx < lengthof(p_headers); v_jdx := v_jdx + 1) { + if (p_headers[v_jdx].header_name == p_headers_to_remove[v_idx]) { + p_headers[v_jdx].header_value := omit; // NOTE Codec won't encode it + break; + } + } // End of 'for' statement + } // End of 'for' statement + } // End of function f_remove_headers_list + + function f_get_header( + in Headers p_headers, + in charstring p_header_name := c_header_content_text, + out charstring_list p_header_value + ) { + // Sanity checks + if (lengthof(p_header_name) == 0) { + return; + } else if (lengthof(p_headers) == 0) { + return; + } + + for (var integer v_jdx := 0; v_jdx < lengthof(p_headers); v_jdx := v_jdx + 1) { + if (p_headers[v_jdx].header_name == p_header_name) { + p_header_value := p_headers[v_jdx].header_value; // NOTE Codec won't encode it + break; + } + } // End of 'for' statement + } // End of function f_get_header + + /** + * @desc Check HTTP response headers + * @param p_headers The HTTP headers + * @param p_value the expected va;ue + * @return true on success, false otherwise + */ + function f_check_headers( + in Headers p_headers, + in charstring p_header_name := "Location", + in template (present) charstring p_value := ? + ) return boolean { + // Local variables + var boolean v_header_matched := false; + + for (var integer v_idx := 0; v_idx < lengthof(p_headers); v_idx := v_idx + 1) { + if (p_headers[v_idx].header_name == p_header_name) { + if (match(p_headers[v_idx].header_value[0], p_value) == true) { + v_header_matched := true; + } // else, nothing to do + break; + } + } // End of 'for' statement + + return v_header_matched; + } // End of function f_check_headers + + } // End of group http_headers + + group altsteps { + + altstep a_default_requests() runs on HttpComponent { + [] httpPort.receive(mw_http_request) { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Server error: Receive request instead of response on httpPort ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + [] httpPort_notif.receive(mw_http_request) { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Server error: Receive unsollicited request on httpPort_notif ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + } // End of altstep a_default_requests + + altstep a_default_responses() runs on HttpComponent { + var HttpMessage v_response; + + [] httpPort.receive( + mw_http_response( + mw_http_response_ok( + mw_http_message_body_xml + ))) { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Unexpected XML response ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + [] httpPort.receive( + mw_http_response( + mw_http_response_ok( + mw_http_message_body_json + ))) { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Unexpected JSON response ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + [] httpPort.receive( + mw_http_response( + mw_http_response_ok( + mw_http_message_body_binary + ))) { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Unexpected binary response ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + [PICS_USE_TOKEN_HEADER == false] httpPort.receive( + mw_http_response( + mw_http_response_401_unauthorized + )) -> value v_response { + tc_ac.stop; + log("*** " & testcasename() & ": PASS: Received HTTP error message in case of non authenticated HTTP request ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_success); + } + [] httpPort.receive( + mw_http_response( + mw_http_response_ko + )) -> value v_response { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Server error: " & int2str(v_response.response.statuscode) & "/" & v_response.response.statustext & " ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + // Do not repeat! + } + [] httpPort.receive(mw_http_response) -> value v_response { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Server error: " & int2str(v_response.response.statuscode) & "/" & v_response.response.statustext & " ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + [] httpPort.receive { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Server error: Receive unsollicited message on httpPort ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + [] httpPort_notif.receive(mw_http_response) -> value v_response { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Server error: " & int2str(v_response.response.statuscode) & "/" & v_response.response.statustext & " on httpPort_notif ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + [] httpPort_notif.receive { + tc_ac.stop; + log("*** " & testcasename() & ": FAIL: Server error: Receive unsollicited message on httpPort_notif ***"); + f_selfOrClientSyncAndVerdictTestBody(c_tbDone, e_error); + } + } // End of altstep a_default_responses + + } // end of group altsteps + +} // End of module LibHttp_Functions diff --git a/ttcn/LibHttp/ttcn/LibHttp_JSONTypes.ttcn b/ttcn/LibHttp/ttcn/LibHttp_JSONTypes.ttcn new file mode 100644 index 0000000..e4a7ec3 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_JSONTypes.ttcn @@ -0,0 +1,9 @@ +module LibHttp_JSONTypes { // FIXME To be removed + + /** + * This file is volontary empry. You have to declare all XSD files required by your project + * In addition, the TTCN-3 module LibHttp_XmlMessageBodyTypes have to be updated too. + */ + // TODO Add here your custom RFCs import + +} // End of module LibHttp_JSONTypes diff --git a/ttcn/LibHttp/ttcn/LibHttp_JsonMessageBodyTypes.ttcn b/ttcn/LibHttp/ttcn/LibHttp_JsonMessageBodyTypes.ttcn new file mode 100644 index 0000000..f945e12 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_JsonMessageBodyTypes.ttcn @@ -0,0 +1,18 @@ +module LibHttp_JsonMessageBodyTypes { + + /** + * This file volontary contains a trivial declaration of the type JsonBody. + * In accordance with your TTCN-3 module LibHttp_JSONTypes, you have to change the JsonBody typing. + */ + // TODO Add here your custom RFCs import + + type union JsonBody { + // TODO Add here your custom variants + universal charstring raw + } with { + variant "" + } + +} with { + variant "" +} // End of module LibHttp_JsonMessageBodyTypes diff --git a/ttcn/LibHttp/ttcn/LibHttp_JsonTemplates.ttcn b/ttcn/LibHttp/ttcn/LibHttp_JsonTemplates.ttcn new file mode 100644 index 0000000..a7aa7f0 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_JsonTemplates.ttcn @@ -0,0 +1,31 @@ +/** + * @author ETSI / STF569 + * @version $URL$ + * $ID:$ + * @desc This module provides the custom templates for ITS HTTP based protocols. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + */ +module LibHttp_JsonTemplates { + + // TODO Add here your custom RFCs import + + // LibHttp + import from LibHttp_JsonMessageBodyTypes all; + import from LibHttp_XMLTypes all; + + template (value) JsonBody m_json_body_raw( + in template (value) charstring p_raw + ) := { + raw := p_raw + } // End of template m_json_body_raw + + template (present) JsonBody mw_json_body_raw( + template (present) charstring p_raw := ? + ) := { + raw := p_raw + } // End of template mw_json_body_raw + +} // End of module LibHttp_JsonTemplates diff --git a/ttcn/LibHttp/ttcn/LibHttp_MessageBodyTypes.ttcn b/ttcn/LibHttp/ttcn/LibHttp_MessageBodyTypes.ttcn new file mode 100644 index 0000000..ed1d549 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_MessageBodyTypes.ttcn @@ -0,0 +1,24 @@ +module LibHttp_MessageBodyTypes { + + // LibHttp + import from LibHttp_XmlMessageBodyTypes all; + import from LibHttp_JsonMessageBodyTypes all; + import from LibHttp_BinaryMessageBodyTypes all; + + type charstring HtmlBody; + + type charstring TextBody; + + type union HttpMessageBody { + BinaryBody binary_body, + HtmlBody html_body, + XmlBody xml_body, + JsonBody json_body, + TextBody text_body + } with { + variant "" + } // End of type HttpMessageBody + +} with { + encode "HttpCodec" +}// End of module LibHttp_MessageBodyTypes diff --git a/ttcn/LibHttp/ttcn/LibHttp_Pics.ttcn b/ttcn/LibHttp/ttcn/LibHttp_Pics.ttcn new file mode 100644 index 0000000..c4c7b8a --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_Pics.ttcn @@ -0,0 +1,36 @@ +module LibHttp_Pics { + + /** + * @desc HTTP major version + */ + modulepar integer PICS_HTTP_VERSION_MAJOR := 1; + + /** + * @desc HTTP minor version + */ + modulepar integer PICS_HTTP_VERSION_MINOR := 1; + + /** + * @desc + */ + modulepar charstring PICS_HEADER_HOST := "www.lisp.com"; + + /** + * @desc + */ + modulepar charstring PICS_HEADER_CONTENT_TYPE := "application/x-its-request"; + modulepar charstring PICS_HEADER_CTL_CONTENT_TYPE := "application/x-its-ctl"; + modulepar charstring PICS_HEADER_CRL_CONTENT_TYPE := "application/x-its-crl"; + + /** + * @desc Set to false in TOKEN header shall not be used + */ + modulepar boolean PICS_USE_TOKEN_HEADER := true; + + /** + * @desc HTTP TOKEN value + * "YWxhZGRpbjpvcGVuc2VzYW1l==" is the base64 encoding of the login:password "aladdin:opensesame" + */ + modulepar charstring PICS_TOKEN_HEADER := "Basic " & "YWxhZGRpbjpvcGVuc2VzYW1l==" ; // aladdin:opensesame + +} // End of module LibHttp_Pics diff --git a/ttcn/LibHttp/ttcn/LibHttp_Pixits.ttcn b/ttcn/LibHttp/ttcn/LibHttp_Pixits.ttcn new file mode 100644 index 0000000..4ef36c4 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_Pixits.ttcn @@ -0,0 +1,9 @@ +module LibHttp_Pixits { + + import from LibHttp_TypesAndValues all; + + modulepar charstring_list PX_ADDITIONAL_HTTP_HEADERS_KEYS := { }; + + modulepar charstring_list PX_ADDITIONAL_HTTP_HEADERS_VALUES := { }; + +} // End of module LibHttp_Pixits diff --git a/ttcn/LibHttp/ttcn/LibHttp_Templates.ttcn b/ttcn/LibHttp/ttcn/LibHttp_Templates.ttcn new file mode 100644 index 0000000..4b28a91 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_Templates.ttcn @@ -0,0 +1,406 @@ +/** + * @author ETSI / STF549 + * @version $URL$ + * $ID:$ + * @desc This module provides the templates for ITS HTTP based protocols. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * @see ETSI TS 103 478 + */ +module LibHttp_Templates { + + // LibHttp + import from LibHttp_TypesAndValues all; + import from LibHttp_MessageBodyTypes all; + import from LibHttp_XmlMessageBodyTypes all; + import from LibHttp_JsonMessageBodyTypes all; + import from LibHttp_BinaryMessageBodyTypes all; + + group http_messages { + + template (value) HttpMessage m_http_request( + in template (value) Request p_request + ) := { + request := p_request + } // End of template m_http_request + + template (present) HttpMessage mw_http_request( + template (present) Request p_request := ? + ) := { + request := p_request + } // End of template mw_http_request + + template (value) HttpMessage m_http_response( + in template (value) Response p_response + ) := { + response := p_response + } // End of template m_http_response + + template (present) HttpMessage mw_http_response( + template (present) Response p_response := ? + ) := { + response := p_response + } // End of template mw_http_response + + } // End of group http_messages + + group http_headers { + + template (value) Header m_header_line( + in template (value) charstring p_header_name, + in template (value) charstring_list p_header_value + ) := { + header_name := p_header_name, + header_value := p_header_value + } // End of template m_header_line + + } // End of group http_headers + + group http_requests { + + template (omit) Request m_http_request_get( + in charstring p_uri, + in template (value) Headers p_headers, + in template (omit) HttpMessageBody p_body := omit + ) := { + method := "GET", + uri := p_uri, + version_major := c_http_version_major, + version_minor := c_http_version_minor, + header := p_headers, + body := p_body + } // End of template m_http_request_get + + template Request mw_http_request_get( + template (present) charstring p_uri := ?, + template (present) Headers p_headers := ?, + template HttpMessageBody p_body := * + ) := { + method := "GET", + uri := p_uri, + version_major := c_http_version_major, + version_minor := c_http_version_minor, + header := p_headers, + body := p_body + } // End of template mw_http_request_get + + template (omit) Request m_http_request_post( + in charstring p_uri, + in template (value) Headers p_headers, + in template (omit) HttpMessageBody p_body := omit + ) modifies m_http_request_get := { + method := "POST" + } // End of template m_http_request_post + + template Request mw_http_request_post( + template (present) charstring p_uri := ?, + template (present) Headers p_headers := ?, + template HttpMessageBody p_body := * + ) modifies mw_http_request_get := { + method := "POST" + } // End of template mw_http_request_post + + template (omit) Request m_http_request_patch( + in charstring p_uri, + in template (value) Headers p_headers, + in template (omit) HttpMessageBody p_body := omit + ) modifies m_http_request_get := { + method := "PATCH" + } // End of template m_http_request_patch + + template Request mw_http_request_patch( + template (present) charstring p_uri := ?, + template (present) Headers p_headers := ?, + template HttpMessageBody p_body := * + ) modifies mw_http_request_get := { + method := "PATCH" + } // End of template mw_http_request_patch + + template (omit) Request m_http_request_put( + in charstring p_uri, + in template (value) Headers p_headers, + in template (omit) HttpMessageBody p_body := omit + ) modifies m_http_request_get := { + method := "PUT" + } // End of template m_http_request_put + + template Request mw_http_request_put( + template (present) charstring p_uri := ?, + template (present) Headers p_headers := ?, + template HttpMessageBody p_body := * + ) modifies mw_http_request_get := { + method := "PUT" + } // End of template mw_http_request_put + + template (omit) Request m_http_request_delete( + in charstring p_uri, + in template (value) Headers p_headers, + in template (omit) HttpMessageBody p_body := omit + ) modifies m_http_request_get := { + method := "DELETE" + } // End of template m_http_request_delete + + template Request mw_http_request_delete( + template (present) charstring p_uri := ?, + template (present) Headers p_headers := ?, + template HttpMessageBody p_body := * + ) modifies mw_http_request_get := { + method := "DELETE" + } // End of template mw_http_request_post + + } // End of group http_requests + + group http_responses { + + template (value) Response m_http_response_ok( + in template (value) HttpMessageBody p_body, + in template (value) Headers p_header + ) := { + version_major := 1, + version_minor := 1, + statuscode := 200, + statustext := "OK", + header := p_header, + body := p_body + } // End of template m_http_response_ok + + template (value) Response m_http_response_ok_no_body( + in template (value) Headers p_header + ) := { + version_major := 1, + version_minor := 1, + statuscode := 200, + statustext := "OK", + header := p_header, + body := omit + } // End of template m_http_response_ok_no_body + + template (present) Response mw_http_response_ok_no_body( + template (present) Headers p_header := ? + ) := { + version_major := 1, + version_minor := 1, + statuscode := 200, + statustext := "OK", + header := p_header, + body := omit + } // End of template mw_http_response_ok_no_body + + template (value) Response m_http_response_204_no_content( + in template (value) Headers p_header + ) modifies m_http_response_ok_no_body := { + statuscode := 204, + statustext := "No Content" + } // End of template m_http_response_204_no_content + + template (present) Response mw_http_response_ok( + template (present) HttpMessageBody p_body := ?, + template (present) Headers p_header := ? + ) := { + version_major := 1, + version_minor := 1, + statuscode := 200, + statustext := ?, + header := p_header, + body := p_body + } // End of template mw_http_response_ok + + template (present) Response mw_http_response_201_created( + template (present) HttpMessageBody p_body := ?, + template (present) Headers p_header := ? + ) modifies mw_http_response_ok := { + statuscode := 201, + statustext := "Created" + } // End of template mw_http_response_201_created + + template (present) Response mw_http_response_202_accepted( + template (present) HttpMessageBody p_body := ?, + template (present) Headers p_header := ? + ) modifies mw_http_response_ok := { + statuscode := 202, + statustext := "Accepted" + } // End of template mw_http_response_202_accepted + + template (present) Response mw_http_response_204_no_content( + template (present) Headers p_header := ? + ) modifies mw_http_response_ok_no_body := { + statuscode := 204, + statustext := "No Content" + } // End of template mw_http_response_204_no_content + + template (value) Response m_http_response_ko( + in template (value) HttpMessageBody p_body, + in template (value) Headers p_header, + in template (value) integer p_statuscode := 404, + in template (value) charstring p_statustext := "Not found" + ) := { + version_major := 1, + version_minor := 1, + statuscode := p_statuscode, + statustext := p_statustext, + header := p_header, + body := p_body + } // End of template m_http_response_ko + + template (value) Response m_http_response_ko_no_body( + in template (value) Headers p_header, + in template (value) integer p_statuscode := 404, + in template (value) charstring p_statustext := "Not found" + ) := { + version_major := 1, + version_minor := 1, + statuscode := p_statuscode, + statustext := p_statustext, + header := p_header, + body := omit + } // End of template m_http_response_ko_no_body + + template (value) Response m_http_response_500_internal_error( + in template (value) Headers p_header, + in template (value) integer p_statuscode := 500, + in template (value) charstring p_statustext := "Internal Error" + ) modifies m_http_response_ko_no_body := { + } // End of template m_http_response_ko + + template (present) Response mw_http_response_ko_no_body( + template (present) Headers p_header := ?, + template (present) integer p_statuscode := 404, + template (present) charstring p_statustext := "Not found" + ) := { + version_major := 1, + version_minor := 1, + statuscode := p_statuscode, + statustext := p_statustext, + header := p_header, + body := omit + } // End of template mw_http_response_ko_no_body + + template Response mw_http_response_ko( + template HttpMessageBody p_body := *, + template (present) Headers p_header := ? + ) := { + version_major := 1, + version_minor := 1, + statuscode := complement(200), + statustext := ?, + header := p_header, + body := p_body + } // End of template mw_http_response_ko + + template (value) Response m_http_response_400_bad_request( + in template (value) HttpMessageBody p_body, + in template (value) Headers p_header, + in template (value) integer p_statuscode := 400, + in template (value) charstring p_statustext := "Bad Request" + ) modifies m_http_response_ko := { + } // End of template m_http_response_400_bad_request + + template Response mw_http_response_400_bad_request( + template HttpMessageBody p_body := *, + template (present) Headers p_header := ? + ) modifies mw_http_response_ko := { + statuscode := 400, + statustext := "Bad Request" + } // End of template mw_http_response_400_bad_request + + template Response mw_http_response_401_unauthorized( + template HttpMessageBody p_body := *, + template (present) Headers p_header := ? + ) modifies mw_http_response_ko := { + statuscode := 401, + statustext := "Unauthorized" + } // End of template mw_http_response_401_unauthorized + + template Response mw_http_response_403_forbidden( + template HttpMessageBody p_body := *, + template (present) Headers p_header := ? + ) modifies mw_http_response_ko := { + statuscode := 403, + statustext := "Forbidden" + } // End of template mw_http_response_403_forbidden + + template Response mw_http_response_404_not_found( + template HttpMessageBody p_body := *, + template (present) Headers p_header := ? + ) modifies mw_http_response_ko := { + statuscode := 404, + statustext := "Not Found" + } // End of template mw_http_response_404_not_found + + template Response mw_http_response_412_precondition_failed( + template HttpMessageBody p_body := *, + template (present) Headers p_header := ? + ) modifies mw_http_response_ko := { + statuscode := 412, + statustext := "Precondition Failed" + } // End of template mw_http_response_412_not_found + + } // End of group http_responses + + group http_html_body { + + template (value) HttpMessageBody m_http_message_body_html( + in template (value) HtmlBody p_html_body + ) := { + html_body := p_html_body + } // End of template m_http_message_body_html + + template (present) HttpMessageBody mw_http_message_body_html( + template (present) HtmlBody p_html_body := ? + ) := { + html_body := p_html_body + } // End of template mw_http_message_body_html + + } // End of group http_html_body + + group http_xml_body { + + template (value) HttpMessageBody m_http_message_body_xml( + in template (value) XmlBody p_xml_body + ) := { + xml_body := p_xml_body + } // End of template m_http_message_body_xml + + template (present) HttpMessageBody mw_http_message_body_xml( + template (present) XmlBody p_xml_body := ? + ) := { + xml_body := p_xml_body + } // End of template mw_http_message_body_xml + + } // End of group http_xml_body + + group http_json_body { + + template (value) HttpMessageBody m_http_message_body_json( + in template (value) JsonBody p_json_body + ) := { + json_body := p_json_body + } // End of template m_http_message_body_json + + template (present) HttpMessageBody mw_http_message_body_json( + template (present) JsonBody p_json_body := ? + ) := { + json_body := p_json_body + } // End of template mw_http_message_body_json + + } // End of group http_json_body + + group http_binary_body { + + template (value) HttpMessageBody m_http_message_body_binary( + in template (value) BinaryBody p_binary_body + ) := { + binary_body := p_binary_body + } // End of template m_http_message_body_binary + + template (present) HttpMessageBody mw_http_message_body_binary( + template (present) BinaryBody p_binary_body := ? + ) := { + binary_body := p_binary_body + } // End of template mw_http_message_body_binary + + } // End of group http_binary_body + +} // End of module LibHttp_Templates diff --git a/ttcn/LibHttp/ttcn/LibHttp_TestSystem.ttcn b/ttcn/LibHttp/ttcn/LibHttp_TestSystem.ttcn new file mode 100644 index 0000000..767bc83 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_TestSystem.ttcn @@ -0,0 +1,40 @@ +/** + * @author ETSI / STF545 + * @version $URL$ + * $ID:$ + * @desc This module provides the test system used by ITS HTTP based protocols. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * @see ETSI TS 103 478 + */ +module LibHttp_TestSystem { + + // LibCommon + import from LibCommon_Sync all; + import from LibCommon_Time all; + + // LibHttp + import from LibHttp_TypesAndValues all; + + type port HttpPort message { + inout HttpMessage; + } + + type component HttpComponent extends SelfSyncComp { // FIXME To be rename into HttpTest + port HttpPort httpPort; + port HttpPort httpPort_notif; + // timers + timer tc_wait := PX_TWAIT; + timer tc_ac := PX_TAC; + timer tc_noac := PX_TNOAC; + + } // End of component HttpComponent + + type component HttpTestAdapter { // FIXME To be rename into HttpTestSystem + port HttpPort httpPort; + port HttpPort httpPort_notif; + } // End of component TestAdapter + +} // End of module LibHttp_TestSystem diff --git a/ttcn/LibHttp/ttcn/LibHttp_TypesAndValues.ttcn b/ttcn/LibHttp/ttcn/LibHttp_TypesAndValues.ttcn new file mode 100644 index 0000000..8f88f68 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_TypesAndValues.ttcn @@ -0,0 +1,73 @@ +/** + * @author ETSI / STF545 + * @version $URL$ + * $ID:$ + * @desc This module provides the types and values used by ITS HTTP based protocols. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * @see ETSI TS 103 478 + */ +module LibHttp_TypesAndValues { + + // LibHttp + import from LibHttp_MessageBodyTypes all; + import from LibHttp_Pics all; + + const charstring c_header_host := "Host"; + const charstring c_header_content_type := "Content-Type"; + const charstring c_header_content_text := "Content-Text"; + const charstring c_header_content_length := "Content-Length"; + const charstring c_header_accept := "Accept"; + const charstring c_header_connection := "Connection"; + const charstring c_header_pragma := "Pragma"; + const charstring c_header_cache_control := "Cache-Control"; + const charstring c_header_authorization := "Authorization"; + + const integer c_http_version_major := PICS_HTTP_VERSION_MAJOR; + const integer c_http_version_minor := PICS_HTTP_VERSION_MINOR; + + type record of charstring charstring_list; + type record Header { + charstring header_name, + charstring_list header_value optional + } with { + variant "FIELDORDER(msb)" + } + + type record of Header Headers; + + type record Request { + charstring method, + charstring uri, + integer version_major, + integer version_minor, + Headers header, + HttpMessageBody body optional + } with { + variant "FIELDORDER(msb)" + } + + type record Response { + integer version_major, + integer version_minor, + integer statuscode, + charstring statustext, + Headers header, + HttpMessageBody body optional + } with { + variant "FIELDORDER(msb)" + } + + type union HttpMessage { + Response response, + Request request + } with { + variant "" + } + +} with { + variant "" + encode "HttpCodec" +} // End of module LibHttp_TypesAndValues diff --git a/ttcn/LibHttp/ttcn/LibHttp_XMLTypes.ttcn b/ttcn/LibHttp/ttcn/LibHttp_XMLTypes.ttcn new file mode 100644 index 0000000..bdb9f77 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_XMLTypes.ttcn @@ -0,0 +1,11 @@ +module LibHttp_XMLTypes { // FIXME To be removed + + /** + * This file is volontary empry. You have to declare all XSD files required by your project + * In addition, the TTCN-3 module LibHttp_XmlMessageBodyTypes have to be updated too. + */ + // TODO Add here your custom RFCs import + + //import from XSD all; + +} // End of module LibHttp_XMLTypes diff --git a/ttcn/LibHttp/ttcn/LibHttp_XmlMessageBodyTypes.ttcn b/ttcn/LibHttp/ttcn/LibHttp_XmlMessageBodyTypes.ttcn new file mode 100644 index 0000000..147f0c3 --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_XmlMessageBodyTypes.ttcn @@ -0,0 +1,18 @@ +module LibHttp_XmlMessageBodyTypes { + + /** + * This file volontary contains a trivial declaration of the type XmlBody. + * In accordance with your TTCN-3 module LibHttp_XMLTypes, you have to change the XmlBody typing. + */ + // TODO Add here your custom RFCs import + + type union XmlBody { + // TODO Add here your custom variants + charstring raw + } with { + variant "" + } + +} with { + variant "" +} // End of LibHttp_XmlMessageBodyTypes diff --git a/ttcn/LibHttp/ttcn/LibHttp_XmlTemplates.ttcn b/ttcn/LibHttp/ttcn/LibHttp_XmlTemplates.ttcn new file mode 100644 index 0000000..fbfab3a --- /dev/null +++ b/ttcn/LibHttp/ttcn/LibHttp_XmlTemplates.ttcn @@ -0,0 +1,33 @@ +/** + * @author ETSI / STF545 + * @version $URL$ + * $ID:$ + * @desc This module provides the custom templates for ITS HTTP based protocols. + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + */ +module LibHttp_XmlTemplates { + + // import from XSD all; + + // TODO Add here your custom RFCs import + + // LibHttp + import from LibHttp_XmlMessageBodyTypes all; + import from LibHttp_XMLTypes all; + + template (value) XmlBody m_xml_body_raw( + in template (value) charstring p_raw + ) := { + raw := p_raw + } // End of template m_xml_body_raw + + template (present) XmlBody mw_xml_body_raw( + template (present) charstring p_raw := ? + ) := { + raw := p_raw + } // End of template mw_xml_body_raw + +} // End of module LibHttp_XmlTemplates -- GitLab From efce1fbd81bf78207a428d1e0ab4c1d436bfa469 Mon Sep 17 00:00:00 2001 From: YannGarcia Date: Thu, 29 Sep 2022 10:28:01 +0200 Subject: [PATCH 2/9] Add submodule for common TITAN framework; Re-organize ATSs --- ccsrc/Helpers/helpers_externals.cc | 46 +++++ ccsrc/Helpers/module.mk | 2 + ccsrc/Protocols/Http/http_codec.cc | 160 +++-------------- ccsrc/Protocols/Http/http_codec.hh | 26 ++- ccsrc/Protocols/Http/http_layer.cc | 34 ++-- ccsrc/Protocols/Http/http_layer.hh | 12 +- ccsrc/Protocols/Http/module.mk | 3 +- ttcn/LibHelpers/module.mk | 3 + .../LibHelpers/ttcn/LibHelpers_Functions.ttcn | 165 ++++++++++++++++++ ttcn/LibHttp/LibHelpers/module.mk | 0 ttcn/LibHttp/module.mk | 8 +- .../ttcn/LibHttp_BinaryMessageBodyTypes.ttcn | 2 +- 12 files changed, 306 insertions(+), 155 deletions(-) create mode 100644 ccsrc/Helpers/helpers_externals.cc create mode 100644 ttcn/LibHelpers/module.mk create mode 100644 ttcn/LibHelpers/ttcn/LibHelpers_Functions.ttcn delete mode 100644 ttcn/LibHttp/LibHelpers/module.mk diff --git a/ccsrc/Helpers/helpers_externals.cc b/ccsrc/Helpers/helpers_externals.cc new file mode 100644 index 0000000..b387292 --- /dev/null +++ b/ccsrc/Helpers/helpers_externals.cc @@ -0,0 +1,46 @@ +#include "LibHelpers_Functions.hh" +#include + +#include "base_time.hh" +#include "loggers.hh" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#define earthRadius 6378137.0L +#define rbis = ((double)(earthRadius * M_PI / 180)) + +namespace LibHelpers__Functions { + + /** + * @desc This external function gets the current time since 01/01/1970 in UTC format + * @return The current time since 01/01/1970 in UTC format in milliseconds + * @see fx_getCurrentTimeUtc() return UInt64 + */ + INTEGER fx__getCurrentTimeUtc() { + INTEGER i; + i.set_long_long_val(base_time::get_instance().get_current_time_ms()); + loggers::get_instance().log_msg("<<< fx__getCurrentTimeUtc: ", i); + return i; + } + /** + * @desc Gets the Minute of current UTC year + * @return MinuteOfTheYear - tenths of a second in the current or next hour in units of 1/10th second from UTC time + * @see function f_getMinuteOfTheYear() return MinuteOfTheYear + */ + INTEGER fx__getMinuteOfTheYear() { + // TODO: this is just a sceleton. fill in the function + return 0; + } + + /** + * @desc Gets the milliseconds point in the current UTC minute + * @return DSecond - The milliseconds point in the current UTC minute (0..60000) + * @see function f_getDSecond() return DSecond + */ + INTEGER fx__getDSecond() { + // TODO: this is just a sceleton. fill in the function + return 0; + } + +} // namespace LibHelpers__Functions diff --git a/ccsrc/Helpers/module.mk b/ccsrc/Helpers/module.mk index e69de29..dff7d4e 100644 --- a/ccsrc/Helpers/module.mk +++ b/ccsrc/Helpers/module.mk @@ -0,0 +1,2 @@ +sources := helpers_externals.cc +includes := . diff --git a/ccsrc/Protocols/Http/http_codec.cc b/ccsrc/Protocols/Http/http_codec.cc index 8433b8b..d42daed 100644 --- a/ccsrc/Protocols/Http/http_codec.cc +++ b/ccsrc/Protocols/Http/http_codec.cc @@ -8,11 +8,10 @@ #include "loggers.hh" -#include "LibHttp_MessageBodyTypes.hh" #include "LibHttp_TypesAndValues.hh" +#include "LibHttp_MessageBodyTypes.hh" #include "LibHttp_XmlMessageBodyTypes.hh" - -#include "http_etsi_ieee1609dot2_codec.hh" +#include "LibHttp_JsonMessageBodyTypes.hh" int http_codec::encode(const LibHttp__TypesAndValues::HttpMessage &msg, OCTETSTRING &data) { loggers::get_instance().log_msg(">>> http_codec::encode: ", (const Base_Type &)msg); @@ -422,61 +421,26 @@ int http_codec::encode_body(const LibHttp__MessageBodyTypes::HttpMessageBody &p_ if (binary_body.ischosen(LibHttp__BinaryMessageBodyTypes::BinaryBody::ALT_raw)) { p_encoding_buffer = OCTETSTRING(binary_body.raw().lengthof(), (unsigned char *)static_cast(binary_body.raw())); } else { - std::map>>::const_iterator it; - bool processed = false; - if (p_content_type.find("x-its") != std::string::npos) { - loggers::get_instance().log("http_codec::encode_body: Find x-its"); - it = _codecs.find("http_its"); // TODO Use params - if (it != _codecs.cend()) { - loggers::get_instance().log("http_codec::encode_body: Call '%s'", it->first.c_str()); - if (binary_body.ischosen(LibHttp__BinaryMessageBodyTypes::BinaryBody::ALT_ieee1609dot2__data)) { - _codecs["http_its"]->encode((Record_Type &)binary_body.ieee1609dot2__data(), p_encoding_buffer); // TODO Use params - processed = true; - } else if (binary_body.ischosen(LibHttp__BinaryMessageBodyTypes::BinaryBody::ALT_ieee1609dot2__certificate)) { - _codecs["http_its"]->encode((Record_Type &)binary_body.ieee1609dot2__certificate(), p_encoding_buffer); // TODO Use params - processed = true; - } else { - loggers::get_instance().warning("http_codec::encode_body: Unsupported variant"); - } - } - } // TODO Add new HTTP message codec here - if (!processed) { - loggers::get_instance().warning("http_codec::encode_body: Unsupported HTTP codec, use raw field as default"); - p_encoding_buffer = OCTETSTRING(0, nullptr); - } + encode_body_binary(binary_body, p_encoding_buffer, p_content_type); + } + } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_json__body)) { + const LibHttp__JsonMessageBodyTypes::JsonBody& json_body = p_message_body.json__body(); + if (json_body.ischosen(LibHttp__JsonMessageBodyTypes::JsonBody::ALT_raw)) { + p_encoding_buffer = unichar2oct(json_body.raw()); + } else { + encode_body_json(json_body, p_encoding_buffer, p_content_type); } - } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_html__body)) { - p_encoding_buffer = OCTETSTRING(p_message_body.html__body().lengthof(), (unsigned char *)static_cast(p_message_body.html__body())); - } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_text__body)) { - p_encoding_buffer = OCTETSTRING(p_message_body.text__body().lengthof(), (unsigned char *)static_cast(p_message_body.text__body())); } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_xml__body)) { const LibHttp__XmlMessageBodyTypes::XmlBody &xml_body = p_message_body.xml__body(); if (xml_body.ischosen(LibHttp__XmlMessageBodyTypes::XmlBody::ALT_raw)) { p_encoding_buffer = OCTETSTRING(xml_body.raw().lengthof(), (unsigned char *)static_cast(xml_body.raw())); } else { - std::map>>::const_iterator it; - bool processed = false; - loggers::get_instance().log("http_codec::encode_body: Content-Type:'%s'", p_content_type.c_str()); - if (p_content_type.find("held") != std::string::npos) { - it = _codecs.find("held"); // TODO Use params - if (it != _codecs.cend()) { - loggers::get_instance().log("http_codec::encode_body: Call 'held_codec'"); - _codecs["held"]->encode((Record_Type &)xml_body, p_encoding_buffer); // TODO Use params - processed = true; - } - } else if (p_content_type.find("lost") != std::string::npos) { - it = _codecs.find("lost"); // TODO Use params - if (it != _codecs.cend()) { - loggers::get_instance().log("http_codec::encode_body: Call 'lost_codec'"); - _codecs["lost"]->encode((Record_Type &)xml_body, p_encoding_buffer); // TODO Use params - processed = true; - } - } // TODO Add new HTTP message codec here - if (!processed) { - loggers::get_instance().warning("http_codec::encode_body: Unsupported HTTP codec, use raw field as default"); - p_encoding_buffer = OCTETSTRING(0, nullptr); - } + encode_body_xml(xml_body, p_encoding_buffer, p_content_type); } + } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_html__body)) { + p_encoding_buffer = OCTETSTRING(p_message_body.html__body().lengthof(), (unsigned char *)static_cast(p_message_body.html__body())); + } else if (p_message_body.ischosen(LibHttp__MessageBodyTypes::HttpMessageBody::ALT_text__body)) { + p_encoding_buffer = OCTETSTRING(p_message_body.text__body().lengthof(), (unsigned char *)static_cast(p_message_body.text__body())); } else { loggers::get_instance().warning("http_codec::encode_body: Failed to encode HTTP message body"); return -1; @@ -498,7 +462,7 @@ int http_codec::decode_body(TTCN_Buffer &decoding_buffer, LibHttp__MessageBodyTy if (decoding_buffer.get_len() - decoding_buffer.get_pos() <= 0) { return -1; } - /* TODO Uncommentif (p_content_type.empty()) { + /* TODO Uncomment if (p_content_type.empty()) { loggers::get_instance().warning("http_codec::encode_body: Failed to select a codec for HTTP body payload"); return -1; }*/ @@ -589,45 +553,8 @@ int http_codec::decode_body(TTCN_Buffer &decoding_buffer, LibHttp__MessageBodyTy LibHttp__MessageBodyTypes::HttpMessageBody v; if (_dc.is_binary == 0x01) { LibHttp__BinaryMessageBodyTypes::BinaryBody binary_body; - std::map>>::const_iterator it; - bool processed = false; - // TODO To be refined adding a string identifier to check which codec to use. E.g. held_code.id() returns "xmlns=\"urn:ietf:params:xml:ns:geopriv:held\">" - if ((p_content_type.find("x-its") != std::string::npos) || (p_content_type.find("application/octet-stream") != std::string::npos)) { - loggers::get_instance().log("http_codec::decode_body: Find 'x-its'"); - it = _codecs.cbegin(); //_codecs.find("http_its"); - if (it != _codecs.cend()) { - /*** - FIXME: - This code generate a codedump, I don't undertsand the reason. - The same code works file for Ng112 HELD & LOST codec. Ununderstandable!!!! - ==> Use a patch - if (_codecs["http_its"].get() != nullptr) { - loggers::get_instance().log("http_codec::decode_body: Call 'http_etsi_ieee1609dot2_codec'"); - if (_codecs["http_its"]->decode(body, (Record_Type&)binary_body) == 0) { - processed = true; - } - }*/ - loggers::get_instance().log("http_codec::decode_body: Call '%s'", it->first.c_str()); - http_etsi_ieee1609dot2_codec *codec = new http_etsi_ieee1609dot2_codec(); - if (body[0].get_octet() != 0x80) { - if (codec->decode(body, binary_body.ieee1609dot2__data()) == 0) { - message_body.binary__body() = binary_body; - processed = true; - } - } else { - if (codec->decode(body, binary_body.ieee1609dot2__certificate()) == 0) { - message_body.binary__body() = binary_body; - processed = true; - } - } - delete codec; - } - } // TODO Add new HTTP message codec here - if (!processed) { - loggers::get_instance().warning("http_codec::decode_body: Unsupported HTTP codec, use raw field as default"); - binary_body.raw() = body; - message_body.binary__body() = binary_body; - } + decode_body_binary(body, binary_body, p_content_type); + message_body.binary__body() = binary_body; } else { // Convert into string params p; @@ -637,51 +564,20 @@ int http_codec::decode_body(TTCN_Buffer &decoding_buffer, LibHttp__MessageBodyTy if (p["decode_str"].find("" - if ((p["decode_str"].find("=\"urn:ietf:params:xml:ns:geopriv:held\"") != std::string::npos) || - (p["decode_str"].find("=\"urn:ietf:params:xml:ns:pidf\"") != std::string::npos)) { - loggers::get_instance().log("http_codec::decode_body: Find 'urn:ietf:params:xml:ns:geopriv:held'"); - if (_codecs["held"].get() != nullptr) { - loggers::get_instance().log("http_codec::decode_body: Call 'held_codec'"); - if (_codecs["held"]->decode(body, (Record_Type &)xml_body, &p) == -1) { - loggers::get_instance().warning("http_codec::decode_body: Failed to decode HELD message"); - xml_body.raw() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); - } else { - loggers::get_instance().log_msg("http_codec::decode_body: Decoded message:", xml_body); - message_body.xml__body() = xml_body; - } - } else { - loggers::get_instance().warning("http_codec::decode_body: No codec for HELD"); - xml_body.raw() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); - } - message_body.xml__body() = xml_body; - } else if (p["decode_str"].find("=\"urn:ietf:params:xml:ns:lost1\"") != std::string::npos) { - loggers::get_instance().log("http_codec::decode_body: Find 'urn:ietf:params:xml:ns:lost1'"); - if (_codecs["lost"].get() != nullptr) { - loggers::get_instance().log("http_codec::decode_body: Call 'lost_codec'"); - if (_codecs["lost"]->decode(body, (Record_Type &)xml_body, &p) == -1) { - loggers::get_instance().warning("http_codec::decode_body: Failed to decode LOST message"); - xml_body.raw() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); - } else { - loggers::get_instance().log_msg("http_codec::decode_body: Decoded message:", xml_body); - message_body.xml__body() = xml_body; - } - } else { - loggers::get_instance().warning("http_codec::decode_body: No codec for LOST"); - xml_body.raw() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); - } - message_body.xml__body() = xml_body; - } else { - loggers::get_instance().warning("http_codec::decode_body: No XML codec found"); - xml_body.raw() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); - message_body.xml__body() = xml_body; - } + decode_body_xml(body, xml_body, p_content_type, &p); + message_body.xml__body() = xml_body; } else if (p["decode_str"].find("") != std::string::npos) { // Try to identify HTML loggers::get_instance().log("http_codec::decode_body: Find html message"); LibHttp__MessageBodyTypes::HtmlBody html_body; - message_body.html__body() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); + decode_body_html(body, html_body, p_content_type, &p); + message_body.html__body() = html_body; + } else if (p_content_type.find("json") != std::string::npos) { // Try to identify JSON + loggers::get_instance().log("http_codec::decode_body: Find json message"); + LibHttp__JsonMessageBodyTypes::JsonBody json_body; + decode_body_json(body, json_body, p_content_type, &p); + message_body.json__body() = json_body; } else { - loggers::get_instance().log("http_codec::decode_body: Use textBdy as default"); + loggers::get_instance().log("http_codec::decode_body: Use textBody as default"); LibHttp__MessageBodyTypes::TextBody text_body; message_body.text__body() = CHARSTRING(body.lengthof(), (char *)static_cast(body)); } diff --git a/ccsrc/Protocols/Http/http_codec.hh b/ccsrc/Protocols/Http/http_codec.hh index 0a1ff1f..84eef89 100644 --- a/ccsrc/Protocols/Http/http_codec.hh +++ b/ccsrc/Protocols/Http/http_codec.hh @@ -19,7 +19,16 @@ namespace LibHttp__TypesAndValues { } // namespace LibHttp__TypesAndValues namespace LibHttp__MessageBodyTypes { class HttpMessageBody; -} +} // namespace LibHttp__MessageBodyTypes +namespace LibHttp__BinaryMessageBodyTypes { + class BinaryBody; +} // namespace LibHttp__BinaryMessageBodyTypes +namespace LibHttp__XmlMessageBodyTypes { + class XmlBody; +} // namespace LibHttp__XmlMessageBodyTypes +namespace LibHttp__JsonMessageBodyTypes { + class JsonBody; +} // namespace LibHttp__JsonMessageBodyTypes struct encoding_context { unsigned int length; @@ -48,6 +57,8 @@ struct decoding_context { class http_codec : public codec_gen { encoding_context _ec; decoding_context _dc; + +protected: std::map>> _codecs; public: @@ -59,6 +70,19 @@ public: void set_payload_codecs(const std::string &p_codecs); +protected: //! \protectedsection + virtual bool encode_body_binary(const LibHttp__BinaryMessageBodyTypes::BinaryBody &p_binary_body, OCTETSTRING &p_encoding_buffer, const std::string &p_content_type) {return false;}; + virtual bool decode_body_binary(const OCTETSTRING &p_data, LibHttp__BinaryMessageBodyTypes::BinaryBody &p_binary_body, const std::string &p_content_type) {return false;}; + + virtual bool encode_body_xml(const LibHttp__XmlMessageBodyTypes::XmlBody &p_xml_body, OCTETSTRING &p_encoding_buffer, const std::string &p_content_type) {return false;}; + virtual bool decode_body_xml(const OCTETSTRING &p_data, LibHttp__XmlMessageBodyTypes::XmlBody &p_body, const std::string &p_content_type, params* p_params) {return false;}; + + virtual bool encode_body_html(const CHARSTRING &p_html_body, OCTETSTRING &p_encoding_buffer, const std::string &p_content_type) {return false;}; + virtual bool decode_body_html(const OCTETSTRING &p_data, CHARSTRING &p_html_body, const std::string &p_content_type, params* p_params) {return false;}; + + virtual bool encode_body_json(const LibHttp__JsonMessageBodyTypes::JsonBody &p_json_body, OCTETSTRING &p_encoding_buffer, const std::string &p_content_type) {return false;}; + virtual bool decode_body_json(const OCTETSTRING &p_data, LibHttp__JsonMessageBodyTypes::JsonBody &p_json_body, const std::string &p_content_type, params* p_params) {return false;}; + private: int encode_request(const LibHttp__TypesAndValues::Request &p_request, TTCN_Buffer &p_encoding_buffer); int encode_response(const LibHttp__TypesAndValues::Response &p_response, TTCN_Buffer &p_encoding_buffer); diff --git a/ccsrc/Protocols/Http/http_layer.cc b/ccsrc/Protocols/Http/http_layer.cc index 11fcee1..2fb6d42 100644 --- a/ccsrc/Protocols/Http/http_layer.cc +++ b/ccsrc/Protocols/Http/http_layer.cc @@ -12,20 +12,12 @@ using namespace std; // Required for isnan() #include "LibHttp_TypesAndValues.hh" http_layer::http_layer(const std::string &p_type, const std::string ¶m) - : t_layer(p_type), _params(), _device_mode{false} { + : t_layer(p_type), _params(), _codec(nullptr), _device_mode{false} { loggers::get_instance().log(">>> http_layer::http_layer: %s, %s", to_string().c_str(), param.c_str()); // Setup parameters params::convert(_params, param); - params::const_iterator it = _params.find(params::codecs); - if (it != _params.cend()) { - _codec.set_payload_codecs(it->second); - } - // it = _params.find(params::device_mode); - // if (it != _params.cend()) { - // _device_mode = (1 == converter::get_instance().string_to_int(it->second)); - // } - it = _params.find(params::method); + params::const_iterator it = _params.find(params::method); if (it == _params.cend()) { _params[params::method] = "POST"; } @@ -43,12 +35,30 @@ http_layer::http_layer(const std::string &p_type, const std::string ¶m) } } +bool http_layer::set_codec(http_codec *p_codec) { + loggers::get_instance().log(">>> http_layer::set_codec: %p", (void*)p_codec); + + // Sanity check that + if (p_codec == nullptr) { + loggers::get_instance().error("http_layer::set_codec: Wrong parameter"); + return false; + } + + _codec = p_codec; + params::const_iterator it = _params.find(params::codecs); + if (it != _params.cend()) { + _codec->set_payload_codecs(it->second); + } + + return true; +} + void http_layer::sendMsg(const LibHttp__TypesAndValues::HttpMessage &p_http_message, params &p_param) { loggers::get_instance().log_msg(">>> http_layer::sendMsg: ", p_http_message); // Encode HttpMessage OCTETSTRING data; - _codec.encode(p_http_message, data); + _codec->encode(p_http_message, data); send_data(data, _params); } @@ -85,7 +95,7 @@ void http_layer::receive_data(OCTETSTRING &data, params ¶ms) { // Decode HTTP message LibHttp__TypesAndValues::HttpMessage http_message; - if (_codec.decode(data, http_message) == -1) { + if (_codec->decode(data, http_message) == -1) { loggers::get_instance().warning("http_layer::receive_data: Failed to decode data"); return; } diff --git a/ccsrc/Protocols/Http/http_layer.hh b/ccsrc/Protocols/Http/http_layer.hh index bfc47b3..8a7976c 100644 --- a/ccsrc/Protocols/Http/http_layer.hh +++ b/ccsrc/Protocols/Http/http_layer.hh @@ -32,7 +32,7 @@ class OCTETSTRING; //! Forward declaration of TITAN class */ class http_layer : public t_layer { params _params; - http_codec _codec; + http_codec *_codec; bool _device_mode; public: //! \publicsection @@ -53,7 +53,15 @@ public: //! \publicsection /*! * \brief Default destructor */ - virtual ~http_layer() { }; + virtual ~http_layer() { if (_codec != nullptr) { delete _codec;} }; + + /*! + * \fn bool set_codec(http_codec *p_codec); + * \brief Set the specialized HTTP codec to use + * \param[in] p_codec Pointer to the specialized codec + * \return true on success, false if the pointer is NULL + */ + bool set_codec(http_codec *p_codec); /*! * \fn void sendMsg(const LibHttp__TypesAndValues::HttpMessage& p_http_message, params& p_param); diff --git a/ccsrc/Protocols/Http/module.mk b/ccsrc/Protocols/Http/module.mk index 2c92703..856b038 100644 --- a/ccsrc/Protocols/Http/module.mk +++ b/ccsrc/Protocols/Http/module.mk @@ -1,3 +1,2 @@ -sources := http_codec.cc http_layer.cc +sources := http_codec.cc http_layer.cc http_encdec.cc includes := . - diff --git a/ttcn/LibHelpers/module.mk b/ttcn/LibHelpers/module.mk new file mode 100644 index 0000000..f56baa1 --- /dev/null +++ b/ttcn/LibHelpers/module.mk @@ -0,0 +1,3 @@ +sources := \ + ttcn/LibHelpers_Functions.ttcn + diff --git a/ttcn/LibHelpers/ttcn/LibHelpers_Functions.ttcn b/ttcn/LibHelpers/ttcn/LibHelpers_Functions.ttcn new file mode 100644 index 0000000..0ee138b --- /dev/null +++ b/ttcn/LibHelpers/ttcn/LibHelpers_Functions.ttcn @@ -0,0 +1,165 @@ +/** + * @author ETSI / STF405 / STF449 / STF484 / STF517 + * @version $Url$ + * $Id$ + * @desc Module containing common functions for ITS + * @copyright ETSI Copyright Notification + * No part may be reproduced except as authorized by written permission. + * The copyright and the foregoing restriction extend to reproduction in all media. + * All rights reserved. + * + */ + +module LibHelpers_Functions { + + // LibCommon + import from LibCommon_BasicTypesAndValues all; + + group math { + + /** + * @desc function to generate integer random values + * @see ttcn-3 - rnd() + * @param p_lowerbound lowest number in range + * @param p_upperbound highest number in range + * @return random integer + * + */ + function f_random( in integer p_lowerbound, + in integer p_upperbound ) + return integer { + //Variables + var integer v_random := 0; + v_random := float2int(int2float(p_upperbound - p_lowerbound +1)*rnd()) + p_lowerbound; + + log("*** f_random: INFO: OK - random value = " & int2str(v_random) & " ***"); + return v_random; + + } // End of function f_random + + /** + * @desc Computes the absolute value of an integer + * @param p_number the number + * @return Absolute value of the number + */ + function f_abs(in integer p_number) + return integer { + + if(p_number < 0) { + return 0 - p_number; + } + return p_number; + } + + /** + * @desc Computes the absolute value of an integer + * @param p_number the number + * @return Absolute value of the number + */ + function ff_abs(in float p_number) + return float { + + if(p_number < 0.0) { + return 0.0 - p_number; + } + return p_number; + } + + /** + * @desc Compares two values and returns the lowest onde + * @param p_a First value + * @param p_b Second value + * @return Lowest value + */ + function f_min(in integer p_a, in integer p_b) + return integer { + + if(p_a < p_b) { + return p_a; + } + return p_b; + } + + /** + * @desc Removes unsignificant right-most 0 bits of a bitstring + * + * @param p_bitstring Bitstring to be reduced + * @return Reduced bitstring + * + */ + function f_removeUnsignificantBits(in bitstring p_bitstring) + return bitstring { + var integer i, len; + + len := lengthof(p_bitstring); + for(i:=len-1; i >=0 and p_bitstring[i] == '0'B; i:=i-1) {} + return substr(p_bitstring, 0, i + 1); + } + } // End of group math + + group datetime { + + /** + * @desc Gets the current time since 01/01/1970 in UTC format + * @return The current time since 01/01/1970 in UTC format in milliseconds + */ + function f_getCurrentTimeUtc() return UInt64 { + var UInt64 v_time := 0; + +// log("*** f_getCurrentTimeUtc: INFO: calling fx_getCurrentTimeUtc() ***"); + v_time := fx_getCurrentTimeUtc(); + + return v_time; + } + + /** + * @desc Gets the Minute of current UTC year + * @return MinuteOfTheYear - tenths of a second in the current or next hour in units of 1/10th second from UTC time + */ + function f_getMinuteOfTheYear() return UInt16 { + var UInt16 v_minuteOfTheYear := 0; + +// log("*** f_getMinuteOfTheYear: INFO: calling fx_getMinuteOfTheYear() ***"); + v_minuteOfTheYear := fx_getMinuteOfTheYear(); + + return v_minuteOfTheYear; + } + + /** + * @desc Gets the milliseconds point in the current UTC minute + * @return DSecond - The milliseconds point in the current UTC minute (0..60000) + */ + function f_getDSecond() return UInt16 { + var UInt16 v_dSecond := 0; + +// log("*** f_getDSecond: INFO: calling fx_getDSecond() ***"); + v_dSecond := fx_getDSecond(); + + return v_dSecond; + } + + } // End of group datetime + + group externals { + + /** + * @desc Gets the current time since 01/01/1970 in UTC format + * @return The current time since 01/01/1970 in UTC format + */ + external function fx_getCurrentTimeUtc() return UInt64; + + /** + * @desc Gets the minutes of current UTC year + * @return MinuteOfTheYear - minutes of current UTC year + */ + external function fx_getMinuteOfTheYear() return UInt16; + + /** + * @desc Gets the milliseconds point in the current UTC minute + * @return DSecond - the milliseconds point in the current UTC minute + */ + external function fx_getDSecond() return UInt16; + + } // End of externals + +} // End of module LibHelpers_Functions diff --git a/ttcn/LibHttp/LibHelpers/module.mk b/ttcn/LibHttp/LibHelpers/module.mk deleted file mode 100644 index e69de29..0000000 diff --git a/ttcn/LibHttp/module.mk b/ttcn/LibHttp/module.mk index d962d1e..5fcea7f 100644 --- a/ttcn/LibHttp/module.mk +++ b/ttcn/LibHttp/module.mk @@ -1,10 +1,8 @@ sources := \ - ttcn/LibHttp_BinaryTypes.ttcn \ + ttcn/LibHttp_BinaryMessageBodyTypes.ttcn \ ttcn/LibHttp_EncdecDeclarations.ttcn \ ttcn/LibHttp_Functions.ttcn \ ttcn/LibHttp_JSONTypes.ttcn \ - ttcn/LibHttp_JsonMessageBodyTypes.ttcn \ - ttcn/LibHttp_JsonTemplates.ttcn \ ttcn/LibHttp_MessageBodyTypes.ttcn \ ttcn/LibHttp_Pics.ttcn \ ttcn/LibHttp_Pixits.ttcn \ @@ -16,5 +14,5 @@ sources := \ ttcn/LibHttp_XmlTemplates.ttcn # Please, move and comment the module you need to overwrite tofit your project -# ttcn/LibHttp_BinaryTemplates.ttcn \ -# ttcn/LibHttp_BinaryMessageBodyTypes.ttcn \ +# ttcn/LibHttp_JsonMessageBodyTypes.ttcn \ +# ttcn/LibHttp_JsonTemplates.ttcn \ diff --git a/ttcn/LibHttp/ttcn/LibHttp_BinaryMessageBodyTypes.ttcn b/ttcn/LibHttp/ttcn/LibHttp_BinaryMessageBodyTypes.ttcn index 6fbc5fc..76a8b86 100644 --- a/ttcn/LibHttp/ttcn/LibHttp_BinaryMessageBodyTypes.ttcn +++ b/ttcn/LibHttp/ttcn/LibHttp_BinaryMessageBodyTypes.ttcn @@ -18,7 +18,7 @@ module LibHttp_BinaryMessageBodyTypes { type union BinaryBody { // TODO Add here your custom variants - charstring raw + octetstring raw } with { variant "" } -- GitLab From 24f9b4e18ff2d41b9349fa8c1e23cbc9905eb551 Mon Sep 17 00:00:00 2001 From: YannGarcia Date: Thu, 29 Sep 2022 13:21:55 +0200 Subject: [PATCH 3/9] Finalyze support if titan-test-system-framework --- ttcn/LibHttp/module_its.mk | 18 ++++++++++++++++++ ttcn/LibHttp/{module.mk => module_mec.mk} | 0 2 files changed, 18 insertions(+) create mode 100644 ttcn/LibHttp/module_its.mk rename ttcn/LibHttp/{module.mk => module_mec.mk} (100%) diff --git a/ttcn/LibHttp/module_its.mk b/ttcn/LibHttp/module_its.mk new file mode 100644 index 0000000..a94ec33 --- /dev/null +++ b/ttcn/LibHttp/module_its.mk @@ -0,0 +1,18 @@ +sources := \ + ttcn/LibHttp_EncdecDeclarations.ttcn \ + ttcn/LibHttp_Functions.ttcn \ + ttcn/LibHttp_JSONTypes.ttcn \ + ttcn/LibHttp_JsonMessageBodyTypes.ttcn \ + ttcn/LibHttp_JsonTemplates.ttcn \ + ttcn/LibHttp_MessageBodyTypes.ttcn \ + ttcn/LibHttp_Pics.ttcn \ + ttcn/LibHttp_Pixits.ttcn \ + ttcn/LibHttp_Templates.ttcn \ + ttcn/LibHttp_TestSystem.ttcn \ + ttcn/LibHttp_TypesAndValues.ttcn \ + ttcn/LibHttp_XMLTypes.ttcn \ + ttcn/LibHttp_XmlMessageBodyTypes.ttcn \ + ttcn/LibHttp_XmlTemplates.ttcn + +# Please, move and comment the module you need to overwrite tofit your project +# ttcn/LibHttp_BinaryMessageBodyTypes.ttcn \ diff --git a/ttcn/LibHttp/module.mk b/ttcn/LibHttp/module_mec.mk similarity index 100% rename from ttcn/LibHttp/module.mk rename to ttcn/LibHttp/module_mec.mk -- GitLab From deefde41602b6562248e1b51386e135de0114be5 Mon Sep 17 00:00:00 2001 From: YannGarcia Date: Fri, 30 Sep 2022 15:11:32 +0200 Subject: [PATCH 4/9] Add XML support for NG112 --- ttcn/LibHttp/module_emtel.mk | 19 +++ .../ttcn/LibHttp_JsonMessageBodyTypes.ttcn | 1 + ttcn/LibHttp/ttcn/LibHttp_Templates.ttcn | 12 +- ttcn/LibHttp/ttcn/LibHttp_TypesAndValues.ttcn | 4 +- ttcn/LibJson/module.mk | 4 + ttcn/LibJson/ttcn/Json.ttcn | 151 ++++++++++++++++++ 6 files changed, 187 insertions(+), 4 deletions(-) create mode 100644 ttcn/LibHttp/module_emtel.mk create mode 100644 ttcn/LibJson/module.mk create mode 100644 ttcn/LibJson/ttcn/Json.ttcn diff --git a/ttcn/LibHttp/module_emtel.mk b/ttcn/LibHttp/module_emtel.mk new file mode 100644 index 0000000..ad14023 --- /dev/null +++ b/ttcn/LibHttp/module_emtel.mk @@ -0,0 +1,19 @@ +sources := \ + ttcn/LibHttp_BinaryMessageBodyTypes.ttcn \ + ttcn/LibHttp_EncdecDeclarations.ttcn \ + ttcn/LibHttp_JSONTypes.ttcn \ + ttcn/LibHttp_MessageBodyTypes.ttcn \ + ttcn/LibHttp_Pics.ttcn \ + ttcn/LibHttp_Pixits.ttcn \ + ttcn/LibHttp_TypesAndValues.ttcn \ + ttcn/LibHttp_XMLTypes.ttcn \ + + +# Please, move and comment the module you need to overwrite tofit your project +# ttcn/LibHttp_XmlMessageBodyTypes.ttcn \ +# ttcn/LibHttp_XmlTemplates.ttcn +# ttcn/LibHttp_JsonMessageBodyTypes.ttcn \ +# ttcn/LibHttp_JsonTemplates.ttcn \ +# ttcn/LibHttp_Templates.ttcn \ +# ttcn/LibHttp_TestSystem.ttcn \ +# ttcn/LibHttp_Functions.ttcn \ diff --git a/ttcn/LibHttp/ttcn/LibHttp_JsonMessageBodyTypes.ttcn b/ttcn/LibHttp/ttcn/LibHttp_JsonMessageBodyTypes.ttcn index f945e12..ef31c51 100644 --- a/ttcn/LibHttp/ttcn/LibHttp_JsonMessageBodyTypes.ttcn +++ b/ttcn/LibHttp/ttcn/LibHttp_JsonMessageBodyTypes.ttcn @@ -14,5 +14,6 @@ module LibHttp_JsonMessageBodyTypes { } } with { + encode "JSON"; variant "" } // End of module LibHttp_JsonMessageBodyTypes diff --git a/ttcn/LibHttp/ttcn/LibHttp_Templates.ttcn b/ttcn/LibHttp/ttcn/LibHttp_Templates.ttcn index 4b28a91..08359cb 100644 --- a/ttcn/LibHttp/ttcn/LibHttp_Templates.ttcn +++ b/ttcn/LibHttp/ttcn/LibHttp_Templates.ttcn @@ -163,7 +163,9 @@ module LibHttp_Templates { statuscode := 200, statustext := "OK", header := p_header, - body := p_body + body := p_body, + tls := omit, + mutual_tls := omit } // End of template m_http_response_ok template (value) Response m_http_response_ok_no_body( @@ -185,7 +187,9 @@ module LibHttp_Templates { statuscode := 200, statustext := "OK", header := p_header, - body := omit + body := omit, + tls := *, + mutual_tls := * } // End of template mw_http_response_ok_no_body template (value) Response m_http_response_204_no_content( @@ -204,7 +208,9 @@ module LibHttp_Templates { statuscode := 200, statustext := ?, header := p_header, - body := p_body + body := p_body, + tls := *, + mutual_tls := * } // End of template mw_http_response_ok template (present) Response mw_http_response_201_created( diff --git a/ttcn/LibHttp/ttcn/LibHttp_TypesAndValues.ttcn b/ttcn/LibHttp/ttcn/LibHttp_TypesAndValues.ttcn index 8f88f68..77ca51b 100644 --- a/ttcn/LibHttp/ttcn/LibHttp_TypesAndValues.ttcn +++ b/ttcn/LibHttp/ttcn/LibHttp_TypesAndValues.ttcn @@ -55,7 +55,9 @@ module LibHttp_TypesAndValues { integer statuscode, charstring statustext, Headers header, - HttpMessageBody body optional + HttpMessageBody body optional, + boolean tls optional, + boolean mutual_tls optional } with { variant "FIELDORDER(msb)" } diff --git a/ttcn/LibJson/module.mk b/ttcn/LibJson/module.mk new file mode 100644 index 0000000..a5d3e3b --- /dev/null +++ b/ttcn/LibJson/module.mk @@ -0,0 +1,4 @@ +sources := \ + ttcn/Json.ttcn + +# Please, move and comment the module you need to overwrite tofit your project diff --git a/ttcn/LibJson/ttcn/Json.ttcn b/ttcn/LibJson/ttcn/Json.ttcn new file mode 100644 index 0000000..7fb36ef --- /dev/null +++ b/ttcn/LibJson/ttcn/Json.ttcn @@ -0,0 +1,151 @@ +module Json { + + // These constants are used in the Json date/time type definitions + const charstring + dash := "-", + cln := ":", + year := "[0-9]#4", + yearExpansion := "(-([1-9][0-9]#(0,))#(,1))#(,1)", + month := "(0[1-9]|1[0-2])", + dayOfMonth := "(0[1-9]|[12][0-9]|3[01])", + hour := "([01][0-9]|2[0-3])", + minute := "([0-5][0-9])", + second := "([0-5][0-9])", + sFraction := "(.[0-9]#(1,))#(,1)", + endOfDayExt := "24:00:00(.0#(1,))#(,1)", + nums := "[0-9]#(1,)", + ZorTimeZoneExt := "(Z|[+-]((0[0-9]|1[0-3]):[0-5][0-9]|14:00))#(,1)", + durTime := "(T[0-9]#(1,)"& + "(H([0-9]#(1,)(M([0-9]#(1,)(S|.[0-9]#(1,)S))#(,1)|.[0-9]#(1,)S|S))#(,1)|"& + "M([0-9]#(1,)(S|.[0-9]#(1,)S)|.[0-9]#(1,)M)#(,1)|"& + "S|"& + ".[0-9]#(1,)S))"; + type universal charstring utf8string; + type utf8string JSONCompatibleString + ( + char(0,0,0,9)..char(0,0,0,9), + char(0,0,0,10)..char(0,0,0,10), + char(0,0,0,13)..char(0,0,0,13), + char(0,0,0,32)..char(0,0,215,255), + char(0,0,224,0)..char(0,0,255,253), + char(0,1,0,0)..char(0,16,255,253) + ); + type utf8string JSONStringWithNoWhitespace + ( + char(0,0,0,33)..char(0,0,215,255), + char(0,0,224,0)..char(0,0,255,253), + char(0,1,0,0)..char(0,16,255,253) + ); + type utf8string JSONStringWithNoCRLFHT + ( + char(0,0,0,32)..char(0,0,215,255), + char(0,0,224,0)..char(0,0,255,253), + char(0,1,0,0)..char(0,16,255,253) + ); + + // Json Number type (generic) + type float Number (!-infinity .. !infinity) /*with { + variant "Json:number" + }*/; + // Integer type + type integer Integer (-infinity .. infinity) /*with { + variant "Json:integer" + }*/; + // String type + type utf8string String /*with { + variant "Json:string" + }*/; + type JSONStringWithNoCRLFHT NormalizedString /*with { + variant "Json:normalizedString"; + }*/; + type JSONStringWithNoWhitespace Name /*with { + variant "Json:Name"; + }*/; + type Name NCName /*with { + variant "Json:NCName"; + }*/; + type NormalizedString Token /*with { + variant "Json:token"; + }*/; + type JSONStringWithNoCRLFHT AnyURI /*with { + variant "Json:anyURI"; + }*/; + type charstring Duration (pattern + "{dash}#(,1)P({nums}(Y({nums}(M({nums}D{durTime}#(,1)|{durTime}#(,1))|D{durTime}#(,1))|" & + "{durTime}#(,1))|M({nums}D{durTime}#(,1)|{durTime}#(,1))|D{durTime}#(,1))|{durTime})") + /*with { + variant "Json:duration"; + }*/; + type charstring DateTime (pattern + "{yearExpansion}{year}{dash}{month}{dash}{dayOfMonth}T({hour}{cln}{minute}{cln}{second}" & + "{sFraction}|{endOfDayExt}){ZorTimeZoneExt}" ) + /*with { + variant "Json:dateTime"; + }*/; + type charstring Time (pattern "({hour}{cln}{minute}{cln}{second}{sFraction}|{endOfDayExt}){ZorTimeZoneExt}" ) + /*with { + variant "Json:time"; + }*/; + type charstring Date (pattern "{yearExpansion}{year}{dash}{month}{dash}{dayOfMonth}{ZorTimeZoneExt}" ) + /*with { + variant "Json:date"; + }*/; + // Array type + /*type record of Json.Values Array with { + variant "Json:array" + }*/ + // Subsidiary array types + type record of Json.String StrArray; /*with { + variant "Json:array" + }*/ + type record of Json.Number NumArray; /*with { + variant "Json:array" + }*/ + type record of Json.Integer IntArray; /*with { + variant "Json:array" + } + type record of Json.Bool BoolArray with { + variant "Json:array" + } + type record of Json.Object ObjArray with { + variant "Json:array" + }*/ + // Object member + /*type record ObjectMember { + Json.String name, + Json.Values value_ + } with { + variant "Json:objectMember" + }*/ + // Generic Json object type + /*type record Object { + record length (1..infinity) of Json.ObjectMember memberList optional + } with { + variant "Json:object" + } + type union Values { + Json.String str, + Json.Number num, + Json.Integer int, + Json.Object object, + Json.Array array, + Json.StrArray strArray, + Json.NumArray numArray, + Json.IntArray intArray, + Json.BoolArray boolArray, + Json.ObjArray objArray, + Json.Bool bool, + Json.Null null_ + } with { + variant "asValue" + }*/ + + //Json literals + //When only the true and false literals are allowed + type boolean Bool; /*with { variant "Json:literal" }*/ + //When only the null literal is allowed + type enumerated Null { null_ }; /*with { variant "Json:literal" }*/ + +} with { + encode "Json" +} // End of module Json -- GitLab From 7427ac0ca2da68810d8287893986fcf4dd131cea Mon Sep 17 00:00:00 2001 From: YannGarcia Date: Tue, 4 Oct 2022 08:30:23 +0200 Subject: [PATCH 5/9] Bug fixed in Layers --- ccsrc/Framework/include/layer.hh | 9 ++++++--- ccsrc/Framework/src/layer_factory.cc | 4 +++- ccsrc/Protocols/UDP/udp_layer.cc | 4 ++-- ccsrc/Protocols/UDP/udp_layer.hh | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/ccsrc/Framework/include/layer.hh b/ccsrc/Framework/include/layer.hh index 66377a1..8268401 100644 --- a/ccsrc/Framework/include/layer.hh +++ b/ccsrc/Framework/include/layer.hh @@ -123,20 +123,23 @@ protected: //! \protectedsection inline void to_all_layers(std::vector &layers, OCTETSTRING &data, params ¶ms) { for (std::vector::const_iterator it = layers.cbegin(); it != layers.cend(); ++it) { layer *p = *it; - p->receive_data(data, params); // FIXME BUG I + printf("to_all_layers: %p\n", (void*)p); + p->receive_data(data, params); } // End of 'for' statement }; inline void receive_to_all_layers(OCTETSTRING &data, params ¶ms) { for (std::vector::const_iterator it = upperLayers.cbegin(); it != upperLayers.cend(); ++it) { - layer *p = *it; + layer *p = static_cast(*it); + printf("receive_to_all_layers: %p\n", (void*)p); p->receive_data(data, params); } // End of 'for' statement }; inline void send_to_all_layers(OCTETSTRING &data, params ¶ms) { for (std::vector::const_iterator it = lowerLayers.cbegin(); it != lowerLayers.cend(); ++it) { - layer *p = *it; + layer *p = static_cast(*it); + printf("send_to_all_layers: %p\n", (void*)p); p->send_data(data, params); } // End of 'for' statement }; diff --git a/ccsrc/Framework/src/layer_factory.cc b/ccsrc/Framework/src/layer_factory.cc index ef3ad21..2a10630 100644 --- a/ccsrc/Framework/src/layer_factory.cc +++ b/ccsrc/Framework/src/layer_factory.cc @@ -44,14 +44,16 @@ layer *layer_stack_builder::create_layer_stack(const char *p_layer_stack_descrip loggers::get_instance().error("layer_stack_builder::create_layer_stack: %s: Layer creation error", m[1].str().c_str()); } - loggers::get_instance().log("layer_stack_builder::create_layer_stack: Setup layers for %s", l->to_string().c_str()); + loggers::get_instance().log("layer_stack_builder::create_layer_stack: Setup layers for %s - %p", l->to_string().c_str(), (void*)l); l->add_upper_layer(up); if (entry == NULL) { // Set the first declared layer entry = l; } + loggers::get_instance().log("layer_stack_builder::create_layer_stack: Set up for %s - %p", l->to_string().c_str(), (void*)l); up = l; // Build the linked list of layers } // End of 'for' statement } catch (const std::logic_error &e) { + loggers::get_instance().error("layer_stack_builder::create_layer_stack: In catch %s", e.what()); if (up) { // FIXME To be reviewed up->delete_layer(); up = NULL; diff --git a/ccsrc/Protocols/UDP/udp_layer.cc b/ccsrc/Protocols/UDP/udp_layer.cc index e98c078..3231b06 100644 --- a/ccsrc/Protocols/UDP/udp_layer.cc +++ b/ccsrc/Protocols/UDP/udp_layer.cc @@ -14,7 +14,7 @@ udp_layer::udp_layer(const std::string &p_type, const std::string ¶m) : layer(p_type), PORT(p_type.c_str()), _params(), _saddr{0}, _daddr{0}, _reuse_incoming_source_adddress(false), _fd(-1), _time_key("udp_layer::Handle_Fd_Event_Readable") { - loggers::get_instance().log(">>> udp_layer::udp_layer (1): %s, %s", to_string().c_str(), param.c_str()); + loggers::get_instance().log(">>> udp_layer::udp_layer (1): %s, %s, %p", to_string().c_str(), param.c_str(), (void*)this); // Setup parameters params::convert(_params, param); @@ -110,7 +110,7 @@ void udp_layer::close() { } } -void udp_layer::send_data(OCTETSTRING &data, params_its ¶ms) { +void udp_layer::send_data(OCTETSTRING &data, params ¶ms) { loggers::get_instance().log(">>> udp_layer::send_data: %d", _fd); loggers::get_instance().log_msg(">>> udp_layer::send_data: ", data); diff --git a/ccsrc/Protocols/UDP/udp_layer.hh b/ccsrc/Protocols/UDP/udp_layer.hh index 6ac61f4..9b5c6ea 100644 --- a/ccsrc/Protocols/UDP/udp_layer.hh +++ b/ccsrc/Protocols/UDP/udp_layer.hh @@ -65,7 +65,7 @@ public: //! \publicsection * \param[in] p_data The data to be sent * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters */ - virtual void send_data(OCTETSTRING &data, params_its ¶ms); + virtual void send_data(OCTETSTRING &data, params ¶ms); /*! * \virtual * \fn void receive_data(OCTETSTRING& data, params_its& params); -- GitLab From 1e363a445c9be2ea40578342d86bb1f777daf709 Mon Sep 17 00:00:00 2001 From: YannGarcia Date: Tue, 4 Oct 2022 10:11:08 +0200 Subject: [PATCH 6/9] Remove refence to params_its --- ccsrc/Protocols/UDP/udp_layer.cc | 4 ++-- ccsrc/Protocols/UDP/udp_layer.hh | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ccsrc/Protocols/UDP/udp_layer.cc b/ccsrc/Protocols/UDP/udp_layer.cc index 3231b06..04a6737 100644 --- a/ccsrc/Protocols/UDP/udp_layer.cc +++ b/ccsrc/Protocols/UDP/udp_layer.cc @@ -22,7 +22,7 @@ udp_layer::udp_layer(const std::string &p_type, const std::string ¶m) init(); } -udp_layer::udp_layer(const std::string &p_type, const params_its ¶m) +udp_layer::udp_layer(const std::string &p_type, const params ¶m) : layer(p_type), PORT(p_type.c_str()), _params(), _saddr{0}, _daddr{0}, _reuse_incoming_source_adddress(false), _fd(-1), _time_key("udp_layer::Handle_Fd_Event_Readable") { loggers::get_instance().log(">>> udp_layer::udp_layer (2): %s", to_string().c_str()); @@ -130,7 +130,7 @@ void udp_layer::Handle_Fd_Event_Readable(int fd) { unsigned char buffer[3072] = {0}; struct sockaddr_in from = {0}; socklen_t len = sizeof(struct sockaddr_in); // Length of sender's address - params_its params; + params params; std::vector acc; int result = ::recvfrom(fd, buffer, 3072, 0, (struct sockaddr *)&from, &len); loggers::get_instance().log("udp_layer::Handle_Fd_Event_Readable: src_port = %s:%d, payload length = %d, errno = %d", ::inet_ntoa(from.sin_addr), diff --git a/ccsrc/Protocols/UDP/udp_layer.hh b/ccsrc/Protocols/UDP/udp_layer.hh index 9b5c6ea..c24d833 100644 --- a/ccsrc/Protocols/UDP/udp_layer.hh +++ b/ccsrc/Protocols/UDP/udp_layer.hh @@ -17,8 +17,6 @@ #include "layer.hh" -#include "params_its.hh" - using namespace std; // Required for isnan() #include "Abstract_Socket.hh" @@ -29,7 +27,7 @@ using namespace std; // Required for isnan() * \brief This class provides description of ITS UDP/IP protocol layer */ class udp_layer : public layer, public PORT { - params_its _params; //! Layer parameters + params _params; //! Layer parameters struct sockaddr_in _saddr; //! Source socket address description struct sockaddr_in _daddr; //! Destination socket address description bool _reuse_incoming_source_adddress; @@ -52,7 +50,7 @@ public: //! \publicsection * \param[in] p_type \todo * \param[in] p_param \todo */ - udp_layer(const std::string &p_type, const params_its &p_param); + udp_layer(const std::string &p_type, const params &p_param); /*! * \brief Default destructor */ @@ -60,7 +58,7 @@ public: //! \publicsection /*! * \virtual - * \fn void send_data(OCTETSTRING& data, params_its& params); + * \fn void send_data(OCTETSTRING& data, params& params); * \brief Send bytes formated data to the lower layers * \param[in] p_data The data to be sent * \param[in] p_params Some parameters to overwrite default value of the lower layers parameters @@ -68,7 +66,7 @@ public: //! \publicsection virtual void send_data(OCTETSTRING &data, params ¶ms); /*! * \virtual - * \fn void receive_data(OCTETSTRING& data, params_its& params); + * \fn void receive_data(OCTETSTRING& data, params& params); * \brief Receive bytes formated data from the lower layers * \param[in] p_data The bytes formated data received * \param[in] p_params Some lower layers parameters values when data was received -- GitLab From a49d8daee3e5df27198a1902a7c630a84351e427 Mon Sep 17 00:00:00 2001 From: YannGarcia Date: Wed, 5 Oct 2022 14:17:07 +0200 Subject: [PATCH 7/9] Add support of mutual_tls & tls support --- ccsrc/Protocols/Http/http_codec.cc | 22 ++++++++++++++++++++++ ccsrc/Protocols/Http/http_codec.hh | 2 +- ccsrc/Protocols/Http/http_layer.cc | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ccsrc/Protocols/Http/http_codec.cc b/ccsrc/Protocols/Http/http_codec.cc index d42daed..8322769 100644 --- a/ccsrc/Protocols/Http/http_codec.cc +++ b/ccsrc/Protocols/Http/http_codec.cc @@ -94,6 +94,23 @@ int http_codec::decode(const OCTETSTRING &data, LibHttp__TypesAndValues::HttpMes } else { response.body() = OPTIONAL(body); } + // Add lower layers parameters + loggers::get_instance().log("http_codec::decode: Add lower layers parameters"); + params->log(); + params::const_iterator it = params->find(params::use_ssl); + if (it != params->cend()) { + loggers::get_instance().log("http_codec::receive_data: tls=%s", it->second.c_str()); + response.tls() = (it->second.compare("1") == 0) ? true : false; + } else { + response.tls().set_to_omit(); + } + it = params->find(params::mutual_auth); + if (it != params->cend()) { + loggers::get_instance().log("http_codec::receive_data: mutual_tls=%s", it->second.c_str()); + response.mutual__tls() = (it->second.compare("1") == 0) ? true : false; + } else { + response.mutual__tls().set_to_omit(); + } msg.response() = response; } else { // HTTP request LibHttp__TypesAndValues::Request request; @@ -660,3 +677,8 @@ void http_codec::set_payload_codecs(const std::string &p_codecs) { _codecs.clear(); } } + +bool http_codec::decode_body_html(const OCTETSTRING &p_data, CHARSTRING &p_html_body, const std::string &p_content_type, params* p_params) { + p_html_body = CHARSTRING(p_data.lengthof(), (char*)static_cast(p_data)); + return true; +} diff --git a/ccsrc/Protocols/Http/http_codec.hh b/ccsrc/Protocols/Http/http_codec.hh index 84eef89..efcebe2 100644 --- a/ccsrc/Protocols/Http/http_codec.hh +++ b/ccsrc/Protocols/Http/http_codec.hh @@ -78,7 +78,7 @@ protected: //! \protectedsection virtual bool decode_body_xml(const OCTETSTRING &p_data, LibHttp__XmlMessageBodyTypes::XmlBody &p_body, const std::string &p_content_type, params* p_params) {return false;}; virtual bool encode_body_html(const CHARSTRING &p_html_body, OCTETSTRING &p_encoding_buffer, const std::string &p_content_type) {return false;}; - virtual bool decode_body_html(const OCTETSTRING &p_data, CHARSTRING &p_html_body, const std::string &p_content_type, params* p_params) {return false;}; + virtual bool decode_body_html(const OCTETSTRING &p_data, CHARSTRING &p_html_body, const std::string &p_content_type, params* p_params); virtual bool encode_body_json(const LibHttp__JsonMessageBodyTypes::JsonBody &p_json_body, OCTETSTRING &p_encoding_buffer, const std::string &p_content_type) {return false;}; virtual bool decode_body_json(const OCTETSTRING &p_data, LibHttp__JsonMessageBodyTypes::JsonBody &p_json_body, const std::string &p_content_type, params* p_params) {return false;}; diff --git a/ccsrc/Protocols/Http/http_layer.cc b/ccsrc/Protocols/Http/http_layer.cc index 2fb6d42..451c7d6 100644 --- a/ccsrc/Protocols/Http/http_layer.cc +++ b/ccsrc/Protocols/Http/http_layer.cc @@ -95,7 +95,7 @@ void http_layer::receive_data(OCTETSTRING &data, params ¶ms) { // Decode HTTP message LibHttp__TypesAndValues::HttpMessage http_message; - if (_codec->decode(data, http_message) == -1) { + if (_codec->decode(data, http_message, ¶ms) == -1) { loggers::get_instance().warning("http_layer::receive_data: Failed to decode data"); return; } -- GitLab From e66c0a243ae49d7ca5351b6d142df1cce6257af4 Mon Sep 17 00:00:00 2001 From: YannGarcia Date: Tue, 25 Oct 2022 10:19:41 +0200 Subject: [PATCH 8/9] Update TLS certificates support --- ccsrc/Framework/include/params.hh | 32 ++++++++++++++++--------------- ccsrc/Framework/src/params.cc | 2 ++ ccsrc/Protocols/Tcp/tcp_layer.cc | 23 +++++++++++++++------- ttcn/LibHttp/module.mk | 1 + 4 files changed, 36 insertions(+), 22 deletions(-) create mode 120000 ttcn/LibHttp/module.mk diff --git a/ccsrc/Framework/include/params.hh b/ccsrc/Framework/include/params.hh index 99e81d1..6bb5dcd 100644 --- a/ccsrc/Framework/include/params.hh +++ b/ccsrc/Framework/include/params.hh @@ -22,26 +22,28 @@ class params : public std::map { public: //! \publicsection // TODO Use static constexpr (see commsignia_layer.hh) - static const std::string& debug; //! Set to 1 to enable the debug mode + static const std::string& debug; //! Set to 1 to enable the debug mode static const std::string& loopback; - static const std::string& timestamp; //! Packet reception timestamp - static const std::string& mac_src; //! Source MAC address parameter name - static const std::string& mac_dst; //! Destination MAC address parameter name - static const std::string& mac_bc; //! Broadcast MAC address parameter name - static const std::string& eth_type; //! Ethernet type parameter name - static const std::string& filter; //! Additinal PCAP filter + static const std::string& timestamp; //! Packet reception timestamp + static const std::string& mac_src; //! Source MAC address parameter name + static const std::string& mac_dst; //! Destination MAC address parameter name + static const std::string& mac_bc; //! Broadcast MAC address parameter name + static const std::string& eth_type; //! Ethernet type parameter name + static const std::string& filter; //! Additinal PCAP filter - static const std::string& nic; //! Network Interface Card parameter name + static const std::string& nic; //! Network Interface Card parameter name - static const std::string& server; //! HTTP server address (e.g. www.etsi.org) - static const std::string& port; //! HTTP server port. Default: 80 - static const std::string& use_ssl; //! Set to 1 to use SSL to communicate with the HTTP server. Default: false - static const std::string& mutual_auth; //! Set to 1 to use mutual authentication. Default: false - static const std::string& trusted_ca_list; //! List of trusted CA certificates - static const std::string& server_mode; //! Does the test sytem acting as a server. Default: 0 - static const std::string& local_port; //! Local listener port. Default: 80 + static const std::string& server; //! HTTP server address (e.g. www.etsi.org) + static const std::string& port; //! HTTP server port. Default: 80 + static const std::string& use_ssl; //! Set to 1 to use SSL to communicate with the HTTP server. Default: false + static const std::string& mutual_auth; //! Set to 1 to use mutual authentication. Default: false + static const std::string& trusted_ca_list; //! List of trusted CA certificates + static const std::string& certificate; //! Chain of certificates + static const std::string& privkey; //! Certificate private key + static const std::string& server_mode; //! Does the test sytem acting as a server. Default: 0 + static const std::string& local_port; //! Local listener port. Default: 80 static const std::string& method; //! HTTP method type. Default: POST static const std::string& uri; //! HTTP URI value. Default: / diff --git a/ccsrc/Framework/src/params.cc b/ccsrc/Framework/src/params.cc index 5d7a44d..5040dbe 100644 --- a/ccsrc/Framework/src/params.cc +++ b/ccsrc/Framework/src/params.cc @@ -33,6 +33,8 @@ const std::string& params::port = std::string("port"); const std::string& params::use_ssl = std::string("use_ssl"); const std::string& params::mutual_auth = std::string("mutual_auth"); const std::string& params::trusted_ca_list = std::string("trusted_ca_list"); +const std::string& params::certificate = std::string("certificate"); +const std::string& params::privkey = std::string("privkey"); const std::string& params::server_mode = std::string("server_mode"); const std::string& params::local_port = std::string("local_port"); diff --git a/ccsrc/Protocols/Tcp/tcp_layer.cc b/ccsrc/Protocols/Tcp/tcp_layer.cc index d216f9c..e134867 100644 --- a/ccsrc/Protocols/Tcp/tcp_layer.cc +++ b/ccsrc/Protocols/Tcp/tcp_layer.cc @@ -97,23 +97,32 @@ void tcp_layer::init() { _params.insert(std::pair(std::string("mutual_tls"), "0")); parameter_set(ssl_verifycertificate_name(), "no"); it = _params.find(params::mutual_auth); - if (it == _params.cend()) { + if (it != _params.cend()) { if (_params[params::mutual_auth].compare("1") == 0) { // Use mutual authentication parameter_set(ssl_verifycertificate_name(), "yes"); _params.insert(std::pair(std::string("mutual_tls"), "1")); } } // Set trusted CA file - it = _params.find(params::mutual_auth); - if (it == _params.cend()) { + it = _params.find(params::trusted_ca_list); + if (it != _params.cend()) { parameter_set(ssl_trustedCAlist_file_name(), it->second.c_str()); - _params.insert(std::pair(std::string("mutual_tls"), it->second)); } else { - parameter_set(ssl_trustedCAlist_file_name(), "/usr/share/ca-certificates/mozilla/Amazon_Root_CA_1.crt"); + parameter_set(ssl_trustedCAlist_file_name(), "/home/yann/var/ssl/archive/yanngarcia.ddns.net/fullchain1.pem"); } // Set additional certificates - //parameter_set(ssl_private_key_file_name(), "../certificates/out/privates/e5e11abad8003766e4a7b721afb175a189b5f4cc7046af9b0d8eaebb86f28c40_server_dsa.key.pem"); - //parameter_set(ssl_certificate_file_name(), "../certificates/out/certs/e5e11abad8003766e4a7b721afb175a189b5f4cc7046af9b0d8eaebb86f28c40_server_dsa.cert.pem"); + it = _params.find(params::privkey); + if (it != _params.cend()) { + parameter_set(ssl_private_key_file_name(), it->second.c_str()); + } else { + parameter_set(ssl_private_key_file_name(), "/home/yann/var/ssl/archive/yanngarcia.ddns.net/privkey1.pem"); + } + it = _params.find(params::certificate); + if (it != _params.cend()) { + parameter_set(ssl_certificate_file_name(), it->second.c_str()); + } else { + parameter_set(ssl_certificate_file_name(), "/home/yann/var/ssl/archive/yanngarcia.ddns.net/fullchain1.pem"); + } } set_ttcn_buffer_usercontrol(false); set_handle_half_close(true); diff --git a/ttcn/LibHttp/module.mk b/ttcn/LibHttp/module.mk new file mode 120000 index 0000000..2239d97 --- /dev/null +++ b/ttcn/LibHttp/module.mk @@ -0,0 +1 @@ +module_mec.mk \ No newline at end of file -- GitLab From e1b672512b77277ab0fe969b03f66eb76a65d293 Mon Sep 17 00:00:00 2001 From: YannGarcia Date: Tue, 25 Oct 2022 10:21:09 +0200 Subject: [PATCH 9/9] Remove link file --- ttcn/LibHttp/module.mk | 1 - 1 file changed, 1 deletion(-) delete mode 120000 ttcn/LibHttp/module.mk diff --git a/ttcn/LibHttp/module.mk b/ttcn/LibHttp/module.mk deleted file mode 120000 index 2239d97..0000000 --- a/ttcn/LibHttp/module.mk +++ /dev/null @@ -1 +0,0 @@ -module_mec.mk \ No newline at end of file -- GitLab