]> git.meshlink.io Git - meshlink/commitdiff
Add a callback that notifies the application when it is blacklisted.
authorGuus Sliepen <guus@meshlink.io>
Thu, 29 Oct 2020 22:30:41 +0000 (23:30 +0100)
committerGuus Sliepen <guus@meshlink.io>
Thu, 29 Oct 2020 22:30:41 +0000 (23:30 +0100)
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.

12 files changed:
src/meshlink++.h
src/meshlink.c
src/meshlink.h
src/meshlink.sym
src/meshlink_internal.h
src/net.h
src/net_socket.c
src/protocol.c
src/protocol.h
src/protocol_auth.c
src/protocol_misc.c
test/blacklist.c

index 4f63a6d9d0507ed1c9e6b86307ea64b8d519d97a..551913d4ba2a94ceb4df51852dd8595ce563a28d 100644 (file)
@@ -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<mesh *>(handle->priv);
+               that->blacklisted(static_cast<node *>(peer));
+       }
+
        static void connection_try_trampoline(meshlink_handle_t *handle, meshlink_node_t *peer) {
                if(!(handle->priv)) {
                        return;
index 4f9c5f407ba93b6680faa8aef5c8c3d6c0a10ede..ee62078d8b16a55a31c363eb3d77f01fdbdb5b63 100644 (file)
@@ -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;
 
index 5f11607bb161928ede2fbae09d610f3bd5833587..1acd99b0663bd021ba6fc70a4e0ecfcb48ff319a 100644 (file)
@@ -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
index 649eba1d50a008cfb6dce1fc5e456a79a5502fdf..51511ee9e1d5ed9a1eb8680306ac09f386657160 100644 (file)
@@ -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
index ca2df2c543cf055866d1bf0618617c5eabee920c..011075079e86473166c17a1dd28bfb389ff0325f 100644 (file)
@@ -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;
index a139c4328cfc3aad67bcaa025ddf49d7dd35fa97..5ad279793aa022c1721fafb477f93e551044f45c 100644 (file)
--- 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)
index d846237275c023010b07b5aaca342e31f9be7d47..abf6a7cf0e0eb7fa878f347efa45a6910c646927 100644 (file)
@@ -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;
index e2842abe5c7665ab341b1af86213c3f4f981b04b..5d28883778aa4957a5b849e547aee89a422c76f6 100644 (file)
@@ -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;
                }
index 794b0515dec1205cff55d5408d6be127c952d7d3..060d7dff8b201a4a923615652e6e1d50b708259c 100644 (file)
@@ -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);
index e1a9a31341f8b73846628e49509335b1f4c0e511..1f2e24a2ce8d9d6def723ee1a851ac4b5928ccef 100644 (file)
@@ -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));
 }
index c6a3630ae2ea190fc27ee41dbe8b9206c85591b9..7410ad13c8f5316b3d274ede08877e58f2e54050 100644 (file)
@@ -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;
 }
 
index 585c1526cc123c5c23b067ca4d8b3b733e61c429..5fa0f98deebd442c825eb384e3dbe7a14a53de7f 100644 (file)
@@ -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);