]> git.meshlink.io Git - meshlink/commitdiff
Merge branch 'discovery' into everbase
authorNiklas Hofmann <niklas.hofmann@everbase.net>
Fri, 8 Aug 2014 12:32:03 +0000 (14:32 +0200)
committerNiklas Hofmann <niklas.hofmann@everbase.net>
Fri, 8 Aug 2014 12:32:03 +0000 (14:32 +0200)
Conflicts:
examples/chatpp.cc
src/meshlink++.h
src/meshlink.c

18 files changed:
.gitmodules [new file with mode: 0644]
README.avahi [new file with mode: 0644]
avahi [new submodule]
examples/chat.c
examples/chatpp.cc
examples/manynodes.c
examples/meshlinkapp.c
src/Makefile.am
src/discovery.c [new file with mode: 0644]
src/discovery.h [new file with mode: 0644]
src/meshlink++.h
src/meshlink.c
src/meshlink.h
src/meshlink_internal.h
src/node.c
src/node.h
src/utils.c
src/utils.h

diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..0e6f544
--- /dev/null
@@ -0,0 +1,4 @@
+[submodule "avahi"]
+       path = avahi
+       url = git@chicago.everbase.net:meshlink/avahi-noptr.git
+       branch = noptr
diff --git a/README.avahi b/README.avahi
new file mode 100644 (file)
index 0000000..f2a7322
--- /dev/null
@@ -0,0 +1,5 @@
+
+cd avahi
+./bootstrap.sh
+./configure --disable-qt3 CFLAGS=-fPIC
+make
diff --git a/avahi b/avahi
new file mode 160000 (submodule)
index 0000000..abf0d60
--- /dev/null
+++ b/avahi
@@ -0,0 +1 @@
+Subproject commit abf0d60e6dd933273887c1600e0793eac2706438
index 6ffebb865ceaec0404df278beca4bb6002b803bc..ddb43bf7c477aa6eee66718e37d8208ea539de14 100644 (file)
@@ -187,7 +187,7 @@ int main(int argc, char *argv[]) {
        if(argc > 2)
                nick = argv[2];
 
-       meshlink_handle_t *mesh = meshlink_open(confbase, nick);
+       meshlink_handle_t *mesh = meshlink_open(confbase, nick, "chat");
        if(!mesh) {
                fprintf(stderr, "Could not open MeshLink: %s\n", meshlink_strerror(meshlink_errno));
                return 1;
index 7625f6574f2c492a81211d44e338b61d77ade1b1..c94cf2bb3d2aea82b1df805a126736708ad809be 100644 (file)
@@ -187,7 +187,8 @@ int main(int argc, char *argv[]) {
        if(argc > 2)
                nick = argv[2];
 
-       ChatMesh* mesh = meshlink::open<ChatMesh>(confbase, nick);
+       ChatMesh* mesh = meshlink::open<ChatMesh>(confbase, nick, "chatpp");
+
        if(!mesh) {
                fprintf(stderr, "Could not open MeshLink: %s\n", meshlink::strerror());
                return 1;
index a3dc1873cad509d0eba9940df23c5afd838c2493..3fa88c3b26decc143450a2abccbfad8b47c7a6a5 100644 (file)
@@ -199,7 +199,7 @@ int main(int argc, char *argv[]) {
                snprintf(nodename, sizeof nodename, "node%d", i);
                snprintf(filename, sizeof filename, "%s/%s", basebase, nodename);
                bool itsnew = access(filename, R_OK);
-               mesh[i] = meshlink_open(filename, nodename);
+               mesh[i] = meshlink_open(filename, nodename, "manynodes");
                if(itsnew)
                        meshlink_add_address(mesh[i], "localhost");
                if(!mesh[i]) {
index f506c964162a52537bed63301f7988440fbd6127..85cdf6feab2ccbadba61532fcc49680dfc06e5a5 100644 (file)
@@ -14,7 +14,7 @@ int main(int argc , char **argv){
 
        meshlink_handle_t* myhandle;
 
-       myhandle = meshlink_open(confbase, name);
+       myhandle = meshlink_open(confbase, name, "meshlinkapp");
 
        //Register callback function for incoming data
        meshlink_set_receive_cb(myhandle, (meshlink_receive_cb_t)handle_recv_data);
index 9a92c422ef4d9fc7842d69fc64ce8bf928ecadf5..bead49e64acaaa2fa06b237864f4a0c95f05144c 100644 (file)
@@ -65,6 +65,7 @@ libmeshlink_la_SOURCES = \
        conf.c conf.h \
        connection.c connection.h \
        crypto.c crypto.h \
+       discovery.c discovery.h \
        dropin.c dropin.h \
        ecdh.h \
        ecdsa.h \
@@ -101,9 +102,9 @@ libmeshlink_la_SOURCES = \
        $(ed25519_SOURCES) \
        $(chacha_poly1305_SOURCES)
 
-libmeshlink_la_CFLAGS = -fPIC
+libmeshlink_la_CFLAGS = -fPIC -I../avahi/
 
-libmeshlink_la_LIBADD = -lpthread
+libmeshlink_la_LIBADD = -lpthread -luuid ../avahi/avahi-core/.libs/libavahi-core.a ../avahi/avahi-common/.libs/libavahi-common.a ../avahi/avahi-client/.libs/libavahi-client.a
 
 libmeshlink_la_SOURCES += \
        ed25519/ecdh.c \
diff --git a/src/discovery.c b/src/discovery.c
new file mode 100644 (file)
index 0000000..4d1209b
--- /dev/null
@@ -0,0 +1,541 @@
+
+#include "meshlink_internal.h"
+#include "discovery.h"
+#include "sockaddr.h"
+
+#include <pthread.h>
+
+#include <avahi-core/core.h>
+#include <avahi-core/lookup.h>
+#include <avahi-core/publish.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+
+#include <netinet/in.h>
+
+#include <uuid/uuid.h>
+
+#define MESHLINK_MDNS_SERVICE_TYPE "_%s._tcp"
+#define MESHLINK_MDNS_NAME_KEY "name"
+#define MESHLINK_MDNS_FINGERPRINT_KEY "fingerprint"
+
+static void discovery_entry_group_callback(AvahiServer *server, AvahiSEntryGroup *group, AvahiEntryGroupState state, void *userdata)
+{
+    meshlink_handle_t *mesh = userdata;
+
+    // asserts
+    assert(mesh != NULL);
+    assert(mesh->avahi_server != NULL);
+    assert(mesh->avahi_poll != NULL);
+
+    /* Called whenever the entry group state changes */
+    switch(state)
+    {
+        case AVAHI_ENTRY_GROUP_ESTABLISHED:
+            /* The entry group has been established successfully */
+            fprintf(stderr, "Service successfully established.\n");
+            break;
+
+        case AVAHI_ENTRY_GROUP_COLLISION:
+            fprintf(stderr, "Service collision\n");
+            // @TODO can we just set a new name and retry?
+            break;
+
+        case AVAHI_ENTRY_GROUP_FAILURE :
+            /* Some kind of failure happened while we were registering our services */
+            fprintf(stderr, "Entry group failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+            avahi_simple_poll_quit(mesh->avahi_poll);
+            break;
+
+        case AVAHI_ENTRY_GROUP_UNCOMMITED:
+        case AVAHI_ENTRY_GROUP_REGISTERING:
+            ;
+    }
+}
+
+
+static void discovery_create_services(meshlink_handle_t *mesh)
+{
+    char *txt_name = NULL;
+
+    // asserts
+    assert(mesh != NULL);
+    assert(mesh->name != NULL);
+    assert(mesh->myport != NULL);
+    assert(mesh->avahi_server != NULL);
+    assert(mesh->avahi_poll != NULL);
+    assert(mesh->avahi_servicetype != NULL);
+    assert(mesh->self != NULL);
+
+    fprintf(stderr, "Adding service\n");
+
+    /* Ifthis is the first time we're called, let's create a new entry group */
+    if(!mesh->avahi_group)
+    {
+        if(!(mesh->avahi_group = avahi_s_entry_group_new(mesh->avahi_server, discovery_entry_group_callback, mesh)))
+        {
+            fprintf(stderr, "avahi_entry_group_new() failed: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+            goto fail;
+        }
+    }
+
+    /* 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)
+    {
+        fprintf(stderr, "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));
+
+    /* Add the service */
+    int ret = 0;
+    if((ret = avahi_server_add_service(mesh->avahi_server, mesh->avahi_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self), mesh->avahi_servicetype, NULL, NULL, atoi(mesh->myport), txt_name, txt_fingerprint, NULL)) < 0)
+    {
+        fprintf(stderr, "Failed to add service: %s\n", avahi_strerror(ret));
+        goto fail;
+    }
+
+    /* Tell the server to register the service */
+    if((ret = avahi_s_entry_group_commit(mesh->avahi_group)) < 0)
+    {
+        fprintf(stderr, "Failed to commit entry_group: %s\n", avahi_strerror(ret));
+        goto fail;
+    }
+
+    goto done;
+
+fail:
+    avahi_simple_poll_quit(mesh->avahi_poll);
+
+done:
+    if(txt_name)
+        { free(txt_name); }
+}
+
+static void discovery_server_callback(AvahiServer *server, AvahiServerState state, void * userdata)
+{
+       meshlink_handle_t *mesh = userdata;
+
+    // asserts
+    assert(mesh != NULL);
+
+    switch(state)
+    {
+        case AVAHI_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->avahi_group)
+                {
+                    discovery_create_services(mesh);
+                }
+            }
+            break;
+
+        case AVAHI_SERVER_COLLISION:
+            {
+                // asserts
+                assert(mesh->avahi_server != NULL);
+                assert(mesh->avahi_poll != NULL);
+
+                /* A host name collision happened. Let's pick a new name for the server */
+                uuid_t hostname;
+                uuid_generate(hostname);
+
+                char hostnamestr[36+1];
+                uuid_unparse_lower(hostname, hostnamestr);
+
+                fprintf(stderr, "Host name collision, retrying with '%s'\n", hostnamestr);
+                int result = avahi_server_set_host_name(mesh->avahi_server, hostnamestr);
+
+                if(result < 0)
+                {
+                    fprintf(stderr, "Failed to set new host name: %s\n", avahi_strerror(result));
+                    avahi_simple_poll_quit(mesh->avahi_poll);
+                    return;
+                }
+            }
+            break;
+
+        case AVAHI_SERVER_REGISTERING:
+            {
+                /* Let's drop our registered services. When the server is back
+                 * in AVAHI_SERVER_RUNNING state we will register them
+                 * again with the new host name. */
+                if(mesh->avahi_group)
+                {
+                    avahi_s_entry_group_reset(mesh->avahi_group);
+                    mesh->avahi_group = NULL;
+                }
+            }
+            break;
+
+        case AVAHI_SERVER_FAILURE:
+            {
+                // asserts
+                assert(mesh->avahi_server != NULL);
+                assert(mesh->avahi_poll != NULL);
+
+                /* Terminate on failure */
+                fprintf(stderr, "Server failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+                avahi_simple_poll_quit(mesh->avahi_poll);
+            }
+            break;
+
+        case AVAHI_SERVER_INVALID:
+            break;
+    }
+}
+
+static void discovery_resolve_callback(AvahiSServiceResolver *resolver, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata)
+{
+    meshlink_handle_t *mesh = userdata;
+
+    // asserts
+    assert(resolver != NULL);
+    assert(mesh != NULL);
+    assert(mesh->avahi_server != NULL);
+
+    /* Called whenever a service has been resolved successfully or timed out */
+    switch(event)
+    {
+        case AVAHI_RESOLVER_FAILURE:
+            {
+                // asserts
+                assert(name != NULL);
+                assert(type != NULL);
+                assert(domain != NULL);
+
+                fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+            }
+            break;
+
+        case AVAHI_RESOLVER_FOUND:
+            {
+                // asserts
+                assert(name != NULL);
+                assert(type != NULL);
+                assert(domain != NULL);
+                assert(host_name != NULL);
+                assert(address != NULL);
+                assert(txt != NULL);
+        
+                char straddr[AVAHI_ADDRESS_STR_MAX], *strtxt;
+
+                fprintf(stderr, "(Resolver) Service '%s' of type '%s' in domain '%s':\n", name, type, domain);
+
+                avahi_address_snprint(straddr, sizeof(straddr), address);
+                strtxt = avahi_string_list_to_string(txt);
+                fprintf(stderr,
+                        "\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,
+                        avahi_string_list_get_service_cookie(txt),
+                        !!(flags & AVAHI_LOOKUP_RESULT_LOCAL),
+                        !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA),
+                        !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST),
+                        !!(flags & AVAHI_LOOKUP_RESULT_CACHED));
+                avahi_free(strtxt);
+
+                // retrieve fingerprint
+                AvahiStringList *node_name_li = avahi_string_list_find(txt, MESHLINK_MDNS_NAME_KEY);
+                AvahiStringList *node_fp_li = avahi_string_list_find(txt, MESHLINK_MDNS_FINGERPRINT_KEY);
+
+                if(node_name_li != NULL && node_fp_li != NULL)
+                {
+                    char *node_name = avahi_string_list_get_text(node_name_li) + strlen(MESHLINK_MDNS_NAME_KEY) + 1;
+                    char *node_fp = avahi_string_list_get_text(node_fp_li) + strlen(MESHLINK_MDNS_FINGERPRINT_KEY) + 1;
+
+                    meshlink_node_t *node = meshlink_get_node(mesh, node_name);
+
+                    if(node != NULL)
+                    {
+                        fprintf(stderr, "Node %s is part of the mesh network.\n", node->name);
+
+                        sockaddr_t naddress;
+                        memset(&naddress, 0, sizeof(naddress));
+
+                        switch(address->proto)
+                        {
+                            case AVAHI_PROTO_INET:
+                                {
+                                    naddress.in.sin_family = AF_INET;
+                                    naddress.in.sin_port = port;
+                                    naddress.in.sin_addr.s_addr = address->data.ipv4.address;
+                                }
+                                break;
+
+                            case AVAHI_PROTO_INET6:
+                                {
+                                    naddress.in6.sin6_family = AF_INET6;
+                                    naddress.in6.sin6_port = port;
+                                    memcpy(naddress.in6.sin6_addr.s6_addr, address->data.ipv6.address, sizeof(naddress.in6.sin6_addr.s6_addr));
+                                }
+                                break;
+
+                            default:
+                                naddress.unknown.family = AF_UNKNOWN;
+                                break;
+                        }
+
+                        if(naddress.unknown.family != AF_UNKNOWN)
+                        {
+                            meshlink_hint_address(mesh, (meshlink_node_t *)node, (struct sockaddr*)&naddress);
+                        }
+                        else
+                        {
+                            fprintf(stderr, "Could not resolve node %s to a known address family type.\n", node->name);
+                        }
+                    }
+                    else
+                    {
+                        fprintf(stderr, "Node %s is not part of the mesh network.\n", node_name);
+                    }
+                }
+                else
+                {
+                    fprintf(stderr, "TXT records missing.\n");
+                }
+            }
+            break;
+    }
+
+    avahi_s_service_resolver_free(resolver);
+}
+
+static void discovery_browse_callback(AvahiSServiceBrowser *browser, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata)
+{
+       meshlink_handle_t *mesh = userdata;
+
+    // asserts
+    assert(mesh != NULL);
+    assert(mesh->avahi_server != NULL);
+    assert(mesh->avahi_poll != NULL);
+
+    /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
+    switch (event)
+    {
+        case AVAHI_BROWSER_FAILURE:
+            {
+                fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+                avahi_simple_poll_quit(mesh->avahi_poll);
+            }
+            return;
+
+        case AVAHI_BROWSER_NEW:
+            {
+                // asserts
+                assert(name != NULL);
+                assert(type != NULL);
+                assert(domain != NULL);
+
+                fprintf(stderr, "(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(!(avahi_s_service_resolver_new(mesh->avahi_server, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, discovery_resolve_callback, mesh)))
+                {
+                    fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+                }
+            }
+            break;
+
+        case AVAHI_BROWSER_REMOVE:
+            {
+                // asserts
+                assert(name != NULL);
+                assert(type != NULL);
+                assert(domain != NULL);
+
+                fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
+            }
+            break;
+
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+            {
+                fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
+            }
+            break;
+    }
+}
+
+static void *discovery_loop(void *userdata)
+{
+       meshlink_handle_t *mesh = userdata;
+
+    // asserts
+    assert(mesh != NULL);
+    assert(mesh->avahi_poll != NULL);
+
+    avahi_simple_poll_loop(mesh->avahi_poll);
+       
+    return NULL;
+}
+
+bool discovery_start(meshlink_handle_t *mesh)
+{
+    // asserts
+    assert(mesh != NULL);
+    assert(mesh->avahi_poll == NULL);
+    assert(mesh->avahi_server == NULL);
+    assert(mesh->avahi_browser == NULL);
+    assert(mesh->discovery_threadstarted == false);
+    assert(mesh->avahi_servicetype == NULL);
+
+    // create service type string
+    size_t servicetype_strlen = sizeof(MESHLINK_MDNS_SERVICE_TYPE) + strlen(mesh->appname) + 1;
+    mesh->avahi_servicetype = malloc(servicetype_strlen);
+
+    if(mesh->avahi_servicetype == NULL)
+    {
+        fprintf(stderr, "Failed to allocate memory for service type string.\n");
+        goto fail;
+    }
+
+    snprintf(mesh->avahi_servicetype, servicetype_strlen, MESHLINK_MDNS_SERVICE_TYPE, mesh->appname);
+
+    // Allocate discovery loop object
+    if(!(mesh->avahi_poll = avahi_simple_poll_new()))
+    {
+        fprintf(stderr, "Failed to create discovery poll object.\n");
+               goto fail;
+    }
+
+    // generate some unique host name (we actually do not care about it)
+    uuid_t hostname;
+    uuid_generate(hostname);
+
+    char hostnamestr[36+1];
+    uuid_unparse_lower(hostname, hostnamestr);
+
+    // Let's set the host name for this server.
+    AvahiServerConfig config;
+    avahi_server_config_init(&config);
+    config.host_name = avahi_strdup(hostnamestr);
+    config.publish_workstation = 0;
+    config.disallow_other_stacks = 0;
+    config.publish_hinfo = 0;
+    config.publish_addresses = 1;
+    config.publish_no_reverse = 1;
+
+    /* Allocate a new server */
+    int error;
+    mesh->avahi_server = avahi_server_new(avahi_simple_poll_get(mesh->avahi_poll), &config, discovery_server_callback, mesh, &error);
+
+    /* Free the configuration data */
+    avahi_server_config_free(&config);
+
+    /* Check wether creating the server object succeeded */
+    if(!mesh->avahi_server)
+    {
+        fprintf(stderr, "Failed to create discovery server: %s\n", avahi_strerror(error));
+        goto fail;
+    }
+
+    // Create the service browser
+    if(!(mesh->avahi_browser = avahi_s_service_browser_new(mesh->avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, mesh->avahi_servicetype, NULL, 0, discovery_browse_callback, mesh)))
+    {
+        fprintf(stderr, "Failed to create discovery service browser: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
+        goto fail;
+    }
+
+       // Start the discovery thread
+       if(pthread_create(&mesh->discovery_thread, NULL, discovery_loop, mesh) != 0)
+    {
+               fprintf(stderr, "Could not start discovery thread: %s\n", strerror(errno));
+               memset(&mesh->discovery_thread, 0, sizeof mesh->discovery_thread);
+               goto fail;
+       }
+
+       mesh->discovery_threadstarted = true;
+
+       return true;
+
+fail:
+    if(mesh->avahi_browser != NULL)
+    {
+        avahi_s_service_browser_free(mesh->avahi_browser);
+        mesh->avahi_browser = NULL;
+    }
+
+    if(mesh->avahi_server != NULL)
+    {
+        avahi_server_free(mesh->avahi_server);
+        mesh->avahi_server = NULL;
+    }
+
+    if(mesh->avahi_poll != NULL)
+    {
+        avahi_simple_poll_free(mesh->avahi_poll);
+        mesh->avahi_poll = NULL;
+    }
+
+    if(mesh->avahi_servicetype != NULL)
+    {
+        free(mesh->avahi_servicetype);
+        mesh->avahi_servicetype = NULL;
+    }
+
+    return false;
+}
+
+void discovery_stop(meshlink_handle_t *mesh)
+{
+    // asserts
+    assert(mesh != NULL);
+
+       // Shut down
+    if(mesh->avahi_poll)
+    {
+        avahi_simple_poll_quit(mesh->avahi_poll);
+    }
+
+       // Wait for the discovery thread to finish
+    if(mesh->discovery_threadstarted == true)
+    {
+        pthread_join(mesh->discovery_thread, NULL);
+        mesh->discovery_threadstarted = false;
+    }
+
+       // Clean up resources
+    if(mesh->avahi_browser != NULL)
+    {
+        avahi_s_service_browser_free(mesh->avahi_browser);
+        mesh->avahi_browser = NULL;
+    }
+
+    if(mesh->avahi_server != NULL)
+    {
+        avahi_server_free(mesh->avahi_server);
+        mesh->avahi_server = NULL;
+    }
+
+    if(mesh->avahi_poll != NULL)
+    {
+        avahi_simple_poll_free(mesh->avahi_poll);
+        mesh->avahi_poll = NULL;
+    }
+
+    if(mesh->avahi_servicetype != NULL)
+    {
+        free(mesh->avahi_servicetype);
+        mesh->avahi_servicetype = NULL;
+    }
+}
diff --git a/src/discovery.h b/src/discovery.h
new file mode 100644 (file)
index 0000000..0455d6e
--- /dev/null
@@ -0,0 +1,10 @@
+
+#ifndef __MESHLINK_DISCOVERY_H__
+#define __MESHLINK_DISCOVERY_H__
+
+#include <stdbool.h>
+
+extern bool discovery_start(meshlink_handle_t *mesh);
+extern void discovery_stop(meshlink_handle_t *mesh);
+
+#endif
index 8f852d9f1d22213e9f29c988a82e6c1a25542b97..9500852bbf2c05f59715f3d36f16916aaebe82e4 100644 (file)
@@ -381,12 +381,13 @@ namespace meshlink {
         *
         *  @param confbase The directory in which MeshLink will store its configuration files.
         *  @param name     The name which this instance of the application will use in the mesh.
+        *  @param appname  The application name which will be used in the mesh.
         *
         *  @return         This function will return a pointer to a meshlink::mesh if MeshLink has succesfully set up its configuration files, NULL otherwise.
         */
        template<class MESH>
-       static MESH* open(const char *confbase, const char *name) {
-               void* mp = (void *)meshlink_open_with_size(confbase, name, sizeof(MESH));
+       static MESH* open(const char *confbase, const char *name, const char* appname) {
+               void* mp = (void *)meshlink_open_with_size(confbase, name, appname, sizeof(MESH));
                return new (mp) MESH;
        }
 
index 4fa3a58000135bb71713659de90599dac492458e..d5ea5418e974c398ba181aa131dacf1cdd321df8 100644 (file)
@@ -21,6 +21,8 @@
 #define VAR_MULTIPLE 4  /* Multiple statements allowed */
 #define VAR_OBSOLETE 8  /* Should not be used anymore */
 #define VAR_SAFE 16     /* Variable is safe when accepting invitations */
+#define MAX_ADDRESS_LENGTH 45 /* Max length of an (IPv6) address */
+#define MAX_PORT_LENGTH 5 /* 0-65535 */
 typedef struct {
        const char *name;
        int type;
@@ -39,6 +41,7 @@ typedef struct {
 #include "utils.h"
 #include "xalloc.h"
 #include "ed25519/sha512.h"
+#include "discovery.h"
 
 #ifndef MSG_NOSIGNAL
 #define MSG_NOSIGNAL 0
@@ -734,13 +737,12 @@ static bool meshlink_setup(meshlink_handle_t *mesh) {
        return true;
 }
 
-
-meshlink_handle_t *meshlink_open(const char *confbase, const char *name) {
-       return meshlink_open_with_size(confbase, name, sizeof(meshlink_handle_t) );
+meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char* appname) {
+       return meshlink_open_with_size(confbase, name, appname, sizeof(meshlink_handle_t));
 }
 
+meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, const char* appname, size_t size) {
 
-meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, size_t size) {
        // Validate arguments provided by the application
        bool usingname = false;
 
@@ -750,6 +752,12 @@ meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *nam
                return NULL;
        }
 
+       if(!appname || !*appname) {
+               fprintf(stderr, "No appname given!\n");
+               meshlink_errno = MESHLINK_EINVAL;
+               return NULL;
+       }
+
        if(!name || !*name) {
                fprintf(stderr, "No name given!\n");
                //return NULL;
@@ -765,6 +773,7 @@ meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *nam
 
        meshlink_handle_t *mesh = xzalloc(size);
        mesh->confbase = xstrdup(confbase);
+       mesh->appname = xstrdup(appname);
        if (usingname) mesh->name = xstrdup(name);
        pthread_mutex_init ( &(mesh->nodes_mutex), NULL);
        mesh->threadstarted = false;
@@ -859,19 +868,26 @@ bool meshlink_start(meshlink_handle_t *mesh) {
 
        mesh->threadstarted=true;
 
+       // Start discovery
+       if(!discovery_start(mesh))
+               return false;
+
        return true;
 }
 
 void meshlink_stop(meshlink_handle_t *mesh) {
+       
        if(!mesh) {
                meshlink_errno = MESHLINK_EINVAL;
                return;
        }
 
-       listen_socket_t *s = &mesh->listen_socket[0];
+       // Stop discovery
+       discovery_stop(mesh);
 
        // Shut down a listening socket to signal the main thread to shut down
 
+       listen_socket_t *s = &mesh->listen_socket[0];
        shutdown(s->tcp.fd, SHUT_RDWR);
 
        // Wait for the main thread to finish
@@ -1653,6 +1669,47 @@ void meshlink_whitelist(meshlink_handle_t *mesh, meshlink_node_t *node) {
        return;
 }
 
+/* Hint that a hostname may be found at an address
+ * See header file for detailed comment.
+ */
+extern void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, struct sockaddr *addr) {
+       if(!mesh || !node || !addr)
+               return;
+       
+       char *addr_str = malloc(MAX_ADDRESS_LENGTH*sizeof(char));
+       memset(addr_str, 0, MAX_ADDRESS_LENGTH*sizeof(char));
+
+       char *port_str = malloc(MAX_PORT_LENGTH*sizeof(char));
+       memset(port_str, 0, MAX_PORT_LENGTH*sizeof(char));
+       
+       // extra byte for a space, and one to make sure string is null-terminated
+       int full_addr_len = MAX_ADDRESS_LENGTH + MAX_PORT_LENGTH + 2;
+
+       char *full_addr_str = malloc(full_addr_len*sizeof(char));
+       memset(full_addr_str, 0, full_addr_len*sizeof(char));
+       
+       // get address and port number
+       if(!get_ip_str(addr, addr_str, MAX_ADDRESS_LENGTH))
+               goto fail;
+       if(!get_port_str(addr, port_str, MAX_ADDRESS_LENGTH))
+               goto fail;
+
+       // append_config_file expects an address, a space, and then a port number
+       strcat(full_addr_str, addr_str);
+       strcat(full_addr_str, " ");
+       strcat(full_addr_str, port_str);
+       
+       append_config_file(mesh, node->name, "Address", full_addr_str);
+
+fail:
+done:
+       free(addr_str);
+       free(port_str);
+       free(full_addr_str);
+
+       // @TODO do we want to fire off a connection attempt right away?
+}
+
 static void __attribute__((constructor)) meshlink_init(void) {
        crypto_init();
 }
index 603dbbd2e42a6004dbdb958b930ba812bfd9f11d..96fb8d6e7492da240e1505c06ce94dbd294f43b9 100644 (file)
 #include <stddef.h>
 #include <unistd.h>
 
+#if defined(_WIN32)
+#include <Winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -32,6 +39,9 @@ extern "C" {
 /// The length in bytes of a signature made with meshlink_sign()
 #define MESHLINK_SIGLEN  (64)
 
+// The maximum length of fingerprints
+#define MESHLINK_FINGERPRINTLEN  (64)
+
 /// A handle for an instance of MeshLink.
 typedef struct meshlink_handle meshlink_handle_t;
 
@@ -111,14 +121,16 @@ extern const char *meshlink_strerror(meshlink_errno_t err);
  *                  After the function returns, the application is free to overwrite or free @a confbase @a.
  *  @param name     The name which this instance of the application will use in the mesh.
  *                  After the function returns, the application is free to overwrite or free @a name @a.
+ *  @param appname  The application name which will be used in the mesh.
+ *                  After the function returns, the application is free to overwrite or free @a name @a.
  *
  *  @return         A pointer to a meshlink_handle_t which represents this instance of MeshLink, or NULL in case of an error.
  *                  The pointer is valid until meshlink_close() is called.
  */
-extern meshlink_handle_t *meshlink_open(const char *confbase, const char *name);
+extern meshlink_handle_t *meshlink_open(const char *confbase, const char *name, const char* appname);
 
 /// is used by the C++ wrapper to allocate more memory behind the handle
-extern meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, size_t size);
+extern meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, const char* appname, size_t size);
 
 /// Start MeshLink.
 /** This function causes MeshLink to open network sockets, make outgoing connections, and
@@ -565,6 +577,20 @@ extern void meshlink_channel_close(meshlink_handle_t *mesh, meshlink_channel_t *
  */
 extern ssize_t meshlink_channel_send(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len);
 
+/// Hint that a hostname may be found at an address
+/** This function indicates to meshlink that the given hostname is likely found
+ *  at the given IP address and port.
+ *
+ *  @param mesh                A handle which represents an instance of MeshLink.
+ *  @param hostname    The hostname which can be found at the given address.
+ *                     The caller is free to overwrite or free this string
+ *                     once meshlink returns.
+ *  @param addr                The IP address and port which should be tried for the
+ *                     given hostname. The caller is free to overwrite or free
+ *                     this memory once meshlink returns.
+ */
+extern void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, struct sockaddr *addr);
+
 #ifdef __cplusplus
 }
 #endif
index c9c1eeaa38ffa9bd0f5f9bb785afaa9bafeb66c3..6b47481f879e972fb4a83f5d801fe9b985cd2382 100644 (file)
 
 #define MAXSOCKETS 8    /* Probably overkill... */
 
+struct AvahiServer;
+struct AvahiSServiceBrowser;
+struct AvahiSimplePoll;
+struct AvahiSEntryGroup;
+
 typedef struct listen_socket_t {
        struct io_t tcp;
        struct io_t udp;
@@ -63,6 +68,8 @@ struct meshlink_handle {
 
        char *confbase;
 
+       char *appname;
+
        meshlink_receive_cb_t receive_cb;
        meshlink_node_status_cb_t node_status_cb;
        meshlink_log_cb_t log_cb;
@@ -127,6 +134,14 @@ struct meshlink_handle {
        char line[4096];
        char buffer[4096];
        size_t blen;
+
+       pthread_t discovery_thread;
+       bool discovery_threadstarted;
+       struct AvahiServer *avahi_server;
+       struct AvahiSServiceBrowser *avahi_browser;
+       struct AvahiSimplePoll *avahi_poll;
+       struct AvahiSEntryGroup *avahi_group;
+       char* avahi_servicetype;
 };
 
 /// A handle for a MeshLink node.
index b7b39927948b2e8579b71a1f07a2aa9261266f99..edf2c85809134bc55581b7a9b5d46d562c2be663 100644 (file)
@@ -104,7 +104,7 @@ void node_del(meshlink_handle_t *mesh, node_t *n) {
        pthread_mutex_unlock(&(mesh->nodes_mutex));
 }
 
-node_t *lookup_node(meshlink_handle_t *mesh, char *name) {
+node_t *lookup_node(meshlink_handle_t *mesh, const char *name) {
        node_t n = {NULL};
        node_t* result;
 
index 4f0f13089bf6b1901f401a9b1787bc6ac055c9be..e9bc0538b847ad7550da763050349111af26ec5f 100644 (file)
@@ -97,7 +97,7 @@ extern node_t *new_node(void) __attribute__ ((__malloc__));
 extern void free_node(node_t *);
 extern void node_add(struct meshlink_handle *mesh, node_t *);
 extern void node_del(struct meshlink_handle *mesh, node_t *);
-extern node_t *lookup_node(struct meshlink_handle *mesh, char *);
+extern node_t *lookup_node(struct meshlink_handle *mesh, const char *);
 extern node_t *lookup_node_udp(struct meshlink_handle *mesh, const sockaddr_t *);
 extern void update_node_udp(struct meshlink_handle *mesh, node_t *, const sockaddr_t *);
 
index e45e5942d8a477a82e08924ba59e727c1feca4ff..0f7b00cc7949807aa1ce4d2a39577049c3e4dc6e 100644 (file)
@@ -178,3 +178,51 @@ unsigned int bitfield_to_int(const void *bitfield, size_t size) {
        memcpy(&value, bitfield, size);
        return value;
 }
+
+/* Write IP address from sockaddr to string.
+ * Returns NULL on error.
+ */
+char *get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen)
+{
+       switch(sa->sa_family) {
+               case AF_INET:
+                       inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr),
+                               s, maxlen);
+                       break;
+
+               case AF_INET6:
+                       inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr),
+                               s, maxlen);
+                       break;
+
+               default:
+                       strncpy(s, "Unknown AF", maxlen);
+                       return NULL;
+       }
+
+       return s;
+}
+
+/* Write port from sockaddr to string.
+ * Returns NULL on error.
+ */
+char *get_port_str(const struct sockaddr *sa, char *s, size_t maxlen)
+{
+       switch(sa->sa_family) {
+               case AF_INET:
+                       snprintf(s, maxlen, "%d",
+                               ntohs(((struct sockaddr_in*)sa)->sin_port));
+                       break;
+
+               case AF_INET6:
+                       snprintf(s, maxlen, "%d",
+                               ntohs(((struct sockaddr_in6*)sa)->sin6_port));
+                       break;
+
+               default:
+                       strncpy(s, "Unknown AF", maxlen);
+                       return NULL;
+       }
+
+       return s;
+}
index 1ea1026a52303bfa82681eb1dc025eb45a323a7c..c0b677fcccbef542069800b916bf625e020308f9 100644 (file)
@@ -47,4 +47,7 @@ extern const char *winerror(int);
 
 extern unsigned int bitfield_to_int(const void *bitfield, size_t size);
 
+extern char *get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen);
+extern char *get_port_str(const struct sockaddr *sa, char *s, size_t maxlen);
+
 #endif /* __MESHLINK_UTILS_H__ */