From e0625111fbd75da6a5bbf8284577b0bc7f56b2b6 Mon Sep 17 00:00:00 2001 From: Niklas Hofmann Date: Thu, 31 Jul 2014 00:39:39 +0200 Subject: [PATCH] avahi mdns skeleton --- src/Makefile.am | 3 +- src/discovery.c | 246 ++++++++++++++++++++++++++++++++++++++++ src/discovery.h | 10 ++ src/ed25519/.dirstamp | 0 src/meshlink.c | 8 ++ src/meshlink_internal.h | 12 ++ 6 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 src/discovery.c create mode 100644 src/discovery.h delete mode 100644 src/ed25519/.dirstamp diff --git a/src/Makefile.am b/src/Makefile.am index 9a92c422..8b72c5b5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ @@ -103,7 +104,7 @@ libmeshlink_la_SOURCES = \ libmeshlink_la_CFLAGS = -fPIC -libmeshlink_la_LIBADD = -lpthread +libmeshlink_la_LIBADD = -lpthread -lavahi-core -lavahi-common -lavahi-client libmeshlink_la_SOURCES += \ ed25519/ecdh.c \ diff --git a/src/discovery.c b/src/discovery.c new file mode 100644 index 00000000..a57cfb83 --- /dev/null +++ b/src/discovery.c @@ -0,0 +1,246 @@ + +#include "meshlink_internal.h" +#include "discovery.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define MESHLINK_MDNS_SERVICE_TYPE "_meshlink._tcp" +#define MESHLINK_MDNS_SERVICE_NAME "Meshlink" + + +static void discovery_entry_group_callback(AvahiServer *server, AvahiSEntryGroup *group, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) +{ + meshlink_handle_t *mesh = userdata; + + /* Called whenever the entry group state changes */ + switch (state) + { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + /* The entry group has been established successfully */ + fprintf(stderr, "Service '%s' successfully established.\n", MESHLINK_MDNS_SERVICE_NAME); + break; + + case AVAHI_ENTRY_GROUP_COLLISION: + fprintf(stderr, "Service name collision '%s'\n", MESHLINK_MDNS_SERVICE_NAME); + break; + + case AVAHI_ENTRY_GROUP_FAILURE : + fprintf(stderr, "Entry group failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server))); + + /* Some kind of failure happened while we were registering our services */ + 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) +{ + /* If this 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, NULL))) { + fprintf(stderr, "avahi_entry_group_new() failed: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server))); + goto fail; + } + + fprintf(stderr, "Adding service '%s'\n", MESHLINK_MDNS_SERVICE_NAME); + + /* Create some random TXT data */ + char fingerprint[1024]; + snprintf(fingerprint, sizeof(fingerprint), "fingerprint=%s", /*meshlink_get_fingerprint(mesh, mesh->self)*/ ""); + + /* Add the service for IPP */ + int ret = 0; + if ((ret = avahi_server_add_service(mesh->avahi_server, mesh->avahi_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, MESHLINK_MDNS_SERVICE_NAME, MESHLINK_MDNS_SERVICE_TYPE, NULL, NULL, mesh->myport ? atoi(mesh->myport) : 655, fingerprint, NULL)) < 0) { + fprintf(stderr, "Failed to add _ipp._tcp 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; + } + + return; + +fail: + avahi_simple_poll_quit(mesh->avahi_poll); +} + +static void discovery_server_callback(AvahiServer *server, AvahiServerState state, AVAHI_GCC_UNUSED void * userdata) +{ + meshlink_handle_t *mesh = userdata; + + 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: + /* A host name collision happened. Let's do nothing */ + 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); + break; + + case AVAHI_SERVER_FAILURE: + /* 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_browse_callback( + AvahiSServiceBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, + void* userdata) +{ + meshlink_handle_t *mesh = userdata; + + /* 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: + 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. If the server is terminated before + the callback function is called the server will free + the resolver for us. */ + //if (!(avahi_s_service_resolver_new(s, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, server))) + // fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(server))); + break; + + case AVAHI_BROWSER_REMOVE: + 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 *arg) +{ + meshlink_handle_t *mesh = arg; + + avahi_simple_poll_loop(mesh->avahi_poll); + + return NULL; +} + +bool discovery_start(meshlink_handle_t *mesh) +{ + // Allocate discovery loop object + if (!(mesh->avahi_poll = avahi_simple_poll_new())) { + fprintf(stderr, "Failed to create discovery poll object.\n"); + goto fail; + } + + // Let's set the host name for this server. + AvahiServerConfig config; + avahi_server_config_init(&config); + config.host_name = avahi_strdup(mesh->name); + config.publish_workstation = 0; + + /* Allocate a new server */ + int error; + mesh->avahi_server = avahi_server_new(avahi_simple_poll_get(mesh->avahi_poll), &config, discovery_server_callback, NULL, &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, MESHLINK_MDNS_SERVICE_TYPE, 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) + avahi_s_service_browser_free(mesh->avahi_browser); + + if (mesh->avahi_server) + avahi_server_free(mesh->avahi_server); + + if (mesh->avahi_poll) + avahi_simple_poll_free(mesh->avahi_poll); + + return false; +} + +void discovery_stop(meshlink_handle_t *mesh) +{ + // @TODO: Shut down + avahi_simple_poll_quit(mesh->avahi_poll); + + // Wait for the discovery thread to finish + + pthread_join(mesh->discovery_thread, NULL); + + // Clean up resources + if (mesh->avahi_browser) + avahi_s_service_browser_free(mesh->avahi_browser); + + if (mesh->avahi_server) + avahi_server_free(mesh->avahi_server); + + if (mesh->avahi_poll) + avahi_simple_poll_free(mesh->avahi_poll); +} diff --git a/src/discovery.h b/src/discovery.h new file mode 100644 index 00000000..0455d6ed --- /dev/null +++ b/src/discovery.h @@ -0,0 +1,10 @@ + +#ifndef __MESHLINK_DISCOVERY_H__ +#define __MESHLINK_DISCOVERY_H__ + +#include + +extern bool discovery_start(meshlink_handle_t *mesh); +extern void discovery_stop(meshlink_handle_t *mesh); + +#endif diff --git a/src/ed25519/.dirstamp b/src/ed25519/.dirstamp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/meshlink.c b/src/meshlink.c index fcdaff62..d33c0d17 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -39,6 +39,7 @@ typedef struct { #include "utils.h" #include "xalloc.h" #include "ed25519/sha512.h" +#include "discovery.h" #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 @@ -823,6 +824,10 @@ bool meshlink_start(meshlink_handle_t *mesh) { mesh->threadstarted=true; + // Start discovery + if(!discovery_start(mesh)) + return false; + return true; } @@ -830,6 +835,9 @@ void meshlink_stop(meshlink_handle_t *mesh) { if(!mesh) return; + // Stop discovery + discovery_stop(mesh); + // Shut down the listening sockets to signal the main thread to shut down for(int i = 0; i < mesh->listen_sockets; i++) { diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index 7c3a7d4c..d4ab46ca 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -33,6 +33,11 @@ #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; @@ -124,6 +129,13 @@ 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; }; /// A handle for a MeshLink node. -- 2.39.2