]> 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

1  2 
examples/chat.c
examples/chatpp.cc
examples/manynodes.c
src/discovery.c
src/meshlink++.h
src/meshlink.c
src/meshlink.h
src/meshlink_internal.h

diff --combined examples/chat.c
index 6ffebb865ceaec0404df278beca4bb6002b803bc,070600f43e938673a20af2f4d3e63b7a053e0e05..ddb43bf7c477aa6eee66718e37d8208ea539de14
@@@ -56,16 -56,11 +56,16 @@@ static void parse_command(meshlink_hand
                        fprintf(stderr, "/join requires an argument!\n");
                        return;
                }
 -
 +              meshlink_stop(mesh);
                if(!meshlink_join(mesh, arg))
                        fprintf(stderr, "Could not join using invitation: %s\n", meshlink_strerror(meshlink_errno));
 -              else
 +              else {
                        fprintf(stderr, "Invitation accepted!\n");
 +                      if(!meshlink_start(mesh)) {
 +                              fprintf(stderr, "Could not start MeshLink: %s\n", meshlink_strerror(meshlink_errno));
 +                      return;
 +                      }
 +              }
        } else if(!strcasecmp(buf, "kick")) {
                if(!arg) {
                        fprintf(stderr, "/kick requires an argument!\n");
@@@ -187,7 -182,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;
diff --combined examples/chatpp.cc
index 7625f6574f2c492a81211d44e338b61d77ade1b1,608367a9c432f6fd6aeaa1ca6841db1cdb13d20f..c94cf2bb3d2aea82b1df805a126736708ad809be
@@@ -4,32 -4,28 +4,32 @@@
  #include <strings.h>
  #include "../src/meshlink++.h"
  
 -static void log_message(meshlink::mesh *mesh, meshlink::log_level_t level, const char *text) {
 -      const char *levelstr[] = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"};
 -      fprintf(stderr, "%s: %s\n", levelstr[level], text);
 -}
 -
 -static void receive(meshlink::mesh *mesh, meshlink::node *source, const void *data, size_t len) {
 -      const char *msg = (const char *)data;
 +class ChatMesh : public meshlink::mesh
 +{
 +public:
 +      void log(meshlink::log_level_t level, const char *text) {
 +              const char *levelstr[] = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"};
 +              fprintf(stderr, "%s: %s\n", levelstr[level], text);
 +              }
  
 -      if(!len || msg[len - 1]) {
 -              fprintf(stderr, "Received invalid data from %s\n", source->name);
 -              return;
 +      void receive(meshlink::node *source, const void *data, size_t len) {
 +              const char *msg = (const char *)data;
 +      
 +              if(!len || msg[len - 1]) {
 +                      fprintf(stderr, "Received invalid data from %s\n", source->name);
 +                      return;
 +              }
 +              
 +              printf("%s says: %s\n", source->name, msg);
        }
  
 -      printf("%s says: %s\n", source->name, msg);
 -}
 -
 -static void node_status(meshlink::mesh *mesh, meshlink::node *node, bool reachable) {
 -      if(reachable)
 -              printf("%s joined.\n", node->name);
 -      else
 -              printf("%s left.\n", node->name);
 -}
 +      void node_status(meshlink::node *node, bool reachable) {
 +              if(reachable)
 +                      printf("%s joined.\n", node->name);
 +              else
 +                      printf("%s left.\n", node->name);
 +      }
 +};
  
  static meshlink::node **nodes;
  static size_t nnodes;
@@@ -86,7 -82,7 +86,7 @@@ static void parse_command(meshlink::mes
                        if(!nodes) {
                                fprintf(stderr, "Could not get list of nodes: %s\n", meshlink::strerror());
                        } else {
 -                              printf("%d known nodes:", nnodes);
 +                              printf("%zu known nodes:", nnodes);
                                for(size_t i = 0; i < nnodes; i++)
                                        printf(" %s", nodes[i]->name);
                                printf("\n");
@@@ -175,7 -171,6 +175,7 @@@ static void parse_input(meshlink::mesh 
        printf("Message sent to '%s'.\n", destination->name);
  }
  
 +
  int main(int argc, char *argv[]) {
        const char *confbase = ".chat";
        const char *nick = NULL;
        if(argc > 2)
                nick = argv[2];
  
-       ChatMesh* mesh = meshlink::open<ChatMesh>(confbase, nick);
 -      meshlink::mesh *mesh = meshlink::open(confbase, nick, "chatpp");
++      ChatMesh* mesh = meshlink::open<ChatMesh>(confbase, nick, "chatpp");
++
        if(!mesh) {
                fprintf(stderr, "Could not open MeshLink: %s\n", meshlink::strerror());
                return 1;
        }
  
 -      mesh->set_receive_cb(receive);
 -      mesh->set_node_status_cb(node_status);
 -      mesh->set_log_cb(MESHLINK_INFO, log_message);
 -
        if(!mesh->start()) {
                fprintf(stderr, "Could not start MeshLink: %s\n", meshlink::strerror());
                return 1;
diff --combined examples/manynodes.c
index a3dc1873cad509d0eba9940df23c5afd838c2493,e2b1630cf4ab15da68ffb78cc2004214ff8cd2b6..3fa88c3b26decc143450a2abccbfad8b47c7a6a5
  static int n = 100;
  static meshlink_handle_t **mesh;
  
 -static void log_message(meshlink_handle_t *mesh, meshlink_log_level_t level, const char *text) {
 -      const char *levelstr[] = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"};
 -      fprintf(stderr, "%s: %s\n", levelstr[level], text);
 -}
 -
 -static void receive(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len) {
 -      const char *msg = data;
 -
 -      if(!len || msg[len - 1]) {
 -              fprintf(stderr, "Received invalid data from %s\n", source->name);
 -              return;
 -      }
 -
 -      printf("%s says: %s\n", source->name, msg);
 -}
 -
 -static void node_status(meshlink_handle_t *mesh, meshlink_node_t *node, bool reachable) {
 -      if(reachable)
 -              printf("%s joined.\n", node->name);
 -      else
 -              printf("%s left.\n", node->name);
 -}
 +static meshlink_node_t **nodes;
 +static size_t nnodes;
  
  // Make all nodes know about each other by importing each others public keys and addresses.
  static void linkmesh() {
@@@ -78,13 -98,16 +78,13 @@@ static void parse_command(char *buf) 
                printf("Node '%s' blacklisted.\n", arg);
        } else if(!strcasecmp(buf, "who")) {
                if(!arg) {
 -                      meshlink_node_t *nodes[100];
 -                      size_t n = meshlink_get_all_nodes(mesh[0], nodes, 100);
 -                      if(!n) {
 -                              fprintf(stderr, "No nodes known!\n");
 +                      nodes = meshlink_get_all_nodes(mesh[0], nodes, &nnodes);
 +                      if(!nodes) {
 +                              fprintf(stderr, "Could not get list of nodes: %s\n", meshlink_strerror(meshlink_errno));
                        } else {
 -                              printf("Known nodes:");
 -                              for(int i = 0; i < n && i < 100; i++)
 +                              printf("%zu known nodes:", nnodes);
 +                              for(int i = 0; i < nnodes; i++)
                                        printf(" %s", nodes[i]->name);
 -                              if(n > 100)
 -                                      printf(" (and %zu more)", n - 100);
                                printf("\n");
                        }
                } else {
@@@ -199,7 -222,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]) {
diff --combined src/discovery.c
index 0000000000000000000000000000000000000000,1f163cf682a41e596a8d7eb8a3a414538f4de0cb..4d1209b856ab9cef92d4bacab5af9fbc2a361d61
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,527 +1,541 @@@
 -    assert(mesh->avahi_poll != NULL);
 -    assert(mesh->avahi_server != NULL);
 -    assert(mesh->avahi_browser != NULL);
 -    assert(mesh->discovery_threadstarted == true);
 -    assert(mesh->avahi_servicetype != NULL);
+ #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 
 -      avahi_simple_poll_quit(mesh->avahi_poll);
 -      pthread_join(mesh->discovery_thread, NULL);
++      // Shut down
++    if(mesh->avahi_poll)
++    {
++        avahi_simple_poll_quit(mesh->avahi_poll);
++    }
+       // Wait for the discovery thread to finish
 -    avahi_s_service_browser_free(mesh->avahi_browser);
 -    mesh->avahi_browser = NULL;
++    if(mesh->discovery_threadstarted == true)
++    {
++        pthread_join(mesh->discovery_thread, NULL);
++        mesh->discovery_threadstarted = false;
++    }
+       // Clean up resources
 -    avahi_server_free(mesh->avahi_server);
 -    mesh->avahi_server = NULL;
++    if(mesh->avahi_browser != NULL)
++    {
++        avahi_s_service_browser_free(mesh->avahi_browser);
++        mesh->avahi_browser = NULL;
++    }
 -    avahi_simple_poll_free(mesh->avahi_poll);
 -    mesh->avahi_poll = NULL;
++    if(mesh->avahi_server != NULL)
++    {
++        avahi_server_free(mesh->avahi_server);
++        mesh->avahi_server = NULL;
++    }
 -    free(mesh->avahi_servicetype);
 -    mesh->avahi_servicetype = 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 --combined src/meshlink++.h
index 8f852d9f1d22213e9f29c988a82e6c1a25542b97,2bc1f8b0d59cdf02b905efa8dbee58cc777a313b..9500852bbf2c05f59715f3d36f16916aaebe82e4
@@@ -21,7 -21,6 +21,7 @@@
  #define MESHLINKPP_H
  
  #include <meshlink.h>
 +#include <new> // for 'placement new'
  
  namespace meshlink {
        class mesh;
  
        /// A class describing a MeshLink mesh.
        class mesh: public meshlink_handle_t {
 -              public:
 -              // TODO: delete constructor, add a destructor.
 -
 +      public:
 +              mesh() {}
 +      
 +              virtual ~mesh() {
 +                      meshlink_close(this);
 +              }
 +              
 +              /** instead of registerin callbacks you derive your own class and overwrite the following abstract member functions.
 +               *  These functions are run in MeshLink's own thread.
 +               *  It is therefore important that these functions use apprioriate methods (queues, pipes, locking, etc.)
 +               *  to hand the data over to the application's thread.
 +               *  These functions should also not block itself and return as quickly as possible.
 +               * The default member functions are no-ops, so you are not required to overwrite all these member functions
 +               */
 +              
 +              /// This function is called whenever another node sends data to the local node.
 +              virtual void receive(node* source, const void* data, size_t length) { /* do nothing */ }
 +              
 +              /// This functions is called  whenever another node's status changed.
 +              virtual void node_status(node* peer, bool reachable)                { /* do nothing */ }
 +              
 +              /// This functions is called whenever MeshLink has some information to log.
 +              virtual void log(log_level_t level, const char* message)            { /* do nothing */ }
 +              
                /// Start MeshLink.
                /** This function causes MeshLink to open network sockets, make outgoing connections, and
                 *  create a new thread, which will handle all network I/O.
                 *  @return         This function will return true if MeshLink has succesfully started its thread, false otherwise.
                 */
                bool start() {
 +                      meshlink_set_receive_cb    (this, &receive_trampoline);
 +                      meshlink_set_node_status_cb(this, &node_status_trampoline);
 +                      meshlink_set_log_cb        (this, MESHLINK_DEBUG, &log_trampoline);
                        return meshlink_start(this);
                }
  
                        meshlink_stop(this);
                }
  
 -              /// Set the receive callback.
 -              /** This functions sets the callback that is called whenever another node sends data to the local node.
 -               *  The callback is run in MeshLink's own thread.
 -               *  It is therefore 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.
 -               *
 -               *  @param cb        A pointer to the function which will be called when another node sends data to the local node.
 -               */
 -              void set_receive_cb(receive_cb_t cb) {
 -                      meshlink_set_receive_cb(this, (meshlink_receive_cb_t)cb);
 -              }
 -
 -              /// Set the node status callback.
 -              /** This functions sets the callback that is called whenever another node's status changed.
 -               *  The callback is run in MeshLink's own thread.
 -               *  It is therefore 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.
 -               *
 -               *  @param cb        A pointer to the function which will be called when another node's status changes.
 -               */
 -              void set_node_status_cb(node_status_cb_t cb) {
 -                      meshlink_set_node_status_cb(this, (meshlink_node_status_cb_t)cb);
 -              }
 -
 -              /// Set the log callback.
 -              /** This functions sets the callback that is called whenever MeshLink has some information to log.
 -               *  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.
 -               *
 -               *  @param level     An enum describing the minimum severity level. Debugging information with a lower level will not trigger the callback.
 -               *  @param cb        A pointer to the function which will be called when another node sends data to the local node.
 -               */
 -              void set_log_cb(meshlink_log_level_t level, log_cb_t cb) {
 -                      meshlink_set_log_cb(this, level, (meshlink_log_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
                        return meshlink_channel_send(this, channel, data, len);
                }
  
 +      private:
 +              // non-copyable:
 +              mesh(const mesh&) /* TODO: C++11: = delete */;
 +              void operator=(const mesh&) /* TODO: C++11: = delete */ ;
 +              
 +              /// static callback trampolines:
 +              static void receive_trampoline(meshlink_handle_t* handle, meshlink_node_t* source, const void* data, size_t length)
 +              {
 +                      mesh* that = static_cast<mesh*>(handle);
 +                      that->receive(static_cast<node*>(source), data, length);
 +              }
 +              
 +              static void node_status_trampoline(meshlink_handle_t* handle, meshlink_node_t* peer, bool reachable)
 +              {
 +                      mesh* that = static_cast<mesh*>(handle);
 +                      that->node_status(static_cast<node*>(peer), reachable);
 +              }
 +
 +              static void log_trampoline(meshlink_handle_t* handle, log_level_t level, const char* message)
 +              {
 +                      mesh* that = static_cast<mesh*>(handle);
 +                      that->log(level, message);
 +              }
        };
  
        /// Initialize MeshLink's configuration directory.
         *
         *  @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.
         */
 -      static mesh *open(const char *confbase, const char *name, const char* appname) {
 -              return (mesh *)meshlink_open(confbase, name, appname);
 +      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;
        }
  
        /// Close the MeshLink handle.
        static const char *strerror(errno_t err = meshlink_errno) {
                return meshlink_strerror(err);
        }
 -};
 +
 +}
  
  #endif // MESHLINKPP_H
diff --combined src/meshlink.c
index 4fa3a58000135bb71713659de90599dac492458e,33af6c36272a4c05aac9c1f1cfab87c0f5f5ccf4..d5ea5418e974c398ba181aa131dacf1cdd321df8
@@@ -21,6 -21,8 +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 +41,7 @@@
  #include "utils.h"
  #include "xalloc.h"
  #include "ed25519/sha512.h"
+ #include "discovery.h"
  
  #ifndef MSG_NOSIGNAL
  #define MSG_NOSIGNAL 0
@@@ -347,7 -350,12 +350,7 @@@ static bool try_bind(int port) 
  }
  
  static int check_port(meshlink_handle_t *mesh) {
 -      if(try_bind(655))
 -              return 655;
 -
 -      fprintf(stderr, "Warning: could not bind to port 655.\n");
 -
 -      for(int i = 0; i < 100; i++) {
 +      for(int i = 0; i < 1000; i++) {
                int port = 0x1000 + (rand() & 0x7fff);
                if(try_bind(port)) {
                        char filename[PATH_MAX];
  
                        fprintf(f, "Port = %d\n", port);
                        fclose(f);
 -                      fprintf(stderr, "MeshLink will instead listen on port %d.\n", port);
                        return port;
                }
        }
@@@ -734,13 -743,7 +737,12 @@@ static bool meshlink_setup(meshlink_han
        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;
  
                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;
                } else { usingname = true;}
        }
  
 -      meshlink_handle_t *mesh = xzalloc(sizeof *mesh);
 +      meshlink_handle_t *mesh = xzalloc(size);
        mesh->confbase = xstrdup(confbase);
+       mesh->appname = xstrdup(appname);
        if (usingname) mesh->name = xstrdup(name);
 -      pthread_mutex_init ( &(mesh->outpacketqueue_mutex), NULL);
        pthread_mutex_init ( &(mesh->nodes_mutex), NULL);
        mesh->threadstarted = false;
        event_loop_init(&mesh->loop);
@@@ -826,9 -837,7 +835,9 @@@ static void *meshlink_main_loop(void *a
  
        try_outgoing_connections(mesh);
  
 +      fprintf(stderr, "Starting main_loop...\n");
        main_loop(mesh);
 +      fprintf(stderr, "main_loop returned.\n");
  
        return NULL;
  }
@@@ -859,35 -868,32 +868,42 @@@ bool meshlink_start(meshlink_handle_t *
  
        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 the listening sockets to signal the main thread to shut down
 +      // Shut down a listening socket to signal the main thread to shut down
  
 -      for(int i = 0; i < mesh->listen_sockets; i++) {
 -              shutdown(mesh->listen_socket[i].tcp.fd, SHUT_RDWR);
 -              shutdown(mesh->listen_socket[i].udp.fd, SHUT_RDWR);
 -      }
++      listen_socket_t *s = &mesh->listen_socket[0];
 +      shutdown(s->tcp.fd, SHUT_RDWR);
  
        // Wait for the main thread to finish
  
        pthread_join(mesh->thread, NULL);
 +      mesh->threadstarted = false;
 +
 +      // Fix the socket
 +      
 +      closesocket(s->tcp.fd);
 +      io_del(&mesh->loop, &s->tcp);
 +      s->tcp.fd = setup_listen_socket(&s->sa);
 +      if(s->tcp.fd < 0)
 +              logger(DEBUG_ALWAYS, LOG_ERR, "Could not repair listenen socket!");
 +      else
 +              io_add(&mesh->loop, &s->tcp, handle_new_meta_connection, s, s->tcp.fd, IO_READ);
  }
  
  void meshlink_close(meshlink_handle_t *mesh) {
@@@ -962,15 -968,19 +978,15 @@@ bool meshlink_send(meshlink_handle_t *m
                return false;
        }
  
 -      /* If there is no outgoing list yet, create one. */
 -
 -      if(!mesh->outpacketqueue)
 -              mesh->outpacketqueue = list_alloc(NULL);
 -
        //add packet to the queue
        outpacketqueue_t *packet_in_queue = xzalloc(sizeof *packet_in_queue);
        packet_in_queue->destination=destination;
        packet_in_queue->data=data;
        packet_in_queue->len=len;
 -      pthread_mutex_lock(&(mesh->outpacketqueue_mutex));
 -      list_insert_head(mesh->outpacketqueue,packet_in_queue);
 -      pthread_mutex_unlock(&(mesh->outpacketqueue_mutex));
 +      if(!meshlink_queue_push(&mesh->outpacketqueue, packet_in_queue)) {
 +              free(packet_in_queue);
 +              return false;
 +      }
  
        //notify event loop
        signal_trigger(&(mesh->loop),&(mesh->datafromapp));
@@@ -981,9 -991,10 +997,9 @@@ void meshlink_send_from_queue(event_loo
        vpn_packet_t packet;
        meshlink_packethdr_t *hdr = (meshlink_packethdr_t *)packet.data;
  
 -      outpacketqueue_t* p = list_get_tail(mesh->outpacketqueue);
 -      if (p)
 -      list_delete_tail(mesh->outpacketqueue);
 -      else return ;
 +      outpacketqueue_t* p = meshlink_queue_pop(&mesh->outpacketqueue);
 +      if(!p)
 +              return;
  
        if (sizeof(meshlink_packethdr_t) + p->len > MAXSIZE) {
                //log something
@@@ -1392,7 -1403,7 +1408,7 @@@ bool meshlink_join(meshlink_handle_t *m
        }
  
        if(!port)
 -              port = "655";
 +              goto invalid;
  
        if(!b64decode(slash, mesh->hash, 18) || !b64decode(slash + 24, mesh->cookie, 18))
                goto invalid;
@@@ -1653,6 -1664,47 +1669,47 @@@ void meshlink_whitelist(meshlink_handle
        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();
  }
diff --combined src/meshlink.h
index 603dbbd2e42a6004dbdb958b930ba812bfd9f11d,37fe5540f41f1c78e472bfe14a7348bdacfe7b44..96fb8d6e7492da240e1505c06ce94dbd294f43b9
  #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 -32,9 +39,9 @@@
  /// 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,15 -114,14 +121,17 @@@ extern const char *meshlink_strerror(me
   *                  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);
  
- extern meshlink_handle_t *meshlink_open_with_size(const char *confbase, const char *name, size_t size);
 +/// 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, const char* appname, size_t size);
 +
  /// Start MeshLink.
  /** This function causes MeshLink to open network sockets, make outgoing connections, and
   *  create a new thread, which will handle all network I/O.
@@@ -565,6 -567,20 +577,20 @@@ extern void meshlink_channel_close(mesh
   */
  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
diff --combined src/meshlink_internal.h
index c9c1eeaa38ffa9bd0f5f9bb785afaa9bafeb66c3,8ac270bedb706a4af6dedca012224f16b5c98ba3..6b47481f879e972fb4a83f5d801fe9b985cd2382
@@@ -26,7 -26,6 +26,7 @@@
  #include "hash.h"
  #include "logger.h"
  #include "meshlink.h"
 +#include "meshlink_queue.h"
  #include "sockaddr.h"
  #include "sptps.h"
  
  
  #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 -67,8 +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;
@@@ -86,7 -92,7 +93,7 @@@
        struct list_t *connections;
        struct list_t *outgoings;
  
 -      struct list_t *outpacketqueue;
 +      meshlink_queue_t outpacketqueue;
  
        struct splay_tree_t *past_request_tree;
        timeout_t past_request_timeout;
        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.