Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
MDNS.cxx
Go to the documentation of this file.
1
35#include "os/MDNS.hxx"
36
38#define MDNS_DEBUG 0
39
40#if !defined (__linux__)
41void mdns_publish(const char *name, const char *service, uint16_t port) __attribute__ ((weak));
42void mdns_unpublish(const char *name, const char *service) __attribute__ ((weak));
43int mdns_lookup(const char *service, struct addrinfo *hints,
44 struct addrinfo **addr) __attribute__ ((weak));
45void mdns_scan(const char *service) __attribute__ ((weak));
46
52void mdns_publish(const char *name, const char *service, uint16_t port)
53{
54 HASSERT(0);
55}
56
61void mdns_unpublish(const char *name, const char *service)
62{
63 HASSERT(0);
64}
65
73int mdns_lookup(const char *service, struct addrinfo *hints,
74 struct addrinfo **addr)
75{
76#if defined(__FreeRTOS__)
77 string mdns_name(service);
78 mdns_name.append(".local");
79 return ::getaddrinfo(nullptr, mdns_name.c_str(), hints, addr);
80#else
81 DIE("Your OS does not support mDNS");
82#endif
83}
84
88void mdns_scan(const char *service)
89{
90 HASSERT(0);
91}
92#endif
93
94/*
95 * MDNS::publish()
96 */
97void MDNS::publish(const char *name, const char *service, uint16_t port)
98{
99#if defined (__linux__)
100 name = avahi_strdup(name);
101
102 if (!group_)
103 {
104 group_ = avahi_entry_group_new(client_, entry_group_callback, this);
105 if (!group_)
106 {
107 fprintf(stderr, "avahi_entry_group_new() failed: %s\n",
108 avahi_strerror(avahi_client_errno(client_)));
109 return;
110 }
111 HASSERT(group_);
112 }
113
114 int result = avahi_entry_group_add_service(group_, AVAHI_IF_UNSPEC,
115 AVAHI_PROTO_UNSPEC,
116 (AvahiPublishFlags)0, name,
117 service, NULL, NULL,
118 port, NULL);
119
120 if (result != 0)
121 {
122#if MDNS_DEBUG
123 fprintf(stderr, "Error exporting mDNS name (%d) %s\n", result,
124 avahi_strerror(result));
125#endif
126 return;
127 }
128 HASSERT(result == 0);
129#else
130 mdns_publish(name, service, port);
131#endif
132}
133
134/*
135 * MDNS::unpublish()
136 */
137void MDNS::unpublish(const char *name, const char *service)
138{
139#if defined(__linux__)
140 DIE("unimplemented");
141#else
142 mdns_unpublish(name, service);
143#endif
144}
145
146/*
147 * MDNS::lookup()
148 */
149int MDNS::lookup(const char *service, struct addrinfo *hints,
150 struct addrinfo **addr)
151{
152#if defined (__linux__)
153 LookupUserdata lu(hints);
154 AvahiServiceBrowser *sb = nullptr;
155 int error;
156 int result = 0;
157 int protocol;
158
159
160 switch (hints->ai_family)
161 {
162 case AF_INET:
163 protocol = AVAHI_PROTO_INET;
164 break;
165 case AF_INET6:
166 protocol = AVAHI_PROTO_INET6;
167 break;
168 case AF_UNSPEC:
169 protocol = AVAHI_PROTO_UNSPEC;
170 break;
171 default:
172 result = EAI_FAMILY;
173 goto fail;
174 }
175
176 *addr = nullptr;
177
178 lu.sp = avahi_simple_poll_new();
179 if (!lu.sp)
180 {
181 result = EAI_MEMORY;
182 goto fail;
183 }
184
185 lu.c = avahi_client_new(avahi_simple_poll_get(lu.sp),
186 (AvahiClientFlags)0, client_callback,
187 nullptr, &error);
188
189 if (!lu.c)
190 {
191 result = EAI_MEMORY;
192 goto fail;
193 }
194
195 sb = avahi_service_browser_new(lu.c, AVAHI_IF_UNSPEC, protocol, service,
196 nullptr, (AvahiLookupFlags)0,
197 browse_callback, (void*)&lu);
198
199 if (!sb)
200 {
201 result = EAI_MEMORY;
202 goto fail;
203 }
204
205 avahi_simple_poll_loop(lu.sp);
206
207 if (lu.addr)
208 {
209 *addr = lu.addr;
210 printf("lu.addr\n");
211 }
212 else
213 {
214 *addr = nullptr;
215 result = EAI_NONAME;
216 }
217
218fail:
219 if (sb)
220 {
221 avahi_service_browser_free(sb);
222 }
223
224 if (lu.c)
225 {
226 avahi_client_free(lu.c);
227 }
228
229 if (lu.sp)
230 {
231 avahi_simple_poll_free(lu.sp);
232 }
233
234 return result;
235#else
236 return mdns_lookup(service, hints, addr);
237#endif
238}
239
240/*
241 * MDNS::scan()
242 */
243void MDNS::scan(const char *service)
244{
245#if defined (__linux__)
246#else
247 mdns_scan(service);
248#endif
249}
250
251#if defined (__linux__)
252/*
253 * MDNS::resolve_callback()
254 */
255void MDNS::resolve_callback(AvahiServiceResolver *r,
256 AvahiIfIndex interface, AvahiProtocol protocol,
257 AvahiResolverEvent event, const char *name,
258 const char *type, const char *domain,
259 const char *host_name,
260 const AvahiAddress *address, uint16_t port,
261 AvahiStringList *txt,
262 AvahiLookupResultFlags flags, void* userdata)
263{
264 HASSERT(r);
265 LookupUserdata *lu = static_cast<LookupUserdata*>(userdata);
266
267 /* Called whenever a service has been resolved successfully or timed out */
268 switch (event)
269 {
270 case AVAHI_RESOLVER_FAILURE:
271#if MDNS_DEBUG
272 fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type "
273 "'%s' in domain '%s': %s\n", name, type, domain,
274 avahi_strerror(avahi_client_errno(
275 avahi_service_resolver_get_client(r))));
276#endif
277 break;
278 case AVAHI_RESOLVER_FOUND:
279 {
280 struct addrinfo *ai;
281 struct sockaddr *sa;
282
283 ai = (struct addrinfo*)malloc(sizeof(struct addrinfo));
284 sa = (struct sockaddr*)malloc(sizeof(struct sockaddr));
285
286 memset(ai, 0, sizeof(struct addrinfo));
287 memset(sa, 0, sizeof(struct sockaddr));
288
289 ai->ai_addr = sa;
290
294
295 if (host_name)
296 {
297 ai->ai_canonname = (char*)malloc(strlen(host_name) + 1);
298 strcpy(ai->ai_canonname, host_name);
299 }
300
301 switch (protocol)
302 {
303 case AVAHI_PROTO_INET:
304 {
305 struct sockaddr_in *sa_in = (struct sockaddr_in*)sa;
306 ai->ai_family = AF_INET;
307 ai->ai_addrlen = sizeof(struct sockaddr_in);
308 sa_in->sin_family = AF_INET;
309 sa_in->sin_port = htons(port);
310 sa_in->sin_addr.s_addr = address->data.ipv4.address;
311 break;
312 }
313 case AVAHI_PROTO_INET6:
314 {
315 struct sockaddr_in6 *sa_in = (struct sockaddr_in6*)sa;
316 ai->ai_family = AF_INET6;
317 ai->ai_addrlen = sizeof(struct sockaddr_in6);
318 sa_in->sin6_flowinfo = 0;
319 sa_in->sin6_family = AF_INET6;
320 sa_in->sin6_port = htons(port);
321 memcpy(&(sa_in->sin6_addr.s6_addr),
322 address->data.ipv6.address,
323 sizeof(address->data.ipv6.address));
324 break;
325 }
326 default:
327 HASSERT(0);
328 }
329
330 HASSERT(lu->hints->ai_family == ai->ai_family);
331
332 if (!lu->addr)
333 {
334 lu->addr = ai;
335 }
336 else
337 {
338 struct addrinfo *current = lu->addr;
339 while (current->ai_next)
340 {
341 current = current->ai_next;
342 }
343 current->ai_next = ai;
344 }
345#if MDNS_DEBUG
346 char a[AVAHI_ADDRESS_STR_MAX], *t;
347 char nil = '\0';
348 fprintf(stderr, "Service '%s' of type '%s' in domain '%s':\n",
349 name, type, domain);
350 avahi_address_snprint(a, sizeof(a), address);
351 t = txt ? &nil : avahi_string_list_to_string(txt);
352 fprintf(stderr,
353 "\t%s:%u (%s)\n"
354 "\tTXT=%s\n"
355 "\tcookie is %u\n"
356 "\tis_local: %i\n"
357 "\tour_own: %i\n"
358 "\twide_area: %i\n"
359 "\tmulticast: %i\n"
360 "\tcached: %i\n",
361 host_name, port, a, t,
362 avahi_string_list_get_service_cookie(txt),
363 !!(flags & AVAHI_LOOKUP_RESULT_LOCAL),
364 !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN),
365 !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA),
366 !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST),
367 !!(flags & AVAHI_LOOKUP_RESULT_CACHED));
368 if (t != &nil)
369 {
370 avahi_free(t);
371 }
372#endif
373 }
374 }
375 if (--lu->count == 0)
376 {
377 if (lu->done)
378 {
379 avahi_simple_poll_quit(lu->sp);
380 }
381 }
382 avahi_service_resolver_free(r);
383}
384
385/*
386 * MDNS::browse_callback()
387 */
388void MDNS::browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface,
389 AvahiProtocol protocol, AvahiBrowserEvent event,
390 const char *name, const char *type,
391 const char *domain,
392 AvahiLookupResultFlags flags, void* userdata)
393{
394 LookupUserdata *lu = static_cast<LookupUserdata*>(userdata);
395 AvahiClient *c = lu->c;
396
397 switch (event)
398 {
399 case AVAHI_BROWSER_FAILURE:
400 break;
401 case AVAHI_BROWSER_NEW:
402#if MDNS_DEBUG
403 fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' "
404 "in domain '%s'\n", name, type, domain);
405#endif
406 /* We ignore the returned resolver object. In the callback
407 function we free it. If the server is terminated before
408 the callback function is called the server will free
409 the resolver for us. */
410 if (!avahi_service_resolver_new(c, interface, protocol, name, type,
411 domain, AVAHI_PROTO_UNSPEC,
412 (AvahiLookupFlags)0,
413 resolve_callback, userdata))
414 {
415 #if MDNS_DEBUG
416 fprintf(stderr, "Failed to resolve service '%s': %s\n",
417 name, avahi_strerror(avahi_client_errno(c)));
418#endif
419 }
420 else
421 {
422 }
423 ++lu->count;
424 break;
425 case AVAHI_BROWSER_REMOVE:
426 break;
427 case AVAHI_BROWSER_ALL_FOR_NOW:
428 case AVAHI_BROWSER_CACHE_EXHAUSTED:
429#if MDNS_DEBUG
430 fprintf(stderr, "(Browser) %s\n",
431 event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" :
432 "ALL_FOR_NOW");
433#endif
434 lu->done = 1;
435 if (lu->count == 0)
436 {
437 avahi_simple_poll_quit(lu->sp);
438 }
439 break;
440 }
441}
442
443/*
444 * MDNS::entry_group_callback()
445 */
446void MDNS::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
447 void *userdata)
448{
449 MDNS *mdns = static_cast<MDNS *>(userdata);
450 mdns->group_ = g;
451}
452
453/*
454 * MDNS::client_callback()
455 */
456void MDNS::client_callback(AvahiClient *c, AvahiClientState state,
457 void * userdata)
458{
459 switch (state)
460 {
461 default:
462 break;
463 case AVAHI_CLIENT_S_RUNNING:
464 printf("mDNS client running\n");
465 break;
466 }
467}
468
469/*
470 * MDNS::entry()
471 */
472void *MDNS::entry()
473{
474 int error;
475
476 simplePoll_ = avahi_simple_poll_new();
477 HASSERT(simplePoll_);
478
479 client_ = avahi_client_new(avahi_simple_poll_get(simplePoll_),
480 (AvahiClientFlags)0, client_callback, this,
481 &error);
482 if (!client_)
483 {
484#if MDNS_DEBUG
485
486 fprintf(stderr, "Error creating AvaHi client (%d) %s\nmDNS export will "
487 "not be functional.\n", error, avahi_strerror(error));
488#endif
489 return nullptr;
490 }
491 printf("mDNS client created\n");
492 HASSERT(client_);
493
494 sem_.post();
495 avahi_simple_poll_loop(simplePoll_);
496
497 printf("mdns_thread exit\n");
498
499 if (group_)
500 {
501 avahi_entry_group_reset(group_);
502 avahi_entry_group_free(group_);
503 group_ = nullptr;
504 }
505
506 if (client_)
507 {
508 avahi_client_free(client_);
509 client_ = nullptr;
510 }
511
512 avahi_simple_poll_free(simplePoll_);
513 simplePoll_ = nullptr;
514
515 sem_.post();
516 return nullptr;
517}
518
519void MDNS::shutdown()
520{
521 if (simplePoll_)
522 {
523 avahi_simple_poll_quit(simplePoll_);
524 sem_.wait();
525 HASSERT(!simplePoll_);
526 }
527}
528
529#endif
void mdns_publish(const char *name, const char *service, uint16_t port)
Publish an mDNS name.
Definition MDNS.cxx:52
void mdns_unpublish(const char *name, const char *service)
Unpublish an mDNS name.
Definition MDNS.cxx:61
void mdns_scan(const char *service)
Start continuous scan for mDNS service name.
Definition MDNS.cxx:88
int mdns_lookup(const char *service, struct addrinfo *hints, struct addrinfo **addr)
Lookup an mDNS name.
Definition MDNS.cxx:73
MDNS abstraction object.
Definition MDNS.hxx:66
static void scan(const char *service)
Start continuous scan for mDNS service name.
Definition MDNS.cxx:243
static int lookup(const char *service, struct addrinfo *hints, struct addrinfo **addr)
Lookup an mDNS name.
Definition MDNS.cxx:149
void unpublish(const char *name, const char *service)
Constructor.
Definition MDNS.cxx:137
#define htons(x)
Converts a host endian short value to network endian.
Definition in.h:93
#define IPPROTO_TCP
TCP Raw Socket.
Definition in.h:70
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143
void mdns_publish(const char *name, const char *service, uint16_t port)
Publish an mDNS name.
void mdns_unpublish(const char *name, const char *service)
Unpublish an mDNS name.
#define EAI_MEMORY
Memory allocation failure.
Definition netdb.h:69
#define SOCK_STREAM
TCP Socket.
Definition socket.h:45
#define AF_INET
IPv4 Socket (UDP, TCP, etc...)
Definition socket.h:54
#define AF_INET6
IPv6 Socket (UDP, TCP, etc...)
Definition socket.h:57
Structure to contain information about address of a service provider.
Definition netdb.h:48
struct sockaddr * ai_addr
Socket address for socket.
Definition netdb.h:54
char * ai_canonname
Canonical name for service location.
Definition netdb.h:55
int ai_socktype
Socket type.
Definition netdb.h:51
int ai_protocol
Protocol for socket.
Definition netdb.h:52
socklen_t ai_addrlen
Length of socket address.
Definition netdb.h:53
struct addrinfo * ai_next
Pointer to next in list.
Definition netdb.h:56
int ai_family
Protocol family for socket.
Definition netdb.h:50
in_addr_t s_addr
Address.
Definition in.h:51
Structure describing an Internet socket address.
Definition in.h:56
uint16_t sin_family
protocol family (AF_INET)
Definition in.h:57
uint16_t sin_port
port number
Definition in.h:58
struct in_addr sin_addr
internet address
Definition in.h:59
IPv4 socket address.
Definition socket.h:83