2 #include "meshlink_internal.h"
9 #include <catta/core.h>
10 #include <catta/lookup.h>
11 #include <catta/publish.h>
12 #include <catta/log.h>
13 #include <catta/simple-watch.h>
14 #include <catta/malloc.h>
15 #include <catta/alternative.h>
16 #include <catta/error.h>
18 #include <netinet/in.h>
20 #include <uuid/uuid.h>
22 #define MESHLINK_MDNS_SERVICE_TYPE "_%s._tcp"
23 #define MESHLINK_MDNS_NAME_KEY "name"
24 #define MESHLINK_MDNS_FINGERPRINT_KEY "fingerprint"
26 static void discovery_entry_group_callback(CattaServer *server, CattaSEntryGroup *group, CattaEntryGroupState state, void *userdata)
28 meshlink_handle_t *mesh = userdata;
32 assert(mesh->catta_server != NULL);
33 assert(mesh->catta_poll != NULL);
35 pthread_mutex_lock(&(mesh->mesh_mutex));
37 /* Called whenever the entry group state changes */
40 case CATTA_ENTRY_GROUP_ESTABLISHED:
41 /* The entry group has been established successfully */
42 logger(mesh, MESHLINK_DEBUG, "Catta Service successfully established.\n");
45 case CATTA_ENTRY_GROUP_COLLISION:
46 logger(mesh, MESHLINK_WARNING, "Catta Service collision.\n");
47 // @TODO can we just set a new name and retry?
50 case CATTA_ENTRY_GROUP_FAILURE :
51 /* Some kind of failure happened while we were registering our services */
52 logger(mesh, MESHLINK_ERROR, "Catta Entry group failure: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
53 catta_simple_poll_quit(mesh->catta_poll);
56 case CATTA_ENTRY_GROUP_UNCOMMITED:
57 case CATTA_ENTRY_GROUP_REGISTERING:
61 pthread_mutex_unlock(&(mesh->mesh_mutex));
65 static void discovery_create_services(meshlink_handle_t *mesh)
67 char *txt_name = NULL;
71 assert(mesh->name != NULL);
72 assert(mesh->myport != NULL);
73 assert(mesh->catta_server != NULL);
74 assert(mesh->catta_poll != NULL);
75 assert(mesh->catta_servicetype != NULL);
76 assert(mesh->self != NULL);
78 pthread_mutex_lock(&(mesh->mesh_mutex));
80 logger(mesh, MESHLINK_DEBUG, "Adding service\n");
82 /* Ifthis is the first time we're called, let's create a new entry group */
83 if(!mesh->catta_group)
85 if(!(mesh->catta_group = catta_s_entry_group_new(mesh->catta_server, discovery_entry_group_callback, mesh)))
87 logger(mesh, MESHLINK_ERROR, "catta_entry_group_new() failed: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
92 /* Create txt records */
93 size_t txt_name_len = sizeof(MESHLINK_MDNS_NAME_KEY) + 1 + strlen(mesh->name) + 1;
94 txt_name = malloc(txt_name_len);
98 logger(mesh, MESHLINK_ERROR, "Could not allocate memory for TXT record\n");
102 snprintf(txt_name, txt_name_len, "%s=%s", MESHLINK_MDNS_NAME_KEY, mesh->name);
104 char txt_fingerprint[sizeof(MESHLINK_MDNS_FINGERPRINT_KEY) + 1 + MESHLINK_FINGERPRINTLEN + 1];
105 snprintf(txt_fingerprint, sizeof(txt_fingerprint), "%s=%s", MESHLINK_MDNS_FINGERPRINT_KEY, meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self));
107 /* Add the service */
109 if((ret = catta_server_add_service(mesh->catta_server, mesh->catta_group, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, 0, meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self), mesh->catta_servicetype, NULL, NULL, atoi(mesh->myport), txt_name, txt_fingerprint, NULL)) < 0)
111 logger(mesh, MESHLINK_ERROR, "Failed to add service: %s\n", catta_strerror(ret));
115 /* Tell the server to register the service */
116 if((ret = catta_s_entry_group_commit(mesh->catta_group)) < 0)
118 logger(mesh, MESHLINK_ERROR, "Failed to commit entry_group: %s\n", catta_strerror(ret));
125 catta_simple_poll_quit(mesh->catta_poll);
131 pthread_mutex_unlock(&(mesh->mesh_mutex));
134 static void discovery_server_callback(CattaServer *server, CattaServerState state, void * userdata)
136 meshlink_handle_t *mesh = userdata;
139 assert(mesh != NULL);
141 pthread_mutex_lock(&(mesh->mesh_mutex));
145 case CATTA_SERVER_RUNNING:
147 /* The serve has startup successfully and registered its host
148 * name on the network, so it's time to create our services */
149 if(!mesh->catta_group)
151 discovery_create_services(mesh);
156 case CATTA_SERVER_COLLISION:
159 assert(mesh->catta_server != NULL);
160 assert(mesh->catta_poll != NULL);
162 /* A host name collision happened. Let's pick a new name for the server */
164 uuid_generate(hostname);
166 char hostnamestr[36+1];
167 uuid_unparse_lower(hostname, hostnamestr);
169 logger(mesh, MESHLINK_WARNING, "Catta host name collision, retrying with '%s'\n", hostnamestr);
170 int result = catta_server_set_host_name(mesh->catta_server, hostnamestr);
174 logger(mesh, MESHLINK_ERROR, "Catta failed to set new host name: %s\n", catta_strerror(result));
175 catta_simple_poll_quit(mesh->catta_poll);
180 case CATTA_SERVER_REGISTERING:
182 /* Let's drop our registered services. When the server is back
183 * in CATTA_SERVER_RUNNING state we will register them
184 * again with the new host name. */
185 if(mesh->catta_group)
187 catta_s_entry_group_reset(mesh->catta_group);
188 mesh->catta_group = NULL;
193 case CATTA_SERVER_FAILURE:
196 assert(mesh->catta_server != NULL);
197 assert(mesh->catta_poll != NULL);
199 /* Terminate on failure */
200 logger(mesh, MESHLINK_ERROR, "Catta server failure: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
201 catta_simple_poll_quit(mesh->catta_poll);
205 case CATTA_SERVER_INVALID:
209 pthread_mutex_unlock(&(mesh->mesh_mutex));
212 static void discovery_resolve_callback(CattaSServiceResolver *resolver, CattaIfIndex interface, CattaProtocol protocol, CattaResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const CattaAddress *address, uint16_t port, CattaStringList *txt, CattaLookupResultFlags flags, void* userdata)
214 meshlink_handle_t *mesh = userdata;
217 assert(resolver != NULL);
218 assert(mesh != NULL);
219 assert(mesh->catta_server != NULL);
221 pthread_mutex_lock(&(mesh->mesh_mutex));
223 /* Called whenever a service has been resolved successfully or timed out */
226 case CATTA_RESOLVER_FAILURE:
229 assert(name != NULL);
230 assert(type != NULL);
231 assert(domain != NULL);
233 logger(mesh, MESHLINK_WARNING, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, catta_strerror(catta_server_errno(mesh->catta_server)));
237 case CATTA_RESOLVER_FOUND:
240 assert(name != NULL);
241 assert(type != NULL);
242 assert(domain != NULL);
243 assert(host_name != NULL);
244 assert(address != NULL);
247 char straddr[CATTA_ADDRESS_STR_MAX], *strtxt;
249 logger(mesh, MESHLINK_DEBUG, "(Resolver) Service '%s' of type '%s' in domain '%s':\n", name, type, domain);
251 catta_address_snprint(straddr, sizeof(straddr), address);
252 strtxt = catta_string_list_to_string(txt);
253 logger(mesh, MESHLINK_DEBUG,
261 host_name, port, straddr,
263 catta_string_list_get_service_cookie(txt),
264 !!(flags & CATTA_LOOKUP_RESULT_LOCAL),
265 !!(flags & CATTA_LOOKUP_RESULT_WIDE_AREA),
266 !!(flags & CATTA_LOOKUP_RESULT_MULTICAST),
267 !!(flags & CATTA_LOOKUP_RESULT_CACHED));
270 // retrieve fingerprint
271 CattaStringList *node_name_li = catta_string_list_find(txt, MESHLINK_MDNS_NAME_KEY);
272 CattaStringList *node_fp_li = catta_string_list_find(txt, MESHLINK_MDNS_FINGERPRINT_KEY);
274 if(node_name_li != NULL && node_fp_li != NULL)
276 char *node_name = (char*)catta_string_list_get_text(node_name_li) + strlen(MESHLINK_MDNS_NAME_KEY);
277 char *node_fp = (char*)catta_string_list_get_text(node_fp_li) + strlen(MESHLINK_MDNS_FINGERPRINT_KEY);
279 if(node_name[0] == '=' && node_fp[0] == '=')
284 meshlink_node_t *node = meshlink_get_node(mesh, node_name);
288 logger(mesh, MESHLINK_INFO, "Node %s is part of the mesh network.\n", node->name);
291 memset(&naddress, 0, sizeof(naddress));
293 switch(address->proto)
295 case CATTA_PROTO_INET:
297 naddress.in.sin_family = AF_INET;
298 naddress.in.sin_port = htons(port);
299 naddress.in.sin_addr.s_addr = address->data.ipv4.address;
303 case CATTA_PROTO_INET6:
305 naddress.in6.sin6_family = AF_INET6;
306 naddress.in6.sin6_port = htons(port);
307 memcpy(naddress.in6.sin6_addr.s6_addr, address->data.ipv6.address, sizeof(naddress.in6.sin6_addr.s6_addr));
312 naddress.unknown.family = AF_UNKNOWN;
316 if(naddress.unknown.family != AF_UNKNOWN)
318 meshlink_hint_address(mesh, (meshlink_node_t *)node, (struct sockaddr*)&naddress);
322 logger(mesh, MESHLINK_WARNING, "Could not resolve node %s to a known address family type.\n", node->name);
327 logger(mesh, MESHLINK_WARNING, "Node %s is not part of the mesh network.\n", node_name);
332 logger(mesh, MESHLINK_WARNING, "TXT records invalid.\n");
337 logger(mesh, MESHLINK_WARNING, "TXT records missing.\n");
343 catta_s_service_resolver_free(resolver);
345 pthread_mutex_unlock(&(mesh->mesh_mutex));
348 static void discovery_browse_callback(CattaSServiceBrowser *browser, CattaIfIndex interface, CattaProtocol protocol, CattaBrowserEvent event, const char *name, const char *type, const char *domain, CattaLookupResultFlags flags, void* userdata)
350 meshlink_handle_t *mesh = userdata;
353 assert(mesh != NULL);
354 assert(mesh->catta_server != NULL);
355 assert(mesh->catta_poll != NULL);
357 pthread_mutex_lock(&(mesh->mesh_mutex));
359 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
362 case CATTA_BROWSER_FAILURE:
364 logger(mesh, MESHLINK_ERROR, "(Browser) %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
365 catta_simple_poll_quit(mesh->catta_poll);
369 case CATTA_BROWSER_NEW:
372 assert(name != NULL);
373 assert(type != NULL);
374 assert(domain != NULL);
376 logger(mesh, MESHLINK_DEBUG, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
377 /* We ignore the returned resolver object. In the callback
378 function we free it. Ifthe server is terminated before
379 the callback function is called the server will free
380 the resolver for us. */
381 if(!(catta_s_service_resolver_new(mesh->catta_server, interface, protocol, name, type, domain, CATTA_PROTO_UNSPEC, 0, discovery_resolve_callback, mesh)))
383 logger(mesh, MESHLINK_DEBUG, "Failed to resolve service '%s': %s\n", name, catta_strerror(catta_server_errno(mesh->catta_server)));
388 case CATTA_BROWSER_REMOVE:
391 assert(name != NULL);
392 assert(type != NULL);
393 assert(domain != NULL);
395 logger(mesh, MESHLINK_DEBUG, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
399 case CATTA_BROWSER_ALL_FOR_NOW:
400 case CATTA_BROWSER_CACHE_EXHAUSTED:
402 logger(mesh, MESHLINK_DEBUG, "(Browser) %s\n", event == CATTA_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
407 pthread_mutex_unlock(&(mesh->mesh_mutex));
410 static void *discovery_loop(void *userdata)
412 meshlink_handle_t *mesh = userdata;
415 assert(mesh != NULL);
416 assert(mesh->catta_poll != NULL);
418 catta_simple_poll_loop(mesh->catta_poll);
423 static void discovery_log_cb(CattaLogLevel level, const char *txt)
425 meshlink_log_level_t mlevel = MESHLINK_CRITICAL;
429 case CATTA_LOG_ERROR:
430 mlevel = MESHLINK_ERROR;
434 mlevel = MESHLINK_WARNING;
437 case CATTA_LOG_NOTICE:
439 mlevel = MESHLINK_INFO;
442 case CATTA_LOG_DEBUG:
443 mlevel = MESHLINK_DEBUG;
447 logger(NULL, mlevel, "%s\n", txt);
450 bool discovery_start(meshlink_handle_t *mesh)
452 logger(mesh, MESHLINK_DEBUG, "discovery_start called\n");
455 assert(mesh != NULL);
456 assert(mesh->catta_poll == NULL);
457 assert(mesh->catta_server == NULL);
458 assert(mesh->catta_browser == NULL);
459 assert(mesh->discovery_threadstarted == false);
460 assert(mesh->catta_servicetype == NULL);
463 catta_set_log_function(discovery_log_cb);
465 // create service type string
466 size_t servicetype_strlen = sizeof(MESHLINK_MDNS_SERVICE_TYPE) + strlen(mesh->appname) + 1;
467 mesh->catta_servicetype = malloc(servicetype_strlen);
469 if(mesh->catta_servicetype == NULL)
471 logger(mesh, MESHLINK_ERROR, "Failed to allocate memory for service type string.\n");
475 snprintf(mesh->catta_servicetype, servicetype_strlen, MESHLINK_MDNS_SERVICE_TYPE, mesh->appname);
477 // Allocate discovery loop object
478 if(!(mesh->catta_poll = catta_simple_poll_new()))
480 logger(mesh, MESHLINK_ERROR, "Failed to create discovery poll object.\n");
484 // generate some unique host name (we actually do not care about it)
486 uuid_generate(hostname);
488 char hostnamestr[36+1];
489 uuid_unparse_lower(hostname, hostnamestr);
491 // Let's set the host name for this server.
492 CattaServerConfig config;
493 catta_server_config_init(&config);
494 config.host_name = catta_strdup(hostnamestr);
495 config.publish_workstation = 0;
496 config.disallow_other_stacks = 0;
497 config.publish_hinfo = 0;
498 config.publish_addresses = 1;
499 config.publish_no_reverse = 1;
501 /* Allocate a new server */
503 mesh->catta_server = catta_server_new(catta_simple_poll_get(mesh->catta_poll), &config, discovery_server_callback, mesh, &error);
505 /* Free the configuration data */
506 catta_server_config_free(&config);
508 /* Check wether creating the server object succeeded */
509 if(!mesh->catta_server)
511 logger(mesh, MESHLINK_ERROR, "Failed to create discovery server: %s\n", catta_strerror(error));
515 // Create the service browser
516 if(!(mesh->catta_browser = catta_s_service_browser_new(mesh->catta_server, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, mesh->catta_servicetype, NULL, 0, discovery_browse_callback, mesh)))
518 logger(mesh, MESHLINK_ERROR, "Failed to create discovery service browser: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
522 // Start the discovery thread
523 if(pthread_create(&mesh->discovery_thread, NULL, discovery_loop, mesh) != 0)
525 logger(mesh, MESHLINK_ERROR, "Could not start discovery thread: %s\n", strerror(errno));
526 memset(&mesh->discovery_thread, 0, sizeof mesh->discovery_thread);
530 mesh->discovery_threadstarted = true;
535 if(mesh->catta_browser != NULL)
537 catta_s_service_browser_free(mesh->catta_browser);
538 mesh->catta_browser = NULL;
541 if(mesh->catta_server != NULL)
543 catta_server_free(mesh->catta_server);
544 mesh->catta_server = NULL;
547 if(mesh->catta_poll != NULL)
549 catta_simple_poll_free(mesh->catta_poll);
550 mesh->catta_poll = NULL;
553 if(mesh->catta_servicetype != NULL)
555 free(mesh->catta_servicetype);
556 mesh->catta_servicetype = NULL;
562 void discovery_stop(meshlink_handle_t *mesh)
564 logger(mesh, MESHLINK_DEBUG, "discovery_stop called\n");
567 assert(mesh != NULL);
572 catta_simple_poll_quit(mesh->catta_poll);
575 // Wait for the discovery thread to finish
576 if(mesh->discovery_threadstarted == true)
578 pthread_join(mesh->discovery_thread, NULL);
579 mesh->discovery_threadstarted = false;
582 // Clean up resources
583 if(mesh->catta_browser != NULL)
585 catta_s_service_browser_free(mesh->catta_browser);
586 mesh->catta_browser = NULL;
589 if(mesh->catta_group)
591 catta_s_entry_group_reset(mesh->catta_group);
592 catta_s_entry_group_free(mesh->catta_group);
593 mesh->catta_group = NULL;
596 if(mesh->catta_server != NULL)
598 catta_server_free(mesh->catta_server);
599 mesh->catta_server = NULL;
602 if(mesh->catta_poll != NULL)
604 catta_simple_poll_free(mesh->catta_poll);
605 mesh->catta_poll = NULL;
608 if(mesh->catta_servicetype != NULL)
610 free(mesh->catta_servicetype);
611 mesh->catta_servicetype = NULL;