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 #define MESHLINK_MDNS_SERVICE_TYPE "_%s._tcp"
21 #define MESHLINK_MDNS_NAME_KEY "name"
22 #define MESHLINK_MDNS_FINGERPRINT_KEY "fingerprint"
24 static void generate_rand_string(char* buffer, size_t size)
26 for(size_t i = 0; i < (size - 1); ++i)
28 buffer[i] = 'a' + (rand() % ('z' - 'a' + 1));
31 buffer[size-1] = '\0';
34 static void discovery_entry_group_callback(CattaServer *server, CattaSEntryGroup *group, CattaEntryGroupState state, void *userdata)
36 meshlink_handle_t *mesh = userdata;
40 assert(mesh->catta_server != NULL);
41 assert(mesh->catta_poll != NULL);
43 pthread_mutex_lock(&(mesh->mesh_mutex));
45 /* Called whenever the entry group state changes */
48 case CATTA_ENTRY_GROUP_ESTABLISHED:
49 /* The entry group has been established successfully */
50 logger(mesh, MESHLINK_DEBUG, "Catta Service successfully established.\n");
53 case CATTA_ENTRY_GROUP_COLLISION:
54 logger(mesh, MESHLINK_WARNING, "Catta Service collision.\n");
55 // @TODO can we just set a new name and retry?
58 case CATTA_ENTRY_GROUP_FAILURE :
59 /* Some kind of failure happened while we were registering our services */
60 logger(mesh, MESHLINK_ERROR, "Catta Entry group failure: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
61 catta_simple_poll_quit(mesh->catta_poll);
64 case CATTA_ENTRY_GROUP_UNCOMMITED:
65 case CATTA_ENTRY_GROUP_REGISTERING:
69 pthread_mutex_unlock(&(mesh->mesh_mutex));
73 static void discovery_create_services(meshlink_handle_t *mesh)
75 char *txt_name = NULL;
79 assert(mesh->name != NULL);
80 assert(mesh->myport != NULL);
81 assert(mesh->catta_server != NULL);
82 assert(mesh->catta_poll != NULL);
83 assert(mesh->catta_servicetype != NULL);
84 assert(mesh->self != NULL);
86 pthread_mutex_lock(&(mesh->mesh_mutex));
88 logger(mesh, MESHLINK_DEBUG, "Adding service\n");
90 /* Ifthis is the first time we're called, let's create a new entry group */
91 if(!mesh->catta_group)
93 if(!(mesh->catta_group = catta_s_entry_group_new(mesh->catta_server, discovery_entry_group_callback, mesh)))
95 logger(mesh, MESHLINK_ERROR, "catta_entry_group_new() failed: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
100 /* Create txt records */
101 size_t txt_name_len = sizeof(MESHLINK_MDNS_NAME_KEY) + 1 + strlen(mesh->name) + 1;
102 txt_name = malloc(txt_name_len);
106 logger(mesh, MESHLINK_ERROR, "Could not allocate memory for TXT record\n");
110 snprintf(txt_name, txt_name_len, "%s=%s", MESHLINK_MDNS_NAME_KEY, mesh->name);
112 char txt_fingerprint[sizeof(MESHLINK_MDNS_FINGERPRINT_KEY) + 1 + MESHLINK_FINGERPRINTLEN + 1];
113 snprintf(txt_fingerprint, sizeof(txt_fingerprint), "%s=%s", MESHLINK_MDNS_FINGERPRINT_KEY, meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self));
115 /* Add the service */
117 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)
119 logger(mesh, MESHLINK_ERROR, "Failed to add service: %s\n", catta_strerror(ret));
123 /* Tell the server to register the service */
124 if((ret = catta_s_entry_group_commit(mesh->catta_group)) < 0)
126 logger(mesh, MESHLINK_ERROR, "Failed to commit entry_group: %s\n", catta_strerror(ret));
133 catta_simple_poll_quit(mesh->catta_poll);
139 pthread_mutex_unlock(&(mesh->mesh_mutex));
142 static void discovery_server_callback(CattaServer *server, CattaServerState state, void * userdata)
144 meshlink_handle_t *mesh = userdata;
147 assert(mesh != NULL);
149 pthread_mutex_lock(&(mesh->mesh_mutex));
153 case CATTA_SERVER_RUNNING:
155 /* The serve has startup successfully and registered its host
156 * name on the network, so it's time to create our services */
157 if(!mesh->catta_group)
159 discovery_create_services(mesh);
164 case CATTA_SERVER_COLLISION:
167 assert(mesh->catta_server != NULL);
168 assert(mesh->catta_poll != NULL);
170 /* A host name collision happened. Let's pick a new name for the server */
172 generate_rand_string(hostname, sizeof(hostname));
174 logger(mesh, MESHLINK_WARNING, "Catta host name collision, retrying with '%s'\n", hostname);
175 int result = catta_server_set_host_name(mesh->catta_server, hostname);
179 logger(mesh, MESHLINK_ERROR, "Catta failed to set new host name: %s\n", catta_strerror(result));
180 catta_simple_poll_quit(mesh->catta_poll);
185 case CATTA_SERVER_REGISTERING:
187 /* Let's drop our registered services. When the server is back
188 * in CATTA_SERVER_RUNNING state we will register them
189 * again with the new host name. */
190 if(mesh->catta_group)
192 catta_s_entry_group_reset(mesh->catta_group);
193 mesh->catta_group = NULL;
198 case CATTA_SERVER_FAILURE:
201 assert(mesh->catta_server != NULL);
202 assert(mesh->catta_poll != NULL);
204 /* Terminate on failure */
205 logger(mesh, MESHLINK_ERROR, "Catta server failure: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
206 catta_simple_poll_quit(mesh->catta_poll);
210 case CATTA_SERVER_INVALID:
214 pthread_mutex_unlock(&(mesh->mesh_mutex));
217 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)
219 meshlink_handle_t *mesh = userdata;
222 assert(resolver != NULL);
223 assert(mesh != NULL);
224 assert(mesh->catta_server != NULL);
226 pthread_mutex_lock(&(mesh->mesh_mutex));
228 /* Called whenever a service has been resolved successfully or timed out */
231 case CATTA_RESOLVER_FAILURE:
234 assert(name != NULL);
235 assert(type != NULL);
236 assert(domain != NULL);
238 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)));
242 case CATTA_RESOLVER_FOUND:
245 assert(name != NULL);
246 assert(type != NULL);
247 assert(domain != NULL);
248 assert(host_name != NULL);
249 assert(address != NULL);
252 char straddr[CATTA_ADDRESS_STR_MAX], *strtxt;
254 logger(mesh, MESHLINK_DEBUG, "(Resolver) Service '%s' of type '%s' in domain '%s':\n", name, type, domain);
256 catta_address_snprint(straddr, sizeof(straddr), address);
257 strtxt = catta_string_list_to_string(txt);
258 logger(mesh, MESHLINK_DEBUG,
266 host_name, port, straddr,
268 catta_string_list_get_service_cookie(txt),
269 !!(flags & CATTA_LOOKUP_RESULT_LOCAL),
270 !!(flags & CATTA_LOOKUP_RESULT_WIDE_AREA),
271 !!(flags & CATTA_LOOKUP_RESULT_MULTICAST),
272 !!(flags & CATTA_LOOKUP_RESULT_CACHED));
275 // retrieve fingerprint
276 CattaStringList *node_name_li = catta_string_list_find(txt, MESHLINK_MDNS_NAME_KEY);
277 CattaStringList *node_fp_li = catta_string_list_find(txt, MESHLINK_MDNS_FINGERPRINT_KEY);
279 if(node_name_li != NULL && node_fp_li != NULL)
281 char *node_name = (char*)catta_string_list_get_text(node_name_li) + strlen(MESHLINK_MDNS_NAME_KEY);
282 char *node_fp = (char*)catta_string_list_get_text(node_fp_li) + strlen(MESHLINK_MDNS_FINGERPRINT_KEY);
284 if(node_name[0] == '=' && node_fp[0] == '=')
289 meshlink_node_t *node = meshlink_get_node(mesh, node_name);
293 logger(mesh, MESHLINK_INFO, "Node %s is part of the mesh network.\n", node->name);
296 memset(&naddress, 0, sizeof(naddress));
298 switch(address->proto)
300 case CATTA_PROTO_INET:
302 naddress.in.sin_family = AF_INET;
303 naddress.in.sin_port = htons(port);
304 naddress.in.sin_addr.s_addr = address->data.ipv4.address;
308 case CATTA_PROTO_INET6:
310 naddress.in6.sin6_family = AF_INET6;
311 naddress.in6.sin6_port = htons(port);
312 memcpy(naddress.in6.sin6_addr.s6_addr, address->data.ipv6.address, sizeof(naddress.in6.sin6_addr.s6_addr));
317 naddress.unknown.family = AF_UNKNOWN;
321 if(naddress.unknown.family != AF_UNKNOWN)
323 meshlink_hint_address(mesh, (meshlink_node_t *)node, (struct sockaddr*)&naddress);
327 logger(mesh, MESHLINK_WARNING, "Could not resolve node %s to a known address family type.\n", node->name);
332 logger(mesh, MESHLINK_WARNING, "Node %s is not part of the mesh network.\n", node_name);
337 logger(mesh, MESHLINK_WARNING, "TXT records invalid.\n");
342 logger(mesh, MESHLINK_WARNING, "TXT records missing.\n");
348 catta_s_service_resolver_free(resolver);
350 pthread_mutex_unlock(&(mesh->mesh_mutex));
353 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)
355 meshlink_handle_t *mesh = userdata;
358 assert(mesh != NULL);
359 assert(mesh->catta_server != NULL);
360 assert(mesh->catta_poll != NULL);
362 pthread_mutex_lock(&(mesh->mesh_mutex));
364 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
367 case CATTA_BROWSER_FAILURE:
369 logger(mesh, MESHLINK_ERROR, "(Browser) %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
370 catta_simple_poll_quit(mesh->catta_poll);
374 case CATTA_BROWSER_NEW:
377 assert(name != NULL);
378 assert(type != NULL);
379 assert(domain != NULL);
381 logger(mesh, MESHLINK_DEBUG, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
382 /* We ignore the returned resolver object. In the callback
383 function we free it. Ifthe server is terminated before
384 the callback function is called the server will free
385 the resolver for us. */
386 if(!(catta_s_service_resolver_new(mesh->catta_server, interface, protocol, name, type, domain, CATTA_PROTO_UNSPEC, 0, discovery_resolve_callback, mesh)))
388 logger(mesh, MESHLINK_DEBUG, "Failed to resolve service '%s': %s\n", name, catta_strerror(catta_server_errno(mesh->catta_server)));
393 case CATTA_BROWSER_REMOVE:
396 assert(name != NULL);
397 assert(type != NULL);
398 assert(domain != NULL);
400 logger(mesh, MESHLINK_DEBUG, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
404 case CATTA_BROWSER_ALL_FOR_NOW:
405 case CATTA_BROWSER_CACHE_EXHAUSTED:
407 logger(mesh, MESHLINK_DEBUG, "(Browser) %s\n", event == CATTA_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
412 pthread_mutex_unlock(&(mesh->mesh_mutex));
415 static void *discovery_loop(void *userdata)
417 meshlink_handle_t *mesh = userdata;
420 assert(mesh != NULL);
421 assert(mesh->catta_poll != NULL);
423 catta_simple_poll_loop(mesh->catta_poll);
428 static void discovery_log_cb(CattaLogLevel level, const char *txt)
430 meshlink_log_level_t mlevel = MESHLINK_CRITICAL;
434 case CATTA_LOG_ERROR:
435 mlevel = MESHLINK_ERROR;
439 mlevel = MESHLINK_WARNING;
442 case CATTA_LOG_NOTICE:
444 mlevel = MESHLINK_INFO;
447 case CATTA_LOG_DEBUG:
448 mlevel = MESHLINK_DEBUG;
452 logger(NULL, mlevel, "%s\n", txt);
455 bool discovery_start(meshlink_handle_t *mesh)
457 logger(mesh, MESHLINK_DEBUG, "discovery_start called\n");
460 assert(mesh != NULL);
461 assert(mesh->catta_poll == NULL);
462 assert(mesh->catta_server == NULL);
463 assert(mesh->catta_browser == NULL);
464 assert(mesh->discovery_threadstarted == false);
465 assert(mesh->catta_servicetype == NULL);
468 catta_set_log_function(discovery_log_cb);
470 // create service type string
471 size_t servicetype_strlen = sizeof(MESHLINK_MDNS_SERVICE_TYPE) + strlen(mesh->appname) + 1;
472 mesh->catta_servicetype = malloc(servicetype_strlen);
474 if(mesh->catta_servicetype == NULL)
476 logger(mesh, MESHLINK_ERROR, "Failed to allocate memory for service type string.\n");
480 snprintf(mesh->catta_servicetype, servicetype_strlen, MESHLINK_MDNS_SERVICE_TYPE, mesh->appname);
482 // Allocate discovery loop object
483 if(!(mesh->catta_poll = catta_simple_poll_new()))
485 logger(mesh, MESHLINK_ERROR, "Failed to create discovery poll object.\n");
489 // generate some unique host name (we actually do not care about it)
491 generate_rand_string(hostname, sizeof(hostname));
493 // Let's set the host name for this server.
494 CattaServerConfig config;
495 catta_server_config_init(&config);
496 config.host_name = catta_strdup(hostname);
497 config.publish_workstation = 0;
498 config.disallow_other_stacks = 0;
499 config.publish_hinfo = 0;
500 config.publish_addresses = 1;
501 config.publish_no_reverse = 1;
503 /* Allocate a new server */
505 mesh->catta_server = catta_server_new(catta_simple_poll_get(mesh->catta_poll), &config, discovery_server_callback, mesh, &error);
507 /* Free the configuration data */
508 catta_server_config_free(&config);
510 /* Check wether creating the server object succeeded */
511 if(!mesh->catta_server)
513 logger(mesh, MESHLINK_ERROR, "Failed to create discovery server: %s\n", catta_strerror(error));
517 // Create the service browser
518 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)))
520 logger(mesh, MESHLINK_ERROR, "Failed to create discovery service browser: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
524 // Start the discovery thread
525 if(pthread_create(&mesh->discovery_thread, NULL, discovery_loop, mesh) != 0)
527 logger(mesh, MESHLINK_ERROR, "Could not start discovery thread: %s\n", strerror(errno));
528 memset(&mesh->discovery_thread, 0, sizeof mesh->discovery_thread);
532 mesh->discovery_threadstarted = true;
537 if(mesh->catta_browser != NULL)
539 catta_s_service_browser_free(mesh->catta_browser);
540 mesh->catta_browser = NULL;
543 if(mesh->catta_server != NULL)
545 catta_server_free(mesh->catta_server);
546 mesh->catta_server = NULL;
549 if(mesh->catta_poll != NULL)
551 catta_simple_poll_free(mesh->catta_poll);
552 mesh->catta_poll = NULL;
555 if(mesh->catta_servicetype != NULL)
557 free(mesh->catta_servicetype);
558 mesh->catta_servicetype = NULL;
564 void discovery_stop(meshlink_handle_t *mesh)
566 logger(mesh, MESHLINK_DEBUG, "discovery_stop called\n");
569 assert(mesh != NULL);
574 catta_simple_poll_quit(mesh->catta_poll);
577 // Wait for the discovery thread to finish
578 if(mesh->discovery_threadstarted == true)
580 pthread_join(mesh->discovery_thread, NULL);
581 mesh->discovery_threadstarted = false;
584 // Clean up resources
585 if(mesh->catta_browser != NULL)
587 catta_s_service_browser_free(mesh->catta_browser);
588 mesh->catta_browser = NULL;
591 if(mesh->catta_group)
593 catta_s_entry_group_reset(mesh->catta_group);
594 catta_s_entry_group_free(mesh->catta_group);
595 mesh->catta_group = NULL;
598 if(mesh->catta_server != NULL)
600 catta_server_free(mesh->catta_server);
601 mesh->catta_server = NULL;
604 if(mesh->catta_poll != NULL)
606 catta_simple_poll_free(mesh->catta_poll);
607 mesh->catta_poll = NULL;
610 if(mesh->catta_servicetype != NULL)
612 free(mesh->catta_servicetype);
613 mesh->catta_servicetype = NULL;