From a23ddd4f4e273b2ed68fe173b4faa69aadc98756 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Thu, 29 Oct 2020 23:30:41 +0100 Subject: [PATCH] Add a callback that notifies the application when it is blacklisted. If an outgoing connection is made to a node that blacklists the local node, a callback can now be set that informs the application of it. --- src/meshlink++.h | 16 ++++++++++++++++ src/meshlink.c | 14 ++++++++++++++ src/meshlink.h | 21 +++++++++++++++++++++ src/meshlink.sym | 1 + src/meshlink_internal.h | 1 + src/net.h | 1 + src/net_socket.c | 4 ++++ src/protocol.c | 2 +- src/protocol.h | 6 ++++++ src/protocol_auth.c | 12 +++++++----- src/protocol_misc.c | 13 +++++++++++++ test/blacklist.c | 13 +++++++++++++ 12 files changed, 98 insertions(+), 6 deletions(-) diff --git a/src/meshlink++.h b/src/meshlink++.h index 4f63a6d9..551913d4 100644 --- a/src/meshlink++.h +++ b/src/meshlink++.h @@ -274,6 +274,12 @@ public: (void)meshlink_errno; } + /// This functions is called whenever MeshLink is blacklisted by another node. + virtual void blacklisted(node *peer) { + /* do nothing */ + (void)peer; + } + /// This functions is called whenever MeshLink a meta-connection attempt is made. virtual void connection_try(node *peer) { /* do nothing */ @@ -374,6 +380,7 @@ public: meshlink_set_node_duplicate_cb(handle, &node_duplicate_trampoline); meshlink_set_log_cb(handle, MESHLINK_DEBUG, &log_trampoline); meshlink_set_error_cb(handle, &error_trampoline); + meshlink_set_blacklisted_cb(handle, &blacklisted_trampoline); meshlink_set_channel_listen_cb(handle, &channel_listen_trampoline); meshlink_set_channel_accept_cb(handle, &channel_accept_trampoline); meshlink_set_connection_try_cb(handle, &connection_try_trampoline); @@ -1208,6 +1215,15 @@ private: that->error(meshlink_errno); } + static void blacklisted_trampoline(meshlink_handle_t *handle, meshlink_node_t *peer) { + if(!(handle->priv)) { + return; + } + + meshlink::mesh *that = static_cast(handle->priv); + that->blacklisted(static_cast(peer)); + } + static void connection_try_trampoline(meshlink_handle_t *handle, meshlink_node_t *peer) { if(!(handle->priv)) { return; diff --git a/src/meshlink.c b/src/meshlink.c index 4f9c5f40..ee62078d 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -2060,6 +2060,20 @@ void meshlink_set_error_cb(struct meshlink_handle *mesh, meshlink_error_cb_t cb) pthread_mutex_unlock(&mesh->mutex); } +void meshlink_set_blacklisted_cb(struct meshlink_handle *mesh, meshlink_blacklisted_cb_t cb) { + if(!mesh) { + meshlink_errno = MESHLINK_EINVAL; + return; + } + + if(pthread_mutex_lock(&mesh->mutex) != 0) { + abort(); + } + + mesh->blacklisted_cb = cb; + pthread_mutex_unlock(&mesh->mutex); +} + static bool prepare_packet(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len, vpn_packet_t *packet) { meshlink_packethdr_t *hdr; diff --git a/src/meshlink.h b/src/meshlink.h index 5f11607b..1acd99b0 100644 --- a/src/meshlink.h +++ b/src/meshlink.h @@ -547,6 +547,27 @@ typedef void (*meshlink_error_cb_t)(struct meshlink_handle *mesh, meshlink_errno */ void meshlink_set_error_cb(struct meshlink_handle *mesh, meshlink_error_cb_t cb); +/// A callback for receiving blacklisted conditions encountered by the MeshLink thread. +/** @param mesh A handle which represents an instance of MeshLink, or NULL. + * @param node The node that blacklisted the local node. + */ +typedef void (*meshlink_blacklisted_cb_t)(struct meshlink_handle *mesh, struct meshlink_node *node); + +/// Set the blacklisted callback. +/** This functions sets the callback that is called whenever MeshLink detects that it is blacklisted by another node. + * + * The callback is run in MeshLink's own thread. + * It is important that the callback uses apprioriate methods (queues, pipes, locking, etc.) + * to hand the data over to the application's thread. + * The callback should also not block itself and return as quickly as possible. + * + * \memberof meshlink_handle + * @param mesh A handle which represents an instance of MeshLink, or NULL. + * @param cb A pointer to the function which will be called when a serious error is encountered. + * If a NULL pointer is given, the callback will be disabled. + */ +void meshlink_set_blacklisted_cb(struct meshlink_handle *mesh, meshlink_blacklisted_cb_t cb); + /// Send data to another node. /** This functions sends one packet of data to another node in the mesh. * The packet is sent using UDP semantics, which means that diff --git a/src/meshlink.sym b/src/meshlink.sym index 649eba1d..51511ee9 100644 --- a/src/meshlink.sym +++ b/src/meshlink.sym @@ -69,6 +69,7 @@ meshlink_open_params_set_netns meshlink_open_params_set_storage_key meshlink_reset_timers meshlink_send +meshlink_set_blacklisted_cb meshlink_set_canonical_address meshlink_set_channel_accept_cb meshlink_set_channel_listen_cb diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index ca2df2c5..01107507 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -142,6 +142,7 @@ struct meshlink_handle { meshlink_node_duplicate_cb_t node_duplicate_cb; meshlink_connection_try_cb_t connection_try_cb; meshlink_error_cb_t error_cb; + meshlink_blacklisted_cb_t blacklisted_cb; // Mesh parameters char *appname; diff --git a/src/net.h b/src/net.h index a139c432..5ad27979 100644 --- a/src/net.h +++ b/src/net.h @@ -111,6 +111,7 @@ void send_mtu_probe(struct meshlink_handle *mesh, struct node_t *); void handle_meta_connection_data(struct meshlink_handle *mesh, struct connection_t *); void retry(struct meshlink_handle *mesh); int check_port(struct meshlink_handle *mesh); +void flush_meta(struct meshlink_handle *mesh, struct connection_t *); #ifndef HAVE_MINGW #define closesocket(s) close(s) diff --git a/src/net_socket.c b/src/net_socket.c index d8462372..abf6a7cf 100644 --- a/src/net_socket.c +++ b/src/net_socket.c @@ -143,6 +143,10 @@ static void handle_meta_write(meshlink_handle_t *mesh, connection_t *c) { } } +void flush_meta(meshlink_handle_t *mesh, connection_t *c) { + handle_meta_write(mesh, c); +} + static void handle_meta_io(event_loop_t *loop, void *data, int flags) { meshlink_handle_t *mesh = loop->data; connection_t *c = data; diff --git a/src/protocol.c b/src/protocol.c index e2842abe..5d288837 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -140,7 +140,7 @@ bool receive_request(meshlink_handle_t *mesh, connection_t *c, const char *reque logger(mesh, MESHLINK_DEBUG, "Got %s from %s: %s", request_name[reqno], c->name, request); } - if((c->allow_request != ALL) && (c->allow_request != reqno)) { + if((c->allow_request != ALL) && (c->allow_request != reqno) && (reqno != ERROR)) { logger(mesh, MESHLINK_ERROR, "Unauthorized request from %s", c->name); return false; } diff --git a/src/protocol.h b/src/protocol.h index 794b0515..060d7dff 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -51,6 +51,11 @@ typedef enum request_t { LAST /* Guardian for the highest request number */ } request_t; +typedef enum request_error_t { + NONE = 0, + BLACKLISTED = 1, +} request_error_t; + typedef struct past_request_t { const char *request; time_t firstseen; @@ -83,6 +88,7 @@ bool seen_request(struct meshlink_handle *mesh, const char *); bool send_id(struct meshlink_handle *mesh, struct connection_t *); bool send_ack(struct meshlink_handle *mesh, struct connection_t *); +bool send_error(struct meshlink_handle *mesh, struct connection_t *, request_error_t, const char *); bool send_ping(struct meshlink_handle *mesh, struct connection_t *); bool send_pong(struct meshlink_handle *mesh, struct connection_t *); bool send_add_edge(struct meshlink_handle *mesh, struct connection_t *, const struct edge_t *, int contradictions); diff --git a/src/protocol_auth.c b/src/protocol_auth.c index e1a9a313..1f2e24a2 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -267,11 +267,6 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { return false; } - if(n->status.blacklisted) { - logger(mesh, MESHLINK_WARNING, "Peer %s is blacklisted", c->name); - return false; - } - if(!node_read_public_key(mesh, n)) { logger(mesh, MESHLINK_ERROR, "No key known for peer %s", c->name); @@ -309,6 +304,13 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { } bool send_ack(meshlink_handle_t *mesh, connection_t *c) { + node_t *n = lookup_node(mesh, c->name); + + if(n && n->status.blacklisted) { + logger(mesh, MESHLINK_WARNING, "Peer %s is blacklisted", c->name); + return send_error(mesh, c, BLACKLISTED, "blacklisted"); + } + c->last_ping_time = mesh->loop.now.tv_sec; return send_request(mesh, c, NULL, "%d %s %d %x", ACK, mesh->myport, mesh->devclass, OPTION_PMTU_DISCOVERY | (PROT_MINOR << 24)); } diff --git a/src/protocol_misc.c b/src/protocol_misc.c index c6a3630a..7410ad13 100644 --- a/src/protocol_misc.c +++ b/src/protocol_misc.c @@ -50,6 +50,12 @@ bool status_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { return true; } +bool send_error(meshlink_handle_t *mesh, connection_t *c, request_error_t err, const char *message) { + send_request(mesh, c, NULL, "%d %d %s", ERROR, err, message); + flush_meta(mesh, c); + return false; +} + bool error_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { assert(request); assert(*request); @@ -64,6 +70,13 @@ bool error_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { logger(mesh, MESHLINK_INFO, "Error message from %s: %d: %s", c->name, err, errorstring); + switch(err) { + case BLACKLISTED: + if(mesh->blacklisted_cb) { + mesh->blacklisted_cb(mesh, (meshlink_node_t *)lookup_node(mesh, c->name)); + } + } + return false; } diff --git a/test/blacklist.c b/test/blacklist.c index 585c1526..5fa0f98d 100644 --- a/test/blacklist.c +++ b/test/blacklist.c @@ -19,6 +19,7 @@ static struct sync_flag bar_connected; static struct sync_flag bar_disconnected; +static struct sync_flag bar_blacklisted; static struct sync_flag baz_connected; static void foo_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) { @@ -45,6 +46,14 @@ static void baz_status_cb(meshlink_handle_t *mesh, meshlink_node_t *node, bool r } } +static void bar_blacklisted_cb(meshlink_handle_t *mesh, meshlink_node_t *node) { + (void)mesh; + + if(!strcmp(node->name, "foo")) { + set_sync_flag(&bar_blacklisted, true); + } +} + int main(void) { init_sync_flag(&bar_connected); init_sync_flag(&bar_disconnected); @@ -166,11 +175,15 @@ int main(void) { // Blacklist bar + meshlink_set_blacklisted_cb(mesh[1], bar_blacklisted_cb); + 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]))); + assert(wait_sync_flag(&bar_blacklisted, 10)); + // Whitelist bar set_sync_flag(&bar_connected, false); -- 2.39.2