24 #include <netcomm/dns-sd/avahi_thread.h> 25 #include <netcomm/dns-sd/avahi_resolver_handler.h> 27 #include <core/threading/mutex.h> 28 #include <core/threading/wait_condition.h> 29 #include <core/exceptions/software.h> 30 #include <utils/misc/string_conversions.h> 32 #include <avahi-client/lookup.h> 33 #include <avahi-client/publish.h> 34 #include <avahi-common/alternative.h> 35 #include <avahi-common/simple-watch.h> 36 #include <avahi-common/malloc.h> 37 #include <avahi-common/error.h> 38 #include <avahi-common/timeval.h> 40 #include <sys/socket.h> 41 #include <sys/types.h> 42 #include <netinet/in.h> 44 #include <arpa/inet.h> 72 enable_ipv4(enable_ipv4), enable_ipv6(enable_ipv6)
78 do_reset_groups =
false;
80 if (enable_ipv4 && enable_ipv6) {
81 service_protocol = AVAHI_PROTO_UNSPEC;
82 }
else if (enable_ipv4) {
83 service_protocol = AVAHI_PROTO_INET;
84 }
else if (enable_ipv6) {
85 service_protocol = AVAHI_PROTO_INET6;
87 throw Exception(
"Neither IPv4 nor IPv6 enabled");
101 remove_pending_services();
102 remove_pending_browsers();
108 avahi_client_free( client );
111 avahi_simple_poll_free( simple_poll );
123 if ( need_recover ) {
125 avahi_client_free( client );
130 avahi_simple_poll_free( simple_poll );
135 if ( ! simple_poll ) {
139 if ( (simple_poll = avahi_simple_poll_new()) ) {
141 client = avahi_client_new( avahi_simple_poll_get(simple_poll), AVAHI_CLIENT_NO_FAIL,
142 AvahiThread::client_callback,
this, &error );
145 avahi_simple_poll_free( simple_poll );
152 if ( do_reset_groups ) {
156 if ( need_recover ) {
162 if ( client_state == AVAHI_CLIENT_S_RUNNING ) {
163 remove_pending_services();
164 remove_pending_browsers();
165 create_pending_services();
166 create_pending_browsers();
167 start_hostname_resolvers();
168 start_address_resolvers();
171 need_recover =
false;
173 avahi_simple_poll_iterate( simple_poll, -1);
183 AvahiThread::recover()
190 AvahiThread::wake_poller()
193 avahi_simple_poll_wakeup( simple_poll );
204 AvahiThread::client_callback(AvahiClient *c, AvahiClientState state,
void *instance)
207 at->client_state = state;
210 case AVAHI_CLIENT_S_RUNNING:
219 case AVAHI_CLIENT_S_COLLISION:
224 at->do_reset_groups =
true;
227 case AVAHI_CLIENT_FAILURE:
233 case AVAHI_CLIENT_CONNECTING:
237 case AVAHI_CLIENT_S_REGISTERING:
255 if ( __services.find(service) == __services.end() ) {
256 __pending_services.push_locked(service);
258 throw Exception(
"Service already registered");
268 if ( __services.find(*service) != __services.end() ) {
269 __pending_remove_services.push_locked(service);
271 throw Exception(
"Service not registered");
280 AvahiThread::create_service(
const NetworkService &service, AvahiEntryGroup *exgroup)
284 if ( ! client )
return NULL;
286 AvahiEntryGroup *group;
290 if ( ! (group = avahi_entry_group_new(client,
291 AvahiThread::entry_group_callback,
297 AvahiStringList *al = NULL;
298 const std::list<std::string> &l = service.
txt();
299 for (std::list<std::string>::const_iterator j = l.begin(); j != l.end(); ++j) {
300 al = avahi_string_list_add(al, j->c_str());
303 int rv = AVAHI_ERR_COLLISION;
305 for (
int i = 1; (i <= 100) && (rv == AVAHI_ERR_COLLISION); ++i) {
306 rv = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC,
308 (AvahiPublishFlags)0,
309 name.c_str(), service.
type(),
314 if (rv == AVAHI_ERR_COLLISION) {
315 char *n = avahi_alternative_service_name(name.c_str());
322 avahi_string_list_free(al);
325 throw Exception(
"Adding Avahi/mDNS-SD service failed: %s", avahi_strerror(rv));
337 if (avahi_entry_group_commit(group) < 0) {
338 throw Exception(
"Registering Avahi services failed");
345 AvahiThread::recreate_services()
347 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
348 (*__sit).second = create_service(__sit->first, __sit->second);
354 AvahiThread::create_pending_services()
356 __pending_services.lock();
357 while ( ! __pending_services.empty()) {
359 __services[s] = create_service(s, NULL);
360 __pending_services.pop();
362 __pending_services.unlock();
367 AvahiThread::remove_pending_services()
371 __pending_remove_services.lock();
372 while ( ! __pending_remove_services.empty()) {
374 if ( __services.find(s) != __services.end() ) {
375 group_erase(__services[s]);
376 __services.erase_locked(s);
378 __pending_remove_services.pop();
380 __pending_remove_services.unlock();
390 AvahiThread::group_reset(AvahiEntryGroup *g)
393 avahi_entry_group_reset(g);
400 AvahiThread::group_erase(AvahiEntryGroup *g)
403 avahi_entry_group_reset( g );
404 avahi_entry_group_free( g );
410 AvahiThread::erase_groups()
412 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
413 if (__sit->second) group_erase(__sit->second);
414 __sit->second = NULL;
420 AvahiThread::reset_groups()
422 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
423 group_reset((*__sit).second);
430 AvahiThread::name_collision(AvahiEntryGroup *g)
432 for (__sit = __services.begin(); __sit != __services.end(); ++__sit) {
433 if ( (*__sit).second == g ) {
438 char *n = avahi_alternative_service_name((*__sit).first.name());
442 __pending_remove_services.push_locked(service);
443 __pending_services.push_locked(service);
455 AvahiThread::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
461 case AVAHI_ENTRY_GROUP_ESTABLISHED :
466 case AVAHI_ENTRY_GROUP_COLLISION : {
467 at->name_collision(g);
471 case AVAHI_ENTRY_GROUP_FAILURE :
476 case AVAHI_ENTRY_GROUP_UNCOMMITED:
477 case AVAHI_ENTRY_GROUP_REGISTERING:
498 __handlers[service_type].push_back(h);
514 if ( __handlers.find(service_type) != __handlers.end() ) {
515 __handlers[service_type].remove(h);
516 if ( __handlers[service_type].size() == 0 ) {
517 if ( __browsers.find(service_type) != __browsers.end() ) {
518 __pending_browser_removes.
push_locked(service_type);
522 __handlers.erase(service_type);
534 AvahiThread::create_browser(
const char *service_type)
536 if ( __browsers.find(service_type) == __browsers.end() ) {
538 AvahiServiceBrowser *b = avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
539 service_protocol, service_type,
540 NULL, (AvahiLookupFlags)0,
541 AvahiThread::browse_callback,
this);
544 __handlers[service_type].pop_back();
547 __browsers[service_type] = b;
557 AvahiThread::recreate_browsers()
560 for (i = __handlers.begin(); i != __handlers.end(); ++i) {
561 create_browser( (*i).first.c_str() );
567 AvahiThread::create_pending_browsers()
569 __pending_browsers.
lock();
570 while ( ! __pending_browsers.empty() ) {
572 create_browser(__pending_browsers.front().c_str());
573 __pending_browsers.pop();
575 __pending_browsers.
unlock();
580 AvahiThread::remove_pending_browsers()
584 __pending_browser_removes.
lock();
585 while ( ! __pending_browser_removes.empty()) {
586 std::string &s = __pending_browser_removes.front();
587 avahi_service_browser_free(__browsers[s]);
589 __pending_browser_removes.pop();
591 __pending_browser_removes.
unlock();
598 AvahiThread::erase_browsers()
600 std::map< std::string, AvahiServiceBrowser * >::iterator i;
601 for (i = __browsers.begin(); i != __browsers.end(); ++i) {
602 avahi_service_browser_free((*i).second);
614 AvahiThread::call_handler_service_removed(
const char *
name,
618 if ( __handlers.find(type) != __handlers.end() ) {
619 std::list<ServiceBrowseHandler *>::iterator i;
620 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
621 (*i)->service_removed(name, type, domain);
638 AvahiThread::call_handler_service_added(
const char *name,
641 const char *host_name,
642 const AvahiIfIndex interface,
643 const AvahiAddress *address,
645 std::list<std::string> &txt,
646 AvahiLookupResultFlags flags)
648 char ifname[IF_NAMESIZE];
650 if (if_indextoname(interface, ifname) == NULL) {
651 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 if_indextoname failed");
656 struct sockaddr *s = NULL;
658 if ( address->proto == AVAHI_PROTO_INET ) {
659 if (! enable_ipv4)
return;
660 slen =
sizeof(
struct sockaddr_in);
661 struct sockaddr_in *sin = (
struct sockaddr_in *)malloc(slen);
662 sin->sin_family = AF_INET;
663 sin->sin_addr.s_addr = address->data.ipv4.address;
664 sin->sin_port = htons(port);
665 s = (
struct sockaddr *)sin;
666 }
else if ( address->proto == AVAHI_PROTO_INET6 ) {
667 if (! enable_ipv6)
return;
668 slen =
sizeof(
struct sockaddr_in6);
669 struct sockaddr_in6 *sin = (
struct sockaddr_in6 *)malloc(slen);
670 sin->sin6_family = AF_INET6;
671 memcpy(&sin->sin6_addr, &address->data.ipv6.address,
sizeof(in6_addr));
673 char ipaddr[INET6_ADDRSTRLEN];
674 if (inet_ntop(AF_INET6, &sin->sin6_addr, ipaddr,
sizeof(ipaddr)) != NULL) {
675 std::string addr_with_scope = std::string(ipaddr) +
"%" + ifname;
679 struct addrinfo hints, *res;
680 memset(&hints, 0,
sizeof(hints));
681 hints.ai_family = AF_INET6;
682 hints.ai_flags = AI_NUMERICHOST;
683 if (getaddrinfo(addr_with_scope.c_str(), port_s.c_str(), &hints, &res) == 0) {
684 if (slen == res[0].ai_addrlen) {
685 memcpy(sin, res[0].ai_addr, slen);
686 printf(
"Scope %u interface %u\n", sin->sin6_scope_id, interface);
689 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 address lengths different");
694 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 getaddrinfo failed");
698 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 inet_ntop failed");
701 s = (
struct sockaddr *)sin;
706 if ( __handlers.find(type) != __handlers.end() ) {
707 std::list<ServiceBrowseHandler *>::iterator i;
708 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
709 (*i)->service_added(name, type, domain, host_name, ifname,
710 (
struct sockaddr *)s, slen, port, txt, (
int)flags);
723 AvahiThread::call_handler_failed(
const char *name,
727 if ( __handlers.find(type) != __handlers.end() ) {
728 std::list<ServiceBrowseHandler *>::iterator i;
729 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
730 (*i)->browse_failed(name, type, domain);
740 AvahiThread::call_handler_all_for_now(
const char *type)
742 if ( __handlers.find(type) != __handlers.end() ) {
743 std::list<ServiceBrowseHandler *>::iterator i;
744 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
755 AvahiThread::call_handler_cache_exhausted(
const char *type)
757 if ( __handlers.find(type) != __handlers.end() ) {
758 std::list<ServiceBrowseHandler *>::iterator i;
759 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
760 (*i)->cache_exhausted();
780 AvahiThread::browse_callback( AvahiServiceBrowser *b,
781 AvahiIfIndex interface,
782 AvahiProtocol protocol,
783 AvahiBrowserEvent event,
787 AvahiLookupResultFlags flags,
793 case AVAHI_BROWSER_FAILURE:
797 case AVAHI_BROWSER_NEW:
803 if (!(avahi_service_resolver_new(at->client, interface, protocol,
804 name, type, domain, protocol, (AvahiLookupFlags)0,
805 AvahiThread::resolve_callback, instance))) {
810 case AVAHI_BROWSER_REMOVE:
813 at->call_handler_service_removed(name, type, domain);
816 case AVAHI_BROWSER_ALL_FOR_NOW:
819 at->call_handler_all_for_now(type);
822 case AVAHI_BROWSER_CACHE_EXHAUSTED:
825 at->call_handler_cache_exhausted(type);
850 AvahiThread::resolve_callback( AvahiServiceResolver *r,
851 AvahiIfIndex interface,
852 AVAHI_GCC_UNUSED AvahiProtocol protocol,
853 AvahiResolverEvent event,
857 const char *host_name,
858 const AvahiAddress *address,
860 AvahiStringList *txt,
861 AvahiLookupResultFlags flags,
867 case AVAHI_RESOLVER_FAILURE:
869 at->call_handler_failed(name, type, domain);
872 case AVAHI_RESOLVER_FOUND:
875 std::list< std::string > txts;
876 AvahiStringList *l = txt;
880 txts.push_back((
char *)avahi_string_list_get_text(l));
881 l = avahi_string_list_get_next( l );
884 at->call_handler_service_added(name, type, domain, host_name, interface, address, port, txts, flags);
889 avahi_service_resolver_free(r);
907 AvahiResolverCallbackData *data =
new AvahiResolverCallbackData(
this, handler);
909 if ( __pending_hostname_resolves.find(name) == __pending_hostname_resolves.end()) {
910 __pending_hostname_resolves[
name] = data;
918 AvahiThread::start_hostname_resolver(
const char *name, AvahiResolverCallbackData *data)
920 AvahiHostNameResolver *resolver;
921 if ( (resolver = avahi_host_name_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
922 name, service_protocol,
923 AVAHI_LOOKUP_USE_MULTICAST,
924 AvahiThread::host_name_resolver_callback,
926 throw Exception(
"Cannot create Avahi name resolver");
928 __running_hostname_resolvers.push_back(resolver);
935 AvahiThread::start_hostname_resolvers()
938 for (phrit = __pending_hostname_resolves.begin(); phrit != __pending_hostname_resolves.end(); ++phrit) {
939 start_hostname_resolver(phrit->first.c_str(), phrit->second);
941 __pending_hostname_resolves.clear();
946 AvahiThread::start_address_resolvers()
950 for (parit = __pending_address_resolves.begin(); parit != __pending_address_resolves.end(); ++parit) {
951 start_address_resolver(parit->first, parit->second);
954 __pending_address_resolves.clear();
969 struct ::sockaddr_storage *sstor =
970 (struct ::sockaddr_storage *)malloc(
sizeof(struct ::sockaddr_storage));
971 if (addr->sa_family == AF_INET) {
972 if (addrlen !=
sizeof(sockaddr_in)) {
973 throw Exception(
"Invalid size for IPv4 address struct");
975 memcpy(sstor, addr,
sizeof(sockaddr_in));
976 }
else if (addr->sa_family == AF_INET6) {
977 if (addrlen !=
sizeof(sockaddr_in6)) {
978 throw Exception(
"Invalid size for IPv6 address struct");
980 memcpy(sstor, addr,
sizeof(sockaddr_in6));
982 throw Exception(
"Unknown address family");
984 AvahiResolverCallbackData *data =
new AvahiResolverCallbackData(
this, handler);
986 __pending_address_resolves[sstor] = data;
992 AvahiThread::start_address_resolver(
const struct sockaddr_storage *in_addr, AvahiResolverCallbackData *data)
996 if (in_addr->ss_family == AF_INET) {
997 a.proto = AVAHI_PROTO_INET;
998 a.data.ipv4.address = ((sockaddr_in *)in_addr)->sin_addr.s_addr;
999 }
else if (in_addr->ss_family == AF_INET6) {
1000 a.proto = AVAHI_PROTO_INET6;
1001 memcpy(&a.data.ipv6.address, &((sockaddr_in6 *)in_addr)->sin6_addr,
sizeof(in6_addr));
1003 throw Exception(
"Unknown address family");
1006 AvahiAddressResolver *resolver;
1007 if ( (resolver = avahi_address_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
1008 &a, AVAHI_LOOKUP_USE_MULTICAST,
1009 AvahiThread::address_resolver_callback,
1011 Exception e(
"Cannot create Avahi address resolver");
1012 e.
append(
"Avahi error: %s", avahi_strerror(avahi_client_errno(client)));
1025 AvahiThread::remove_hostname_resolver(AvahiHostNameResolver *r)
1036 AvahiThread::remove_address_resolver(AvahiAddressResolver *r)
1046 AvahiThread::host_name_resolver_callback(AvahiHostNameResolver *r,
1047 AvahiIfIndex interface,
1048 AvahiProtocol protocol,
1049 AvahiResolverEvent event,
1051 const AvahiAddress *a,
1052 AvahiLookupResultFlags flags,
1055 AvahiResolverCallbackData *cd =
static_cast<AvahiResolverCallbackData *
>(userdata);
1057 cd->first->remove_hostname_resolver(r);
1058 avahi_host_name_resolver_free(r);
1061 case AVAHI_RESOLVER_FOUND:
1063 if (protocol == AVAHI_PROTO_INET) {
1064 struct sockaddr_in *res = (
struct sockaddr_in *)malloc(
sizeof(
struct sockaddr_in));
1065 res->sin_family = (
unsigned short)avahi_proto_to_af(protocol);
1066 res->sin_addr.s_addr = a->data.ipv4.address;
1067 cd->second->resolved_name(strdup(name), (
struct sockaddr *)res,
sizeof(
struct sockaddr_in));
1068 }
else if (protocol == AVAHI_PROTO_INET6) {
1069 struct sockaddr_in6 *res = (
struct sockaddr_in6 *)malloc(
sizeof(
struct sockaddr_in6));
1070 res->sin6_family = (
unsigned short)avahi_proto_to_af(protocol);
1071 memcpy(&res->sin6_addr, &a->data.ipv6.address,
sizeof(in6_addr));
1072 cd->second->resolved_name(strdup(name), (
struct sockaddr *)res,
sizeof(
struct sockaddr_in6));
1074 cd->second->name_resolution_failed(strdup(name));
1079 case AVAHI_RESOLVER_FAILURE:
1081 cd->second->name_resolution_failed(strdup(name));
1093 AvahiThread::address_resolver_callback(AvahiAddressResolver *r,
1094 AvahiIfIndex interface,
1095 AvahiProtocol protocol,
1096 AvahiResolverEvent event,
1097 const AvahiAddress *a,
1099 AvahiLookupResultFlags flags,
1102 AvahiResolverCallbackData *cd =
static_cast<AvahiResolverCallbackData *
>(userdata);
1104 cd->first->remove_address_resolver(r);
1105 avahi_address_resolver_free(r);
1107 struct sockaddr *res = NULL;
1108 socklen_t res_size = 0;
1110 if (protocol == AVAHI_PROTO_INET) {
1111 res_size =
sizeof(
struct sockaddr_in);
1112 res = (
struct sockaddr *)malloc(res_size);
1113 sockaddr_in *res_4 = (
struct sockaddr_in *)res;
1114 res_4->sin_family = (
unsigned short)avahi_proto_to_af(protocol);
1115 res_4->sin_addr.s_addr = a->data.ipv4.address;
1116 }
else if (protocol == AVAHI_PROTO_INET6) {
1117 res_size =
sizeof(
struct sockaddr_in6);
1118 res = (
struct sockaddr *)malloc(res_size);
1119 sockaddr_in6 *res_6 = (
struct sockaddr_in6 *)res;
1120 res_6->sin6_family = (
unsigned short)avahi_proto_to_af(protocol);
1121 memcpy(&res_6->sin6_addr, &a->data.ipv6.address,
sizeof(in6_addr));
1125 case AVAHI_RESOLVER_FOUND:
1126 cd->second->resolved_address(res, res_size, strdup(name));
1128 case AVAHI_RESOLVER_FAILURE:
1129 cd->second->address_resolution_failed(res, res_size);
1133 cd->second->address_resolution_failed(NULL, 0);
1145 AvahiThread::init_done()
Wait until a given condition holds.
void erase_locked(const KeyType &key)
Remove item with lock.
~AvahiThread()
Destructor.
void unlock() const
Unlock list.
virtual void loop()
Avahi thread loop.
Fawkes library namespace.
void wake_all()
Wake up all waiting threads.
const char * host() const
Get host of service.
thread cannot be cancelled
A NULL pointer was supplied where not allowed.
Thread class encapsulation of pthreads.
void set_prepfin_conc_loop(bool concurrent=true)
Set concurrent execution of prepare_finalize() and loop().
void push_back_locked(const Type &x)
Push element to list at back with lock protection.
void remove_locked(const Type &x)
Remove element from list with lock protection.
static void set_cancel_state(CancelState new_state, CancelState *old_state=0)
Set the cancel state of the current thread.
Interface for class that process browse results.
unsigned short int port() const
Get port of service.
Avahi resolver handler interface.
void publish_service(NetworkService *service)
Publish service.
void resolve_address(struct sockaddr *addr, socklen_t addrlen, AvahiResolverHandler *handler)
Order address resolution.
Base class for exceptions in Fawkes.
void resolve_name(const char *name, AvahiResolverHandler *handler)
Order name resolution.
void watch_service(const char *service_type, ServiceBrowseHandler *h)
Add a result handler.
const char * name() const
Get name of thread.
const char * modified_name() const
Get modified name of service.
void wait()
Wait for the condition forever.
Representation of a service announced or found via service discovery (i.e.
const std::list< std::string > & txt() const
Get TXT record list of service.
void push_locked(const Type &x)
Push element to queue with lock protection.
const char * domain() const
Get domain of service.
void lock() const
Lock queue.
const char * type() const
Get type of service.
void set_modified_name(const char *new_name) const
Set modified name of service.
AvahiThread(bool enable_ipv4=true, bool enable_ipv6=true)
Constructor.
static std::string to_string(unsigned int i)
Convert unsigned int value to a string.
void unpublish_service(NetworkService *service)
Revoke service publication.
void append(const char *format,...)
Append messages to the message list.
void wait_initialized()
Waits for the AvahiThread to be initialized.
void unwatch_service(const char *service_type, ServiceBrowseHandler *h)
Remove a handler.
const char * name() const
Get name of service.