24 #include <arpa/inet.h>
25 #include <avahi-client/lookup.h>
26 #include <avahi-client/publish.h>
27 #include <avahi-common/alternative.h>
28 #include <avahi-common/error.h>
29 #include <avahi-common/malloc.h>
30 #include <avahi-common/simple-watch.h>
31 #include <avahi-common/timeval.h>
32 #include <core/exceptions/software.h>
33 #include <core/threading/mutex.h>
34 #include <core/threading/wait_condition.h>
36 #include <netcomm/dns-sd/avahi_resolver_handler.h>
37 #include <netcomm/dns-sd/avahi_thread.h>
38 #include <netinet/in.h>
39 #include <sys/socket.h>
40 #include <sys/types.h>
41 #include <utils/misc/string_conversions.h>
62 const std::chrono::seconds AvahiThread::wait_on_init_failure{5};
73 :
Thread(
"AvahiThread"), enable_ipv4(enable_ipv4), enable_ipv6(enable_ipv6)
79 do_reset_groups =
false;
81 if (enable_ipv4 && enable_ipv6) {
82 service_protocol = AVAHI_PROTO_UNSPEC;
83 }
else if (enable_ipv4) {
84 service_protocol = AVAHI_PROTO_INET;
85 }
else if (enable_ipv6) {
86 service_protocol = AVAHI_PROTO_INET6;
88 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 avahi_client_free(client);
128 avahi_simple_poll_free(simple_poll);
137 if ((simple_poll = avahi_simple_poll_new())) {
138 client = avahi_client_new(avahi_simple_poll_get(simple_poll),
139 AVAHI_CLIENT_NO_FAIL,
140 AvahiThread::client_callback,
145 avahi_simple_poll_free(simple_poll);
152 if (do_reset_groups) {
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);
177 std::this_thread::sleep_for(wait_on_init_failure);
186 AvahiThread::recover()
193 AvahiThread::wake_poller()
196 avahi_simple_poll_wakeup(simple_poll);
206 AvahiThread::client_callback(AvahiClient *c, AvahiClientState state,
void *instance)
209 at->client_state = state;
212 case AVAHI_CLIENT_S_RUNNING:
221 case AVAHI_CLIENT_S_COLLISION:
226 at->do_reset_groups =
true;
229 case AVAHI_CLIENT_FAILURE:
235 case AVAHI_CLIENT_CONNECTING:
239 case AVAHI_CLIENT_S_REGISTERING:
256 if (services_.find(service) == services_.end()) {
257 pending_services_.push_locked(service);
259 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");
279 AvahiThread::create_service(
const NetworkService &service, AvahiEntryGroup *exgroup)
286 AvahiEntryGroup *group;
290 if (!(group = avahi_entry_group_new(client, AvahiThread::entry_group_callback,
this))) {
291 throw NullPointerException(
"Cannot create service group");
295 AvahiStringList * al = NULL;
296 const std::list<std::string> &l = service.
txt();
297 for (std::list<std::string>::const_iterator j = l.begin(); j != l.end(); ++j) {
298 al = avahi_string_list_add(al, j->c_str());
301 int rv = AVAHI_ERR_COLLISION;
303 for (
int i = 1; (i <= 100) && (rv == AVAHI_ERR_COLLISION); ++i) {
304 rv = avahi_entry_group_add_service_strlst(group,
307 (AvahiPublishFlags)0,
315 if (rv == AVAHI_ERR_COLLISION) {
316 char *n = avahi_alternative_service_name(
name.c_str());
323 avahi_string_list_free(al);
326 throw Exception(
"Adding Avahi/mDNS-SD service failed: %s", avahi_strerror(rv));
338 if (avahi_entry_group_commit(group) < 0) {
339 throw Exception(
"Registering Avahi services failed");
346 AvahiThread::recreate_services()
348 for (sit_ = services_.begin(); sit_ != services_.end(); ++sit_) {
349 (*sit_).second = create_service(sit_->first, sit_->second);
354 AvahiThread::create_pending_services()
356 pending_services_.lock();
357 while (!pending_services_.empty()) {
358 NetworkService &s = pending_services_.front();
359 services_[s] = create_service(s, NULL);
360 pending_services_.pop();
362 pending_services_.unlock();
366 AvahiThread::remove_pending_services()
370 pending_remove_services_.lock();
371 while (!pending_remove_services_.empty()) {
372 NetworkService &s = pending_remove_services_.front();
373 if (services_.find(s) != services_.end()) {
374 group_erase(services_[s]);
375 services_.erase_locked(s);
377 pending_remove_services_.pop();
379 pending_remove_services_.unlock();
388 AvahiThread::group_reset(AvahiEntryGroup *g)
391 avahi_entry_group_reset(g);
397 AvahiThread::group_erase(AvahiEntryGroup *g)
400 avahi_entry_group_reset(g);
401 avahi_entry_group_free(g);
406 AvahiThread::erase_groups()
408 for (sit_ = services_.begin(); sit_ != services_.end(); ++sit_) {
410 group_erase(sit_->second);
416 AvahiThread::reset_groups()
418 for (sit_ = services_.begin(); sit_ != services_.end(); ++sit_) {
419 group_reset((*sit_).second);
425 AvahiThread::name_collision(AvahiEntryGroup *g)
427 for (sit_ = services_.begin(); sit_ != services_.end(); ++sit_) {
428 if ((*sit_).second == g) {
429 NetworkService service = sit_->first;
430 std::string
name = service.modified_name() ? service.modified_name() : service.name();
433 char *n = avahi_alternative_service_name((*sit_).first.name());
434 service.set_modified_name(n);
437 pending_remove_services_.push_locked(service);
438 pending_services_.push_locked(service);
449 AvahiThread::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
void *instance)
454 case AVAHI_ENTRY_GROUP_ESTABLISHED:
459 case AVAHI_ENTRY_GROUP_COLLISION: {
460 at->name_collision(g);
464 case AVAHI_ENTRY_GROUP_FAILURE:
469 case AVAHI_ENTRY_GROUP_UNCOMMITED:
470 case AVAHI_ENTRY_GROUP_REGISTERING:
break;
488 handlers_[service_type].push_back(h);
503 if (handlers_.find(service_type) != handlers_.end()) {
504 handlers_[service_type].remove(h);
505 if (handlers_[service_type].size() == 0) {
506 if (browsers_.find(service_type) != browsers_.end()) {
507 pending_browser_removes_.
push_locked(service_type);
511 handlers_.erase(service_type);
522 AvahiThread::create_browser(
const char *service_type)
524 if (browsers_.find(service_type) == browsers_.end()) {
526 AvahiServiceBrowser *b = avahi_service_browser_new(client,
532 AvahiThread::browse_callback,
536 handlers_[service_type].pop_back();
539 browsers_[service_type] = b;
548 AvahiThread::recreate_browsers()
550 LockMap<std::string, std::list<ServiceBrowseHandler *>>::iterator i;
551 for (i = handlers_.begin(); i != handlers_.end(); ++i) {
552 create_browser((*i).first.c_str());
557 AvahiThread::create_pending_browsers()
559 pending_browsers_.
lock();
560 while (!pending_browsers_.empty()) {
562 create_browser(pending_browsers_.front().c_str());
563 pending_browsers_.pop();
565 pending_browsers_.
unlock();
569 AvahiThread::remove_pending_browsers()
573 pending_browser_removes_.
lock();
574 while (!pending_browser_removes_.empty()) {
575 std::string &s = pending_browser_removes_.front();
576 avahi_service_browser_free(browsers_[s]);
578 pending_browser_removes_.pop();
580 pending_browser_removes_.
unlock();
586 AvahiThread::erase_browsers()
588 std::map<std::string, AvahiServiceBrowser *>::iterator i;
589 for (i = browsers_.begin(); i != browsers_.end(); ++i) {
590 avahi_service_browser_free((*i).second);
601 AvahiThread::call_handler_service_removed(
const char *name,
const char *type,
const char *domain)
603 if (handlers_.find(type) != handlers_.end()) {
604 std::list<ServiceBrowseHandler *>::iterator i;
605 for (i = handlers_[type].begin(); i != handlers_[type].end(); ++i) {
606 (*i)->service_removed(
name, type, domain);
622 AvahiThread::call_handler_service_added(
const char * name,
625 const char * host_name,
626 const AvahiIfIndex interface,
627 const AvahiAddress * address,
629 std::list<std::string> &txt,
630 AvahiLookupResultFlags flags)
632 char ifname[IF_NAMESIZE];
634 if (if_indextoname(interface, ifname) == NULL) {
635 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 if_indextoname failed");
639 struct sockaddr *s = NULL;
641 if (address->proto == AVAHI_PROTO_INET) {
644 slen =
sizeof(
struct sockaddr_in);
645 struct sockaddr_in *sin = (
struct sockaddr_in *)malloc(slen);
646 sin->sin_family = AF_INET;
647 sin->sin_addr.s_addr = address->data.ipv4.address;
648 sin->sin_port = htons(port);
649 s = (
struct sockaddr *)sin;
650 }
else if (address->proto == AVAHI_PROTO_INET6) {
653 slen =
sizeof(
struct sockaddr_in6);
654 struct sockaddr_in6 *sin = (
struct sockaddr_in6 *)malloc(slen);
655 sin->sin6_family = AF_INET6;
656 memcpy(&sin->sin6_addr, &address->data.ipv6.address,
sizeof(in6_addr));
658 char ipaddr[INET6_ADDRSTRLEN];
659 if (inet_ntop(AF_INET6, &sin->sin6_addr, ipaddr,
sizeof(ipaddr)) != NULL) {
660 std::string addr_with_scope = std::string(ipaddr) +
"%" + ifname;
664 struct addrinfo hints, *res;
665 memset(&hints, 0,
sizeof(hints));
666 hints.ai_family = AF_INET6;
667 hints.ai_flags = AI_NUMERICHOST;
668 if (getaddrinfo(addr_with_scope.c_str(), port_s.c_str(), &hints, &res) == 0) {
669 if (slen == res[0].ai_addrlen) {
670 memcpy(sin, res[0].ai_addr, slen);
674 "AvahiThread::call_handler_service_added: IPv6 address lengths different");
679 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 getaddrinfo failed");
683 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 inet_ntop failed");
686 s = (
struct sockaddr *)sin;
691 if (handlers_.find(type) != handlers_.end()) {
692 std::list<ServiceBrowseHandler *>::iterator i;
693 for (i = handlers_[type].begin(); i != handlers_[type].end(); ++i) {
695 name, type, domain, host_name, ifname, (
struct sockaddr *)s, slen, port, txt, (int)flags);
707 AvahiThread::call_handler_failed(
const char *name,
const char *type,
const char *domain)
709 if (handlers_.find(type) != handlers_.end()) {
710 std::list<ServiceBrowseHandler *>::iterator i;
711 for (i = handlers_[type].begin(); i != handlers_[type].end(); ++i) {
712 (*i)->browse_failed(
name, type, domain);
721 AvahiThread::call_handler_all_for_now(
const char *type)
723 if (handlers_.find(type) != handlers_.end()) {
724 std::list<ServiceBrowseHandler *>::iterator i;
725 for (i = handlers_[type].begin(); i != handlers_[type].end(); ++i) {
735 AvahiThread::call_handler_cache_exhausted(
const char *type)
737 if (handlers_.find(type) != handlers_.end()) {
738 std::list<ServiceBrowseHandler *>::iterator i;
739 for (i = handlers_[type].begin(); i != handlers_[type].end(); ++i) {
740 (*i)->cache_exhausted();
759 AvahiThread::browse_callback(AvahiServiceBrowser * b,
760 AvahiIfIndex interface,
761 AvahiProtocol protocol,
762 AvahiBrowserEvent event,
766 AvahiLookupResultFlags flags,
772 case AVAHI_BROWSER_FAILURE:
776 case AVAHI_BROWSER_NEW:
782 if (!(avahi_service_resolver_new(at->client,
790 AvahiThread::resolve_callback,
792 throw NullPointerException(
"Could not instantiate resolver");
796 case AVAHI_BROWSER_REMOVE:
799 at->call_handler_service_removed(
name, type, domain);
802 case AVAHI_BROWSER_ALL_FOR_NOW:
805 at->call_handler_all_for_now(type);
808 case AVAHI_BROWSER_CACHE_EXHAUSTED:
811 at->call_handler_cache_exhausted(type);
834 AvahiThread::resolve_callback(AvahiServiceResolver * r,
835 AvahiIfIndex interface,
836 AVAHI_GCC_UNUSED AvahiProtocol protocol,
837 AvahiResolverEvent event,
841 const char * host_name,
842 const AvahiAddress * address,
844 AvahiStringList * txt,
845 AvahiLookupResultFlags flags,
851 case AVAHI_RESOLVER_FAILURE:
853 at->call_handler_failed(
name, type, domain);
856 case AVAHI_RESOLVER_FOUND:
859 std::list<std::string> txts;
860 AvahiStringList * l = txt;
864 txts.push_back((
char *)avahi_string_list_get_text(l));
865 l = avahi_string_list_get_next(l);
868 at->call_handler_service_added(
869 name, type, domain, host_name, interface, address, port, txts, flags);
874 avahi_service_resolver_free(r);
890 AvahiResolverCallbackData *data =
new AvahiResolverCallbackData(
this, handler);
892 if (pending_hostname_resolves_.find(
name) == pending_hostname_resolves_.end()) {
893 pending_hostname_resolves_[
name] = data;
900 AvahiThread::start_hostname_resolver(
const char *name, AvahiResolverCallbackData *data)
902 AvahiHostNameResolver *resolver;
903 if ((resolver = avahi_host_name_resolver_new(client,
908 AVAHI_LOOKUP_USE_MULTICAST,
909 AvahiThread::host_name_resolver_callback,
912 throw Exception(
"Cannot create Avahi name resolver");
914 running_hostname_resolvers_.push_back(resolver);
919 AvahiThread::start_hostname_resolvers()
921 LockMap<std::string, AvahiResolverCallbackData *>::iterator phrit;
922 for (phrit = pending_hostname_resolves_.begin(); phrit != pending_hostname_resolves_.end();
924 start_hostname_resolver(phrit->first.c_str(), phrit->second);
926 pending_hostname_resolves_.clear();
930 AvahiThread::start_address_resolvers()
932 LockMap<struct ::sockaddr_storage *, AvahiResolverCallbackData *>::iterator parit;
934 for (parit = pending_address_resolves_.begin(); parit != pending_address_resolves_.end();
936 start_address_resolver(parit->first, parit->second);
939 pending_address_resolves_.clear();
954 struct ::sockaddr_storage *sstor =
955 (struct ::sockaddr_storage *)malloc(
sizeof(struct ::sockaddr_storage));
956 if (addr->sa_family == AF_INET) {
957 if (addrlen !=
sizeof(sockaddr_in)) {
958 throw Exception(
"Invalid size for IPv4 address struct");
960 memcpy(sstor, addr,
sizeof(sockaddr_in));
961 }
else if (addr->sa_family == AF_INET6) {
962 if (addrlen !=
sizeof(sockaddr_in6)) {
963 throw Exception(
"Invalid size for IPv6 address struct");
965 memcpy(sstor, addr,
sizeof(sockaddr_in6));
967 throw Exception(
"Unknown address family");
969 AvahiResolverCallbackData *data =
new AvahiResolverCallbackData(
this, handler);
971 pending_address_resolves_[sstor] = data;
976 AvahiThread::start_address_resolver(
const struct sockaddr_storage *in_addr,
977 AvahiResolverCallbackData * data)
981 if (in_addr->ss_family == AF_INET) {
982 a.proto = AVAHI_PROTO_INET;
983 a.data.ipv4.address = ((sockaddr_in *)in_addr)->sin_addr.s_addr;
984 }
else if (in_addr->ss_family == AF_INET6) {
985 a.proto = AVAHI_PROTO_INET6;
986 memcpy(&a.data.ipv6.address, &((sockaddr_in6 *)in_addr)->sin6_addr,
sizeof(in6_addr));
988 throw Exception(
"Unknown address family");
991 AvahiAddressResolver *resolver;
992 if ((resolver = avahi_address_resolver_new(client,
996 AVAHI_LOOKUP_USE_MULTICAST,
997 AvahiThread::address_resolver_callback,
1000 Exception e(
"Cannot create Avahi address resolver");
1001 e.append(
"Avahi error: %s", avahi_strerror(avahi_client_errno(client)));
1013 AvahiThread::remove_hostname_resolver(AvahiHostNameResolver *r)
1023 AvahiThread::remove_address_resolver(AvahiAddressResolver *r)
1032 AvahiThread::host_name_resolver_callback(AvahiHostNameResolver *r,
1033 AvahiIfIndex interface,
1034 AvahiProtocol protocol,
1035 AvahiResolverEvent event,
1037 const AvahiAddress * a,
1038 AvahiLookupResultFlags flags,
1041 AvahiResolverCallbackData *cd =
static_cast<AvahiResolverCallbackData *
>(userdata);
1043 cd->first->remove_hostname_resolver(r);
1044 avahi_host_name_resolver_free(r);
1047 case AVAHI_RESOLVER_FOUND: {
1048 if (protocol == AVAHI_PROTO_INET) {
1049 struct sockaddr_in *res = (
struct sockaddr_in *)malloc(
sizeof(
struct sockaddr_in));
1050 res->sin_family = (
unsigned short)avahi_proto_to_af(protocol);
1051 res->sin_addr.s_addr = a->data.ipv4.address;
1052 cd->second->resolved_name(strdup(
name), (
struct sockaddr *)res,
sizeof(
struct sockaddr_in));
1053 }
else if (protocol == AVAHI_PROTO_INET6) {
1054 struct sockaddr_in6 *res = (
struct sockaddr_in6 *)malloc(
sizeof(
struct sockaddr_in6));
1055 res->sin6_family = (
unsigned short)avahi_proto_to_af(protocol);
1056 memcpy(&res->sin6_addr, &a->data.ipv6.address,
sizeof(in6_addr));
1057 cd->second->resolved_name(strdup(
name), (
struct sockaddr *)res,
sizeof(
struct sockaddr_in6));
1059 cd->second->name_resolution_failed(strdup(
name));
1063 case AVAHI_RESOLVER_FAILURE:
1064 default: cd->second->name_resolution_failed(strdup(
name));
break;
1074 AvahiThread::address_resolver_callback(AvahiAddressResolver * r,
1075 AvahiIfIndex interface,
1076 AvahiProtocol protocol,
1077 AvahiResolverEvent event,
1078 const AvahiAddress * a,
1080 AvahiLookupResultFlags flags,
1083 AvahiResolverCallbackData *cd =
static_cast<AvahiResolverCallbackData *
>(userdata);
1085 cd->first->remove_address_resolver(r);
1086 avahi_address_resolver_free(r);
1088 struct sockaddr *res = NULL;
1089 socklen_t res_size = 0;
1091 if (protocol == AVAHI_PROTO_INET) {
1092 res_size =
sizeof(
struct sockaddr_in);
1093 res = (
struct sockaddr *)malloc(res_size);
1094 sockaddr_in *res_4 = (
struct sockaddr_in *)res;
1095 res_4->sin_family = (
unsigned short)avahi_proto_to_af(protocol);
1096 res_4->sin_addr.s_addr = a->data.ipv4.address;
1097 }
else if (protocol == AVAHI_PROTO_INET6) {
1098 res_size =
sizeof(
struct sockaddr_in6);
1099 res = (
struct sockaddr *)malloc(res_size);
1100 sockaddr_in6 *res_6 = (
struct sockaddr_in6 *)res;
1101 res_6->sin6_family = (
unsigned short)avahi_proto_to_af(protocol);
1102 memcpy(&res_6->sin6_addr, &a->data.ipv6.address,
sizeof(in6_addr));
1106 case AVAHI_RESOLVER_FOUND: cd->second->resolved_address(res, res_size, strdup(
name));
break;
1107 case AVAHI_RESOLVER_FAILURE: cd->second->address_resolution_failed(res, res_size);
break;
1109 default: cd->second->address_resolution_failed(NULL, 0);
break;
1119 AvahiThread::init_done()
Avahi resolver handler interface.
void resolve_name(const char *name, AvahiResolverHandler *handler)
Order name resolution.
void watch_service(const char *service_type, ServiceBrowseHandler *h)
Add a result handler.
~AvahiThread()
Destructor.
void publish_service(NetworkService *service)
Publish service.
void unwatch_service(const char *service_type, ServiceBrowseHandler *h)
Remove a handler.
void unpublish_service(NetworkService *service)
Revoke service publication.
AvahiThread(bool enable_ipv4=true, bool enable_ipv6=true)
Constructor.
void wait_initialized()
Waits for the AvahiThread to be initialized.
virtual void loop()
Avahi thread loop.
void resolve_address(struct sockaddr *addr, socklen_t addrlen, AvahiResolverHandler *handler)
Order address resolution.
Base class for exceptions in Fawkes.
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.
void erase_locked(const KeyType &key)
Remove item with lock.
void push_locked(const Type &x)
Push element to queue with lock protection.
void lock() const
Lock queue.
void unlock() const
Unlock list.
Representation of a service announced or found via service discovery (i.e.
void set_modified_name(const char *new_name) const
Set modified name of service.
const char * modified_name() const
Get modified name of service.
const char * type() const
Get type of service.
unsigned short int port() const
Get port of service.
const char * name() const
Get name of service.
const char * host() const
Get host of service.
const char * domain() const
Get domain of service.
const std::list< std::string > & txt() const
Get TXT record list of service.
A NULL pointer was supplied where not allowed.
Interface for class that process browse results.
static std::string to_string(unsigned int i)
Convert unsigned int value to a string.
Thread class encapsulation of pthreads.
void set_prepfin_conc_loop(bool concurrent=true)
Set concurrent execution of prepare_finalize() and loop().
const char * name() const
Get name of thread.
@ CANCEL_DISABLED
thread cannot be cancelled
static void set_cancel_state(CancelState new_state, CancelState *old_state=0)
Set the cancel state of the current thread.
Wait until a given condition holds.
void wait()
Wait for the condition forever.
void wake_all()
Wake up all waiting threads.
Fawkes library namespace.