From 001cbb9549f17153536ee6a01a783eb85bf54962 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Sun, 25 Oct 2020 22:18:53 +0100 Subject: [PATCH] Add API functions to query the blacklist status of nodes. --- src/meshlink++.h | 26 ++++++++++++++++++++++++ src/meshlink.c | 35 +++++++++++++++++++++++++++++++++ src/meshlink.h | 34 +++++++++++++++++++++++++++++++- src/meshlink.sym | 2 ++ test/blacklist.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 1 deletion(-) diff --git a/src/meshlink++.h b/src/meshlink++.h index 183544ac..c1550865 100644 --- a/src/meshlink++.h +++ b/src/meshlink++.h @@ -431,6 +431,17 @@ public: return meshlink_get_node_reachability(handle, node, last_reachable, last_unreachable); } + /// Get a node's blacklist status. + /** This function returns the current blacklist status of a given node. + * + * @param node A pointer to a meshlink::node describing the node. + * + * @return This function returns true if the node is currently blacklisted, false otherwise. + */ + bool get_node_blacklisted(node *node) { + return meshlink_get_node_blacklisted(handle, node); + } + /// Get a handle for a specific submesh. /** This function returns a handle for the submesh with the given name. * @@ -465,6 +476,21 @@ public: return (node **)meshlink_get_all_nodes(handle, (meshlink_node_t **)nodes, nmemb); } + /// Get a list of all nodes by blacklist status. + /** This function returns a list with handles for all the nodes who were either blacklisted or whitelisted. + * + * @param nodes A pointer to an array of pointers to meshlink::node, which should be allocated by the application. + * @param nmemb The maximum number of pointers that can be stored in the nodes array. + * + * @return A pointer to an array containing pointers to all known nodes with the given blacklist status. + * If the @a nodes argument was not NULL, then the return value can either be the same value or a different value. + * If it is a new value, the old value of @a nodes should not be used anymore. + * If the new value is NULL, then the old array will have been freed by MeshLink. + */ + node **get_all_nodes_by_blacklisted(bool blacklisted, node **nodes, size_t *nmemb) { + return (node **)meshlink_get_all_nodes_by_blacklisted(handle, blacklisted, (meshlink_node_t **)nodes, nmemb); + } + /// Sign data using the local node's MeshLink key. /** This function signs data using the local node's MeshLink key. * The generated signature can be securely verified by other nodes. diff --git a/src/meshlink.c b/src/meshlink.c index 3f191f9a..4f9c5f40 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -2360,6 +2360,10 @@ static bool search_node_by_dev_class(const node_t *node, const void *condition) return false; } +static bool search_node_by_blacklisted(const node_t *node, const void *condition) { + return *(bool *)condition == node->status.blacklisted; +} + static bool search_node_by_submesh(const node_t *node, const void *condition) { if(condition == node->submesh) { return true; @@ -2422,6 +2426,15 @@ meshlink_node_t **meshlink_get_all_nodes_by_last_reachable(meshlink_handle_t *me return meshlink_get_all_nodes_by_condition(mesh, &range, nodes, nmemb, search_node_by_last_reachable); } +meshlink_node_t **meshlink_get_all_nodes_by_blacklisted(meshlink_handle_t *mesh, bool blacklisted, meshlink_node_t **nodes, size_t *nmemb) { + if(!mesh || !nmemb) { + meshlink_errno = MESHLINK_EINVAL; + return NULL; + } + + return meshlink_get_all_nodes_by_condition(mesh, &blacklisted, nodes, nmemb, search_node_by_blacklisted); +} + dev_class_t meshlink_get_node_dev_class(meshlink_handle_t *mesh, meshlink_node_t *node) { if(!mesh || !node) { meshlink_errno = MESHLINK_EINVAL; @@ -2441,6 +2454,28 @@ dev_class_t meshlink_get_node_dev_class(meshlink_handle_t *mesh, meshlink_node_t return devclass; } +bool meshlink_get_node_blacklisted(meshlink_handle_t *mesh, meshlink_node_t *node) { + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; + } + + if(!node) { + return mesh->default_blacklist; + } + + bool blacklisted; + + if(pthread_mutex_lock(&mesh->mutex) != 0) { + abort(); + } + + blacklisted = ((node_t *)node)->status.blacklisted; + + pthread_mutex_unlock(&mesh->mutex); + + return blacklisted; +} + meshlink_submesh_t *meshlink_get_node_submesh(meshlink_handle_t *mesh, meshlink_node_t *node) { if(!mesh || !node) { meshlink_errno = MESHLINK_EINVAL; diff --git a/src/meshlink.h b/src/meshlink.h index 738f8647..5f11607b 100644 --- a/src/meshlink.h +++ b/src/meshlink.h @@ -739,17 +739,49 @@ struct meshlink_node **meshlink_get_all_nodes_by_submesh(struct meshlink_handle */ struct meshlink_node **meshlink_get_all_nodes_by_last_reachable(struct meshlink_handle *mesh, time_t start, time_t end, struct meshlink_node **nodes, size_t *nmemb) __attribute__((__warn_unused_result__)); +/// Get the list of all nodes by blacklist status. +/** This function returns a list with handles for all the nodes who were either blacklisted or whitelisted. + * + * \memberof meshlink_handle + * @param mesh A handle which represents an instance of MeshLink. + * @param blacklisted If true, a list of blacklisted nodes will be returned, otherwise whitelisted nodes. + * @param nodes A pointer to a previously allocated array of pointers to struct meshlink_node, or NULL in which case MeshLink will allocate a new array. + * The application can supply an array it allocated itself with malloc, or the return value from the previous call to this function (which is the preferred way). + * The application is allowed to call free() on the array whenever it wishes. + * The pointers in the array are valid until meshlink_close() is called. + * @param nmemb A pointer to a variable holding the number of nodes that were reachable within the period given by @a start and @a end. + * In case the @a nodes argument is not NULL, MeshLink might call realloc() on the array to change its size. + * The contents of this variable will be changed to reflect the new size of the array. + * + * @return A pointer to an array containing pointers to all known nodes with the given blacklist status. + * If the @a nodes argument was not NULL, then the return value can either be the same value or a different value. + * If it is a new value, the old value of @a nodes should not be used anymore. + * If the new value is NULL, then the old array will have been freed by MeshLink. + */ +struct meshlink_node **meshlink_get_all_nodes_by_blacklisted(struct meshlink_handle *mesh, bool blacklisted, struct meshlink_node **nodes, size_t *nmemb) __attribute__((__warn_unused_result__)); + /// Get the node's device class. /** This function returns the device class of the given node. * * \memberof meshlink_node * @param mesh A handle which represents an instance of MeshLink. - * @param node A pointer to a struct meshlink_node describing the node. + * @param node A pointer to a struct meshlink_node describing the node. * * @return This function returns the device class of the @a node, or -1 in case of an error. */ dev_class_t meshlink_get_node_dev_class(struct meshlink_handle *mesh, struct meshlink_node *node) __attribute__((__warn_unused_result__)); +/// Get the node's blacklist status. +/** This function returns the given node is blacklisted. + * + * \memberof meshlink_node + * @param mesh A handle which represents an instance of MeshLink. + * @param node A pointer to a struct meshlink_node describing the node. + * + * @return This function returns true if the node is blacklisted, false otherwise. + */ +bool meshlink_get_node_blacklisted(struct meshlink_handle *mesh, struct meshlink_node *node) __attribute__((__warn_unused_result__)); + /// Get the node's submesh handle. /** This function returns the submesh handle of the given node. * diff --git a/src/meshlink.sym b/src/meshlink.sym index 08dff729..649eba1d 100644 --- a/src/meshlink.sym +++ b/src/meshlink.sym @@ -36,6 +36,7 @@ meshlink_errno meshlink_export meshlink_forget_node meshlink_get_all_nodes +meshlink_get_all_nodes_by_blacklisted meshlink_get_all_nodes_by_dev_class meshlink_get_all_nodes_by_last_reachable meshlink_get_all_nodes_by_submesh @@ -44,6 +45,7 @@ meshlink_get_external_address_for_family meshlink_get_fingerprint meshlink_get_local_address_for_family meshlink_get_node +meshlink_get_node_blacklisted meshlink_get_node_dev_class meshlink_get_node_reachability meshlink_get_node_submesh diff --git a/test/blacklist.c b/test/blacklist.c index 1f5cd605..585c1526 100644 --- a/test/blacklist.c +++ b/test/blacklist.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "meshlink.h" #include "devtools.h" @@ -95,14 +96,60 @@ int main(void) { assert(!meshlink_get_node(mesh[1], name[2])); assert(!meshlink_get_node(mesh[2], name[1])); + // Check default blacklist status + + assert(!meshlink_get_node_blacklisted(mesh[0], meshlink_get_self(mesh[0]))); + assert(!meshlink_get_node_blacklisted(mesh[0], meshlink_get_node(mesh[0], name[1]))); + assert(meshlink_get_node_blacklisted(mesh[1], meshlink_get_node(mesh[1], name[2]))); + assert(meshlink_get_node_blacklisted(mesh[2], meshlink_get_node(mesh[2], name[1]))); + + // Generate an invitation for a node that is about to be blacklisted + + char *invitation = meshlink_invite(mesh[0], NULL, "xyzzy"); + assert(invitation); + free(invitation); + // Whitelisting and blacklisting by name should work. assert(meshlink_whitelist_by_name(mesh[0], "quux")); assert(meshlink_blacklist_by_name(mesh[0], "xyzzy")); + assert(meshlink_get_node(mesh[0], "quux")); + assert(!meshlink_get_node_blacklisted(mesh[0], meshlink_get_node(mesh[0], "quux"))); + assert(meshlink_get_node_blacklisted(mesh[0], meshlink_get_node(mesh[0], "xyzzy"))); + + meshlink_node_t **nodes = NULL; + size_t nnodes = 0; + nodes = meshlink_get_all_nodes_by_blacklisted(mesh[0], true, nodes, &nnodes); + assert(nnodes == 1); + assert(!strcmp(nodes[0]->name, "xyzzy")); + + nodes = meshlink_get_all_nodes_by_blacklisted(mesh[0], false, nodes, &nnodes); + assert(nnodes == 4); + assert(!strcmp(nodes[0]->name, "bar")); + assert(!strcmp(nodes[1]->name, "baz")); + assert(!strcmp(nodes[2]->name, "foo")); + assert(!strcmp(nodes[3]->name, "quux")); + + free(nodes); + + // Check that blacklisted nodes are not allowed to be invited, and no invitations are left on disk. + + assert(!meshlink_invite(mesh[0], NULL, "xyzzy")); + + DIR *dir = opendir("blacklist_conf.0/current/invitations"); + assert(dir); + struct dirent *ent; + + while((ent = readdir(dir))) { + assert(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")); + } + + closedir(dir); // Since these nodes now exist we should be able to forget them. assert(meshlink_forget_node(mesh[0], meshlink_get_node(mesh[0], "quux"))); + assert(meshlink_get_node_blacklisted(mesh[0], meshlink_get_node(mesh[0], "quux"))); // default blacklisted again // Start the nodes. @@ -122,12 +169,14 @@ int main(void) { set_sync_flag(&bar_disconnected, false); assert(meshlink_blacklist(mesh[0], meshlink_get_node(mesh[0], name[1]))); assert(wait_sync_flag(&bar_disconnected, 5)); + assert(meshlink_get_node_blacklisted(mesh[0], meshlink_get_node(mesh[0], name[1]))); // Whitelist bar set_sync_flag(&bar_connected, false); assert(meshlink_whitelist(mesh[0], meshlink_get_node(mesh[0], name[1]))); assert(wait_sync_flag(&bar_connected, 15)); + assert(!meshlink_get_node_blacklisted(mesh[0], meshlink_get_node(mesh[0], name[1]))); // Bar should not connect to baz @@ -144,6 +193,8 @@ int main(void) { assert(meshlink_whitelist(mesh[1], baz)); assert(meshlink_whitelist(mesh[2], bar)); + assert(!meshlink_get_node_blacklisted(mesh[1], baz)); + assert(!meshlink_get_node_blacklisted(mesh[2], bar)); // They should connect to each other -- 2.39.2