]> git.meshlink.io Git - meshlink/blobdiff - src/discovery.c
Speed up reconnections on network interface changes.
[meshlink] / src / discovery.c
index 8248e9e7be4e151818f1be74fddc62f0c940bfd0..95e5ed65d86da5e75297fd0fa94a9dd8eb9e762f 100644 (file)
@@ -1,3 +1,5 @@
+#include "system.h"
+
 #include <catta/core.h>
 #include <catta/lookup.h>
 #include <catta/publish.h>
 #include "discovery.h"
 #include "sockaddr.h"
 #include "logger.h"
-
-#include <pthread.h>
-
-#include <netinet/in.h>
+#include "node.h"
+#include "connection.h"
+#include "xalloc.h"
 
 #define MESHLINK_MDNS_SERVICE_TYPE "_%s._tcp"
 #define MESHLINK_MDNS_NAME_KEY "name"
 #define MESHLINK_MDNS_FINGERPRINT_KEY "fingerprint"
 
 static void generate_rand_string(char *buffer, size_t size) {
-       for(size_t i = 0; i < (size - 1); ++i)
+       for(size_t i = 0; i < (size - 1); ++i) {
                buffer[i] = 'a' + (rand() % ('z' - 'a' + 1));
+       }
 
        buffer[size - 1] = '\0';
 }
 
 static void discovery_entry_group_callback(CattaServer *server, CattaSEntryGroup *group, CattaEntryGroupState state, void *userdata) {
+       (void)server;
+       (void)group;
        meshlink_handle_t *mesh = userdata;
 
        // asserts
@@ -57,7 +61,7 @@ static void discovery_entry_group_callback(CattaServer *server, CattaSEntryGroup
 
        case CATTA_ENTRY_GROUP_UNCOMMITED:
        case CATTA_ENTRY_GROUP_REGISTERING:
-               ;
+               break;
        }
 
        pthread_mutex_unlock(&(mesh->mesh_mutex));
@@ -65,7 +69,9 @@ static void discovery_entry_group_callback(CattaServer *server, CattaSEntryGroup
 
 
 static void discovery_create_services(meshlink_handle_t *mesh) {
+       char *fingerprint = NULL;
        char *txt_name = NULL;
+       char *txt_fingerprint = NULL;
 
        // asserts
        assert(mesh != NULL);
@@ -89,22 +95,14 @@ static void discovery_create_services(meshlink_handle_t *mesh) {
        }
 
        /* Create txt records */
-       size_t txt_name_len = sizeof(MESHLINK_MDNS_NAME_KEY) + 1 + strlen(mesh->name) + 1;
-       txt_name = malloc(txt_name_len);
-
-       if(txt_name == NULL) {
-               logger(mesh, MESHLINK_ERROR, "Could not allocate memory for TXT record\n");
-               goto fail;
-       }
-
-       snprintf(txt_name, txt_name_len, "%s=%s", MESHLINK_MDNS_NAME_KEY, mesh->name);
-
-       char txt_fingerprint[sizeof(MESHLINK_MDNS_FINGERPRINT_KEY) + 1 + MESHLINK_FINGERPRINTLEN + 1];
-       snprintf(txt_fingerprint, sizeof(txt_fingerprint), "%s=%s", MESHLINK_MDNS_FINGERPRINT_KEY, meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self));
+       fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
+       xasprintf(&txt_name, "%s=%s", MESHLINK_MDNS_NAME_KEY, mesh->name);
+       xasprintf(&txt_fingerprint, "%s=%s", MESHLINK_MDNS_FINGERPRINT_KEY, fingerprint);
 
        /* Add the service */
        int ret = 0;
-       if((ret = catta_server_add_service(mesh->catta_server, mesh->catta_group, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, 0, meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self), mesh->catta_servicetype, NULL, NULL, atoi(mesh->myport), txt_name, txt_fingerprint, NULL)) < 0) {
+
+       if((ret = catta_server_add_service(mesh->catta_server, mesh->catta_group, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, 0, fingerprint, mesh->catta_servicetype, NULL, NULL, atoi(mesh->myport), txt_name, txt_fingerprint, NULL)) < 0) {
                logger(mesh, MESHLINK_ERROR, "Failed to add service: %s\n", catta_strerror(ret));
                goto fail;
        }
@@ -121,28 +119,30 @@ fail:
        catta_simple_poll_quit(mesh->catta_poll);
 
 done:
-       if(txt_name)
-               free(txt_name);
+       free(fingerprint);
+       free(txt_name);
+       free(txt_fingerprint);
 
        pthread_mutex_unlock(&(mesh->mesh_mutex));
 }
 
 static void discovery_server_callback(CattaServer *server, CattaServerState state, void *userdata) {
+       (void)server;
        meshlink_handle_t *mesh = userdata;
 
        // asserts
        assert(mesh != NULL);
 
-       pthread_mutex_lock(&(mesh->mesh_mutex));
-
        switch(state) {
-       case CATTA_SERVER_RUNNING: {
+       case CATTA_SERVER_RUNNING:
+
                /* The serve has startup successfully and registered its host
                 * name on the network, so it's time to create our services */
-               if(!mesh->catta_group)
+               if(!mesh->catta_group) {
                        discovery_create_services(mesh);
-       }
-       break;
+               }
+
+               break;
 
        case CATTA_SERVER_COLLISION: {
                // asserts
@@ -152,18 +152,16 @@ static void discovery_server_callback(CattaServer *server, CattaServerState stat
                /* A host name collision happened. Let's pick a new name for the server */
                char hostname[17];
                generate_rand_string(hostname, sizeof(hostname));
-
-               logger(mesh, MESHLINK_WARNING, "Catta host name collision, retrying with '%s'\n", hostname);
                int result = catta_server_set_host_name(mesh->catta_server, hostname);
 
                if(result < 0) {
-                       logger(mesh, MESHLINK_ERROR, "Catta failed to set new host name: %s\n", catta_strerror(result));
                        catta_simple_poll_quit(mesh->catta_poll);
                }
        }
        break;
 
-       case CATTA_SERVER_REGISTERING: {
+       case CATTA_SERVER_REGISTERING:
+
                /* Let's drop our registered services. When the server is back
                 * in CATTA_SERVER_RUNNING state we will register them
                 * again with the new host name. */
@@ -171,28 +169,27 @@ static void discovery_server_callback(CattaServer *server, CattaServerState stat
                        catta_s_entry_group_reset(mesh->catta_group);
                        mesh->catta_group = NULL;
                }
-       }
-       break;
 
-       case CATTA_SERVER_FAILURE: {
+               break;
+
+       case CATTA_SERVER_FAILURE:
                // asserts
                assert(mesh->catta_server != NULL);
                assert(mesh->catta_poll != NULL);
 
                /* Terminate on failure */
-               logger(mesh, MESHLINK_ERROR, "Catta server failure: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
                catta_simple_poll_quit(mesh->catta_poll);
-       }
-       break;
+               break;
 
        case CATTA_SERVER_INVALID:
                break;
        }
-
-       pthread_mutex_unlock(&(mesh->mesh_mutex));
 }
 
 static void discovery_resolve_callback(CattaSServiceResolver *resolver, CattaIfIndex interface_, CattaProtocol protocol, CattaResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const CattaAddress *address, uint16_t port, CattaStringList *txt, CattaLookupResultFlags flags, void *userdata) {
+       (void)interface_;
+       (void)protocol;
+       (void)flags;
        meshlink_handle_t *mesh = userdata;
 
        // asserts
@@ -200,19 +197,14 @@ static void discovery_resolve_callback(CattaSServiceResolver *resolver, CattaIfI
        assert(mesh != NULL);
        assert(mesh->catta_server != NULL);
 
-       pthread_mutex_lock(&(mesh->mesh_mutex));
-
        /* Called whenever a service has been resolved successfully or timed out */
        switch(event) {
-       case CATTA_RESOLVER_FAILURE: {
+       case CATTA_RESOLVER_FAILURE:
                // asserts
                assert(name != NULL);
                assert(type != NULL);
                assert(domain != NULL);
-
-               logger(mesh, MESHLINK_WARNING, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, catta_strerror(catta_server_errno(mesh->catta_server)));
-       }
-       break;
+               break;
 
        case CATTA_RESOLVER_FOUND: {
                // asserts
@@ -223,29 +215,6 @@ static void discovery_resolve_callback(CattaSServiceResolver *resolver, CattaIfI
                assert(address != NULL);
                assert(txt != NULL);
 
-               char straddr[CATTA_ADDRESS_STR_MAX], *strtxt;
-
-               logger(mesh, MESHLINK_DEBUG, "(Resolver) Service '%s' of type '%s' in domain '%s':\n", name, type, domain);
-
-               catta_address_snprint(straddr, sizeof(straddr), address);
-               strtxt = catta_string_list_to_string(txt);
-               logger(mesh, MESHLINK_DEBUG,
-                      "\t%s:%u (%s)\n"
-                      "\tTXT=%s\n"
-                      "\tcookie is %u\n"
-                      "\tis_local: %i\n"
-                      "\twide_area: %i\n"
-                      "\tmulticast: %i\n"
-                      "\tcached: %i\n",
-                      host_name, port, straddr,
-                      strtxt,
-                      catta_string_list_get_service_cookie(txt),
-                      !!(flags & CATTA_LOOKUP_RESULT_LOCAL),
-                      !!(flags & CATTA_LOOKUP_RESULT_WIDE_AREA),
-                      !!(flags & CATTA_LOOKUP_RESULT_MULTICAST),
-                      !!(flags & CATTA_LOOKUP_RESULT_CACHED));
-               catta_free(strtxt);
-
                // retrieve fingerprint
                CattaStringList *node_name_li = catta_string_list_find(txt, MESHLINK_MDNS_NAME_KEY);
                CattaStringList *node_fp_li = catta_string_list_find(txt, MESHLINK_MDNS_FINGERPRINT_KEY);
@@ -255,6 +224,8 @@ static void discovery_resolve_callback(CattaSServiceResolver *resolver, CattaIfI
                        char *node_fp = (char *)catta_string_list_get_text(node_fp_li) + strlen(MESHLINK_MDNS_FINGERPRINT_KEY);
 
                        if(node_name[0] == '=' && node_fp[0] == '=') {
+                               pthread_mutex_lock(&(mesh->mesh_mutex));
+
                                node_name += 1;
                                node_fp += 1;
 
@@ -286,26 +257,45 @@ static void discovery_resolve_callback(CattaSServiceResolver *resolver, CattaIfI
                                                break;
                                        }
 
-                                       if(naddress.unknown.family != AF_UNKNOWN)
+                                       if(naddress.unknown.family != AF_UNKNOWN) {
                                                meshlink_hint_address(mesh, (meshlink_node_t *)node, (struct sockaddr *)&naddress);
-                                       else
+                                               pthread_mutex_lock(&(mesh->mesh_mutex));
+
+                                               node_t *n = (node_t *)node;
+
+                                               if(n->connection && n->connection->outgoing) {
+                                                       n->connection->outgoing->timeout = 0;
+
+                                                       if(n->connection->outgoing->ev.cb) {
+                                                               timeout_set(&mesh->loop, &n->connection->outgoing->ev, &(struct timeval) {
+                                                                       0, 0
+                                                               });
+                                                       }
+
+                                                       n->connection->last_ping_time = 0;
+                                               }
+
+                                               pthread_mutex_unlock(&(mesh->mesh_mutex));
+                                       } else {
                                                logger(mesh, MESHLINK_WARNING, "Could not resolve node %s to a known address family type.\n", node->name);
-                               } else
+                                       }
+                               } else {
                                        logger(mesh, MESHLINK_WARNING, "Node %s is not part of the mesh network.\n", node_name);
-                       } else
-                               logger(mesh, MESHLINK_WARNING, "TXT records invalid.\n");
-               } else
-                       logger(mesh, MESHLINK_WARNING, "TXT records missing.\n");
+                               }
+
+                               pthread_mutex_unlock(&(mesh->mesh_mutex));
+                       }
+               }
        }
        break;
        }
 
        catta_s_service_resolver_free(resolver);
-
-       pthread_mutex_unlock(&(mesh->mesh_mutex));
 }
 
 static void discovery_browse_callback(CattaSServiceBrowser *browser, CattaIfIndex interface_, CattaProtocol protocol, CattaBrowserEvent event, const char *name, const char *type, const char *domain, CattaLookupResultFlags flags, void *userdata) {
+       (void)browser;
+       (void)flags;
        meshlink_handle_t *mesh = userdata;
 
        // asserts
@@ -313,50 +303,29 @@ static void discovery_browse_callback(CattaSServiceBrowser *browser, CattaIfInde
        assert(mesh->catta_server != NULL);
        assert(mesh->catta_poll != NULL);
 
-       pthread_mutex_lock(&(mesh->mesh_mutex));
-
        /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
        switch(event) {
-       case CATTA_BROWSER_FAILURE: {
-               logger(mesh, MESHLINK_ERROR, "(Browser) %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
+       case CATTA_BROWSER_FAILURE:
                catta_simple_poll_quit(mesh->catta_poll);
-       }
-       break;
-
-       case CATTA_BROWSER_NEW: {
-               // asserts
-               assert(name != NULL);
-               assert(type != NULL);
-               assert(domain != NULL);
-
-               logger(mesh, MESHLINK_DEBUG, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
-               /* We ignore the returned resolver object. In the callback
-                  function we free it. Ifthe server is terminated before
-                  the callback function is called the server will free
-                  the resolver for us. */
-               if(!(catta_s_service_resolver_new(mesh->catta_server, interface_, protocol, name, type, domain, CATTA_PROTO_UNSPEC, 0, discovery_resolve_callback, mesh)))
-                       logger(mesh, MESHLINK_DEBUG, "Failed to resolve service '%s': %s\n", name, catta_strerror(catta_server_errno(mesh->catta_server)));
-       }
-       break;
+               break;
 
-       case CATTA_BROWSER_REMOVE: {
-               // asserts
-               assert(name != NULL);
-               assert(type != NULL);
-               assert(domain != NULL);
+       case CATTA_BROWSER_NEW:
+               catta_s_service_resolver_new(mesh->catta_server, interface_, protocol, name, type, domain, CATTA_PROTO_UNSPEC, 0, discovery_resolve_callback, mesh);
+               pthread_mutex_lock(&mesh->mesh_mutex);
+               handle_network_change(mesh, ++mesh->catta_interfaces);
+               pthread_mutex_unlock(&mesh->mesh_mutex);
+               break;
 
-               logger(mesh, MESHLINK_DEBUG, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
-       }
-       break;
+       case CATTA_BROWSER_REMOVE:
+               pthread_mutex_lock(&mesh->mesh_mutex);
+               handle_network_change(mesh, --mesh->catta_interfaces);
+               pthread_mutex_unlock(&mesh->mesh_mutex);
+               break;
 
        case CATTA_BROWSER_ALL_FOR_NOW:
-       case CATTA_BROWSER_CACHE_EXHAUSTED: {
-               logger(mesh, MESHLINK_DEBUG, "(Browser) %s\n", event == CATTA_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
-       }
-       break;
+       case CATTA_BROWSER_CACHE_EXHAUSTED:
+               break;
        }
-
-       pthread_mutex_unlock(&(mesh->mesh_mutex));
 }
 
 static void *discovery_loop(void *userdata) {
@@ -389,6 +358,7 @@ static void discovery_log_cb(CattaLogLevel level, const char *txt) {
                break;
 
        case CATTA_LOG_DEBUG:
+       default:
                mlevel = MESHLINK_DEBUG;
                break;
        }
@@ -472,6 +442,7 @@ bool discovery_start(meshlink_handle_t *mesh) {
        return true;
 
 fail:
+
        if(mesh->catta_browser != NULL) {
                catta_s_service_browser_free(mesh->catta_browser);
                mesh->catta_browser = NULL;
@@ -502,8 +473,9 @@ void discovery_stop(meshlink_handle_t *mesh) {
        assert(mesh != NULL);
 
        // Shut down
-       if(mesh->catta_poll)
+       if(mesh->catta_poll) {
                catta_simple_poll_quit(mesh->catta_poll);
+       }
 
        // Wait for the discovery thread to finish
        if(mesh->discovery_threadstarted == true) {
@@ -537,4 +509,6 @@ void discovery_stop(meshlink_handle_t *mesh) {
                free(mesh->catta_servicetype);
                mesh->catta_servicetype = NULL;
        }
+
+       mesh->catta_interfaces = 0;
 }