2 #include "meshlink_internal.h"
7 #include <avahi-core/core.h>
8 #include <avahi-core/lookup.h>
9 #include <avahi-core/publish.h>
10 #include <avahi-common/simple-watch.h>
11 #include <avahi-common/malloc.h>
12 #include <avahi-common/alternative.h>
13 #include <avahi-common/error.h>
15 #define MESHLINK_MDNS_SERVICE_TYPE "_meshlink._tcp"
16 //#define MESHLINK_MDNS_SERVICE_NAME "Meshlink"
17 #define MESHLINK_MDNS_FINGERPRINT_KEY "fingerprint"
19 static void discovery_resolve_callback(
20 AvahiSServiceResolver *resolver,
21 AVAHI_GCC_UNUSED AvahiIfIndex interface,
22 AVAHI_GCC_UNUSED AvahiProtocol protocol,
23 AvahiResolverEvent event,
27 const char *host_name,
28 const AvahiAddress *address,
31 AvahiLookupResultFlags flags,
32 AVAHI_GCC_UNUSED void* userdata)
35 meshlink_handle_t *mesh = userdata;
37 /* Called whenever a service has been resolved successfully or timed out */
41 case AVAHI_RESOLVER_FAILURE:
42 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)));
45 case AVAHI_RESOLVER_FOUND:
47 char straddr[AVAHI_ADDRESS_STR_MAX], *strtxt;
49 fprintf(stderr, "(Resolver) Service '%s' of type '%s' in domain '%s':\n", name, type, domain);
51 avahi_address_snprint(straddr, sizeof(straddr), address);
52 strtxt = avahi_string_list_to_string(txt);
61 host_name, port, straddr,
63 avahi_string_list_get_service_cookie(txt),
64 !!(flags & AVAHI_LOOKUP_RESULT_LOCAL),
65 !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA),
66 !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST),
67 !!(flags & AVAHI_LOOKUP_RESULT_CACHED));
70 // retrieve fingerprint
71 AvahiStringList *fgli = avahi_string_list_find(txt, MESHLINK_MDNS_FINGERPRINT_KEY);
72 meshlink_node_t *node = meshlink_get_node(mesh, name);
74 fprintf(stderr, "%p, %p, %s, %s\n", fgli, node, avahi_string_list_get_text(fgli), meshlink_get_fingerprint(mesh, node));
76 if( node && fgli && strcmp(avahi_string_list_get_text(fgli)+strlen(MESHLINK_MDNS_FINGERPRINT_KEY)+1, meshlink_get_fingerprint(mesh, node)) == 0 )
78 fprintf(stderr, "Node %s is part of the mesh network - updating ip address.\n", node->name);
82 fprintf(stderr, "Node %s is not part of the mesh network - ignoring ip address.\n", node->name);
87 avahi_s_service_resolver_free(resolver);
90 static void discovery_entry_group_callback(AvahiServer *server, AvahiSEntryGroup *group, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata)
92 meshlink_handle_t *mesh = userdata;
94 /* Called whenever the entry group state changes */
97 case AVAHI_ENTRY_GROUP_ESTABLISHED:
98 /* The entry group has been established successfully */
99 fprintf(stderr, "Service '%s' successfully established.\n", /*MESHLINK_MDNS_SERVICE_NAME*/ mesh->name);
102 case AVAHI_ENTRY_GROUP_COLLISION:
103 fprintf(stderr, "Service name collision '%s'\n", /*MESHLINK_MDNS_SERVICE_NAME*/ mesh->name);
106 case AVAHI_ENTRY_GROUP_FAILURE :
107 fprintf(stderr, "Entry group failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
109 /* Some kind of failure happened while we were registering our services */
110 avahi_simple_poll_quit(mesh->avahi_poll);
113 case AVAHI_ENTRY_GROUP_UNCOMMITED:
114 case AVAHI_ENTRY_GROUP_REGISTERING:
120 static void discovery_create_services(meshlink_handle_t *mesh)
122 fprintf(stderr, "Adding service '%s'\n", /*MESHLINK_MDNS_SERVICE_NAME*/ mesh->name);
124 /* If this is the first time we're called, let's create a new entry group */
125 if (!mesh->avahi_group)
126 if (!(mesh->avahi_group = avahi_s_entry_group_new(mesh->avahi_server, discovery_entry_group_callback, mesh))) {
127 fprintf(stderr, "avahi_entry_group_new() failed: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
131 /* Create some random TXT data */
132 char fingerprint[1024] = "";
133 snprintf(fingerprint, sizeof(fingerprint), "%s=%s", MESHLINK_MDNS_FINGERPRINT_KEY, meshlink_get_fingerprint(mesh, meshlink_get_node(mesh, mesh->name)));
135 /* Add the service for IPP */
137 if ((ret = avahi_server_add_service(mesh->avahi_server, mesh->avahi_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, /*MESHLINK_MDNS_SERVICE_NAME*/ mesh->name, MESHLINK_MDNS_SERVICE_TYPE, NULL, NULL, mesh->myport ? atoi(mesh->myport) : 655, fingerprint, NULL)) < 0) {
138 fprintf(stderr, "Failed to add _ipp._tcp service: %s\n", avahi_strerror(ret));
142 /* Tell the server to register the service */
143 if ((ret = avahi_s_entry_group_commit(mesh->avahi_group)) < 0) {
144 fprintf(stderr, "Failed to commit entry_group: %s\n", avahi_strerror(ret));
151 avahi_simple_poll_quit(mesh->avahi_poll);
154 static void discovery_server_callback(AvahiServer *server, AvahiServerState state, AVAHI_GCC_UNUSED void * userdata)
156 meshlink_handle_t *mesh = userdata;
160 case AVAHI_SERVER_RUNNING:
161 /* The serve has startup successfully and registered its host
162 * name on the network, so it's time to create our services */
163 if (!mesh->avahi_group)
164 discovery_create_services(mesh);
167 case AVAHI_SERVER_COLLISION:
168 /* A host name collision happened. Let's do nothing */
171 case AVAHI_SERVER_REGISTERING:
172 /* Let's drop our registered services. When the server is back
173 * in AVAHI_SERVER_RUNNING state we will register them
174 * again with the new host name. */
175 //if (mesh->avahi_group)
176 // avahi_s_entry_group_reset(mesh->avahi_group);
179 case AVAHI_SERVER_FAILURE:
180 /* Terminate on failure */
181 fprintf(stderr, "Server failure: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
182 avahi_simple_poll_quit(mesh->avahi_poll);
185 case AVAHI_SERVER_INVALID:
190 static void discovery_browse_callback(
191 AvahiSServiceBrowser *browser,
192 AvahiIfIndex interface,
193 AvahiProtocol protocol,
194 AvahiBrowserEvent event,
198 AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
201 meshlink_handle_t *mesh = userdata;
203 /* Called whenever a new services becomes available on the LAN or is removed from the LAN */
207 case AVAHI_BROWSER_FAILURE:
208 fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
209 avahi_simple_poll_quit(mesh->avahi_poll);
212 case AVAHI_BROWSER_NEW:
213 fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
214 /* We ignore the returned resolver object. In the callback
215 function we free it. If the server is terminated before
216 the callback function is called the server will free
217 the resolver for us. */
218 if (!(avahi_s_service_resolver_new(mesh->avahi_server, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, discovery_resolve_callback, mesh)))
219 fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_server_errno(mesh->avahi_server)));
222 case AVAHI_BROWSER_REMOVE:
223 fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain);
226 case AVAHI_BROWSER_ALL_FOR_NOW:
227 case AVAHI_BROWSER_CACHE_EXHAUSTED:
228 fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW");
233 static void *discovery_loop(void *arg)
235 meshlink_handle_t *mesh = arg;
237 avahi_simple_poll_loop(mesh->avahi_poll);
242 bool discovery_start(meshlink_handle_t *mesh)
244 // Allocate discovery loop object
245 if (!(mesh->avahi_poll = avahi_simple_poll_new())) {
246 fprintf(stderr, "Failed to create discovery poll object.\n");
250 // Let's set the host name for this server.
251 AvahiServerConfig config;
252 avahi_server_config_init(&config);
253 config.host_name = avahi_strdup(mesh->name);
254 config.publish_workstation = 0;
256 /* Allocate a new server */
258 mesh->avahi_server = avahi_server_new(avahi_simple_poll_get(mesh->avahi_poll), &config, discovery_server_callback, mesh, &error);
260 /* Free the configuration data */
261 avahi_server_config_free(&config);
263 /* Check wether creating the server object succeeded */
264 if (!mesh->avahi_server) {
265 fprintf(stderr, "Failed to create discovery server: %s\n", avahi_strerror(error));
269 // Create the service browser
270 if (!(mesh->avahi_browser = avahi_s_service_browser_new(mesh->avahi_server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, MESHLINK_MDNS_SERVICE_TYPE, NULL, 0, discovery_browse_callback, mesh))) {
271 fprintf(stderr, "Failed to create discovery service browser: %s\n", avahi_strerror(avahi_server_errno(mesh->avahi_server)));
275 // Start the discovery thread
276 if(pthread_create(&mesh->discovery_thread, NULL, discovery_loop, mesh) != 0) {
277 fprintf(stderr, "Could not start discovery thread: %s\n", strerror(errno));
278 memset(&mesh->discovery_thread, 0, sizeof mesh->discovery_thread);
282 mesh->discovery_threadstarted = true;
287 if (mesh->avahi_browser)
288 avahi_s_service_browser_free(mesh->avahi_browser);
290 if (mesh->avahi_server)
291 avahi_server_free(mesh->avahi_server);
293 if (mesh->avahi_poll)
294 avahi_simple_poll_free(mesh->avahi_poll);
299 void discovery_stop(meshlink_handle_t *mesh)
302 avahi_simple_poll_quit(mesh->avahi_poll);
304 // Wait for the discovery thread to finish
306 pthread_join(mesh->discovery_thread, NULL);
308 // Clean up resources
309 if (mesh->avahi_browser)
310 avahi_s_service_browser_free(mesh->avahi_browser);
312 if (mesh->avahi_server)
313 avahi_server_free(mesh->avahi_server);
315 if (mesh->avahi_poll)
316 avahi_simple_poll_free(mesh->avahi_poll);