3 #include <catta/core.h>
4 #include <catta/lookup.h>
5 #include <catta/publish.h>
7 #include <catta/simple-watch.h>
8 #include <catta/malloc.h>
9 #include <catta/alternative.h>
10 #include <catta/error.h>
12 #include "meshlink_internal.h"
13 #include "discovery.h"
17 #include "connection.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) {
25 for(size_t i = 0; i < (size - 1); ++i) {
26 buffer[i] = 'a' + (rand() % ('z' - 'a' + 1));
29 buffer[size - 1] = '\0';
32 static void discovery_entry_group_callback(CattaServer *server, CattaSEntryGroup *group, CattaEntryGroupState state, void *userdata) {
35 meshlink_handle_t *mesh = userdata;
39 assert(mesh->catta_server != NULL);
40 assert(mesh->catta_poll != NULL);
42 pthread_mutex_lock(&(mesh->mesh_mutex));
44 /* Called whenever the entry group state changes */
46 case CATTA_ENTRY_GROUP_ESTABLISHED:
47 /* The entry group has been established successfully */
48 logger(mesh, MESHLINK_DEBUG, "Catta Service successfully established.\n");
51 case CATTA_ENTRY_GROUP_COLLISION:
52 logger(mesh, MESHLINK_WARNING, "Catta Service collision.\n");
53 // @TODO can we just set a new name and retry?
56 case CATTA_ENTRY_GROUP_FAILURE :
57 /* Some kind of failure happened while we were registering our services */
58 logger(mesh, MESHLINK_ERROR, "Catta Entry group failure: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
59 catta_simple_poll_quit(mesh->catta_poll);
62 case CATTA_ENTRY_GROUP_UNCOMMITED:
63 case CATTA_ENTRY_GROUP_REGISTERING:
67 pthread_mutex_unlock(&(mesh->mesh_mutex));
71 static void discovery_create_services(meshlink_handle_t *mesh) {
72 char *fingerprint = NULL;
73 char *txt_name = NULL;
74 char *txt_fingerprint = NULL;
78 assert(mesh->name != NULL);
79 assert(mesh->myport != NULL);
80 assert(mesh->catta_server != NULL);
81 assert(mesh->catta_poll != NULL);
82 assert(mesh->catta_servicetype != NULL);
83 assert(mesh->self != NULL);
85 pthread_mutex_lock(&(mesh->mesh_mutex));
87 logger(mesh, MESHLINK_DEBUG, "Adding service\n");
89 /* Ifthis is the first time we're called, let's create a new entry group */
90 if(!mesh->catta_group) {
91 if(!(mesh->catta_group = catta_s_entry_group_new(mesh->catta_server, discovery_entry_group_callback, mesh))) {
92 logger(mesh, MESHLINK_ERROR, "catta_entry_group_new() failed: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
97 /* Create txt records */
98 fingerprint = meshlink_get_fingerprint(mesh, (meshlink_node_t *)mesh->self);
99 xasprintf(&txt_name, "%s=%s", MESHLINK_MDNS_NAME_KEY, mesh->name);
100 xasprintf(&txt_fingerprint, "%s=%s", MESHLINK_MDNS_FINGERPRINT_KEY, fingerprint);
102 /* Add the service */
105 if((ret = catta_server_add_service(mesh->catta_server, mesh->catta_group, CATTA_IF_UNSPEC, CATTA_PROTO_UNSPEC, 0, fingerprint, mesh->catta_servicetype, NULL, NULL, atoi(mesh->myport), txt_name, txt_fingerprint, NULL)) < 0) {
106 logger(mesh, MESHLINK_ERROR, "Failed to add service: %s\n", catta_strerror(ret));
110 /* Tell the server to register the service */
111 if((ret = catta_s_entry_group_commit(mesh->catta_group)) < 0) {
112 logger(mesh, MESHLINK_ERROR, "Failed to commit entry_group: %s\n", catta_strerror(ret));
119 catta_simple_poll_quit(mesh->catta_poll);
124 free(txt_fingerprint);
126 pthread_mutex_unlock(&(mesh->mesh_mutex));
129 static void discovery_server_callback(CattaServer *server, CattaServerState state, void *userdata) {
131 meshlink_handle_t *mesh = userdata;
134 assert(mesh != NULL);
137 case CATTA_SERVER_RUNNING:
139 /* The serve has startup successfully and registered its host
140 * name on the network, so it's time to create our services */
141 if(!mesh->catta_group) {
142 discovery_create_services(mesh);
147 case CATTA_SERVER_COLLISION: {
149 assert(mesh->catta_server != NULL);
150 assert(mesh->catta_poll != NULL);
152 /* A host name collision happened. Let's pick a new name for the server */
154 generate_rand_string(hostname, sizeof(hostname));
155 int result = catta_server_set_host_name(mesh->catta_server, hostname);
158 catta_simple_poll_quit(mesh->catta_poll);
163 case CATTA_SERVER_REGISTERING:
165 /* Let's drop our registered services. When the server is back
166 * in CATTA_SERVER_RUNNING state we will register them
167 * again with the new host name. */
168 if(mesh->catta_group) {
169 catta_s_entry_group_reset(mesh->catta_group);
170 mesh->catta_group = NULL;
175 case CATTA_SERVER_FAILURE:
177 assert(mesh->catta_server != NULL);
178 assert(mesh->catta_poll != NULL);
180 /* Terminate on failure */
181 catta_simple_poll_quit(mesh->catta_poll);
184 case CATTA_SERVER_INVALID:
189 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) {
193 meshlink_handle_t *mesh = userdata;
196 assert(resolver != NULL);
197 assert(mesh != NULL);
198 assert(mesh->catta_server != NULL);
200 /* Called whenever a service has been resolved successfully or timed out */
202 case CATTA_RESOLVER_FAILURE:
204 assert(name != NULL);
205 assert(type != NULL);
206 assert(domain != NULL);
209 case CATTA_RESOLVER_FOUND: {
211 assert(name != NULL);
212 assert(type != NULL);
213 assert(domain != NULL);
214 assert(host_name != NULL);
215 assert(address != NULL);
218 // retrieve fingerprint
219 CattaStringList *node_name_li = catta_string_list_find(txt, MESHLINK_MDNS_NAME_KEY);
220 CattaStringList *node_fp_li = catta_string_list_find(txt, MESHLINK_MDNS_FINGERPRINT_KEY);
222 if(node_name_li != NULL && node_fp_li != NULL) {
223 char *node_name = (char *)catta_string_list_get_text(node_name_li) + strlen(MESHLINK_MDNS_NAME_KEY);
224 char *node_fp = (char *)catta_string_list_get_text(node_fp_li) + strlen(MESHLINK_MDNS_FINGERPRINT_KEY);
226 if(node_name[0] == '=' && node_fp[0] == '=') {
227 pthread_mutex_lock(&(mesh->mesh_mutex));
232 meshlink_node_t *node = meshlink_get_node(mesh, node_name);
235 logger(mesh, MESHLINK_INFO, "Node %s is part of the mesh network.\n", node->name);
238 memset(&naddress, 0, sizeof(naddress));
240 switch(address->proto) {
241 case CATTA_PROTO_INET: {
242 naddress.in.sin_family = AF_INET;
243 naddress.in.sin_port = htons(port);
244 naddress.in.sin_addr.s_addr = address->data.ipv4.address;
248 case CATTA_PROTO_INET6: {
249 naddress.in6.sin6_family = AF_INET6;
250 naddress.in6.sin6_port = htons(port);
251 memcpy(naddress.in6.sin6_addr.s6_addr, address->data.ipv6.address, sizeof(naddress.in6.sin6_addr.s6_addr));
256 naddress.unknown.family = AF_UNKNOWN;
260 if(naddress.unknown.family != AF_UNKNOWN) {
261 meshlink_hint_address(mesh, (meshlink_node_t *)node, (struct sockaddr *)&naddress);
262 pthread_mutex_lock(&(mesh->mesh_mutex));
264 node_t *n = (node_t *)node;
266 if(n->connection && n->connection->outgoing) {
267 n->connection->outgoing->timeout = 0;
269 if(n->connection->outgoing->ev.cb) {
270 timeout_set(&mesh->loop, &n->connection->outgoing->ev, &(struct timeval) {
275 n->connection->last_ping_time = 0;
278 pthread_mutex_unlock(&(mesh->mesh_mutex));
280 logger(mesh, MESHLINK_WARNING, "Could not resolve node %s to a known address family type.\n", node->name);
283 logger(mesh, MESHLINK_WARNING, "Node %s is not part of the mesh network.\n", node_name);
286 pthread_mutex_unlock(&(mesh->mesh_mutex));
293 catta_s_service_resolver_free(resolver);
296 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) {
299 meshlink_handle_t *mesh = userdata;
302 assert(mesh != NULL);
303 assert(mesh->catta_server != NULL);
304 assert(mesh->catta_poll != NULL);
306 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
308 case CATTA_BROWSER_FAILURE:
309 catta_simple_poll_quit(mesh->catta_poll);
312 case CATTA_BROWSER_NEW:
313 catta_s_service_resolver_new(mesh->catta_server, interface_, protocol, name, type, domain, CATTA_PROTO_UNSPEC, 0, discovery_resolve_callback, mesh);
314 pthread_mutex_lock(&mesh->mesh_mutex);
315 handle_network_change(mesh, ++mesh->catta_interfaces);
316 pthread_mutex_unlock(&mesh->mesh_mutex);
319 case CATTA_BROWSER_REMOVE:
320 pthread_mutex_lock(&mesh->mesh_mutex);
321 handle_network_change(mesh, --mesh->catta_interfaces);
322 pthread_mutex_unlock(&mesh->mesh_mutex);
325 case CATTA_BROWSER_ALL_FOR_NOW:
326 case CATTA_BROWSER_CACHE_EXHAUSTED:
331 static void *discovery_loop(void *userdata) {
332 meshlink_handle_t *mesh = userdata;
335 assert(mesh != NULL);
336 assert(mesh->catta_poll != NULL);
338 catta_simple_poll_loop(mesh->catta_poll);
343 static void discovery_log_cb(CattaLogLevel level, const char *txt) {
344 meshlink_log_level_t mlevel = MESHLINK_CRITICAL;
347 case CATTA_LOG_ERROR:
348 mlevel = MESHLINK_ERROR;
352 mlevel = MESHLINK_WARNING;
355 case CATTA_LOG_NOTICE:
357 mlevel = MESHLINK_INFO;
360 case CATTA_LOG_DEBUG:
362 mlevel = MESHLINK_DEBUG;
366 logger(NULL, mlevel, "%s\n", txt);
369 bool discovery_start(meshlink_handle_t *mesh) {
370 logger(mesh, MESHLINK_DEBUG, "discovery_start called\n");
373 assert(mesh != NULL);
374 assert(mesh->catta_poll == NULL);
375 assert(mesh->catta_server == NULL);
376 assert(mesh->catta_browser == NULL);
377 assert(mesh->discovery_threadstarted == false);
378 assert(mesh->catta_servicetype == NULL);
381 catta_set_log_function(discovery_log_cb);
383 // create service type string
384 size_t servicetype_strlen = sizeof(MESHLINK_MDNS_SERVICE_TYPE) + strlen(mesh->appname) + 1;
385 mesh->catta_servicetype = malloc(servicetype_strlen);
387 if(mesh->catta_servicetype == NULL) {
388 logger(mesh, MESHLINK_ERROR, "Failed to allocate memory for service type string.\n");
392 snprintf(mesh->catta_servicetype, servicetype_strlen, MESHLINK_MDNS_SERVICE_TYPE, mesh->appname);
394 // Allocate discovery loop object
395 if(!(mesh->catta_poll = catta_simple_poll_new())) {
396 logger(mesh, MESHLINK_ERROR, "Failed to create discovery poll object.\n");
400 // generate some unique host name (we actually do not care about it)
402 generate_rand_string(hostname, sizeof(hostname));
404 // Let's set the host name for this server.
405 CattaServerConfig config;
406 catta_server_config_init(&config);
407 config.host_name = catta_strdup(hostname);
408 config.publish_workstation = 0;
409 config.disallow_other_stacks = 0;
410 config.publish_hinfo = 0;
411 config.publish_addresses = 1;
412 config.publish_no_reverse = 1;
414 /* Allocate a new server */
416 mesh->catta_server = catta_server_new(catta_simple_poll_get(mesh->catta_poll), &config, discovery_server_callback, mesh, &error);
418 /* Free the configuration data */
419 catta_server_config_free(&config);
421 /* Check wether creating the server object succeeded */
422 if(!mesh->catta_server) {
423 logger(mesh, MESHLINK_ERROR, "Failed to create discovery server: %s\n", catta_strerror(error));
427 // Create the service browser
428 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))) {
429 logger(mesh, MESHLINK_ERROR, "Failed to create discovery service browser: %s\n", catta_strerror(catta_server_errno(mesh->catta_server)));
433 // Start the discovery thread
434 if(pthread_create(&mesh->discovery_thread, NULL, discovery_loop, mesh) != 0) {
435 logger(mesh, MESHLINK_ERROR, "Could not start discovery thread: %s\n", strerror(errno));
436 memset(&mesh->discovery_thread, 0, sizeof(mesh)->discovery_thread);
440 mesh->discovery_threadstarted = true;
446 if(mesh->catta_browser != NULL) {
447 catta_s_service_browser_free(mesh->catta_browser);
448 mesh->catta_browser = NULL;
451 if(mesh->catta_server != NULL) {
452 catta_server_free(mesh->catta_server);
453 mesh->catta_server = NULL;
456 if(mesh->catta_poll != NULL) {
457 catta_simple_poll_free(mesh->catta_poll);
458 mesh->catta_poll = NULL;
461 if(mesh->catta_servicetype != NULL) {
462 free(mesh->catta_servicetype);
463 mesh->catta_servicetype = NULL;
469 void discovery_stop(meshlink_handle_t *mesh) {
470 logger(mesh, MESHLINK_DEBUG, "discovery_stop called\n");
473 assert(mesh != NULL);
476 if(mesh->catta_poll) {
477 catta_simple_poll_quit(mesh->catta_poll);
480 // Wait for the discovery thread to finish
481 if(mesh->discovery_threadstarted == true) {
482 pthread_join(mesh->discovery_thread, NULL);
483 mesh->discovery_threadstarted = false;
486 // Clean up resources
487 if(mesh->catta_browser != NULL) {
488 catta_s_service_browser_free(mesh->catta_browser);
489 mesh->catta_browser = NULL;
492 if(mesh->catta_group) {
493 catta_s_entry_group_reset(mesh->catta_group);
494 catta_s_entry_group_free(mesh->catta_group);
495 mesh->catta_group = NULL;
498 if(mesh->catta_server != NULL) {
499 catta_server_free(mesh->catta_server);
500 mesh->catta_server = NULL;
503 if(mesh->catta_poll != NULL) {
504 catta_simple_poll_free(mesh->catta_poll);
505 mesh->catta_poll = NULL;
508 if(mesh->catta_servicetype != NULL) {
509 free(mesh->catta_servicetype);
510 mesh->catta_servicetype = NULL;
513 mesh->catta_interfaces = 0;