diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c80aa71d1290602db3391c0166895ae38895a5c9 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.12) +project(eddie-common) + +set(VERSION_MAJOR 0) +set(VERSION_MINOR 1) + +if(CMAKE_BUILD_TYPE STREQUAL "DEBUG") + add_compile_definitions(DEBUG) +endif() + +add_library(${PROJECT_NAME} src/LinkFormat.cpp) + +target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include) diff --git a/include/EddieEndpoint.h b/common/include/EddieEndpoint.h similarity index 93% rename from include/EddieEndpoint.h rename to common/include/EddieEndpoint.h index 5724db359600e1907b670d7eef21fcc4fb336d4b..f8f16228a3d60c6f60eeca804b7ffc32d43d58b6 100644 --- a/include/EddieEndpoint.h +++ b/common/include/EddieEndpoint.h @@ -19,20 +19,20 @@ private: std::string resource_directory_ip; - CoapClient* get_client(); - - CoapServer* get_server(); - public: EddieEndpoint(); ~EddieEndpoint(); + CoapClient* get_client(); + + CoapServer* get_server(); + /* get resource directory */ int discover_rd(); - int get_resources_from_rd(); + std::string get_resources_from_rd(); int add_resource(EddieResource *resource); diff --git a/include/EddieResource.h b/common/include/EddieResource.h similarity index 94% rename from include/EddieResource.h rename to common/include/EddieResource.h index 7773db0958d27e08d7c17b562a7d431ba784d6ed..f565535ce58a6ea52cd78e9735a418de140c38af 100644 --- a/include/EddieResource.h +++ b/common/include/EddieResource.h @@ -10,6 +10,7 @@ #include #include #include +#include typedef enum {GET, POST, PUT, DELETE} method_t; @@ -104,9 +105,14 @@ public: return response; } - virtual const char * const * get_path(); + virtual const char * const * get_path() { + return nullptr; + } - virtual const char * const * get_attributes(); + virtual const char * const * get_attributes(){ + return nullptr; + } + std::string get_path_str() { const char * const * path = get_path(); diff --git a/zephyr/include/LinkFormat.h b/common/include/LinkFormat.h similarity index 75% rename from zephyr/include/LinkFormat.h rename to common/include/LinkFormat.h index de3cf4ed43e801bce3c2fd8af56433b29aa91e3d..14e18e9b33f7d1fa39b30bd944d8675941c7c24a 100644 --- a/zephyr/include/LinkFormat.h +++ b/common/include/LinkFormat.h @@ -12,6 +12,4 @@ std::map> parse_link_format(std::string link_format); -void print_links(std::map> links); - #endif \ No newline at end of file diff --git a/zephyr/src/LinkFormat.cpp b/common/src/LinkFormat.cpp similarity index 81% rename from zephyr/src/LinkFormat.cpp rename to common/src/LinkFormat.cpp index 729f5d862ee2d5374e7477e3cdec84af0055a50a..efb6dc44db96aad7c41a25d8652b55b7d742f931 100644 --- a/zephyr/src/LinkFormat.cpp +++ b/common/src/LinkFormat.cpp @@ -1,7 +1,4 @@ -#include -LOG_MODULE_REGISTER(link_format, LOG_LEVEL_DBG); - -#include +#include "LinkFormat.h" #include #include @@ -13,7 +10,7 @@ std::map> parse_link_format(std: for ( std::string::iterator it=link_format.begin(); it!=end; ++it) { if (*it == ' ') continue; if (*it != '<') { - LOG_ERR("Unexpected character '%c'", *it); + // TODO: throw exception break; } @@ -68,13 +65,4 @@ std::map> parse_link_format(std: } return links; -} - -void print_links(std::map> links) { - for (auto link: links) { - LOG_DBG("uri: %s", link.first.c_str()); - for(auto param: link.second) { - LOG_DBG("%s = %s", param.first.c_str(), param.second.c_str()); - } - } } \ No newline at end of file diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 6a23cef85920836ad8e5ca4fca1d03bdccb8fc1b..171bf38c28c18e7fda41ddd82ee00bf5c8de40a1 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -32,17 +32,17 @@ target_link_libraries(eddie-client PkgConfig::Coap ${CMAKE_THREAD_LIBS_INIT} edd set_property(TARGET eddie-client PROPERTY C_STANDARD 11) install(TARGETS eddie-client RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) -# eddie server executable -add_executable(eddie-server examples/server.cpp ${SOURCES}) -target_link_libraries(eddie-server PkgConfig::Coap ${CMAKE_THREAD_LIBS_INIT} eddie-communication) -set_property(TARGET eddie-server PROPERTY C_STANDARD 11) -install(TARGETS eddie-server RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - -# eddie node executable -add_executable(eddie-node examples/node.cpp ${SOURCES}) -target_link_libraries(eddie-node PkgConfig::Coap ${CMAKE_THREAD_LIBS_INIT} eddie-communication) -set_property(TARGET eddie-node PROPERTY C_STANDARD 11) -install(TARGETS eddie-node RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +# resource directory executable +add_executable(resource-directory examples/resource_directory.cpp ${SOURCES}) +target_link_libraries(resource-directory PkgConfig::Coap ${CMAKE_THREAD_LIBS_INIT} eddie-communication) +set_property(TARGET resource-directory PROPERTY C_STANDARD 11) +install(TARGETS resource-directory RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +# resource directory executable +add_executable(eddie-endpoint examples/eddie_endpoint.cpp ${SOURCES}) +target_link_libraries(eddie-endpoint PkgConfig::Coap ${CMAKE_THREAD_LIBS_INIT} eddie-communication) +set_property(TARGET eddie-endpoint PROPERTY C_STANDARD 11) +install(TARGETS eddie-endpoint RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) # virtualization server add_executable(eddie-virt-server examples/virt_server.cpp ${SOURCES}) @@ -53,4 +53,4 @@ install(TARGETS eddie-virt-server RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) add_executable(eddie-virt-client examples/virt_client.cpp ${SOURCES}) target_link_libraries(eddie-virt-client PkgConfig::GLIB ${CMAKE_THREAD_LIBS_INIT} eddie-virtualization) set_property(TARGET eddie-virt-client PROPERTY C_STANDARD 11) -install(TARGETS eddie-virt-client RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS eddie-virt-client RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) \ No newline at end of file diff --git a/linux/communication/CMakeLists.txt b/linux/communication/CMakeLists.txt index 8141bd94179fa7747f6275220c7f455ea25e1478..9c33031c45ef58752826b8f8d4ce2cfe7ff6ccb9 100644 --- a/linux/communication/CMakeLists.txt +++ b/linux/communication/CMakeLists.txt @@ -12,6 +12,16 @@ if(CMAKE_BUILD_TYPE STREQUAL "DEBUG") add_compile_definitions(DEBUG) endif() -add_library(${PROJECT_NAME} src/CoapClient.cpp src/CoapServer.cpp src/CoapNode.cpp) +add_subdirectory(../../common common) + +add_library( + ${PROJECT_NAME} + src/CoapClient.cpp + src/CoapServer.cpp + src/ResourceDirectory.cpp + src/common.cpp + src/EddieEndpoint.cpp +) + target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include) -target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT} eddie-common) diff --git a/linux/communication/include/CoapClient.h b/linux/communication/include/CoapClient.h index b5bdf6745be3c9c61f72a658dee3ffe9f13a9981..bb79663304ea0c5fd56a96a4db876bff3bb1c15b 100644 --- a/linux/communication/include/CoapClient.h +++ b/linux/communication/include/CoapClient.h @@ -8,11 +8,11 @@ #ifndef EDDIE_COAPCLIENT_H #define EDDIE_COAPCLIENT_H +#include "common.h" +#include "EddieResource.h" + #include #include -#include -#include -#include #include #include @@ -20,151 +20,16 @@ #define COAP_IP6_MULTICAST_LOCAL_LINK "FF02::FD" #define COAP_IP6_MULTICAST_SITE_LOCAL "FF05::FD" -/** - * Structure that holds responses received by the client. - * @tparam Key message_id of the response - * @tparam Value pointer to the response in memory - */ -template -class ThreadSafeMap { - std::unordered_map c_; - -public: - std::mutex m_; - - Value get(Key const &k) { - std::unique_lock lock(m_); - Value elem = c_[k]; - return elem; - } - - std::list get_all() { - std::unique_lock lock(m_); - std::list return_list; - - for(auto const& imap: c_) - return_list.push_back(imap.second); - - return return_list; - } - - template - void set(Key const &k, Value2 &&v) { - std::unique_lock lock(m_); - c_[k] = std::forward(v); - } - - void erase(Key const &k) { - std::unique_lock lock(m_); - c_.erase(k); - } - - void delete_all() { - std::unique_lock lock(m_); - c_.erase(c_.begin(), c_.end()); - } - - int empty() { - std::unique_lock lock(m_); - return c_.empty(); - } -}; - -template -class ThreadSafeStorage { - std::unordered_multimap mm_; - std::unordered_set s_; - -public: - std::mutex m_; - - std::list get_valid_keys() { - std::unique_lock lock(m_); - - std::list return_container; - for (auto key: s_) return_container.push_back(key); - - return return_container; - } - /** - * Add a new key to the set of valid keys - * @param k the key to add - */ - void insert_valid_key(Key const &k) { - std::unique_lock lock(m_); - s_.insert(k); - } - - /** - * Remove a key from the set of valid keys. - * As a side effect, all pairs in the unordered_multimap are removed - * - * @param k the key to remove - */ - void remove_valid_key (Key const &k) { - std::unique_lock lock(m_); - s_.erase(k); - mm_.erase(k); - } - - /** - * Check if the provided key is contained in the set of valid keys. - * @param k the key to find in the set of valid keys - * @return true if the key is found, false otherwise - */ - int contains_key(Key const &k) { - std::unique_lock lock(m_); - auto result = s_.find(k); - return result != s_.end(); - } - - template - void insert_new_message(Key const &k, Value2 &&v) { - std::unique_lock lock(m_); - - if (s_.find(k) != s_.end()) { - mm_.insert(std::make_pair(k, v)); - } - } - - std::list get(Key const &k) { - std::unique_lock lock(m_); - std::list return_list; - - if (s_.find(k) != s_.end()) { - auto items = mm_.equal_range(k); - - for (auto item = items.first; item != items.second; ++item) { - return_list.push_back(item->second); - } - } - - return return_list; - } - - void delete_all() { - std::unique_lock lock(m_); - s_.erase(s_.begin(), s_.end()); - mm_.erase(mm_.begin(), mm_.end()); - } - - int empty() { - std::unique_lock lock(m_); - return mm_.empty(); - } -}; - class CoapClient { private: int quit = 0; coap_context_t *context = nullptr; - static int resolve_address(const char *host, const char *service, coap_address_t *dst); - coap_session_t *open_session(const char *dst_host, const char *dst_port, coap_proto_t protocol); public: + // TODO: move into private static ThreadSafeMap *messages; static ThreadSafeStorage *multicast_messages; std::mutex client_mutex; @@ -204,6 +69,11 @@ public: coap_mid_t send_put_request(const char *dst_host, const char *dst_port, const char *dst_resource, const char *query, const char *data); + + int send_message(method_t method, const char *dst_ip, const char *dst_port, + const char *dst_res, const char *query, const char *data); + + std::string receive_message(int token, int max_timeout); }; #endif //EDDIE_COAPCLIENT_H diff --git a/linux/communication/include/CoapNode.h b/linux/communication/include/CoapNode.h deleted file mode 100644 index f4c9b652839dbcf39ff89f687dde01c20709abb9..0000000000000000000000000000000000000000 --- a/linux/communication/include/CoapNode.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * SPDX-FileCopyrightText: Huawei Inc. - * SPDX-FileCopyrightText: Politecnico Di Milano - */ - -#ifndef EDDIE_COAPNODE_H -#define EDDIE_COAPNODE_H - -#include "CoapServer.h" - -std::string get_local_node_ip(); - -typedef enum {GET, POST, PUT, DELETE} method_t; - -method_t method_from_string(const std::string& input); - -class CoapNode { -private: - CoapClient *client = nullptr; - CoapServer *server = nullptr; - -public: - CoapServer *get_server() { - return server; - } - - CoapClient *get_client() { - return client; - } - - int init_node(); - - int connect(std::vector uris, std::vector attributes); - - int disconnect(); - - std::string discover(); - - int send_message(method_t method, const char *dst_ip, const char *dst_port, - const char *dst_res, const char *query, const char *data); - - std::string receive_message(int token, int max_timeout); - - void run(); -}; - -#endif //EDDIE_COAPNODE_H diff --git a/linux/communication/include/CoapServer.h b/linux/communication/include/CoapServer.h index 58a53ade5f2e2b35ef9e209536eb6b191a1b8e41..f110b8f4d3e6806d4e280eb293c2089b539c5345 100644 --- a/linux/communication/include/CoapServer.h +++ b/linux/communication/include/CoapServer.h @@ -8,6 +8,9 @@ #ifndef EDDIE_COAPSERVER_H #define EDDIE_COAPSERVER_H +#include "CoapClient.h" +#include "ResourceDirectory.h" + #include #include #include @@ -17,7 +20,7 @@ #include #include #include -#include "CoapClient.h" + #define SERVER_LEISURE_TIME (5U) @@ -26,56 +29,23 @@ class CoapClient; class CoapServer { private: int quit = 0; - bool is_resource_dir = false; coap_context_t *context = nullptr; coap_endpoint_t *endpoint = nullptr; CoapClient *client; std::string my_ip; std::string my_port; - std::string resource_dir_ip; - std::string resource_dir_port; - std::string rd_endpoint; // rd endpoint string std::string ep; // endpoint name std::string d; // sector name std::string default_lt; - long last_lt_check = 0; - - static ThreadSafeMap *resources; - static ThreadSafeMap *rd_endpoints; - static ThreadSafeMap *rd_endpoints_by_ep_d; - - static int - resolve_address(const char *host, const char *service, coap_address_t *dst); - - void check_resource_expiration(); - - // ## RESOURCE DIRECTORY FUNCTIONS - static void - hnd_post_rd(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response); - - static void - hnd_get_lookup_res(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response); - static void - hnd_get_lookup_ep(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response); - - static void - hnd_post_rd_endpoint(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response); - - static void - hnd_delete_rd_endpoint(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response); + std::string resource_dir_ip; + std::string resource_dir_port; + std::string rd_endpoint; // rd endpoint string + ResourceDirectory *resource_directory = nullptr; - static void - resource_handler_get(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response); + static ThreadSafeMap *resources; - static void - resource_handler_put(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + static void resource_handler(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, const coap_string_t *query, coap_pdu_t *response); public: @@ -91,37 +61,25 @@ public: CoapClient *get_client(); - bool get_is_rd(); - - std::string get_my_ip(); + bool is_rd() const; std::string get_rd_ip(); - void set_rd_ip(std::string new_ip); - std::string get_rd_port(); - void set_rd_port(std::string new_port); - int init_server(const char *host, const char *port); void init_resource_dir_local(); int discover_rd(); - coap_resource_t *add_resource(const char *uri, const char *attr_list, int actuator); + int add_resource(EddieResource *resource); int publish_resources(int lt = 90000); - int delete_resource(const char *uri); - int unpublish_resources(); - static std::list - endpoints_to_string_list_filtered(std::map filters); - - static std::list - rd_resources_to_string_list_filtered(std::map filters); + std::list rd_resources_to_string_list_filtered(const std::map &filters); int run(); diff --git a/linux/communication/include/ResourceDirectory.h b/linux/communication/include/ResourceDirectory.h new file mode 100644 index 0000000000000000000000000000000000000000..d6495d23e45f210d23a651adf95ac1b88de01001 --- /dev/null +++ b/linux/communication/include/ResourceDirectory.h @@ -0,0 +1,92 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileCopyrightText: Huawei Inc. + * SPDX-FileCopyrightText: Politecnico Di Milano + */ + +#ifndef RESOURCEDIRECTORY_H +#define RESOURCEDIRECTORY_H + +#include "common.h" + +#include +#include + +class ResourceDirectory { +private: + // TODO: remove static + static ThreadSafeMap *rd_endpoints; + static ThreadSafeMap *rd_endpoints_by_ep_d; + + coap_context_t *context; + + bool quit = false; + + static void + hnd_post_rd(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response); + + static void + hnd_get_lookup_res(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response); + + static void + hnd_get_lookup_ep(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response); + + static void + hnd_post_rd_endpoint(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response); + + static void + hnd_delete_rd_endpoint(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response); + + static coap_resource_t* create_endpoint( + const std::unordered_map ¶ms, + const std::string &data + ); + + static std::string add_endpoint_to_rd(const std::string& ep_d_key, + coap_context_t *context, + const std::unordered_map ¶ms, + const std::string &data); + + int init_server_context(); + +public: + + /** + * @brief Construct and initialize a new Resource Directory + * + * @param server_context A preinitialized server context. If not specified, the server context will be created. + */ + explicit ResourceDirectory(coap_context_t *server_context = nullptr); + + ~ResourceDirectory(); + + static std::list + endpoints_to_string_list_filtered(std::map filters); + + static std::list + rd_resources_to_string_list_filtered(std::map filters); + + static int remove_endpoint(const std::string& rd_endpoint, const std::string& ep, const std::string& d); + + int add_endpoint(const std::string& ep_d_key, + const std::unordered_map ¶ms, + const std::string &data); + + bool get_quit() const { + return quit; + } + + void set_quit(bool val) { + quit = val; + } + + int run(); +}; + +#endif \ No newline at end of file diff --git a/linux/communication/include/common.h b/linux/communication/include/common.h new file mode 100644 index 0000000000000000000000000000000000000000..e5d3fa494da994818a5a6e93270bc89f4cc71d56 --- /dev/null +++ b/linux/communication/include/common.h @@ -0,0 +1,168 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileCopyrightText: Huawei Inc. + * SPDX-FileCopyrightText: Politecnico Di Milano + */ + +#ifndef COMMON_H +#define COMMON_H + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_DBG(_str, ...) printf(_str "\n", ##__VA_ARGS__) +#define LOG_INFO(_str, ...) printf(_str "\n", ##__VA_ARGS__) +#define LOG_ERR(_str, ...) printf(_str "\n", ##__VA_ARGS__) + +int resolve_address(const char *host, const char *service, coap_address_t *dst); + +int match(const std::string &src, const std::string &dst, int is_prefix); + +/** + * Split a string in different substrings, given the delimiter character. + * @param s the string to analyze + * @param delimiter the delimiter character of every substring + */ +std::vector split(const std::string &s, char delimiter); + +/** + * Structure that holds responses received by the client. + * @tparam Key message_id of the response + * @tparam Value pointer to the response in memory + */ +template +class ThreadSafeMap { + std::unordered_map c_; + +public: + std::mutex m_; + + Value get(Key const &k) { + std::unique_lock lock(m_); + Value elem = c_[k]; + return elem; + } + + std::list get_all() { + std::unique_lock lock(m_); + std::list return_list; + + for(auto const& imap: c_) + return_list.push_back(imap.second); + + return return_list; + } + + template + void set(Key const &k, Value2 &&v) { + std::unique_lock lock(m_); + c_[k] = std::forward(v); + } + + void erase(Key const &k) { + std::unique_lock lock(m_); + c_.erase(k); + } + + void delete_all() { + std::unique_lock lock(m_); + c_.erase(c_.begin(), c_.end()); + } + + int empty() { + std::unique_lock lock(m_); + return c_.empty(); + } +}; + +template +class ThreadSafeStorage { + std::unordered_multimap mm_; + std::unordered_set s_; + +public: + std::mutex m_; + + std::list get_valid_keys() { + std::unique_lock lock(m_); + + std::list return_container; + for (auto key: s_) return_container.push_back(key); + + return return_container; + } + /** + * Add a new key to the set of valid keys + * @param k the key to add + */ + void insert_valid_key(Key const &k) { + std::unique_lock lock(m_); + s_.insert(k); + } + + /** + * Remove a key from the set of valid keys. + * As a side effect, all pairs in the unordered_multimap are removed + * + * @param k the key to remove + */ + void remove_valid_key (Key const &k) { + std::unique_lock lock(m_); + s_.erase(k); + mm_.erase(k); + } + + /** + * Check if the provided key is contained in the set of valid keys. + * @param k the key to find in the set of valid keys + * @return true if the key is found, false otherwise + */ + int contains_key(Key const &k) { + std::unique_lock lock(m_); + auto result = s_.find(k); + return result != s_.end(); + } + + template + void insert_new_message(Key const &k, Value2 &&v) { + std::unique_lock lock(m_); + + if (s_.find(k) != s_.end()) { + mm_.insert(std::make_pair(k, v)); + } + } + + std::list get(Key const &k) { + std::unique_lock lock(m_); + std::list return_list; + + if (s_.find(k) != s_.end()) { + auto items = mm_.equal_range(k); + + for (auto item = items.first; item != items.second; ++item) { + return_list.push_back(item->second); + } + } + + return return_list; + } + + void delete_all() { + std::unique_lock lock(m_); + s_.erase(s_.begin(), s_.end()); + mm_.erase(mm_.begin(), mm_.end()); + } + + int empty() { + std::unique_lock lock(m_); + return mm_.empty(); + } +}; + +#endif \ No newline at end of file diff --git a/linux/communication/src/CoapClient.cpp b/linux/communication/src/CoapClient.cpp index b44333b4cdf62265513dcf048d4e4123ef0c8f9f..ea442834e4c0a25c6a84152100a80ff102de17c9 100644 --- a/linux/communication/src/CoapClient.cpp +++ b/linux/communication/src/CoapClient.cpp @@ -5,13 +5,18 @@ * SPDX-FileCopyrightText: Politecnico Di Milano */ +#include "CoapClient.h" +#include "common.h" + #include -#include #include #include #include #include -#include "CoapClient.h" + +#define COAP_DEFAULT_LEISURE_UNSIGNED 5 +#define NOW std::chrono::system_clock::now() +#define min(a, b) ((a) < (b) ? (a) : (b)) /** Container for all response received from the various CoAP nodes */ ThreadSafeMap *CoapClient::messages = new ThreadSafeMap(); @@ -19,37 +24,446 @@ ThreadSafeMap *CoapClient::messages = new ThreadSafeMap *CoapClient::multicast_messages = new ThreadSafeStorage(); -int CoapClient::resolve_address(const char *host, const char *service, coap_address_t *dst) { - struct addrinfo *res, *aInfo; - struct addrinfo hints{}; - int error, len = -1; +std::string pdu_type_to_string(coap_pdu_type_t type) { + switch (type) { + case COAP_MESSAGE_ACK: + return "ACK"; + case COAP_MESSAGE_NON: + return "NON"; + case COAP_MESSAGE_RST: + return "RST"; + case COAP_MESSAGE_CON: + return "CON"; + default: + return "???"; + } +} + +std::string pdu_code_to_string(coap_pdu_code_t code) { + static const char *methods[] = {"0.00", "GET", "POST", "PUT", + "DELETE", "FETCH", "PATCH", "iPATCH"}; + static const char *signals[] = {"7.00", "CSM", "Ping", "Pong", "Release", + "Abort"}; + + char buf[5]; + + if (code < sizeof(methods) / sizeof(const char *)) { + return methods[code]; + } else if (code >= 224 && code - 224 < (int) (sizeof(signals) / sizeof(const char *))) { + return signals[code - 224]; + } else { + snprintf(buf, sizeof(buf), "%u.%02u", (code >> 5) & 0x7, code & 0x1f); + return buf; + } +} - memset(&hints, 0, sizeof(hints)); - memset(dst, 0, sizeof(*dst)); - hints.ai_socktype = SOCK_DGRAM; - hints.ai_family = AF_UNSPEC; - error = getaddrinfo(host, service, &hints, &res); +static const char *msg_option_string(uint8_t code, uint16_t option_type) { + struct option_desc_t { + uint16_t type; + const char *name; + }; + + static struct option_desc_t options[] = { + {COAP_OPTION_IF_MATCH, "If-Match"}, + {COAP_OPTION_URI_HOST, "Uri-Host"}, + {COAP_OPTION_ETAG, "ETag"}, + {COAP_OPTION_IF_NONE_MATCH, "If-None-Match"}, + {COAP_OPTION_OBSERVE, "Observe"}, + {COAP_OPTION_URI_PORT, "Uri-Port"}, + {COAP_OPTION_LOCATION_PATH, "Location-Path"}, + {COAP_OPTION_URI_PATH, "Uri-Path"}, + {COAP_OPTION_CONTENT_FORMAT, "Content-Format"}, + {COAP_OPTION_MAXAGE, "Max-Age"}, + {COAP_OPTION_URI_QUERY, "Uri-Query"}, + {COAP_OPTION_HOP_LIMIT, "Hop-Limit"}, + {COAP_OPTION_ACCEPT, "Accept"}, + {COAP_OPTION_LOCATION_QUERY, "Location-Query"}, + {COAP_OPTION_BLOCK2, "Block2"}, + {COAP_OPTION_BLOCK1, "Block1"}, + {COAP_OPTION_SIZE2, "Size2"}, + {COAP_OPTION_PROXY_URI, "Proxy-Uri"}, + {COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme"}, + {COAP_OPTION_SIZE1, "Size1"}, + {COAP_OPTION_NORESPONSE, "No-Response"} + }; + + static struct option_desc_t options_csm[] = { + {COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, "Max-Message-Size"}, + {COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-wise-Transfer"} + }; + + static struct option_desc_t options_pingpong[] = { + {COAP_SIGNALING_OPTION_CUSTODY, "Custody"} + }; + + static struct option_desc_t options_release[] = { + {COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS, "Alternative-Address"}, + {COAP_SIGNALING_OPTION_HOLD_OFF, "Hold-Off"} + }; + + static struct option_desc_t options_abort[] = { + {COAP_SIGNALING_OPTION_BAD_CSM_OPTION, "Bad-CSM-Option"} + }; + + static char buf[6]; + size_t i; + + if (code == COAP_SIGNALING_CSM) { + for (i = 0; i < sizeof(options_csm) / sizeof(struct option_desc_t); i++) { + if (option_type == options_csm[i].type) { + return options_csm[i].name; + } + } + } else if (code == COAP_SIGNALING_PING || code == COAP_SIGNALING_PONG) { + for (i = 0; i < sizeof(options_pingpong) / sizeof(struct option_desc_t); i++) { + if (option_type == options_pingpong[i].type) { + return options_pingpong[i].name; + } + } + } else if (code == COAP_SIGNALING_RELEASE) { + for (i = 0; i < sizeof(options_release) / sizeof(struct option_desc_t); i++) { + if (option_type == options_release[i].type) { + return options_release[i].name; + } + } + } else if (code == COAP_SIGNALING_ABORT) { + for (i = 0; i < sizeof(options_abort) / sizeof(struct option_desc_t); i++) { + if (option_type == options_abort[i].type) { + return options_abort[i].name; + } + } + } else { + /* search option_type in list of known options */ + for (i = 0; i < sizeof(options) / sizeof(struct option_desc_t); i++) { + if (option_type == options[i].type) { + return options[i].name; + } + } + } + + /* unknown option type, just print to buf */ + snprintf(buf, sizeof(buf), "%u", option_type); + return buf; +} - if (error != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error)); - return error; +static unsigned int +print_content_format(unsigned int format_type, unsigned char *result, unsigned int buf_len) { + struct desc_t { + unsigned int type; + const char *name; + }; + + static struct desc_t formats[] = { + {COAP_MEDIATYPE_TEXT_PLAIN, "text/plain"}, + {COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, "application/link-format"}, + {COAP_MEDIATYPE_APPLICATION_XML, "application/xml"}, + {COAP_MEDIATYPE_APPLICATION_OCTET_STREAM, "application/octet-stream"}, + {COAP_MEDIATYPE_APPLICATION_RDF_XML, "application/rdf+xml"}, + {COAP_MEDIATYPE_APPLICATION_EXI, "application/exi"}, + {COAP_MEDIATYPE_APPLICATION_JSON, "application/json"}, + {COAP_MEDIATYPE_APPLICATION_CBOR, "application/cbor"}, + {COAP_MEDIATYPE_APPLICATION_CWT, "application/cwt"}, + {COAP_MEDIATYPE_APPLICATION_COSE_SIGN, "application/cose; cose-type=\"cose-sign\""}, + {COAP_MEDIATYPE_APPLICATION_COSE_SIGN1, "application/cose; cose-type=\"cose-sign1\""}, + {COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT, "application/cose; cose-type=\"cose-encrypt\""}, + {COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0, "application/cose; cose-type=\"cose-encrypt0\""}, + {COAP_MEDIATYPE_APPLICATION_COSE_MAC, "application/cose; cose-type=\"cose-mac\""}, + {COAP_MEDIATYPE_APPLICATION_COSE_MAC0, "application/cose; cose-type=\"cose-mac0\""}, + {COAP_MEDIATYPE_APPLICATION_COSE_KEY, "application/cose-key"}, + {COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, "application/cose-key-set"}, + {COAP_MEDIATYPE_APPLICATION_SENML_JSON, "application/senml+json"}, + {COAP_MEDIATYPE_APPLICATION_SENSML_JSON, "application/sensml+json"}, + {COAP_MEDIATYPE_APPLICATION_SENML_CBOR, "application/senml+cbor"}, + {COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, "application/sensml+cbor"}, + {COAP_MEDIATYPE_APPLICATION_SENML_EXI, "application/senml-exi"}, + {COAP_MEDIATYPE_APPLICATION_SENSML_EXI, "application/sensml-exi"}, + {COAP_MEDIATYPE_APPLICATION_SENML_XML, "application/senml+xml"}, + {COAP_MEDIATYPE_APPLICATION_SENSML_XML, "application/sensml+xml"}, + {COAP_MEDIATYPE_APPLICATION_DOTS_CBOR, "application/dots+cbor"}, + {75, "application/dcaf+cbor"} + }; + + size_t i; + + /* search format_type in list of known content formats */ + for (i = 0; i < sizeof(formats) / sizeof(struct desc_t); i++) { + if (format_type == formats[i].type) { + return snprintf((char *) result, buf_len, "%s", formats[i].name); + } } - for (aInfo = res; aInfo != nullptr; aInfo = aInfo->ai_next) { - switch (aInfo->ai_family) { - case AF_INET6: - case AF_INET: - len = dst->size = aInfo->ai_addrlen; - memcpy(&dst->addr.sin6, aInfo->ai_addr, dst->size); - freeaddrinfo(res); - return len; - default:; + /* unknown content format, just print numeric value to buf */ + return snprintf((char *) result, buf_len, "%d", format_type); +} + +int is_binary(int content_format) { + return !(content_format == -1 || + content_format == COAP_MEDIATYPE_TEXT_PLAIN || + content_format == COAP_MEDIATYPE_APPLICATION_LINK_FORMAT || + content_format == COAP_MEDIATYPE_APPLICATION_XML || + content_format == COAP_MEDIATYPE_APPLICATION_JSON); +} + +static size_t print_readable(const uint8_t *data, size_t len, unsigned char *result, + size_t buf_len, int encode_always) { + const uint8_t hex[] = "0123456789ABCDEF"; + size_t cnt = 0; + assert(data || len == 0); + + if (buf_len == 0) return 0; + + while (len) { + if (!encode_always && isprint(*data)) { + if (cnt + 1 < buf_len) { + *result++ = *data; + ++cnt; + } else { + break; + } + } else { + if (cnt + 4 < buf_len) { + *result++ = '\\'; + *result++ = 'x'; + *result++ = hex[(*data & 0xf0) >> 4]; + *result++ = hex[*data & 0x0f]; + cnt += 4; + } else { + break; + } } + + ++data; + --len; } - freeaddrinfo(res); - return len; + *result = '\0'; + return cnt; +} + +std::string pdu_to_string(coap_pdu_t *pdu) { + int COAP_DEBUG_BUF_SIZE = 4096; + int COAP_DEFAULT_VERSION = 1; + + /* Proxy-Uri: can be 1034 bytes long */ + unsigned char buf[min(COAP_DEBUG_BUF_SIZE, 1035)]; + char outbuf[COAP_DEBUG_BUF_SIZE]; + size_t buf_len = 0; /* takes the number of bytes written to buf */ + int encode = 0, have_options = 0, i; + coap_opt_iterator_t opt_iter; + coap_opt_t *option; + int content_format = -1; + size_t data_len; + const uint8_t *data; + uint32_t opt_len; + const uint8_t *opt_val; + size_t outbuflen = 0; + + auto pdu_type = coap_pdu_get_type(pdu); + auto pdu_code = coap_pdu_get_code(pdu); + auto pdu_mid = coap_pdu_get_mid(pdu); + auto pdu_token = coap_pdu_get_token(pdu); + + snprintf(outbuf, sizeof(outbuf), "v:%d t:%s c:%s i:%04x {", + COAP_DEFAULT_VERSION, pdu_type_to_string(pdu_type).c_str(), + pdu_code_to_string(pdu_code).c_str(), pdu_mid); + + for (i = 0; i < pdu_token.length; i++) { + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, + "%02x", pdu_token.s[i]); + } + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, "}"); + + /* show options, if any */ + coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, " ["); + while ((option = coap_option_next(&opt_iter))) { + buf[0] = '\000'; + if (!have_options) { + have_options = 1; + } else { + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, ","); + } + + if (pdu_code == COAP_SIGNALING_CODE_CSM || pdu_code == COAP_SIGNALING_CODE_ABORT) { + switch (opt_iter.number) { + case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE: + /* this is equal to COAP_SIGNALING_OPTION_BAD_CSM_OPTION */ + buf_len = snprintf((char *) buf, sizeof(buf), "%u", + coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option))); + break; + default: + buf_len = 0; + break; + } + } else if (pdu_code == COAP_SIGNALING_CODE_PING || pdu_code == COAP_SIGNALING_CODE_PONG) { + buf_len = 0; + } else if (pdu_code == COAP_SIGNALING_CODE_RELEASE) { + switch (opt_iter.number) { + case COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS: + buf_len = print_readable(coap_opt_value(option), + coap_opt_length(option), + buf, sizeof(buf), 0); + break; + case COAP_SIGNALING_OPTION_HOLD_OFF: + buf_len = snprintf((char *) buf, sizeof(buf), "%u", + coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option))); + break; + default: + buf_len = 0; + break; + } + } else { + switch (opt_iter.number) { + case COAP_OPTION_CONTENT_FORMAT: + case COAP_OPTION_ACCEPT: + content_format = (int) coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option)); + + buf_len = print_content_format(content_format, buf, sizeof(buf)); + break; + + case COAP_OPTION_BLOCK1: + case COAP_OPTION_BLOCK2: + /* split block option into number/more/size where more is the + * letter M if set, the _ otherwise */ + buf_len = snprintf((char *) buf, sizeof(buf), "%u/%c/%u", + coap_opt_block_num(option), /* block number */ + COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */ + (1 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */ + + break; + + case COAP_OPTION_URI_PORT: + case COAP_OPTION_MAXAGE: + case COAP_OPTION_OBSERVE: + case COAP_OPTION_SIZE1: + case COAP_OPTION_SIZE2: + case COAP_OPTION_HOP_LIMIT: + /* show values as unsigned decimal value */ + buf_len = snprintf((char *) buf, sizeof(buf), "%u", + coap_decode_var_bytes(coap_opt_value(option), + coap_opt_length(option))); + break; + + case COAP_OPTION_IF_MATCH: + case COAP_OPTION_ETAG: + opt_len = coap_opt_length(option); + opt_val = coap_opt_value(option); + snprintf((char *) buf, sizeof(buf), "0x"); + for (i = 0; (uint32_t) i < opt_len; i++) { + buf_len = strlen((char *) buf); + snprintf((char *) &buf[buf_len], sizeof(buf) - buf_len, + "%02x", opt_val[i]); + } + buf_len = strlen((char *) buf); + break; + default: + /* generic output function for all other option types */ + if (opt_iter.number == COAP_OPTION_URI_PATH || + opt_iter.number == COAP_OPTION_PROXY_URI || + opt_iter.number == COAP_OPTION_URI_HOST || + opt_iter.number == COAP_OPTION_LOCATION_PATH || + opt_iter.number == COAP_OPTION_LOCATION_QUERY || + opt_iter.number == COAP_OPTION_PROXY_SCHEME || + opt_iter.number == COAP_OPTION_URI_QUERY) { + encode = 0; + } else { + encode = 1; + } + + buf_len = print_readable(coap_opt_value(option), + coap_opt_length(option), + buf, sizeof(buf), encode); + } + } + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, + " %s:%.*s", msg_option_string(pdu_code, opt_iter.number), + (int) buf_len, buf); + } + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, " ]"); + + if (coap_get_data(pdu, &data_len, &data)) { + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, " :: "); + + if (is_binary(content_format) || !isprint(data[0])) { + size_t keep_data_len = data_len; + const uint8_t *keep_data = data; + + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, + "binary data length %zu\n", data_len); + + /* Output hex dump of binary data as a continuous entry */ + outbuf[0] = '\000'; + snprintf(outbuf, sizeof(outbuf), "<<"); + while (data_len--) { + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, + "%02x", *data++); + } + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, ">>"); + data_len = keep_data_len; + data = keep_data; + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, "\n"); + + /* + * Output ascii readable (if possible), immediately under the + * hex value of the character output above to help binary debugging + */ + outbuf[0] = '\000'; + snprintf(outbuf, sizeof(outbuf), "<<"); + while (data_len--) { + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, + "%c ", isprint(*data) ? *data : '.'); + data++; + } + outbuflen = strlen(outbuf); + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, ">>"); + } else { + size_t max_length; + outbuflen = strlen(outbuf); + max_length = sizeof(outbuf) - outbuflen; + if (max_length > 1) { + outbuf[outbuflen++] = '\''; + outbuf[outbuflen] = '\000'; + max_length--; + } + if (max_length > 1) { + outbuflen += print_readable(data, data_len, + (unsigned char *) &outbuf[outbuflen], + max_length, 0); + } + /* print_readable may be handling unprintables - hence headroom of 4 */ + if (outbuflen < sizeof(outbuf) - 4 - 1) { + outbuf[outbuflen++] = '\''; + outbuf[outbuflen] = '\000'; + } + } + } + + outbuflen = strlen(outbuf); + if (outbuflen == sizeof(outbuf) - 1) outbuflen--; + snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, "\n"); + + return outbuf; } coap_response_t @@ -101,14 +515,14 @@ CoapClient::open_session(const char *dst_host, const char *dst_port, coap_proto_ coap_address_t dst; if (resolve_address(dst_host, dst_port, &dst) < 0) { - coap_log(LOG_CRIT, "Unable to open connection\n"); + LOG_ERR("Unable to open connection"); return nullptr; } session = coap_new_client_session(this->context, nullptr, &dst, protocol); if (!session) { - coap_log(LOG_CRIT, "Unable to initiate session\n"); + LOG_ERR("Unable to initiate session"); coap_session_release(session); return nullptr; } @@ -159,7 +573,7 @@ int CoapClient::init_client() { this->context = coap_new_context(nullptr); if (!this->context) { - coap_log(LOG_EMERG, "Unable to create context\n"); + LOG_ERR("Unable to create context"); coap_free_context(this->context); coap_cleanup(); @@ -193,7 +607,7 @@ CoapClient::send_get_request(const char *dst_host, const char *dst_port, const c session = open_session(dst_host, dst_port, COAP_PROTO_UDP); if (!session) { - coap_log(LOG_EMERG, "Error creating remote session\n"); + LOG_ERR("Error creating remote session"); coap_session_release(session); return -1; } @@ -274,7 +688,7 @@ CoapClient::send_get_nc_request(const char *dst_host, const char *query, unsigne session = open_session("224.0.1.187", "5683", COAP_PROTO_UDP); if (!session) { - coap_log(LOG_EMERG, "Error creating remote session\n"); + LOG_ERR("Error creating remote session"); coap_session_release(session); return ""; } @@ -358,7 +772,7 @@ CoapClient::send_post_request(const char *dst_host, const char *dst_port, const session = open_session(dst_host, dst_port, COAP_PROTO_UDP); if (!session) { - coap_log(LOG_EMERG, "Error creating remote session\n"); + LOG_ERR("Error creating remote session"); coap_session_release(session); return -1; } @@ -431,7 +845,7 @@ CoapClient::send_post_request(const char *dst_host, const char *dst_port, const coap_mid_t CoapClient::send_delete_request(const char *dst_host, const char *dst_port, const char *res_to_delete) { if (!res_to_delete) { - coap_log(LOG_DEBUG, "[send_delete_request]: `res_to_delete` can not be empty\n"); + LOG_DBG("[send_delete_request]: `res_to_delete` can not be empty"); return -1; } @@ -450,7 +864,7 @@ CoapClient::send_delete_request(const char *dst_host, const char *dst_port, cons session = open_session(dst_host, dst_port, COAP_PROTO_UDP); if (!session) { - coap_log(LOG_EMERG, "Error creating remote session\n"); + LOG_ERR("Error creating remote session"); coap_session_release(session); return -1; } @@ -508,7 +922,7 @@ CoapClient::send_put_request(const char *dst_host, const char *dst_port, const c session = open_session(dst_host, dst_port, COAP_PROTO_UDP); if (!session) { - coap_log(LOG_EMERG, "Error creating remote session\n"); + LOG_ERR("Error creating remote session"); coap_session_release(session); return -1; } @@ -572,3 +986,63 @@ CoapClient::send_put_request(const char *dst_host, const char *dst_port, const c return message_id; } + +int CoapClient::send_message(method_t method, const char *dst_ip, const char *dst_port, + const char *dst_res, const char *query, const char *data) { + int token; + + switch (method) { + case GET: { + token = this->send_get_request(dst_ip, dst_port, dst_res, query); + break; + } + + case DELETE: { + token = this->send_delete_request(dst_ip, dst_port, dst_res); + break; + } + + case POST: { + token = this->send_post_request(dst_ip, dst_port, dst_res, query, data); + break; + } + + case PUT: { + token = this->send_put_request(dst_ip, dst_port, dst_res, query, data); + break; + } + + default: { + token = -1; + } + } + + return token; +} + +std::string CoapClient::receive_message(int token, int max_timeout) { + coap_pdu_t *pdu; + auto function = [this, &max_timeout, &token, &pdu]() { + std::unique_lockclient_mutex)> lock(this->client_mutex); + int seconds = max_timeout > COAP_DEFAULT_LEISURE ? max_timeout : COAP_DEFAULT_LEISURE_UNSIGNED; + auto timeout = NOW + std::chrono::seconds(seconds); + this->client_condition.wait_until(lock, timeout); + + auto answer = CoapClient::messages->get(token); + + while (answer == nullptr) { + this->client_condition.wait_until(lock, timeout); + answer = CoapClient::messages->get(token); + } + + pdu = answer; + }; + + std::thread t(function); + + t.join(); + + CoapClient::messages->erase(token); + + return pdu_to_string(pdu); +} \ No newline at end of file diff --git a/linux/communication/src/CoapNode.cpp b/linux/communication/src/CoapNode.cpp deleted file mode 100644 index 26b5de4058e1da06a462581897e9efed3581d4aa..0000000000000000000000000000000000000000 --- a/linux/communication/src/CoapNode.cpp +++ /dev/null @@ -1,692 +0,0 @@ -/* - * Some code was taken from the libcoap library licensed under - * BSD-2-clause (see LICENSES/BSD-2-clause.txt file) - * Copyright (c) 2010--2021, Olaf Bergmann and others - - * SPDX-License-Identifier: Apache-2.0 - * - * SPDX-FileCopyrightText: Huawei Inc. - * SPDX-FileCopyrightText: Politecnico Di Milano - */ - -#include -#include "CoapNode.h" - -#define COAP_DEFAULT_LEISURE_UNSIGNED 5 -#define NOW std::chrono::system_clock::now() -#define min(a, b) ((a) < (b) ? (a) : (b)) - -method_t method_from_string(const std::string &input) { - if (input == "GET") return GET; - else if (input == "POST") return POST; - else if (input == "PUT") return PUT; - else return DELETE; -} - -std::string pdu_type_to_string(coap_pdu_type_t type) { - switch (type) { - case COAP_MESSAGE_ACK: - return "ACK"; - case COAP_MESSAGE_NON: - return "NON"; - case COAP_MESSAGE_RST: - return "RST"; - case COAP_MESSAGE_CON: - return "CON"; - default: - return "???"; - } -} - -std::string pdu_code_to_string(coap_pdu_code_t code) { - static const char *methods[] = {"0.00", "GET", "POST", "PUT", - "DELETE", "FETCH", "PATCH", "iPATCH"}; - static const char *signals[] = {"7.00", "CSM", "Ping", "Pong", "Release", - "Abort"}; - - char buf[5]; - - if (code < sizeof(methods) / sizeof(const char *)) { - return methods[code]; - } else if (code >= 224 && code - 224 < (int) (sizeof(signals) / sizeof(const char *))) { - return signals[code - 224]; - } else { - snprintf(buf, sizeof(buf), "%u.%02u", (code >> 5) & 0x7, code & 0x1f); - return buf; - } -} - -static const char *msg_option_string(uint8_t code, uint16_t option_type) { - struct option_desc_t { - uint16_t type; - const char *name; - }; - - static struct option_desc_t options[] = { - {COAP_OPTION_IF_MATCH, "If-Match"}, - {COAP_OPTION_URI_HOST, "Uri-Host"}, - {COAP_OPTION_ETAG, "ETag"}, - {COAP_OPTION_IF_NONE_MATCH, "If-None-Match"}, - {COAP_OPTION_OBSERVE, "Observe"}, - {COAP_OPTION_URI_PORT, "Uri-Port"}, - {COAP_OPTION_LOCATION_PATH, "Location-Path"}, - {COAP_OPTION_URI_PATH, "Uri-Path"}, - {COAP_OPTION_CONTENT_FORMAT, "Content-Format"}, - {COAP_OPTION_MAXAGE, "Max-Age"}, - {COAP_OPTION_URI_QUERY, "Uri-Query"}, - {COAP_OPTION_HOP_LIMIT, "Hop-Limit"}, - {COAP_OPTION_ACCEPT, "Accept"}, - {COAP_OPTION_LOCATION_QUERY, "Location-Query"}, - {COAP_OPTION_BLOCK2, "Block2"}, - {COAP_OPTION_BLOCK1, "Block1"}, - {COAP_OPTION_SIZE2, "Size2"}, - {COAP_OPTION_PROXY_URI, "Proxy-Uri"}, - {COAP_OPTION_PROXY_SCHEME, "Proxy-Scheme"}, - {COAP_OPTION_SIZE1, "Size1"}, - {COAP_OPTION_NORESPONSE, "No-Response"} - }; - - static struct option_desc_t options_csm[] = { - {COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE, "Max-Message-Size"}, - {COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER, "Block-wise-Transfer"} - }; - - static struct option_desc_t options_pingpong[] = { - {COAP_SIGNALING_OPTION_CUSTODY, "Custody"} - }; - - static struct option_desc_t options_release[] = { - {COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS, "Alternative-Address"}, - {COAP_SIGNALING_OPTION_HOLD_OFF, "Hold-Off"} - }; - - static struct option_desc_t options_abort[] = { - {COAP_SIGNALING_OPTION_BAD_CSM_OPTION, "Bad-CSM-Option"} - }; - - static char buf[6]; - size_t i; - - if (code == COAP_SIGNALING_CSM) { - for (i = 0; i < sizeof(options_csm) / sizeof(struct option_desc_t); i++) { - if (option_type == options_csm[i].type) { - return options_csm[i].name; - } - } - } else if (code == COAP_SIGNALING_PING || code == COAP_SIGNALING_PONG) { - for (i = 0; i < sizeof(options_pingpong) / sizeof(struct option_desc_t); i++) { - if (option_type == options_pingpong[i].type) { - return options_pingpong[i].name; - } - } - } else if (code == COAP_SIGNALING_RELEASE) { - for (i = 0; i < sizeof(options_release) / sizeof(struct option_desc_t); i++) { - if (option_type == options_release[i].type) { - return options_release[i].name; - } - } - } else if (code == COAP_SIGNALING_ABORT) { - for (i = 0; i < sizeof(options_abort) / sizeof(struct option_desc_t); i++) { - if (option_type == options_abort[i].type) { - return options_abort[i].name; - } - } - } else { - /* search option_type in list of known options */ - for (i = 0; i < sizeof(options) / sizeof(struct option_desc_t); i++) { - if (option_type == options[i].type) { - return options[i].name; - } - } - } - - /* unknown option type, just print to buf */ - snprintf(buf, sizeof(buf), "%u", option_type); - return buf; -} - -static unsigned int -print_content_format(unsigned int format_type, unsigned char *result, unsigned int buf_len) { - struct desc_t { - unsigned int type; - const char *name; - }; - - static struct desc_t formats[] = { - {COAP_MEDIATYPE_TEXT_PLAIN, "text/plain"}, - {COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, "application/link-format"}, - {COAP_MEDIATYPE_APPLICATION_XML, "application/xml"}, - {COAP_MEDIATYPE_APPLICATION_OCTET_STREAM, "application/octet-stream"}, - {COAP_MEDIATYPE_APPLICATION_RDF_XML, "application/rdf+xml"}, - {COAP_MEDIATYPE_APPLICATION_EXI, "application/exi"}, - {COAP_MEDIATYPE_APPLICATION_JSON, "application/json"}, - {COAP_MEDIATYPE_APPLICATION_CBOR, "application/cbor"}, - {COAP_MEDIATYPE_APPLICATION_CWT, "application/cwt"}, - {COAP_MEDIATYPE_APPLICATION_COSE_SIGN, "application/cose; cose-type=\"cose-sign\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_SIGN1, "application/cose; cose-type=\"cose-sign1\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT, "application/cose; cose-type=\"cose-encrypt\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_ENCRYPT0, "application/cose; cose-type=\"cose-encrypt0\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_MAC, "application/cose; cose-type=\"cose-mac\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_MAC0, "application/cose; cose-type=\"cose-mac0\""}, - {COAP_MEDIATYPE_APPLICATION_COSE_KEY, "application/cose-key"}, - {COAP_MEDIATYPE_APPLICATION_COSE_KEY_SET, "application/cose-key-set"}, - {COAP_MEDIATYPE_APPLICATION_SENML_JSON, "application/senml+json"}, - {COAP_MEDIATYPE_APPLICATION_SENSML_JSON, "application/sensml+json"}, - {COAP_MEDIATYPE_APPLICATION_SENML_CBOR, "application/senml+cbor"}, - {COAP_MEDIATYPE_APPLICATION_SENSML_CBOR, "application/sensml+cbor"}, - {COAP_MEDIATYPE_APPLICATION_SENML_EXI, "application/senml-exi"}, - {COAP_MEDIATYPE_APPLICATION_SENSML_EXI, "application/sensml-exi"}, - {COAP_MEDIATYPE_APPLICATION_SENML_XML, "application/senml+xml"}, - {COAP_MEDIATYPE_APPLICATION_SENSML_XML, "application/sensml+xml"}, - {COAP_MEDIATYPE_APPLICATION_DOTS_CBOR, "application/dots+cbor"}, - {75, "application/dcaf+cbor"} - }; - - size_t i; - - /* search format_type in list of known content formats */ - for (i = 0; i < sizeof(formats) / sizeof(struct desc_t); i++) { - if (format_type == formats[i].type) { - return snprintf((char *) result, buf_len, "%s", formats[i].name); - } - } - - /* unknown content format, just print numeric value to buf */ - return snprintf((char *) result, buf_len, "%d", format_type); -} - -int is_binary(int content_format) { - return !(content_format == -1 || - content_format == COAP_MEDIATYPE_TEXT_PLAIN || - content_format == COAP_MEDIATYPE_APPLICATION_LINK_FORMAT || - content_format == COAP_MEDIATYPE_APPLICATION_XML || - content_format == COAP_MEDIATYPE_APPLICATION_JSON); -} - -static size_t print_readable(const uint8_t *data, size_t len, unsigned char *result, - size_t buf_len, int encode_always) { - const uint8_t hex[] = "0123456789ABCDEF"; - size_t cnt = 0; - assert(data || len == 0); - - if (buf_len == 0) return 0; - - while (len) { - if (!encode_always && isprint(*data)) { - if (cnt + 1 < buf_len) { - *result++ = *data; - ++cnt; - } else { - break; - } - } else { - if (cnt + 4 < buf_len) { - *result++ = '\\'; - *result++ = 'x'; - *result++ = hex[(*data & 0xf0) >> 4]; - *result++ = hex[*data & 0x0f]; - cnt += 4; - } else { - break; - } - } - - ++data; - --len; - } - - *result = '\0'; - return cnt; -} - -std::string pdu_to_string(coap_pdu_t *pdu) { - int COAP_DEBUG_BUF_SIZE = 4096; - int COAP_DEFAULT_VERSION = 1; - - /* Proxy-Uri: can be 1034 bytes long */ - unsigned char buf[min(COAP_DEBUG_BUF_SIZE, 1035)]; - char outbuf[COAP_DEBUG_BUF_SIZE]; - size_t buf_len = 0; /* takes the number of bytes written to buf */ - int encode = 0, have_options = 0, i; - coap_opt_iterator_t opt_iter; - coap_opt_t *option; - int content_format = -1; - size_t data_len; - const uint8_t *data; - uint32_t opt_len; - const uint8_t *opt_val; - size_t outbuflen = 0; - - auto pdu_type = coap_pdu_get_type(pdu); - auto pdu_code = coap_pdu_get_code(pdu); - auto pdu_mid = coap_pdu_get_mid(pdu); - auto pdu_token = coap_pdu_get_token(pdu); - - snprintf(outbuf, sizeof(outbuf), "v:%d t:%s c:%s i:%04x {", - COAP_DEFAULT_VERSION, pdu_type_to_string(pdu_type).c_str(), - pdu_code_to_string(pdu_code).c_str(), pdu_mid); - - for (i = 0; i < pdu_token.length; i++) { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, - "%02x", pdu_token.s[i]); - } - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, "}"); - - /* show options, if any */ - coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, " ["); - while ((option = coap_option_next(&opt_iter))) { - buf[0] = '\000'; - if (!have_options) { - have_options = 1; - } else { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, ","); - } - - if (pdu_code == COAP_SIGNALING_CODE_CSM || pdu_code == COAP_SIGNALING_CODE_ABORT) { - switch (opt_iter.number) { - case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE: - /* this is equal to COAP_SIGNALING_OPTION_BAD_CSM_OPTION */ - buf_len = snprintf((char *) buf, sizeof(buf), "%u", - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option))); - break; - default: - buf_len = 0; - break; - } - } else if (pdu_code == COAP_SIGNALING_CODE_PING || pdu_code == COAP_SIGNALING_CODE_PONG) { - buf_len = 0; - } else if (pdu_code == COAP_SIGNALING_CODE_RELEASE) { - switch (opt_iter.number) { - case COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS: - buf_len = print_readable(coap_opt_value(option), - coap_opt_length(option), - buf, sizeof(buf), 0); - break; - case COAP_SIGNALING_OPTION_HOLD_OFF: - buf_len = snprintf((char *) buf, sizeof(buf), "%u", - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option))); - break; - default: - buf_len = 0; - break; - } - } else { - switch (opt_iter.number) { - case COAP_OPTION_CONTENT_FORMAT: - case COAP_OPTION_ACCEPT: - content_format = (int) coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option)); - - buf_len = print_content_format(content_format, buf, sizeof(buf)); - break; - - case COAP_OPTION_BLOCK1: - case COAP_OPTION_BLOCK2: - /* split block option into number/more/size where more is the - * letter M if set, the _ otherwise */ - buf_len = snprintf((char *) buf, sizeof(buf), "%u/%c/%u", - coap_opt_block_num(option), /* block number */ - COAP_OPT_BLOCK_MORE(option) ? 'M' : '_', /* M bit */ - (1 << (COAP_OPT_BLOCK_SZX(option) + 4))); /* block size */ - - break; - - case COAP_OPTION_URI_PORT: - case COAP_OPTION_MAXAGE: - case COAP_OPTION_OBSERVE: - case COAP_OPTION_SIZE1: - case COAP_OPTION_SIZE2: - case COAP_OPTION_HOP_LIMIT: - /* show values as unsigned decimal value */ - buf_len = snprintf((char *) buf, sizeof(buf), "%u", - coap_decode_var_bytes(coap_opt_value(option), - coap_opt_length(option))); - break; - - case COAP_OPTION_IF_MATCH: - case COAP_OPTION_ETAG: - opt_len = coap_opt_length(option); - opt_val = coap_opt_value(option); - snprintf((char *) buf, sizeof(buf), "0x"); - for (i = 0; (uint32_t) i < opt_len; i++) { - buf_len = strlen((char *) buf); - snprintf((char *) &buf[buf_len], sizeof(buf) - buf_len, - "%02x", opt_val[i]); - } - buf_len = strlen((char *) buf); - break; - default: - /* generic output function for all other option types */ - if (opt_iter.number == COAP_OPTION_URI_PATH || - opt_iter.number == COAP_OPTION_PROXY_URI || - opt_iter.number == COAP_OPTION_URI_HOST || - opt_iter.number == COAP_OPTION_LOCATION_PATH || - opt_iter.number == COAP_OPTION_LOCATION_QUERY || - opt_iter.number == COAP_OPTION_PROXY_SCHEME || - opt_iter.number == COAP_OPTION_URI_QUERY) { - encode = 0; - } else { - encode = 1; - } - - buf_len = print_readable(coap_opt_value(option), - coap_opt_length(option), - buf, sizeof(buf), encode); - } - } - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, - " %s:%.*s", msg_option_string(pdu_code, opt_iter.number), - (int) buf_len, buf); - } - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, " ]"); - - if (coap_get_data(pdu, &data_len, &data)) { - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, " :: "); - - if (is_binary(content_format) || !isprint(data[0])) { - size_t keep_data_len = data_len; - const uint8_t *keep_data = data; - - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, - "binary data length %zu\n", data_len); - - /* Output hex dump of binary data as a continuous entry */ - outbuf[0] = '\000'; - snprintf(outbuf, sizeof(outbuf), "<<"); - while (data_len--) { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, - "%02x", *data++); - } - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, ">>"); - data_len = keep_data_len; - data = keep_data; - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, "\n"); - - /* - * Output ascii readable (if possible), immediately under the - * hex value of the character output above to help binary debugging - */ - outbuf[0] = '\000'; - snprintf(outbuf, sizeof(outbuf), "<<"); - while (data_len--) { - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, - "%c ", isprint(*data) ? *data : '.'); - data++; - } - outbuflen = strlen(outbuf); - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, ">>"); - } else { - size_t max_length; - outbuflen = strlen(outbuf); - max_length = sizeof(outbuf) - outbuflen; - if (max_length > 1) { - outbuf[outbuflen++] = '\''; - outbuf[outbuflen] = '\000'; - max_length--; - } - if (max_length > 1) { - outbuflen += print_readable(data, data_len, - (unsigned char *) &outbuf[outbuflen], - max_length, 0); - } - /* print_readable may be handling unprintables - hence headroom of 4 */ - if (outbuflen < sizeof(outbuf) - 4 - 1) { - outbuf[outbuflen++] = '\''; - outbuf[outbuflen] = '\000'; - } - } - } - - outbuflen = strlen(outbuf); - if (outbuflen == sizeof(outbuf) - 1) outbuflen--; - snprintf(&outbuf[outbuflen], sizeof(outbuf) - outbuflen, "\n"); - - return outbuf; -} - -std::string get_local_node_ip() { - std::string ip; - struct ifaddrs *if_addresses; - int rc; - char host[NI_MAXHOST]; - - if (getifaddrs(&if_addresses) == -1) { - perror("[CoapNode::connect]: getifaddrs"); - exit(EXIT_FAILURE); - } - - for (struct ifaddrs *ifa = if_addresses; ifa != nullptr; ifa = ifa->ifa_next) { - if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6 || strcmp(ifa->ifa_name, "lo") == 0) - continue; - - rc = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, - nullptr, 0, NI_NUMERICHOST); - - if (rc != 0) { - coap_log(LOG_DEBUG, "[CoapNode::connect]: getnameinfo failed: %s\n", gai_strerror(rc)); - exit(EXIT_FAILURE); - } - - std::string host_str = host; - - ip = host_str.substr(0, host_str.length() - (strlen(ifa->ifa_name) + 1)); - - if (ip.empty()) { - coap_log(LOG_DEBUG, "[CoapNode::connect]: no valid ip address found for this machine\n"); - exit(EXIT_FAILURE); - } - } - - return ip; -} - -int CoapNode::init_node() { - std::string ip = get_local_node_ip(); - - server = new CoapServer(ip.c_str(), std::to_string(COAP_DEFAULT_PORT).c_str(), 0); - client = server->get_client(); - - return !ip.empty(); -} - -int CoapNode::connect(std::vector uris, std::vector attributes) { - if (uris.size() != attributes.size()) { - coap_log(LOG_EMERG, - "[CoapNode::connect]: error while initializing local resources\n"); - exit(-1); - } - - if (server == nullptr) { - printf("[CoapNode::connect]: error setting up the connection, server is null\n"); - exit(-1); - } - - for (int i = 0; i < uris.size(); ++i) { - server->add_resource(uris[i].c_str(), attributes[i].c_str(), uris[i].find('/') != std::string::npos); - } - - server->publish_resources(); - return 1; -} - -int CoapNode::disconnect() { - if (client != nullptr) client->destroy_client(); - if (server != nullptr) server->destroy_server(); - - delete server; - - coap_cleanup(); - - return 1; -} - -std::string CoapNode::discover() { - if (server == nullptr) { - coap_log(LOG_EMERG, "[CoapNode::discover]: server is null\n"); - exit(EXIT_FAILURE); - } - - std::map empty; - - // this node is rd, get the info directly - if (server->get_is_rd()) { - auto list = CoapServer::rd_resources_to_string_list_filtered(empty); - std::string to_return; - - for (const auto &res: list) { - to_return += res + ","; - } - - if (to_return.back() == ',') to_return.pop_back(); - - return to_return; - } - - // this node is not rd, doesn't know who is rd - std::string resource_str; - if (server->get_rd_ip().empty()) { - if (server->discover_rd() != 1) { - coap_log(LOG_ERR, "[CoapNode::discover]: error looking up rd node\n"); - exit(-1); - } - } - - if (!resource_str.empty()) return resource_str; - - auto token = client->send_get_request(server->get_rd_ip().c_str(), - server->get_rd_port().c_str(), - "rd-lookup/res", nullptr); - - auto resource_loop = [this, &token, &resource_str]() { - std::unique_lockclient->client_mutex)> lock(this->client->client_mutex); - auto timeout = NOW + std::chrono::seconds(COAP_DEFAULT_LEISURE); - this->client->client_condition.wait_until(lock, timeout); - - auto answer = CoapClient::messages->get(token); - - size_t data_len; - const uint8_t *data; - coap_get_data(answer, &data_len, &data); - - if (!data) { - resource_str = ""; - } else { - resource_str = std::string(reinterpret_cast(data)); - } - - free((void *) data); - }; - - std::thread resource_thread(resource_loop); - resource_thread.join(); - - return resource_str; -} - -int CoapNode::send_message(method_t method, const char *dst_ip, const char *dst_port, - const char *dst_res, const char *query, const char *data) { - int token; - - switch (method) { - case GET: { - token = client->send_get_request(dst_ip, dst_port, dst_res, query); - break; - } - - case DELETE: { - token = client->send_delete_request(dst_ip, dst_port, dst_res); - break; - } - - case POST: { - token = client->send_post_request(dst_ip, dst_port, dst_res, query, data); - break; - } - - case PUT: { - token = client->send_put_request(dst_ip, dst_port, dst_res, query, data); - break; - } - - default: { - token = -1; - } - } - - return token; -} - -std::string CoapNode::receive_message(int token, int max_timeout) { - coap_pdu_t *pdu; - auto function = [this, &max_timeout, &token, &pdu]() { - std::unique_lockclient_mutex)> lock(client->client_mutex); - int seconds = max_timeout > COAP_DEFAULT_LEISURE ? max_timeout : COAP_DEFAULT_LEISURE_UNSIGNED; - auto timeout = NOW + std::chrono::seconds(seconds); - client->client_condition.wait_until(lock, timeout); - - auto answer = CoapClient::messages->get(token); - - while (answer == nullptr) { - client->client_condition.wait_until(lock, timeout); - answer = CoapClient::messages->get(token); - } - - pdu = answer; - }; - - std::thread t(function); - - t.join(); - - CoapClient::messages->erase(token); - - return pdu_to_string(pdu); -} - -void CoapNode::run() { - auto server_run = [this]() { - if (!this->get_server()) { - coap_log(LOG_DEBUG, "[CoapNode::run]: server is NULL\n"); - exit(EXIT_FAILURE); - } - - while (!this->get_server()->get_quit()) - coap_io_process(this->get_server()->get_context(), COAP_IO_WAIT); - }; - - auto client_run = [this]() { - if (!this->get_server()) { - coap_log(LOG_DEBUG, "[CoapNode::run]: server is NULL\n"); - exit(EXIT_FAILURE); - } - - while (this->get_server()->get_quit() == 0) - coap_io_process(this->get_client()->get_context(), COAP_IO_WAIT); - }; - - std::thread server_thread(server_run); - std::thread client_thread(client_run); - - server_thread.join(); - client_thread.join(); -} diff --git a/linux/communication/src/CoapServer.cpp b/linux/communication/src/CoapServer.cpp index 27d302538d59c3e992a80398fe37e5d72565a4e2..e899e9ee906c9b5d12bffff6bace40f8f8c7a01c 100644 --- a/linux/communication/src/CoapServer.cpp +++ b/linux/communication/src/CoapServer.cpp @@ -5,9 +5,11 @@ * SPDX-FileCopyrightText: Politecnico Di Milano */ +#include "CoapServer.h" +#include "common.h" +#include "ResourceDirectory.h" + #include -#include -#include #include #include #include @@ -17,15 +19,26 @@ #include #include #include -#include - -#include "CoapServer.h" class CoapClient; -ThreadSafeMap *CoapServer::rd_endpoints = new ThreadSafeMap(); ThreadSafeMap *CoapServer::resources = new ThreadSafeMap(); -ThreadSafeMap *CoapServer::rd_endpoints_by_ep_d = new ThreadSafeMap(); + +method_t coap_method_to_method(uint8_t method) { + switch (method) { + case COAP_REQUEST_CODE_GET: + return GET; + case COAP_REQUEST_CODE_POST: + return POST; + case COAP_REQUEST_CODE_PUT: + return PUT; + case COAP_REQUEST_CODE_DELETE: + return DELETE; + default: + LOG_ERR("Invalid coap method"); + return GET; + } +} std::string resource_list_to_string(const std::list &resources) { @@ -40,7 +53,7 @@ resource_list_to_string(const std::list &resources) { auto print_status = coap_print_link(res, buf, &len, &offset); if (print_status & COAP_PRINT_STATUS_ERROR) { - coap_log(LOG_DEBUG, "Unable to create string representation of resource\n"); + LOG_DBG("Unable to create string representation of resource"); free(buf); return ""; } @@ -63,521 +76,40 @@ resource_list_to_string(const std::list &resources) { return to_return; } -static int -match(const std::string &src, const std::string &dst, int is_prefix) { - std::regex re; - - if (is_prefix) { - re = std::regex("^" + dst + ".*"); - } else { - re = std::regex("^" + dst); - } - - return std::regex_match(src, re); -} - -/** - * Split a string in different substrings, given the delimiter character. - * @param s the string to analyze - * @param delimiter the delimiter character of every substring - */ -std::vector -split(const std::string &s, char delimiter) { - std::vector tokens; - std::string token; - std::istringstream tokenStream(s); - - while (std::getline(tokenStream, token, delimiter)) { - tokens.push_back(token); - } - - return tokens; -} - -coap_resource_t *create_endpoint(const std::unordered_map ¶ms, const std::string &data) { - coap_resource_t *new_endpoint; - - auto now = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count(); - - std::ostringstream oss; - oss << now; - - std::string new_ep_name = std::string("rd/") + oss.str(); - new_endpoint = coap_resource_init(coap_make_str_const(new_ep_name.c_str()), 0); - - for (const auto ¶m: params) { - coap_add_attr(new_endpoint, coap_make_str_const(param.first.c_str()), - coap_make_str_const(param.second.c_str()), 0); - } - - auto userdata = malloc(data.size()); - if (!userdata) { - return nullptr; - } - memset(userdata, 0, data.size()); - memcpy(userdata, data.c_str(), data.size()); - - coap_resource_set_userdata(new_endpoint, userdata); - - return new_endpoint; -} - -// ### PRIVATE ### -int CoapServer::resolve_address(const char *host, const char *service, coap_address_t *dst) { - struct addrinfo *res, *aInfo; - struct addrinfo hints{}; - int error, len = -1; - - memset(&hints, 0, sizeof(hints)); - memset(dst, 0, sizeof(*dst)); - hints.ai_socktype = SOCK_DGRAM; - hints.ai_family = AF_UNSPEC; - - error = getaddrinfo(host, service, &hints, &res); - - if (error != 0) { - fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error)); - return error; - } - - for (aInfo = res; aInfo != nullptr; aInfo = aInfo->ai_next) { - switch (aInfo->ai_family) { - case AF_INET6: - case AF_INET: - len = dst->size = aInfo->ai_addrlen; - memcpy(&dst->addr.sin6, aInfo->ai_addr, dst->size); - freeaddrinfo(res); - return len; - default:; - } - } - - freeaddrinfo(res); - return len; -} - -// ## RESOURCE DIR FUNCTIONS ## -void CoapServer::hnd_post_rd(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response) { - if (!query) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); - return; - } +// ### SERVER FUNCTIONS ### +void CoapServer::resource_handler(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response) { + // get payload size_t data_len; - const u_int8_t *data; + const uint8_t *data; coap_get_data(request, &data_len, &data); - if (!data) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); - return; - } - - std::string data_str = std::string(reinterpret_cast(data)); - if (data_str.empty()) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); - return; - } - - std::string query_str = std::string(reinterpret_cast(query->s)); - std::vector sub_queries = split(query_str, '&'); - std::unordered_map query_map; - - for (const auto &token: sub_queries) { - std::vector sub_query = split(token, '='); - query_map.insert(std::make_pair(sub_query[0], sub_query[1])); - } - - auto ep = query_map.find("ep"); - if (ep == query_map.end()) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); - return; - } - - auto base = query_map.find("base"); - std::string base_address; - if (base == query_map.end()) { - auto remote = coap_session_get_addr_remote(session); - char ip[INET6_ADDRSTRLEN]; - int port; - switch (remote->addr.sa.sa_family) { - case AF_INET: - case AF_INET6: - inet_ntop(AF_INET6, &remote->addr.sin6.sin6_addr, ip, INET6_ADDRSTRLEN); - port = htons(remote->addr.sin6.sin6_port); - break; - } - - base->second = "coap://" + std::string(ip) + ":" + std::to_string(port); - } - - auto lt = query_map.find("lt"); - if (lt == query_map.end()) { - lt->second = "90000"; // 25 hours - } - - auto d = query_map.find("d"); - std::string d_name; - if (!(d == query_map.end())) d_name = d->second; - - std::string second_key = ep->second + "@" + d_name; - - auto res = rd_endpoints_by_ep_d->get(second_key); - - if (res) { - auto uri = std::string(reinterpret_cast(coap_resource_get_uri_path(res)->s)); - free(coap_resource_get_userdata(res)); - coap_delete_resource(coap_session_get_context(session), res); - rd_endpoints_by_ep_d->erase(second_key); - rd_endpoints->erase(uri); - } - - auto new_endpoint = create_endpoint(query_map, data_str); - - if (new_endpoint == nullptr) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); - return; - } - - auto ep_name = std::string(reinterpret_cast(coap_resource_get_uri_path(new_endpoint)->s)); - // ADD HANDLERS - coap_register_handler(new_endpoint, COAP_REQUEST_POST, hnd_post_rd_endpoint); - coap_register_handler(new_endpoint, COAP_REQUEST_DELETE, hnd_delete_rd_endpoint); + // get code + coap_pdu_code_t coap_method = coap_pdu_get_code(request); - coap_add_resource(coap_session_get_context(session), new_endpoint); - rd_endpoints->set(ep_name, new_endpoint); - rd_endpoints_by_ep_d->set(second_key, new_endpoint); + // construct request to pass to handler + request_t message_request; + message_request.data = data; + message_request.data_length = data_len; + message_request.method = coap_method_to_method(coap_method); - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED); + auto eddie_resource = static_cast(coap_resource_get_userdata(resource)); + message_t response_message = eddie_resource->request_handler(message_request); - { - unsigned char _b[1024]; - unsigned char *b = _b; - size_t buf_len = sizeof(_b); - int n_seg; - - n_seg = coap_split_path(reinterpret_cast(ep_name.c_str()), - ep_name.length(), - b, - &buf_len); - - while (n_seg--) { - coap_add_option(response, - COAP_OPTION_LOCATION_PATH, - coap_opt_length(b), - coap_opt_value(b)); - b += coap_opt_size(b); - } - } -} - -void CoapServer::hnd_get_lookup_res(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response) { - std::string query_str; - - if (query) { - query_str = std::string(reinterpret_cast(query->s)); - } - - unsigned char content_type_buf[3]; - - std::vector sub_queries = split(query_str, '&'); - std::map query_map; - - for (const auto &token: sub_queries) { - std::vector sub_query = split(token, '='); - query_map.insert(std::make_pair(sub_query[0], sub_query[1])); - } - - auto page = query_map.find("page"); - auto count = query_map.find("count"); - - if (count != query_map.end() && page == query_map.end()) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); - return; - } - - std::string page_val; - std::string count_val; - if (page == query_map.end()) { - page_val = "-1"; - } else { - page_val = page->second; - query_map.erase("page"); - } - - if (count == query_map.end()) { - count_val = "-1"; - } else { - count_val = count->second; - query_map.erase("count"); - } - - std::list results = rd_resources_to_string_list_filtered(query_map); - - if (results.empty()) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); - coap_add_option(response, - COAP_OPTION_CONTENT_TYPE, - coap_encode_var_safe(content_type_buf, sizeof(content_type_buf), - COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), - content_type_buf); - return; - } - - if (results.front() == "error") { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); - return; - } - - if (count_val != "-1" && page_val == "-1") { - // return only the first `count_val` number of strings - auto begin = results.begin(); - std::advance(begin, stoi(count_val)); - - results.erase(begin, results.end()); - } else if (count_val != "-1" && page_val != "-1") { - // return `count_val` number of strings, starting from the `count_val` * `page_val` - auto begin = results.begin(); - std::advance(begin, stoi(count_val) * stoi(page_val)); - auto end = begin; - std::advance(end, stoi(count_val)); - - results.erase(results.begin(), begin); - results.erase(end, results.end()); - } - - std::string result; - for (const auto &item: results) { - result += (item + ","); - } - - // remove the last ',' - if (!result.empty()) - result.pop_back(); - - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); - - coap_add_option(response, - COAP_OPTION_CONTENT_TYPE, - coap_encode_var_safe(content_type_buf, sizeof(content_type_buf), - COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), - content_type_buf); - coap_add_data(response, result.length(), coap_make_str_const(result.c_str())->s); -} - -void CoapServer::hnd_get_lookup_ep(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response) { - std::string query_str; - - if (query) { - query_str = std::string(reinterpret_cast(query->s)); - } - - unsigned char content_type_buf[3]; - - std::vector sub_queries = split(query_str, '&'); - std::map query_map; - - for (const auto &token: sub_queries) { - std::vector sub_query = split(token, '='); - query_map.insert(std::make_pair(sub_query[0], sub_query[1])); - } - - auto page = query_map.find("page"); - auto count = query_map.find("count"); - - if (count != query_map.end() && page == query_map.end()) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); - return; - } - - std::string page_val; - std::string count_val; - if (page == query_map.end()) { - page_val = "-1"; - } else { - page_val = page->second; - query_map.erase("page"); - } - - if (count == query_map.end()) { - count_val = "-1"; - } else { - count_val = count->second; - query_map.erase("count"); - } - - std::list results = endpoints_to_string_list_filtered(query_map); + if (!response_message.data.empty()) { + coap_add_data(response, + response_message.data.length(), + reinterpret_cast(response_message.data.c_str())); - if (results.empty()) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); + unsigned char buf[3]; coap_add_option(response, COAP_OPTION_CONTENT_TYPE, - coap_encode_var_safe(content_type_buf, sizeof(content_type_buf), - COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), - content_type_buf); - return; - } - - if (results.front() == "error") { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); - return; - } - - - if (count_val != "-1" && page_val == "-1") { - // return only the first `count_val` number of strings - auto begin = results.begin(); - std::advance(begin, stoi(count_val)); - - results.erase(begin, results.end()); - } else if (count_val != "-1" && page_val != "-1") { - // return `count_val` number of strings, starting from the `count_val` * `page_val` - auto begin = results.begin(); - std::advance(begin, stoi(count_val) * stoi(page_val)); - auto end = begin; - std::advance(end, stoi(count_val)); - - results.erase(results.begin(), begin); - results.erase(end, results.end()); - } - - std::string result; - for (const auto &item: results) { - result += (item + ","); - } - - // remove the last ',' - if (!result.empty()) - result.pop_back(); - - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); - - coap_add_option(response, - COAP_OPTION_CONTENT_TYPE, - coap_encode_var_safe(content_type_buf, sizeof(content_type_buf), - COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), - content_type_buf); - coap_add_data(response, result.length(), coap_make_str_const(result.c_str())->s); -} - -void CoapServer::hnd_post_rd_endpoint(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response) { - if (!query) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); - return; - } - - std::string query_str = std::string(reinterpret_cast(query->s)); - std::vector sub_queries = split(query_str, '&'); - std::unordered_map query_map; - - for (const auto &token: sub_queries) { - std::vector sub_query = split(token, '='); - query_map.insert(std::make_pair(sub_query[0], sub_query[1])); + coap_encode_var_safe(buf, sizeof(buf), COAP_MEDIATYPE_TEXT_PLAIN), + buf); } - auto lt = query_map.find("lt"); - std::string lt_value; - if (lt == query_map.end()) { - lt_value = "90000"; // 25 hours - } else { - lt_value = lt->second; - query_map.erase("lt"); - } - - auto uri_path = std::string(reinterpret_cast(coap_resource_get_uri_path(resource)->s)); - auto rd_endpoint = rd_endpoints->get(uri_path); - - coap_add_attr(rd_endpoint, - coap_make_str_const("lt"), - coap_make_str_const(lt_value.c_str()), 0); - - for (const auto &pair: query_map) { - coap_add_attr(rd_endpoint, - coap_make_str_const(pair.first.c_str()), - coap_make_str_const(pair.second.c_str()), 0); - } - - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); -} - -void CoapServer::hnd_delete_rd_endpoint(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response) { - auto uri_path = std::string(reinterpret_cast(coap_resource_get_uri_path(resource)->s)); - auto rd_endpoint = rd_endpoints->get(uri_path); - rd_endpoints->erase(uri_path); - auto ep = coap_find_attr(rd_endpoint, coap_make_str_const("ep")); - auto d = coap_find_attr(rd_endpoint, coap_make_str_const("d")); - std::string key; - - if (ep) { - key += std::string(reinterpret_cast(coap_attr_get_value(ep)->s)); - key += "@"; - } else { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); - return; - } - - if (d) - key += std::string(reinterpret_cast(coap_attr_get_value(d)->s)); - - rd_endpoints->erase(uri_path); - rd_endpoints_by_ep_d->erase(key); - coap_delete_resource(coap_session_get_context(session), resource); - - coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED); -} - -// ### SERVER FUNCTIONS ### -void CoapServer::resource_handler_get(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response) { - unsigned char buf[3]; - - std::string resource_name(reinterpret_cast(coap_resource_get_uri_path(resource)->s)); - auto local_resource = resources->get(resource_name); - auto coap_data = coap_resource_get_userdata(local_resource); - - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); - coap_add_option(response, - COAP_OPTION_CONTENT_TYPE, - coap_encode_var_safe(buf, sizeof(buf), COAP_MEDIATYPE_TEXT_PLAIN), - buf); - - if (coap_data) { - std::string resource_data(reinterpret_cast(coap_data)); - - coap_add_data(response, - resource_data.length(), - reinterpret_cast(resource_data.c_str())); - } -} - -void CoapServer::resource_handler_put(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, - const coap_string_t *query, coap_pdu_t *response) { - std::string uri(reinterpret_cast(coap_resource_get_uri_path(resource)->s)); - auto local_resource = resources->get(uri); - - size_t data_len; - const uint8_t *data; - coap_get_data(request, &data_len, &data); - auto data_str = std::string(reinterpret_cast(data), 0, data_len); - - auto tmp = coap_resource_get_userdata(resource); - free(tmp); - - auto userdata = malloc(data_str.size()); - if (!userdata) { - coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); - return; - } - memset(userdata, 0, data_str.size()); - memcpy(userdata, data_str.c_str(), data_str.size()); - coap_resource_set_userdata(resource, userdata); - - coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); + coap_pdu_set_code(response, static_cast(response_message.status_code)); } // ### PUBLIC ### @@ -590,9 +122,7 @@ CoapServer::CoapServer(const char *host, const char *port, int is_resource_dir) this->client = new CoapClient(); this->client->init_client(); if (is_resource_dir) { - init_resource_dir_local(); - resource_dir_ip = host; - resource_dir_port = port; + this->resource_directory = new ResourceDirectory(this->context); } auto now = std::chrono::duration_cast( @@ -607,16 +137,14 @@ CoapServer::CoapServer(const char *host, const char *port, int is_resource_dir) void CoapServer::destroy_server() { resources->delete_all(); - rd_endpoints->delete_all(); - rd_endpoints_by_ep_d->delete_all(); - if (!is_resource_dir) unpublish_resources(); + if (!this->is_rd()) + unpublish_resources(); + else + delete this->resource_directory; delete client; - delete resources; - delete rd_endpoints; - delete rd_endpoints_by_ep_d; coap_free_context(this->context); } @@ -637,30 +165,18 @@ CoapClient *CoapServer::get_client() { return client; } -bool CoapServer::get_is_rd() { - return is_resource_dir; -} - -std::string CoapServer::get_my_ip() { - return my_ip; +bool CoapServer::is_rd() const { + return this->resource_directory != nullptr; } std::string CoapServer::get_rd_ip() { return this->resource_dir_ip; } -void CoapServer::set_rd_ip(std::string new_ip) { - this->resource_dir_ip = std::move(new_ip); -} - std::string CoapServer::get_rd_port() { return this->resource_dir_port; } -void CoapServer::set_rd_port(std::string new_port) { - this->resource_dir_port = std::move(new_port); -} - int CoapServer::init_server(const char *host, const char *port) { coap_address_t dst; @@ -671,7 +187,7 @@ int CoapServer::init_server(const char *host, const char *port) { coap_startup(); if (resolve_address(host, port, &dst) < 0) { - coap_log(LOG_CRIT, "Failed to resolve address\n"); + LOG_ERR("Failed to resolve address"); coap_free_context(this->context); coap_cleanup(); return EXIT_FAILURE; @@ -681,7 +197,7 @@ int CoapServer::init_server(const char *host, const char *port) { this->endpoint = coap_new_endpoint(context, &dst, COAP_PROTO_UDP); if (!this->context || !this->endpoint) { - coap_log(LOG_EMERG, "Cannot initialize context\n"); + LOG_ERR("Cannot initialize context"); coap_free_context(this->context); coap_cleanup(); return EXIT_FAILURE; @@ -695,11 +211,10 @@ int CoapServer::init_server(const char *host, const char *port) { return EXIT_SUCCESS; } -coap_resource_t *CoapServer::add_resource(const char *uri, const char *attr_list, int actuator) { - auto attrs = split(std::string(attr_list), '&'); +int CoapServer::add_resource(EddieResource *resource) { auto attr_map = std::map(); - for (const auto &attr: attrs) { + for (const auto &attr: resource->get_attributes_str()) { auto attr_split = split(attr, '='); attr_map.insert(std::make_pair(attr_split[0], attr_split[1])); } @@ -709,26 +224,29 @@ coap_resource_t *CoapServer::add_resource(const char *uri, const char *attr_list attr_map["lt"] = "90000"; // 25 hours } - coap_resource_t *resource = coap_resource_init(coap_make_str_const(uri), 0); + coap_str_const_t* uri_path = coap_make_str_const(resource->get_path_str().c_str()); + coap_resource_t *new_coap_resource = coap_resource_init(uri_path, 0); - if (!resource) { - coap_log(LOG_DEBUG, "[CoapServer::add_resource]: Error initializing new resource\n"); - return nullptr; + if (!new_coap_resource) { + LOG_DBG("[CoapServer::add_resource]: Error initializing new resource"); + return -1; } - coap_register_handler(resource, COAP_REQUEST_GET, resource_handler_get); - if (actuator) - coap_register_handler(resource, COAP_REQUEST_PUT, resource_handler_put); + coap_register_handler(new_coap_resource, COAP_REQUEST_GET, resource_handler); + coap_register_handler(new_coap_resource, COAP_REQUEST_PUT, resource_handler); + coap_register_handler(new_coap_resource, COAP_REQUEST_POST, resource_handler); + coap_register_handler(new_coap_resource, COAP_REQUEST_DELETE, resource_handler); for (const auto &pair: attr_map) { - coap_add_attr(resource, coap_make_str_const(pair.first.c_str()), + coap_add_attr(new_coap_resource, coap_make_str_const(pair.first.c_str()), coap_make_str_const(pair.second.c_str()), 0); } + coap_resource_set_userdata(new_coap_resource, resource); - coap_add_resource(this->context, resource); - CoapServer::resources->set(uri, resource); + coap_add_resource(this->context, new_coap_resource); + CoapServer::resources->set(resource->get_path_str(), new_coap_resource); - return resource; + return 0; } int CoapServer::discover_rd() { @@ -757,7 +275,7 @@ int CoapServer::discover_rd() { coap_get_data(pdu, &data_len, &data_pdu); if (!data_pdu) { - coap_log(LOG_ERR, "[CoapServer::publish_resources]: empty payload from resource directory\n"); + LOG_ERR("[CoapServer::publish_resources]: empty payload from resource directory"); return -1; } @@ -765,7 +283,7 @@ int CoapServer::discover_rd() { auto rd_start = data_str.find("coap://"); auto rd_end = data_str.find(';', rd_start); - this->resource_dir_ip = data_str.substr(rd_start + 7, (rd_end - 5) - (rd_start + 7));; + this->resource_dir_ip = data_str.substr(rd_start + 7, (rd_end - 5) - (rd_start + 7)); this->resource_dir_port = data_str.substr(rd_end - 4, 4); CoapClient::multicast_messages->remove_valid_key(m_token); @@ -786,121 +304,59 @@ int CoapServer::publish_resources(int lt) { {"lt", std::to_string(lt)}}; if (data.empty() && !res_list.empty()) { - coap_log(LOG_DEBUG, - "[CoapServer::publish_resources]: unable to create string representation of resource\n"); + LOG_DBG("[CoapServer::publish_resources]: unable to create string representation of resource"); return 0; } - if (is_resource_dir) { - auto old_res = rd_endpoints_by_ep_d->get(ep_d_key); - - if (old_res) { - auto old_res_uri = std::string(reinterpret_cast(coap_resource_get_uri_path(old_res)->s)); - free(coap_resource_get_userdata(old_res)); - coap_delete_resource(this->context, old_res); - rd_endpoints_by_ep_d->erase(ep_d_key); - rd_endpoints->erase(old_res_uri); - } - - auto new_endpoint = create_endpoint(params, data); - - coap_register_handler(new_endpoint, COAP_REQUEST_POST, hnd_post_rd_endpoint); - coap_register_handler(new_endpoint, COAP_REQUEST_DELETE, hnd_delete_rd_endpoint); + if (!this->is_rd()) this->discover_rd(); - coap_add_resource(this->context, new_endpoint); - rd_endpoints->set(std::string(reinterpret_cast(coap_resource_get_uri_path(new_endpoint)->s)), - new_endpoint); - rd_endpoints_by_ep_d->set(ep_d_key, new_endpoint); + if (!resource_dir_ip.empty()) { + int success = 0; + while (!success) { + int token = this->client->send_post_request(resource_dir_ip.c_str(), + resource_dir_port.c_str(), + "rd", query.c_str(), data.c_str()); - return 1; - } - - if (resource_dir_ip.empty()) { - int discovered = this->discover_rd(); - - if (discovered == -1) { - return 0; - } + auto post_resources_lambda = [token, this, &success]() { + std::unique_lockclient_mutex)> lock(client->client_mutex); + auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(COAP_DEFAULT_LEISURE * 2); + client->client_condition.wait_until(lock, timeout); - if (discovered == 0) { - init_resource_dir_local(); + auto post_pdu = CoapClient::messages->get(token); - auto new_endpoint = create_endpoint(params, data); + // extract and set endpoint string + coap_opt_iterator_t opt_iter; + coap_opt_t *option = coap_check_option(post_pdu, COAP_OPTION_LOCATION_PATH, &opt_iter); - coap_register_handler(new_endpoint, COAP_REQUEST_POST, hnd_post_rd_endpoint); - coap_register_handler(new_endpoint, COAP_REQUEST_DELETE, hnd_delete_rd_endpoint); + if (option) { + option = coap_option_next(&opt_iter); + this->rd_endpoint = "rd/" + std::string(reinterpret_cast(coap_opt_value(option))); + success = 1; + } - coap_add_resource(this->context, new_endpoint); - rd_endpoints->set(std::string(reinterpret_cast(coap_resource_get_uri_path(new_endpoint)->s)), - new_endpoint); - rd_endpoints_by_ep_d->set(ep_d_key, new_endpoint); + lock.unlock(); + CoapClient::messages->erase(token); + }; - return 1; + std::thread t(post_resources_lambda); + t.join(); } } + else if (!this->is_rd()) init_resource_dir_local(); - int success = 0; - int token = this->client->send_post_request(resource_dir_ip.c_str(), - resource_dir_port.c_str(), - "rd", query.c_str(), data.c_str()); - - auto post_resources_lambda = [token, this, &success]() { - std::unique_lockclient_mutex)> lock(client->client_mutex); - auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(COAP_DEFAULT_LEISURE * 2); - client->client_condition.wait_until(lock, timeout); - - auto post_pdu = CoapClient::messages->get(token); - - // extract and set endpoint string - coap_opt_iterator_t opt_iter; - coap_opt_t *option = coap_check_option(post_pdu, COAP_OPTION_LOCATION_PATH, &opt_iter); - - if (option) { - option = coap_option_next(&opt_iter); - rd_endpoint = "rd/" + std::string(reinterpret_cast(coap_opt_value(option))); - success = 1; - } - - lock.unlock(); - CoapClient::messages->erase(token); - }; - - while (!success) { - std::thread t(post_resources_lambda); - t.join(); - token = this->client->send_post_request(resource_dir_ip.c_str(), - resource_dir_port.c_str(), - "rd", query.c_str(), data.c_str()); - } + if (this->resource_directory) this->resource_directory->add_endpoint(ep_d_key, params, data); return 1; } -int CoapServer::delete_resource(const char *uri) { - coap_resource_t *to_delete = CoapServer::resources->get(uri); - - // delete resource locally - if (to_delete == nullptr) { - coap_log(LOG_WARNING, "Resource [%s] not found\n", uri); - return EXIT_SUCCESS; - } - - coap_delete_resource(this->context, to_delete); - CoapServer::resources->erase(uri); - - return EXIT_SUCCESS; -} - /** * Ask the RD node to delete all registered resources, if no resource is present in the local node. * Otherwise, update the list of resources present in the RD node. * @return 1 on success, 0 on failure */ int CoapServer::unpublish_resources() { - if (is_resource_dir) { - rd_endpoints->erase(rd_endpoint); - rd_endpoints_by_ep_d->erase(ep + "@" + d); - + if (this->resource_directory) { + ResourceDirectory::remove_endpoint(rd_endpoint, ep, d); return 1; } @@ -979,346 +435,12 @@ int CoapServer::unpublish_resources() { } void CoapServer::init_resource_dir_local() { - resource_dir_ip = my_ip; - resource_dir_port = my_port; - std::string base_address = "coap://" + my_ip + ":" + my_port; - coap_resource_t *resource_dir, *rd_lookup_res, *rd_lookup_ep; - - resource_dir = coap_resource_init(coap_make_str_const("rd"), 0); - coap_register_handler(resource_dir, COAP_REQUEST_POST, hnd_post_rd); - coap_add_attr(resource_dir, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); - coap_add_attr(resource_dir, coap_make_str_const("ct"), coap_make_str_const("40"), 0); - coap_add_attr(resource_dir, coap_make_str_const("base"), coap_make_str_const(base_address.c_str()), 0); - coap_add_resource(this->context, resource_dir); - - rd_lookup_res = coap_resource_init(coap_make_str_const("rd-lookup/res"), 0); - coap_register_handler(rd_lookup_res, COAP_REQUEST_GET, hnd_get_lookup_res); - coap_add_attr(rd_lookup_res, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); - coap_add_attr(rd_lookup_res, coap_make_str_const("ct"), coap_make_str_const("40"), 0); - coap_resource_set_get_observable(rd_lookup_res, 1); - coap_add_resource(this->context, rd_lookup_res); - - rd_lookup_ep = coap_resource_init(coap_make_str_const("rd-lookup/ep"), 0); - coap_register_handler(rd_lookup_ep, COAP_REQUEST_GET, hnd_get_lookup_ep); - coap_add_attr(rd_lookup_ep, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); - coap_add_attr(rd_lookup_ep, coap_make_str_const("ct"), coap_make_str_const("40"), 0); - coap_add_resource(this->context, rd_lookup_ep); - - is_resource_dir = true; + this->resource_directory = new ResourceDirectory(this->context); } -void CoapServer::check_resource_expiration() { - if (last_lt_check == 0) { - last_lt_check = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count(); - } - - auto resources_list = resources->get_all(); - std::unique_lockm_)> lock(rd_endpoints->m_); - int local_changes = 0; - - auto now = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()).count(); - - auto delta = now - last_lt_check; - - for (auto resource: resources_list) { - auto lt_attr = coap_find_attr(resource, coap_make_str_const("lt")); - - if (!lt_attr) { - lock.unlock(); - resources->erase(std::string(reinterpret_cast(coap_resource_get_uri_path(resource)->s))); - lock.lock(); - coap_delete_resource(context, resource); - local_changes = 1; - } else { - auto lt_val = std::stol(std::string(reinterpret_cast(coap_attr_get_value(lt_attr)->s))); - lt_val -= delta; - if (lt_val <= 0) { - lock.unlock(); - resources->erase(std::string(reinterpret_cast(coap_resource_get_uri_path(resource)->s))); - lock.lock(); - coap_delete_resource(context, resource); - local_changes = 1; - } else { - coap_add_attr(resource, coap_make_str_const("lt"), - coap_make_str_const(std::to_string(lt_val).c_str()), 0); - } - } - } - - lock.unlock(); - - if (local_changes) this->publish_resources(); - - lock.unlock(); - - if (is_resource_dir) { - auto endpoints = rd_endpoints->get_all(); - lock.lock(); - for (auto rd_ep: endpoints) { - auto lt_attr = coap_find_attr(rd_ep, coap_make_str_const("lt")); - - coap_attr_t *ep_attr = coap_find_attr(rd_ep, coap_make_str_const("ep")); - coap_attr_t *d_attr = coap_find_attr(rd_ep, coap_make_str_const("d")); - auto d_val = coap_attr_get_value(d_attr)->s; - std::string ep_d_key = std::string(reinterpret_cast(coap_attr_get_value(ep_attr)->s)) + "@"; - - if (d_val) { - ep_d_key += std::string(reinterpret_cast(d_val)); - } - - if (!lt_attr) { - lock.unlock(); - rd_endpoints->erase(std::string(reinterpret_cast(coap_resource_get_uri_path(rd_ep)->s))); - rd_endpoints_by_ep_d->erase(ep_d_key); - lock.lock(); - coap_delete_resource(context, rd_ep); - } else { - auto lt_val = std::stol(std::string(reinterpret_cast(coap_attr_get_value(lt_attr)->s))); - lt_val -= delta; - - if (lt_val <= 0) { - lock.unlock(); - rd_endpoints->erase( - std::string(reinterpret_cast(coap_resource_get_uri_path(rd_ep)->s))); - rd_endpoints_by_ep_d->erase(ep_d_key); - lock.lock(); - coap_delete_resource(context, rd_ep); - } else { - coap_add_attr(rd_ep, coap_make_str_const("lt"), - coap_make_str_const(std::to_string(lt_val).c_str()), 0); - } - } - - } - } - - last_lt_check = now; -} - -std::list -CoapServer::endpoints_to_string_list_filtered(std::map filters) { - std::list results; - std::list empty; - auto endpoints = rd_endpoints->get_all(); - std::unique_lockm_)> lock(rd_endpoints->m_); - - size_t buf_size = 1000; - auto *buf = static_cast(malloc(buf_size * sizeof(unsigned char))); - size_t len; - - for (auto endpoint: endpoints) { - size_t offset = 0; - len = buf_size; - coap_print_status_t print_result; - - auto it = filters.begin(); - int ok_to_return = 1; - - while (it != filters.end() && ok_to_return) { - if (it->first.empty() || it->second.empty()) { - free(buf); - return empty; - } - auto key = it->first; - auto value = it->second; - - coap_attr_t *attr = coap_find_attr(endpoint, coap_make_str_const(key.c_str())); - if (!attr) { - ok_to_return = 0; - } else { - auto src = std::string(reinterpret_cast(coap_attr_get_value(attr)->s)); - - if (value.back() == '*') { - auto dst = std::string(value, 0, value.length() - 1); - ok_to_return = match(src, dst, 1); - } else { - ok_to_return = match(src, value, 0); - } - } - - it++; - } - - if (ok_to_return) { - print_result = coap_print_link(endpoint, buf, &len, &offset); - - if (print_result & COAP_PRINT_STATUS_ERROR) { - free(buf); - empty.emplace_back("error"); - return empty; - } - - while (print_result & COAP_PRINT_STATUS_TRUNC) { - buf_size *= 2; - buf = static_cast(realloc(buf, buf_size)); - len = buf_size; - offset = 0; - print_result = coap_print_link(endpoint, buf, &len, &offset); - } - - std::string partial = std::string(reinterpret_cast(buf), 0, len); - auto lt = partial.find("lt="); - auto end = partial.find(';', lt + 1); - - if (end == std::string::npos) { - partial = partial.substr(0, lt); - } else { - partial.erase(lt, end - lt + 1); - } - - results.push_back(partial); - } - } - - free(buf); - return results; -} - -std::list -CoapServer::rd_resources_to_string_list_filtered(std::map filters) { - std::list results; - std::list empty; - auto endpoints = rd_endpoints->get_all(); - std::unique_lockm_)> lock(rd_endpoints->m_); - - if (filters.empty()) { - // return the whole list of resources - for (auto endpoint: endpoints) { - auto data = std::string((char *) coap_resource_get_userdata(endpoint)); - auto data_list = split(data, ','); - - auto base = coap_find_attr(endpoint, coap_make_str_const("base")); - - if (!base) { - empty.emplace_back("error"); - return empty; - } - - auto base_val = std::string(reinterpret_cast(coap_attr_get_value(base)->s)); - for (const auto &data_elem: data_list) { - auto to_add = "<" + base_val + std::string(data_elem, 1); - - if (to_add.back() == ',') { - results.emplace_back(std::string(to_add, 0, to_add.length() - 1)); - } else { - results.push_back(to_add); - } - } - } - - return results; - } else { - auto href = filters.find("href"); - for (auto endpoint: endpoints) { - auto resources_in_endpoint = split(std::string((char *) coap_resource_get_userdata(endpoint)), ','); - auto resources_matched = std::map(); - int ok_to_return = 1; - - for (int i = 0; i < resources_in_endpoint.size(); i++) { - auto string = resources_in_endpoint[i]; - - if (string.empty()) { - empty.emplace_back("error"); - return empty; - } - - if (string.back() == ',') - string.pop_back(); - - resources_matched.insert(std::make_pair(i, string)); - } - - if (href != filters.end()) { - std::string href_value = href->second; - const uint8_t *tmp = coap_attr_get_value(coap_find_attr(endpoint, coap_make_str_const("base")))->s; - auto base_uri = std::string(reinterpret_cast(tmp)); - tmp = coap_resource_get_uri_path(endpoint)->s; - std::string endpoint_uri; - - if (!match(endpoint_uri, href_value, 0)) { - // do the match also for every resource - for (int i = 0; i < resources_in_endpoint.size(); i++) { - auto resource = resources_in_endpoint[i]; - - std::string resource_href; - if (href_value.find("coap://") == std::string::npos) { - auto end_name = resource.find('>'); - if (end_name == std::string::npos) { - empty.emplace_back("error"); - return empty; - } - - resource_href = "coap://" + base_uri + std::string(resource).substr(1, end_name - 1); - } - - if (!match(resource_href, href_value, 0)) { - // no match, remove resource - resources_matched.erase(i); - } - } - } - } - - for (auto it = filters.begin(); it != filters.end() && ok_to_return && !(resources_matched.empty()); it++) { - if (it->first.empty() || it->second.empty()) { - empty.emplace_back("error"); - return empty; - } - - auto key = it->first; - auto value = it->second; - auto ep_attr = coap_find_attr(endpoint, coap_make_str_const(key.c_str())); - - if (ep_attr) { - //attribute is in the endpoint, do the match here - auto ep_attr_val = std::string(reinterpret_cast(coap_attr_get_value(ep_attr)->s)); - - if (value.back() == '*') { - auto dst = std::string(value, 0, value.length() - 1); - ok_to_return = match(ep_attr_val, dst, 1); - } else { - ok_to_return = match(ep_attr_val, value, 0); - } - } else { - for (int i = 0; i < resources_in_endpoint.size(); i++) { - auto resource = resources_in_endpoint[i]; - - auto token_position = resource.find(key); - if (token_position) { - // token is here, do the match - auto token_value_end = resource.find('&', token_position); - auto token_value = std::string(resource, token_position, - token_value_end - key.length() + 1); - - if (token_value.front() == '\"' && token_value.back() == '\"') - token_value = std::string(token_value, 1, token_value.length() - 2); - - int val_match = 0; - if (value.back() == '*') { - auto dst = std::string(value, 0, value.length() - 1); - val_match = match(token_value, dst, 1); - } else { - val_match = match(token_value, value, 0); - } - - if (!val_match) { - resources_matched.erase(i); - } - } else { - // token is not present, remove the resource from the map - resources_matched.erase(i); - } - } - } - } - - for (const auto &pair: resources_matched) results.push_back(pair.second); - } - - return results; - } +std::list CoapServer::rd_resources_to_string_list_filtered(const std::map &filters) { + if (!this->resource_directory) return {}; + return ResourceDirectory::rd_resources_to_string_list_filtered(filters); } int CoapServer::run() { diff --git a/linux/communication/src/EddieEndpoint.cpp b/linux/communication/src/EddieEndpoint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a2781efdb73e628ac7190a571e17425a6f3d176f --- /dev/null +++ b/linux/communication/src/EddieEndpoint.cpp @@ -0,0 +1,179 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileCopyrightText: Huawei Inc. + * SPDX-FileCopyrightText: Politecnico Di Milano + */ +#include "EddieEndpoint.h" + +#include + +#define NOW std::chrono::system_clock::now() + +std::string get_local_node_ip() { + std::string ip; + struct ifaddrs *if_addresses; + int rc; + char host[NI_MAXHOST]; + + if (getifaddrs(&if_addresses) == -1) { + perror("[CoapNode::connect]: getifaddrs"); + exit(EXIT_FAILURE); + } + + for (struct ifaddrs *ifa = if_addresses; ifa != nullptr; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6 || strcmp(ifa->ifa_name, "lo") == 0) + continue; + + rc = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), host, NI_MAXHOST, + nullptr, 0, NI_NUMERICHOST); + + if (rc != 0) { + LOG_DBG("[CoapNode::connect]: getnameinfo failed: %s", gai_strerror(rc)); + exit(EXIT_FAILURE); + } + + std::string host_str = host; + + ip = host_str.substr(0, host_str.length() - (strlen(ifa->ifa_name) + 1)); + + if (ip.empty()) { + LOG_DBG("[CoapNode::connect]: no valid ip address found for this machine"); + exit(EXIT_FAILURE); + } + } + + return ip; +} + +CoapClient* EddieEndpoint::get_client(){ + return this->server ? this->server->get_client() : nullptr; +} + +CoapServer* EddieEndpoint::get_server() { + return this->server; +} + +EddieEndpoint::EddieEndpoint() { + std::string ip = get_local_node_ip(); + + server = new CoapServer(ip.c_str(), std::to_string(COAP_DEFAULT_PORT).c_str(), 0); +} + +EddieEndpoint::~EddieEndpoint() { + // TODO: client is owned by the server therefore server has to destroy it not here + if (get_client() != nullptr) get_client()->destroy_client(); + if (get_server() != nullptr) get_server()->destroy_server(); + + delete server; + + coap_cleanup(); +} + +int EddieEndpoint::discover_rd() { + return get_server()->discover_rd(); +} + +std::string EddieEndpoint::get_resources_from_rd() { + if (get_server() == nullptr) { + coap_log(LOG_EMERG, "[CoapNode::discover]: server is null\n"); + exit(EXIT_FAILURE); + } + + std::map empty; + + // this node is rd, get the info directly + if (get_server()->is_rd()) { + auto list = get_server()->rd_resources_to_string_list_filtered(empty); + std::string to_return; + + for (const auto &res: list) { + to_return += res + ","; + } + + if (to_return.back() == ',') to_return.pop_back(); + + return to_return; + } + + // this node is not rd, doesn't know who is rd + std::string resource_str; + if (get_server()->get_rd_ip().empty()) { + if (get_server()->discover_rd() != 1) { + coap_log(LOG_ERR, "[CoapNode::discover]: error looking up rd node\n"); + exit(-1); + } + } + + if (!resource_str.empty()) return resource_str; + + auto token = get_client()->send_get_request(get_server()->get_rd_ip().c_str(), + get_server()->get_rd_port().c_str(), + "rd-lookup/res", nullptr); + + auto resource_loop = [this, &token, &resource_str]() { + std::unique_lockget_client()->client_mutex)> lock(this->get_client()->client_mutex); + auto timeout = NOW + std::chrono::seconds(COAP_DEFAULT_LEISURE); + this->get_client()->client_condition.wait_until(lock, timeout); + + auto answer = CoapClient::messages->get(token); + + size_t data_len; + const uint8_t *data; + coap_get_data(answer, &data_len, &data); + + if (!data) { + resource_str = ""; + } else { + resource_str = std::string(reinterpret_cast(data)); + } + + free((void *) data); + }; + + std::thread resource_thread(resource_loop); + resource_thread.join(); + + return resource_str; +} + +int EddieEndpoint::add_resource(EddieResource *resource) { + get_server()->add_resource(resource); + this->publish_resources(); + return 0; +} + +int EddieEndpoint::start_server() { + auto server_run = [this]() { + if (!this->get_server()) { + LOG_DBG("[CoapNode::run]: server is NULL"); + exit(EXIT_FAILURE); + } + + while (!this->get_server()->get_quit()) + coap_io_process(this->get_server()->get_context(), COAP_IO_WAIT); + }; + + auto client_run = [this]() { + if (!this->get_server()) { + LOG_DBG("[CoapNode::run]: server is NULL"); + exit(EXIT_FAILURE); + } + + while (this->get_server()->get_quit() == 0) + coap_io_process(this->get_client()->get_context(), COAP_IO_WAIT); + }; + + std::thread server_thread(server_run); + std::thread client_thread(client_run); + + server_thread.join(); + client_thread.join(); + + return 0; +} + +int EddieEndpoint::publish_resources() { + get_server()->publish_resources(); + return 0; +} \ No newline at end of file diff --git a/linux/communication/src/ResourceDirectory.cpp b/linux/communication/src/ResourceDirectory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e1a0a74905e21d4aa853082cca317c830883fd7 --- /dev/null +++ b/linux/communication/src/ResourceDirectory.cpp @@ -0,0 +1,756 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileCopyrightText: Huawei Inc. + * SPDX-FileCopyrightText: Politecnico Di Milano + */ + +#include "ResourceDirectory.h" +#include "common.h" + +#include +#include +#include +#include +#include +#include +#define COAP_IP4_MULTICAST "224.0.1.187" +#define COAP_IP6_MULTICAST_LOCAL_LINK "FF02::FD" +#define COAP_IP6_MULTICAST_SITE_LOCAL "FF05::FD" +#define RD_PORT "5683" +#define DEFAULT_SECTOR_NAME "sectoreddie" + +ThreadSafeMap *ResourceDirectory::rd_endpoints = new ThreadSafeMap(); +ThreadSafeMap *ResourceDirectory::rd_endpoints_by_ep_d = new ThreadSafeMap(); + +coap_resource_t* ResourceDirectory::create_endpoint( + const std::unordered_map ¶ms, + const std::string &data +) { + coap_resource_t *new_endpoint; + + auto now = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + + std::ostringstream oss; + oss << now; + + std::string new_ep_name = std::string("rd/") + oss.str(); + new_endpoint = coap_resource_init(coap_make_str_const(new_ep_name.c_str()), 0); + + for (const auto ¶m: params) { + coap_add_attr(new_endpoint, coap_make_str_const(param.first.c_str()), + coap_make_str_const(param.second.c_str()), 0); + } + + auto userdata = malloc(data.size()); + if (!userdata) { + return nullptr; + } + memset(userdata, 0, data.size()); + memcpy(userdata, data.c_str(), data.size()); + + coap_resource_set_userdata(new_endpoint, userdata); + + return new_endpoint; +} + +std::string ResourceDirectory::add_endpoint_to_rd(const std::string& ep_d_key, + coap_context_t *context, + const std::unordered_map ¶ms, + const std::string &data) { + auto res = rd_endpoints_by_ep_d->get(ep_d_key); + + if (res) { + auto uri = std::string(reinterpret_cast(coap_resource_get_uri_path(res)->s)); + free(coap_resource_get_userdata(res)); + coap_delete_resource(context, res); + rd_endpoints_by_ep_d->erase(ep_d_key); + rd_endpoints->erase(uri); + } + + auto new_endpoint = create_endpoint(params, data); + + if (new_endpoint == nullptr) { + return ""; + } + + auto ep_name = std::string(reinterpret_cast(coap_resource_get_uri_path(new_endpoint)->s)); + + // ADD HANDLERS + coap_register_handler(new_endpoint, COAP_REQUEST_POST, hnd_post_rd_endpoint); + coap_register_handler(new_endpoint, COAP_REQUEST_DELETE, hnd_delete_rd_endpoint); + + coap_add_resource(context, new_endpoint); + rd_endpoints->set(ep_name, new_endpoint); + rd_endpoints_by_ep_d->set(ep_d_key, new_endpoint); + + return ep_name; +} + +std::list +ResourceDirectory::endpoints_to_string_list_filtered(std::map filters) { + std::list results; + std::list empty; + auto endpoints = rd_endpoints->get_all(); + std::unique_lockm_)> lock(rd_endpoints->m_); + + size_t buf_size = 1000; + auto *buf = static_cast(malloc(buf_size * sizeof(unsigned char))); + size_t len; + + for (auto endpoint: endpoints) { + size_t offset = 0; + len = buf_size; + coap_print_status_t print_result; + + auto it = filters.begin(); + int ok_to_return = 1; + + while (it != filters.end() && ok_to_return) { + if (it->first.empty() || it->second.empty()) { + free(buf); + return empty; + } + auto key = it->first; + auto value = it->second; + + coap_attr_t *attr = coap_find_attr(endpoint, coap_make_str_const(key.c_str())); + if (!attr) { + ok_to_return = 0; + } else { + auto src = std::string(reinterpret_cast(coap_attr_get_value(attr)->s)); + + if (value.back() == '*') { + auto dst = std::string(value, 0, value.length() - 1); + ok_to_return = match(src, dst, 1); + } else { + ok_to_return = match(src, value, 0); + } + } + + it++; + } + + if (ok_to_return) { + print_result = coap_print_link(endpoint, buf, &len, &offset); + + if (print_result & COAP_PRINT_STATUS_ERROR) { + free(buf); + empty.emplace_back("error"); + return empty; + } + + while (print_result & COAP_PRINT_STATUS_TRUNC) { + buf_size *= 2; + buf = static_cast(realloc(buf, buf_size)); + len = buf_size; + offset = 0; + print_result = coap_print_link(endpoint, buf, &len, &offset); + } + + std::string partial = std::string(reinterpret_cast(buf), 0, len); + auto lt = partial.find("lt="); + auto end = partial.find(';', lt + 1); + + if (end == std::string::npos) { + partial = partial.substr(0, lt); + } else { + partial.erase(lt, end - lt + 1); + } + + results.push_back(partial); + } + } + + free(buf); + return results; +} + +std::list +ResourceDirectory::rd_resources_to_string_list_filtered(std::map filters) { + std::list results; + std::list empty; + auto endpoints = rd_endpoints->get_all(); + std::unique_lockm_)> lock(rd_endpoints->m_); + + if (filters.empty()) { + // return the whole list of resources + for (auto endpoint: endpoints) { + auto data = std::string((char *) coap_resource_get_userdata(endpoint)); + auto data_list = split(data, ','); + + auto base = coap_find_attr(endpoint, coap_make_str_const("base")); + + if (!base) { + empty.emplace_back("error"); + return empty; + } + + auto base_val = std::string(reinterpret_cast(coap_attr_get_value(base)->s)); + for (const auto &data_elem: data_list) { + auto to_add = "<" + base_val + std::string(data_elem, 1); + + if (to_add.back() == ',') { + results.emplace_back(std::string(to_add, 0, to_add.length() - 1)); + } else { + results.push_back(to_add); + } + } + } + + return results; + } else { + auto href = filters.find("href"); + for (auto endpoint: endpoints) { + auto resources_in_endpoint = split(std::string((char *) coap_resource_get_userdata(endpoint)), ','); + auto resources_matched = std::map(); + int ok_to_return = 1; + + for (int i = 0; i < resources_in_endpoint.size(); i++) { + auto string = resources_in_endpoint[i]; + + if (string.empty()) { + empty.emplace_back("error"); + return empty; + } + + if (string.back() == ',') + string.pop_back(); + + resources_matched.insert(std::make_pair(i, string)); + } + + if (href != filters.end()) { + std::string href_value = href->second; + const uint8_t *tmp = coap_attr_get_value(coap_find_attr(endpoint, coap_make_str_const("base")))->s; + auto base_uri = std::string(reinterpret_cast(tmp)); + tmp = coap_resource_get_uri_path(endpoint)->s; + std::string endpoint_uri; //TODO: fix this + + if (!match(endpoint_uri, href_value, 0)) { + // do the match also for every resource + for (int i = 0; i < resources_in_endpoint.size(); i++) { + auto resource = resources_in_endpoint[i]; + + std::string resource_href; + if (href_value.find("coap://") == std::string::npos) { + auto end_name = resource.find('>'); + if (end_name == std::string::npos) { + empty.emplace_back("error"); + return empty; + } + + resource_href = "coap://" + base_uri + std::string(resource).substr(1, end_name - 1); + } + + if (!match(resource_href, href_value, 0)) { + // no match, remove resource + resources_matched.erase(i); + } + } + } + } + + for (auto it = filters.begin(); it != filters.end() && ok_to_return && !(resources_matched.empty()); it++) { + if (it->first.empty() || it->second.empty()) { + empty.emplace_back("error"); + return empty; + } + + auto key = it->first; + auto value = it->second; + auto ep_attr = coap_find_attr(endpoint, coap_make_str_const(key.c_str())); + + if (ep_attr) { + //attribute is in the endpoint, do the match here + auto ep_attr_val = std::string(reinterpret_cast(coap_attr_get_value(ep_attr)->s)); + + if (value.back() == '*') { + auto dst = std::string(value, 0, value.length() - 1); + ok_to_return = match(ep_attr_val, dst, 1); + } else { + ok_to_return = match(ep_attr_val, value, 0); + } + } else { + for (int i = 0; i < resources_in_endpoint.size(); i++) { + auto resource = resources_in_endpoint[i]; + + auto token_position = resource.find(key); + if (token_position) { + // token is here, do the match + auto token_value_end = resource.find('&', token_position); + auto token_value = std::string(resource, token_position, + token_value_end - key.length() + 1); + + if (token_value.front() == '\"' && token_value.back() == '\"') + token_value = std::string(token_value, 1, token_value.length() - 2); + + int val_match; + if (value.back() == '*') { + auto dst = std::string(value, 0, value.length() - 1); + val_match = match(token_value, dst, 1); + } else { + val_match = match(token_value, value, 0); + } + + if (!val_match) { + resources_matched.erase(i); + } + } else { + // token is not present, remove the resource from the map + resources_matched.erase(i); + } + } + } + } + + for (const auto &pair: resources_matched) results.push_back(pair.second); + } + + return results; + } +} + +void ResourceDirectory::hnd_post_rd(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response) { + if (!query) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); + return; + } + + size_t data_len; + const u_int8_t *data; + coap_get_data(request, &data_len, &data); + if (!data) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); + return; + } + + std::string data_str = std::string(reinterpret_cast(data), data_len); + if (data_str.empty()) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); + return; + } + + std::string query_str = std::string(reinterpret_cast(query->s), query->length); + std::vector sub_queries = split(query_str, '&'); + std::unordered_map query_map; + + for (const auto &token: sub_queries) { + std::vector sub_query = split(token, '='); + query_map.insert(std::make_pair(sub_query[0], sub_query[1])); + } + + auto ep = query_map.find("ep"); + if (ep == query_map.end()) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); + return; + } + + auto base = query_map.find("base"); + if (base == query_map.end()) { + // TODO: fix ipv6 address problem + auto remote = coap_session_get_addr_remote(session); + char ip[INET6_ADDRSTRLEN]; + int port; + switch (remote->addr.sa.sa_family) { + case AF_INET: + case AF_INET6: + inet_ntop(AF_INET6, &remote->addr.sin6.sin6_addr, ip, INET6_ADDRSTRLEN); + port = htons(remote->addr.sin6.sin6_port); + break; + } + + std::string base_address = "coap://" + std::string(ip) + ":" + std::to_string(port); + query_map.insert(std::make_pair("base", base_address)); + } + + auto lt = query_map.find("lt"); + if (lt == query_map.end()) { + const std::string default_lt = "90000"; // 25 hours + query_map.insert(std::make_pair("lt", default_lt)); + } + + auto d = query_map.find("d"); + std::string d_name; + if (!(d == query_map.end())) d_name = d->second; + else d_name = DEFAULT_SECTOR_NAME; + + std::string ep_sector_key = ep->second + "@" + d_name; + + std::string ep_name = add_endpoint_to_rd(ep_sector_key, coap_session_get_context(session), query_map, data_str); + if(ep_name.empty()) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); + } + + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CREATED); + + { + unsigned char _b[1024]; + unsigned char *b = _b; + size_t buf_len = sizeof(_b); + int n_seg; + + n_seg = coap_split_path(reinterpret_cast(ep_name.c_str()), + ep_name.length(), + b, + &buf_len); + + while (n_seg--) { + coap_add_option(response, + COAP_OPTION_LOCATION_PATH, + coap_opt_length(b), + coap_opt_value(b)); + b += coap_opt_size(b); + } + } +} + +void ResourceDirectory::hnd_get_lookup_res(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response) { + std::string query_str; + + if (query) { + query_str = std::string(reinterpret_cast(query->s)); + } + + unsigned char content_type_buf[3]; + + std::vector sub_queries = split(query_str, '&'); + std::map query_map; + + for (const auto &token: sub_queries) { + std::vector sub_query = split(token, '='); + query_map.insert(std::make_pair(sub_query[0], sub_query[1])); + } + + auto page = query_map.find("page"); + auto count = query_map.find("count"); + + if (count != query_map.end() && page == query_map.end()) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); + return; + } + + std::string page_val; + std::string count_val; + if (page == query_map.end()) { + page_val = "-1"; + } else { + page_val = page->second; + query_map.erase("page"); + } + + if (count == query_map.end()) { + count_val = "-1"; + } else { + count_val = count->second; + query_map.erase("count"); + } + + std::list results = rd_resources_to_string_list_filtered(query_map); + + if (results.empty()) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); + coap_add_option(response, + COAP_OPTION_CONTENT_TYPE, + coap_encode_var_safe(content_type_buf, sizeof(content_type_buf), + COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), + content_type_buf); + return; + } + + if (results.front() == "error") { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); + return; + } + + if (count_val != "-1" && page_val == "-1") { + // return only the first `count_val` number of strings + auto begin = results.begin(); + std::advance(begin, stoi(count_val)); + + results.erase(begin, results.end()); + } else if (count_val != "-1" && page_val != "-1") { + // return `count_val` number of strings, starting from the `count_val` * `page_val` + auto begin = results.begin(); + std::advance(begin, stoi(count_val) * stoi(page_val)); + auto end = begin; + std::advance(end, stoi(count_val)); + + results.erase(results.begin(), begin); + results.erase(end, results.end()); + } + + std::string result; + for (const auto &item: results) { + result += (item + ","); + } + + // remove the last ',' + if (!result.empty()) + result.pop_back(); + + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); + + coap_add_option(response, + COAP_OPTION_CONTENT_TYPE, + coap_encode_var_safe(content_type_buf, sizeof(content_type_buf), + COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), + content_type_buf); + coap_add_data(response, result.length(), coap_make_str_const(result.c_str())->s); +} + +void ResourceDirectory::hnd_get_lookup_ep(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response) { + std::string query_str; + + if (query) { + query_str = std::string(reinterpret_cast(query->s)); + } + + unsigned char content_type_buf[3]; + + std::vector sub_queries = split(query_str, '&'); + std::map query_map; + + for (const auto &token: sub_queries) { + std::vector sub_query = split(token, '='); + query_map.insert(std::make_pair(sub_query[0], sub_query[1])); + } + + auto page = query_map.find("page"); + auto count = query_map.find("count"); + + if (count != query_map.end() && page == query_map.end()) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); + return; + } + + std::string page_val; + std::string count_val; + if (page == query_map.end()) { + page_val = "-1"; + } else { + page_val = page->second; + query_map.erase("page"); + } + + if (count == query_map.end()) { + count_val = "-1"; + } else { + count_val = count->second; + query_map.erase("count"); + } + + std::list results = endpoints_to_string_list_filtered(query_map); + + if (results.empty()) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); + coap_add_option(response, + COAP_OPTION_CONTENT_TYPE, + coap_encode_var_safe(content_type_buf, sizeof(content_type_buf), + COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), + content_type_buf); + return; + } + + if (results.front() == "error") { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); + return; + } + + + if (count_val != "-1" && page_val == "-1") { + // return only the first `count_val` number of strings + auto begin = results.begin(); + std::advance(begin, stoi(count_val)); + + results.erase(begin, results.end()); + } else if (count_val != "-1" && page_val != "-1") { + // return `count_val` number of strings, starting from the `count_val` * `page_val` + auto begin = results.begin(); + std::advance(begin, stoi(count_val) * stoi(page_val)); + auto end = begin; + std::advance(end, stoi(count_val)); + + results.erase(results.begin(), begin); + results.erase(end, results.end()); + } + + std::string result; + for (const auto &item: results) { + result += (item + ","); + } + + // remove the last ',' + if (!result.empty()) + result.pop_back(); + + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT); + + coap_add_option(response, + COAP_OPTION_CONTENT_TYPE, + coap_encode_var_safe(content_type_buf, sizeof(content_type_buf), + COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), + content_type_buf); + coap_add_data(response, result.length(), coap_make_str_const(result.c_str())->s); +} + +void ResourceDirectory::hnd_post_rd_endpoint(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response) { + if (!query) { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_BAD_REQUEST); + return; + } + + std::string query_str = std::string(reinterpret_cast(query->s)); + std::vector sub_queries = split(query_str, '&'); + std::unordered_map query_map; + + for (const auto &token: sub_queries) { + std::vector sub_query = split(token, '='); + query_map.insert(std::make_pair(sub_query[0], sub_query[1])); + } + + auto lt = query_map.find("lt"); + std::string lt_value; + if (lt == query_map.end()) { + lt_value = "90000"; // 25 hours + } else { + lt_value = lt->second; + query_map.erase("lt"); + } + + auto uri_path = std::string(reinterpret_cast(coap_resource_get_uri_path(resource)->s)); + auto rd_endpoint = rd_endpoints->get(uri_path); + + coap_add_attr(rd_endpoint, + coap_make_str_const("lt"), + coap_make_str_const(lt_value.c_str()), 0); + + for (const auto &pair: query_map) { + coap_add_attr(rd_endpoint, + coap_make_str_const(pair.first.c_str()), + coap_make_str_const(pair.second.c_str()), 0); + } + + coap_pdu_set_code(response, COAP_RESPONSE_CODE_CHANGED); +} + +void ResourceDirectory::hnd_delete_rd_endpoint(coap_resource_t *resource, coap_session_t *session, const coap_pdu_t *request, + const coap_string_t *query, coap_pdu_t *response) { + auto uri_path = std::string(reinterpret_cast(coap_resource_get_uri_path(resource)->s)); + auto rd_endpoint = rd_endpoints->get(uri_path); + rd_endpoints->erase(uri_path); + auto ep = coap_find_attr(rd_endpoint, coap_make_str_const("ep")); + auto d = coap_find_attr(rd_endpoint, coap_make_str_const("d")); + std::string key; + + if (ep) { + key += std::string(reinterpret_cast(coap_attr_get_value(ep)->s)); + key += "@"; + } else { + coap_pdu_set_code(response, COAP_RESPONSE_CODE_INTERNAL_ERROR); + return; + } + + if (d) + key += std::string(reinterpret_cast(coap_attr_get_value(d)->s)); + + rd_endpoints->erase(uri_path); + rd_endpoints_by_ep_d->erase(key); + coap_delete_resource(coap_session_get_context(session), resource); + + coap_pdu_set_code(response, COAP_RESPONSE_CODE_DELETED); +} + +int ResourceDirectory::init_server_context() { + coap_startup(); + + coap_address_t dst; + if (resolve_address("::", RD_PORT, &dst) < 0) { + LOG_ERR("Failed to resolve address"); + coap_free_context(this->context); + coap_cleanup(); + return -1; + } + + this->context = coap_new_context(nullptr); + coap_new_endpoint(context, &dst, COAP_PROTO_UDP); + + // be available on multicast groups + coap_join_mcast_group(this->context, COAP_IP4_MULTICAST); + coap_join_mcast_group(this->context, COAP_IP6_MULTICAST_LOCAL_LINK); + coap_join_mcast_group(this->context, COAP_IP6_MULTICAST_SITE_LOCAL); + + return 0; +} + +ResourceDirectory::ResourceDirectory(coap_context_t *server_context) { + + if (server_context != nullptr) + this->context = server_context; + else + this->init_server_context(); + + if (this->context == nullptr) { + // TODO: throw exception + return; + } + + coap_resource_t *resource_dir, *rd_lookup_res, *rd_lookup_ep; + + resource_dir = coap_resource_init(coap_make_str_const("rd"), 0); + coap_register_handler(resource_dir, COAP_REQUEST_POST, hnd_post_rd); + coap_add_attr(resource_dir, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); + coap_add_attr(resource_dir, coap_make_str_const("ct"), coap_make_str_const("40"), 0); + coap_add_resource(this->context, resource_dir); + + rd_lookup_res = coap_resource_init(coap_make_str_const("rd-lookup/res"), 0); + coap_register_handler(rd_lookup_res, COAP_REQUEST_GET, hnd_get_lookup_res); + coap_add_attr(rd_lookup_res, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); + coap_add_attr(rd_lookup_res, coap_make_str_const("ct"), coap_make_str_const("40"), 0); + coap_resource_set_get_observable(rd_lookup_res, 1); + coap_add_resource(this->context, rd_lookup_res); + + rd_lookup_ep = coap_resource_init(coap_make_str_const("rd-lookup/ep"), 0); + coap_register_handler(rd_lookup_ep, COAP_REQUEST_GET, hnd_get_lookup_ep); + coap_add_attr(rd_lookup_ep, coap_make_str_const("rt"), coap_make_str_const("\"core.rd\""), 0); + coap_add_attr(rd_lookup_ep, coap_make_str_const("ct"), coap_make_str_const("40"), 0); + coap_add_resource(this->context, rd_lookup_ep); +} + +ResourceDirectory::~ResourceDirectory() { + rd_endpoints->delete_all(); + rd_endpoints_by_ep_d->delete_all(); + + delete rd_endpoints; + delete rd_endpoints_by_ep_d; +} + +int ResourceDirectory::run() { + LOG_INFO("Running resource directory"); + while (this->get_quit() == 0) { + coap_io_process(this->context, COAP_IO_WAIT); + } + + return 0; +} + +int ResourceDirectory::add_endpoint( + const std::string& ep_d_key, + const std::unordered_map ¶ms, + const std::string &data +) { + std::string ep_name = ResourceDirectory::add_endpoint_to_rd(ep_d_key, this->context, params, data); + return ep_name.empty(); +} + +int ResourceDirectory::remove_endpoint(const std::string& rd_endpoint, const std::string& ep, const std::string& d) { + rd_endpoints->erase(rd_endpoint); + rd_endpoints_by_ep_d->erase(ep + "@" + d); + + return 0; +} \ No newline at end of file diff --git a/linux/communication/src/common.cpp b/linux/communication/src/common.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdba77fb7bc144bf1b8a44db5f4067fee99685e3 --- /dev/null +++ b/linux/communication/src/common.cpp @@ -0,0 +1,69 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileCopyrightText: Huawei Inc. + * SPDX-FileCopyrightText: Politecnico Di Milano + */ + +#include "common.h" + +#include +#include +#include + +int resolve_address(const char *host, const char *service, coap_address_t *dst) { + struct addrinfo *res, *aInfo; + struct addrinfo hints{}; + int error, len = -1; + + memset(&hints, 0, sizeof(hints)); + memset(dst, 0, sizeof(*dst)); + hints.ai_socktype = SOCK_DGRAM; + hints.ai_family = AF_UNSPEC; + + error = getaddrinfo(host, service, &hints, &res); + + if (error != 0) { + LOG_ERR("getaddrinfo: %s", gai_strerror(error)); + return error; + } + + for (aInfo = res; aInfo != nullptr; aInfo = aInfo->ai_next) { + switch (aInfo->ai_family) { + case AF_INET6: + case AF_INET: + len = dst->size = aInfo->ai_addrlen; + memcpy(&dst->addr.sin6, aInfo->ai_addr, dst->size); + freeaddrinfo(res); + return len; + default:; + } + } + + freeaddrinfo(res); + return len; +} + +int match(const std::string &src, const std::string &dst, int is_prefix) { + std::regex re; + + if (is_prefix) { + re = std::regex("^" + dst + ".*"); + } else { + re = std::regex("^" + dst); + } + + return std::regex_match(src, re); +} + +std::vector split(const std::string &s, char delimiter) { + std::vector tokens; + std::string token; + std::istringstream tokenStream(s); + + while (std::getline(tokenStream, token, delimiter)) { + tokens.push_back(token); + } + + return tokens; +} \ No newline at end of file diff --git a/linux/examples/eddie_endpoint.cpp b/linux/examples/eddie_endpoint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..86ec7392a5fde50349aa515278acd6d62002bbb1 --- /dev/null +++ b/linux/examples/eddie_endpoint.cpp @@ -0,0 +1,89 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileCopyrightText: Huawei Inc. + */ + +#include "EddieEndpoint.h" +#include "EddieResource.h" + +class EddieLamp : public EddieResource { +private: + const char * const core_path[2] = { "linux-lamp", nullptr }; + const char * const core_attributes[3] = { "rt=eddie.lamp", "ct=40", nullptr }; + + int lamp_status = 0; + +public: + + message_t render_get(request_t &request) override { + message_t response; + response.data = lamp_status == 0 ? "off" : "on"; + response.status_code = CONTENT; + return response; + } + + message_t render_post(request_t &request) override { + message_t response; + std::string request_payload_str = std::string((const char*)request.data, request.data_length); + if (request_payload_str == "on") lamp_status = 1; + else if (request_payload_str == "off") lamp_status = 0; + else { + response.status_code = BAD_REQUEST; + return response; + } + response.status_code = CHANGED; + return response; + } + + message_t render_put(request_t &request) override { + return render_post(request); + } + + const char * const* get_path() override { + return this->core_path; + } + + const char * const* get_attributes() override { + return this->core_attributes; + } +}; + +class EddieTemperature : public EddieResource { +private: + const char * const core_path[2] = { "linux-temp", nullptr }; + const char * const core_attributes[3] = { "rt=eddie.temp", "ct=40", nullptr }; + +public: + + message_t render_get(request_t &request) override { + message_t response; + response.data = "very hot"; + response.status_code = CONTENT; + return response; + } + + const char * const* get_path() override { + return this->core_path; + } + + const char * const* get_attributes() override { + return this->core_attributes; + } +}; + +int main() +{ + EddieEndpoint node = EddieEndpoint(); + EddieLamp lamp_resource; + EddieTemperature temperature_resource; + + node.discover_rd(); + node.add_resource(&lamp_resource); + node.add_resource(&temperature_resource); + node.publish_resources(); + node.get_resources_from_rd(); + node.start_server(); + + return 0; +} diff --git a/linux/examples/node.cpp b/linux/examples/node.cpp deleted file mode 100644 index 0c2bda5911196462617bd1e979f3be2a1891e094..0000000000000000000000000000000000000000 --- a/linux/examples/node.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * SPDX-FileCopyrightText: Huawei Inc. - * SPDX-FileCopyrightText: Politecnico Di Milano - */ - -#include - -#include "CoapNode.h" - -using namespace std; - -int main() { - coap_set_log_level(LOG_DEBUG); - CoapNode node; - - vector uris = { - "lamp", - "lamp/brightness", - "light/color_rgb" - }; - - vector attributes = { - "lt=90000&rt=eddie.lamp&ct=40", - "lt=90000&rt=eddie.lamp.brightness&range=0-1&ct=0", - "rt=eddie.lamp.color&ct=0" - }; - - node.init_node(); - - thread runner([&node]() { - node.run(); - }); - - node.connect(uris, attributes); - - runner.join(); -} diff --git a/linux/examples/resource_directory.cpp b/linux/examples/resource_directory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..207f27dd590234bb5416a23ccb535031685050de --- /dev/null +++ b/linux/examples/resource_directory.cpp @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * SPDX-FileCopyrightText: Huawei Inc. + * SPDX-FileCopyrightText: Politecnico Di Milano + */ + +#include "ResourceDirectory.h" + +int main() { + ResourceDirectory *resource_directory = new ResourceDirectory(); + + resource_directory->run(); + + delete resource_directory; +} \ No newline at end of file diff --git a/linux/examples/server.cpp b/linux/examples/server.cpp deleted file mode 100644 index 06a5f3934c949e12dd3548a61049e1da7ad3b2f8..0000000000000000000000000000000000000000 --- a/linux/examples/server.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * SPDX-FileCopyrightText: Huawei Inc. - * SPDX-FileCopyrightText: Politecnico Di Milano - */ - -#include -#include "CoapServer.h" - -using namespace std; - -int main(int argc, char **argv) { - string ip_str; - struct ifaddrs *ifaddr; - int s; - char host[NI_MAXHOST]; - - if (getifaddrs(&ifaddr) == -1) { - perror("getifaddrs"); - exit(EXIT_FAILURE); - } - - for (struct ifaddrs *ifa = ifaddr; ifa != nullptr && ip_str.empty(); ifa = ifa->ifa_next) { - if (!ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6 || strcmp(ifa->ifa_name, "lo") == 0) - continue; - - s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6), - host, NI_MAXHOST, nullptr, 0, NI_NUMERICHOST); - - if (s != 0) { - printf("getnameinfo() failed: %s\n", gai_strerror(s)); - exit(EXIT_FAILURE); - } - - string host_str = host; - - ip_str = host_str.substr(0, host_str.length() - (strlen(ifa->ifa_name) + 1)); - } - - if (ip_str.empty()) { - printf("ERROR: no valid ip address found for this machine\n"); - exit(EXIT_FAILURE); - } - - coap_set_log_level(LOG_DEBUG); - - CoapServer server = CoapServer(ip_str.c_str(), "5683", 1); - - server.add_resource("lamp", "lt=90000&rt=example.lamp", 1); - server.publish_resources(90000); - - server.run(); -} diff --git a/linux/tests/communication_tests.cpp b/linux/tests/communication_tests.cpp index b999f0d5633567b793df938bfb383b60fe5868ac..b18306cd11278a5b203165b513e67d5ca808dbd7 100644 --- a/linux/tests/communication_tests.cpp +++ b/linux/tests/communication_tests.cpp @@ -7,11 +7,52 @@ #include #include + #include "CoapClient.h" #include "CoapServer.h" -#include "CoapNode.h" - -using namespace std; +#include "EddieEndpoint.h" + +class EddieLamp : public EddieResource { +private: + const char * const core_path[2] = { "linux-lamp", nullptr }; + const char * const core_attributes[3] = { "rt=eddie.lamp", "ct=40", nullptr }; + + int lamp_status = 0; + +public: + + message_t render_get(request_t &request) override { + message_t response; + response.data = lamp_status == 0 ? "off" : "on"; + response.status_code = CONTENT; + return response; + } + + message_t render_post(request_t &request) override { + message_t response; + std::string request_payload_str = std::string((const char*)request.data, request.data_length); + if (request_payload_str == "on") lamp_status = 1; + else if (request_payload_str == "off") lamp_status = 0; + else { + response.status_code = BAD_REQUEST; + return response; + } + response.status_code = CHANGED; + return response; + } + + message_t render_put(request_t &request) override { + return render_post(request); + } + + const char * const* get_path() override { + return this->core_path; + } + + const char * const* get_attributes() override { + return this->core_attributes; + } +}; TEST(Communication, Client_Init) { CoapClient client; @@ -82,7 +123,8 @@ TEST(Communication, Server_QuitGetSet) { TEST(Communication, Server_Resource) { CoapServer server = CoapServer("0.0.0.0", "5683", 1); - server.add_resource("lamp", "lt=90000&rt=example.lamp", 1); + EddieLamp lamp_resource; + server.add_resource(&lamp_resource); server.destroy_server(); } @@ -92,3 +134,8 @@ TEST(Communication, Server_Destroy) { server.destroy_server(); } + + +TEST(Communication, Endpoint_Init) { + EddieEndpoint endpoint; +} diff --git a/linux/virtualization/include/VirtualizationReceiver.h b/linux/virtualization/include/VirtualizationReceiver.h index de88f825a411ea832cd02ecf7ac551363b78faee..fadf631fc237362e1f42148dff59ac4aa3a4de43 100644 --- a/linux/virtualization/include/VirtualizationReceiver.h +++ b/linux/virtualization/include/VirtualizationReceiver.h @@ -11,11 +11,12 @@ #include #include -#include "CoapNode.h" +#include "EddieEndpoint.h" class VirtualizationReceiver { private: - static CoapNode *coap_node; + static EddieEndpoint *eddie_endpoint; + static std::vector resources; static GDBusNodeInfo *introspection_data; static const GDBusInterfaceVTable interface_vtable; @@ -41,7 +42,7 @@ private: public: VirtualizationReceiver(); - static CoapNode *communication_layer(); + static EddieEndpoint *communication_layer(); int run(std::vector uris, std::vector attributes, int node); diff --git a/linux/virtualization/include/common.h b/linux/virtualization/include/common.h index fd0650efa0cb1601360603e78bc5a82cf4716ebe..aa5b66074a5f990e7b7b3e4aba44bf5c2df47c29 100644 --- a/linux/virtualization/include/common.h +++ b/linux/virtualization/include/common.h @@ -12,4 +12,8 @@ #define EDDIE_OBJECT "/org/eddie/TestObject" #define EDDIE_DBUS_NAME "org.eddie.TestServer" +#define LOG_DBG(_str, ...) printf(_str "\n", ##__VA_ARGS__) +#define LOG_INFO(_str, ...) printf(_str "\n", ##__VA_ARGS__) +#define LOG_ERR(_str, ...) printf(_str "\n", ##__VA_ARGS__) + #endif //EDDIE_COMMON_H diff --git a/linux/virtualization/src/VirtualizationReceiver.cpp b/linux/virtualization/src/VirtualizationReceiver.cpp index 2f78ec760c4606ac43f5180d8da4de8fe053c042..9cc69f206675e7ab81e6ff7fa2cdbe9dbb663432 100644 --- a/linux/virtualization/src/VirtualizationReceiver.cpp +++ b/linux/virtualization/src/VirtualizationReceiver.cpp @@ -5,14 +5,15 @@ * SPDX-FileCopyrightText: Politecnico Di Milano */ -#include #include #include #include #include +#include #include "common.h" #include "VirtualizationReceiver.h" +#include "EddieEndpoint.h" static const gchar *introspection_XML = "" @@ -29,7 +30,8 @@ static const gchar *introspection_XML = " " ""; -CoapNode *VirtualizationReceiver::coap_node = new CoapNode(); +EddieEndpoint* VirtualizationReceiver::eddie_endpoint = new EddieEndpoint(); +std::vector VirtualizationReceiver::resources; GDBusNodeInfo *VirtualizationReceiver::introspection_data = g_dbus_node_info_new_for_xml(introspection_XML, nullptr); const GDBusInterfaceVTable VirtualizationReceiver::interface_vtable = {handle_method_call}; @@ -48,6 +50,13 @@ std::vector split_virt(const std::string &s, char delimiter) { return tokens; } +method_t method_from_string(const std::string &input) { + if (input == "GET") return GET; + else if (input == "POST") return POST; + else if (input == "PUT") return PUT; + else return DELETE; +} + int mock_ai_executor(const std::string &method, const std::unordered_map &query_parameters, std::string &ip, std::string &port, std::string &res, std::string &query) { auto preferred_resource_type = query_parameters.find("preferred-resource-type"); @@ -91,6 +100,50 @@ void mock_comm_setup(std::vector &uris, std::vector &a }; } +class MockEddieResource : public EddieResource { +private: + char* path; + char* attributes; + std::vector split_path; + std::vector split_attributes; + +public: + MockEddieResource(const std::string &path, const std::string &attributes) { + this->path = new char[path.length()+1]; + strcpy(this->path, path.c_str()); + + this->attributes = new char[attributes.length()+1]; + strcpy(this->attributes, attributes.c_str()); + + char *token = strtok(this->path, "/"); + while(token) { + split_path.push_back(token); + token = strtok(nullptr, "/"); + } + split_path.push_back(nullptr); + + token = strtok(this->attributes, "&"); + while(token) { + split_path.push_back(token); + token = strtok(nullptr, "&"); + } + split_attributes.push_back(nullptr); + } + + ~MockEddieResource() { + delete[] path; + delete[] attributes; + } + + const char * const* get_path() override { + return &split_path[0]; + } + + const char * const* get_attributes() override { + return &split_attributes[0]; + } +}; + void VirtualizationReceiver::handle_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, @@ -116,7 +169,7 @@ void VirtualizationReceiver::handle_method_call(GDBusConnection *connection, con update_resources(); if (mock_ai_executor(method, query_parameters, ip, port, resource, query)) { - token = coap_node->send_message(method_from_string(method), + token = eddie_endpoint->get_client()->send_message(method_from_string(method), ip.c_str(), port.c_str(), resource.c_str(), @@ -127,7 +180,7 @@ void VirtualizationReceiver::handle_method_call(GDBusConnection *connection, con return; } - auto comm_answer = coap_node->receive_message(token, 0); + auto comm_answer = eddie_endpoint->get_client()->receive_message(token, 0); g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", comm_answer.c_str())); } @@ -154,17 +207,15 @@ void VirtualizationReceiver::on_name_lost(GDBusConnection *connection, const gch } VirtualizationReceiver::VirtualizationReceiver() { - if (coap_node == nullptr) { - printf("[VirtualizationReceiver::VirtualizationReceiver]: error setting up the infrastructure\n"); - printf("\tcoap_node is null\n"); + if (eddie_endpoint == nullptr) { + LOG_ERR("[VirtualizationReceiver::VirtualizationReceiver]: error setting up the infrastructure\n"); + LOG_ERR("\teddie_endpoint is null\n"); exit(-1); } - - coap_node->init_node(); } void VirtualizationReceiver::update_resources() { - auto discover_result = coap_node->discover(); + auto discover_result = eddie_endpoint->get_resources_from_rd(); std::vector resources = split_virt(discover_result, ','); @@ -183,18 +234,22 @@ void VirtualizationReceiver::update_resources() { } } -CoapNode *VirtualizationReceiver::communication_layer() { - return coap_node; +EddieEndpoint *VirtualizationReceiver::communication_layer() { + return eddie_endpoint; } int VirtualizationReceiver::run(std::vector uris, std::vector attributes, int node) { std::thread comm_runner([]() { - coap_node->run(); + eddie_endpoint->start_server(); }); if (uris.empty() || attributes.empty()) mock_comm_setup(uris, attributes); - coap_node->connect(uris, attributes); + for (int i = 0; i < uris.size(); ++i) { + EddieResource* new_resource = new MockEddieResource(uris[i], attributes[i]); + VirtualizationReceiver::resources.push_back(new_resource); + VirtualizationReceiver::eddie_endpoint->add_resource(new_resource); + } guint owner_id; GMainLoop *loop; diff --git a/linux/virtualization/src/VirtualizationSender.cpp b/linux/virtualization/src/VirtualizationSender.cpp index 18e4d1c825eb417134410d2342c7d15d76810f97..accc572e51cc436f9097c58ff85348cf7f6d6f21 100644 --- a/linux/virtualization/src/VirtualizationSender.cpp +++ b/linux/virtualization/src/VirtualizationSender.cpp @@ -108,7 +108,7 @@ const gchar *send_message(const std::unordered_map &pa } if (count >= MAX_RETRIES) { - printf("[VirtualizationSender]: no dbus found, impossible to send the message\n"); + LOG_ERR("[VirtualizationSender]: no dbus found, impossible to send the message\n"); return "error"; } @@ -117,12 +117,12 @@ const gchar *send_message(const std::unordered_map &pa auto payload = param_copy.find("payload"); if (method == param_copy.end()) { - printf("[VirtualizationSender]: 'method' parameter is required\n"); + LOG_ERR("[VirtualizationSender]: 'method' parameter is required\n"); return "error"; } if (payload == param_copy.end()) { - printf("[VirtualizationSender]: 'payload' parameter is required\n"); + LOG_ERR("[VirtualizationSender]: 'payload' parameter is required\n"); return "error"; } diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 064fa4eba3853add60e7d7bc31e058d5cd9398ec..1b7a871673064d8376ce250e3aa4bcfa371e8481 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -6,8 +6,10 @@ cmake_minimum_required(VERSION 3.13.1) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(eddie) +add_subdirectory(../common common) + FILE(GLOB app_sources src/*.cpp) target_sources(app PRIVATE ${app_sources}) target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/net/ip) target_include_directories(app PRIVATE ${PROJECT_SOURCE_DIR}/include) -target_include_directories(app PRIVATE ${PROJECT_SOURCE_DIR}/../include) \ No newline at end of file +target_link_libraries(app PRIVATE ${CMAKE_THREAD_LIBS_INIT} eddie-common) \ No newline at end of file diff --git a/zephyr/src/EddieEndpoint.cpp b/zephyr/src/EddieEndpoint.cpp index 8245c4ac1f0fce66cf8d39cbf2b434fc0ff5fbf9..fcb24dfd6d2c03b43542a793e0b3949c9b1db7b3 100644 --- a/zephyr/src/EddieEndpoint.cpp +++ b/zephyr/src/EddieEndpoint.cpp @@ -121,7 +121,7 @@ int EddieEndpoint::publish_resources() { return 0; } -int EddieEndpoint::get_resources_from_rd() { +std::string EddieEndpoint::get_resources_from_rd() { int r; const char * const path[] = { "rd-lookup", "res", NULL }; @@ -143,10 +143,10 @@ int EddieEndpoint::get_resources_from_rd() { auto response_handler = [](message_t response_message) { LOG_DBG("Response: %s", response_message.data.c_str()); auto links = parse_link_format(response_message.data); - print_links(links); + return response_message.data; // TODO: returned parsed links instead }; r = get_client()->send_request(get_resources_request, response_handler); - return 0; + return ""; }