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>
69 : Thread(
"AvahiThread"), enable_ipv4(enable_ipv4), enable_ipv6(enable_ipv6)
75 do_reset_groups =
false;
77 if (enable_ipv4 && enable_ipv6) {
78 service_protocol = AVAHI_PROTO_UNSPEC;
79 }
else if (enable_ipv4) {
80 service_protocol = AVAHI_PROTO_INET;
81 }
else if (enable_ipv6) {
82 service_protocol = AVAHI_PROTO_INET6;
84 throw Exception(
"Neither IPv4 nor IPv6 enabled");
87 init_wc =
new WaitCondition();
89 set_prepfin_conc_loop(
true);
97 remove_pending_services();
98 remove_pending_browsers();
104 avahi_client_free(client);
107 avahi_simple_poll_free(simple_poll);
119 avahi_client_free(client);
124 avahi_simple_poll_free(simple_poll);
133 if ((simple_poll = avahi_simple_poll_new())) {
134 client = avahi_client_new(avahi_simple_poll_get(simple_poll),
135 AVAHI_CLIENT_NO_FAIL,
136 AvahiThread::client_callback,
141 avahi_simple_poll_free(simple_poll);
148 if (do_reset_groups) {
158 if (client_state == AVAHI_CLIENT_S_RUNNING) {
159 remove_pending_services();
160 remove_pending_browsers();
161 create_pending_services();
162 create_pending_browsers();
163 start_hostname_resolvers();
164 start_address_resolvers();
167 need_recover =
false;
169 avahi_simple_poll_iterate(simple_poll, -1);
178 AvahiThread::recover()
185 AvahiThread::wake_poller()
188 avahi_simple_poll_wakeup(simple_poll);
198 AvahiThread::client_callback(AvahiClient *c, AvahiClientState state,
void *instance)
201 at->client_state = state;
204 case AVAHI_CLIENT_S_RUNNING:
213 case AVAHI_CLIENT_S_COLLISION:
218 at->do_reset_groups =
true;
221 case AVAHI_CLIENT_FAILURE:
227 case AVAHI_CLIENT_CONNECTING:
231 case AVAHI_CLIENT_S_REGISTERING:
248 if (services_.find(service) == services_.end()) {
249 pending_services_.push_locked(service);
251 throw Exception(
"Service already registered");
260 if (services_.find(*service) != services_.end()) {
261 pending_remove_services_.push_locked(service);
263 throw Exception(
"Service not registered");
271 AvahiThread::create_service(
const NetworkService &service, AvahiEntryGroup *exgroup)
278 AvahiEntryGroup *group;
282 if (!(group = avahi_entry_group_new(client, AvahiThread::entry_group_callback,
this))) {
283 throw NullPointerException(
"Cannot create service group");
287 AvahiStringList * al = NULL;
288 const std::list<std::string> &l = service.txt();
289 for (std::list<std::string>::const_iterator j = l.begin(); j != l.end(); ++j) {
290 al = avahi_string_list_add(al, j->c_str());
293 int rv = AVAHI_ERR_COLLISION;
294 std::string
name = service.modified_name() ? service.modified_name() : service.name();
295 for (
int i = 1; (i <= 100) && (rv == AVAHI_ERR_COLLISION); ++i) {
296 rv = avahi_entry_group_add_service_strlst(group,
299 (AvahiPublishFlags)0,
307 if (rv == AVAHI_ERR_COLLISION) {
308 char *n = avahi_alternative_service_name(
name.c_str());
309 service.set_modified_name(n);
315 avahi_string_list_free(al);
318 throw Exception(
"Adding Avahi/mDNS-SD service failed: %s", avahi_strerror(rv));
330 if (avahi_entry_group_commit(group) < 0) {
331 throw Exception(
"Registering Avahi services failed");
338 AvahiThread::recreate_services()
340 for (sit_ = services_.begin(); sit_ != services_.end(); ++sit_) {
341 (*sit_).second = create_service(sit_->first, sit_->second);
346 AvahiThread::create_pending_services()
348 pending_services_.lock();
349 while (!pending_services_.empty()) {
350 NetworkService &s = pending_services_.front();
351 services_[s] = create_service(s, NULL);
352 pending_services_.pop();
354 pending_services_.unlock();
358 AvahiThread::remove_pending_services()
362 pending_remove_services_.lock();
363 while (!pending_remove_services_.empty()) {
364 NetworkService &s = pending_remove_services_.front();
365 if (services_.find(s) != services_.end()) {
366 group_erase(services_[s]);
367 services_.erase_locked(s);
369 pending_remove_services_.pop();
371 pending_remove_services_.unlock();
380 AvahiThread::group_reset(AvahiEntryGroup *g)
383 avahi_entry_group_reset(g);
389 AvahiThread::group_erase(AvahiEntryGroup *g)
392 avahi_entry_group_reset(g);
393 avahi_entry_group_free(g);
398 AvahiThread::erase_groups()
400 for (sit_ = services_.begin(); sit_ != services_.end(); ++sit_) {
402 group_erase(sit_->second);
408 AvahiThread::reset_groups()
410 for (sit_ = services_.begin(); sit_ != services_.end(); ++sit_) {
411 group_reset((*sit_).second);
417 AvahiThread::name_collision(AvahiEntryGroup *g)
419 for (sit_ = services_.begin(); sit_ != services_.end(); ++sit_) {
420 if ((*sit_).second == g) {
421 NetworkService service = sit_->first;
422 std::string
name = service.modified_name() ? service.modified_name() : service.name();
425 char *n = avahi_alternative_service_name((*sit_).first.name());
426 service.set_modified_name(n);
429 pending_remove_services_.push_locked(service);
430 pending_services_.push_locked(service);
441 AvahiThread::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state,
void *instance)
446 case AVAHI_ENTRY_GROUP_ESTABLISHED:
451 case AVAHI_ENTRY_GROUP_COLLISION: {
452 at->name_collision(g);
456 case AVAHI_ENTRY_GROUP_FAILURE:
461 case AVAHI_ENTRY_GROUP_UNCOMMITED:
462 case AVAHI_ENTRY_GROUP_REGISTERING:
break;
480 handlers_[service_type].push_back(h);
495 if (handlers_.find(service_type) != handlers_.end()) {
496 handlers_[service_type].remove(h);
497 if (handlers_[service_type].size() == 0) {
498 if (browsers_.find(service_type) != browsers_.end()) {
503 handlers_.erase(service_type);
514 AvahiThread::create_browser(
const char *service_type)
516 if (browsers_.find(service_type) == browsers_.end()) {
518 AvahiServiceBrowser *b = avahi_service_browser_new(client,
524 AvahiThread::browse_callback,
528 handlers_[service_type].pop_back();
529 throw NullPointerException(
"Could not instantiate AvahiServiceBrowser");
531 browsers_[service_type] = b;
540 AvahiThread::recreate_browsers()
542 LockMap<std::string, std::list<ServiceBrowseHandler *>>::iterator i;
543 for (i = handlers_.begin(); i != handlers_.end(); ++i) {
544 create_browser((*i).first.c_str());
549 AvahiThread::create_pending_browsers()
551 pending_browsers_.
lock();
552 while (!pending_browsers_.empty()) {
554 create_browser(pending_browsers_.front().c_str());
555 pending_browsers_.pop();
557 pending_browsers_.
unlock();
561 AvahiThread::remove_pending_browsers()
565 pending_browser_removes_.
lock();
566 while (!pending_browser_removes_.empty()) {
567 std::string &s = pending_browser_removes_.front();
568 avahi_service_browser_free(browsers_[s]);
570 pending_browser_removes_.pop();
572 pending_browser_removes_.
unlock();
578 AvahiThread::erase_browsers()
580 std::map<std::string, AvahiServiceBrowser *>::iterator i;
581 for (i = browsers_.begin(); i != browsers_.end(); ++i) {
582 avahi_service_browser_free((*i).second);
593 AvahiThread::call_handler_service_removed(
const char *name,
const char *type,
const char *domain)
595 if (handlers_.find(type) != handlers_.end()) {
596 std::list<ServiceBrowseHandler *>::iterator i;
597 for (i = handlers_[type].begin(); i != handlers_[type].end(); ++i) {
598 (*i)->service_removed(
name, type, domain);
614 AvahiThread::call_handler_service_added(
const char * name,
617 const char * host_name,
618 const AvahiIfIndex interface,
619 const AvahiAddress * address,
621 std::list<std::string> &txt,
622 AvahiLookupResultFlags flags)
624 char ifname[IF_NAMESIZE];
626 if (if_indextoname(interface, ifname) == NULL) {
627 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 if_indextoname failed");
631 struct sockaddr *s = NULL;
633 if (address->proto == AVAHI_PROTO_INET) {
636 slen =
sizeof(
struct sockaddr_in);
637 struct sockaddr_in *sin = (
struct sockaddr_in *)malloc(slen);
638 sin->sin_family = AF_INET;
639 sin->sin_addr.s_addr = address->data.ipv4.address;
640 sin->sin_port = htons(port);
641 s = (
struct sockaddr *)sin;
642 }
else if (address->proto == AVAHI_PROTO_INET6) {
645 slen =
sizeof(
struct sockaddr_in6);
646 struct sockaddr_in6 *sin = (
struct sockaddr_in6 *)malloc(slen);
647 sin->sin6_family = AF_INET6;
648 memcpy(&sin->sin6_addr, &address->data.ipv6.address,
sizeof(in6_addr));
650 char ipaddr[INET6_ADDRSTRLEN];
651 if (inet_ntop(AF_INET6, &sin->sin6_addr, ipaddr,
sizeof(ipaddr)) != NULL) {
652 std::string addr_with_scope = std::string(ipaddr) +
"%" + ifname;
656 struct addrinfo hints, *res;
657 memset(&hints, 0,
sizeof(hints));
658 hints.ai_family = AF_INET6;
659 hints.ai_flags = AI_NUMERICHOST;
660 if (getaddrinfo(addr_with_scope.c_str(), port_s.c_str(), &hints, &res) == 0) {
661 if (slen == res[0].ai_addrlen) {
662 memcpy(sin, res[0].ai_addr, slen);
666 "AvahiThread::call_handler_service_added: IPv6 address lengths different");
671 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 getaddrinfo failed");
675 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 inet_ntop failed");
678 s = (
struct sockaddr *)sin;
683 if (handlers_.find(type) != handlers_.end()) {
684 std::list<ServiceBrowseHandler *>::iterator i;
685 for (i = handlers_[type].begin(); i != handlers_[type].end(); ++i) {
687 name, type, domain, host_name, ifname, (
struct sockaddr *)s, slen, port, txt, (int)flags);
699 AvahiThread::call_handler_failed(
const char *name,
const char *type,
const char *domain)
701 if (handlers_.find(type) != handlers_.end()) {
702 std::list<ServiceBrowseHandler *>::iterator i;
703 for (i = handlers_[type].begin(); i != handlers_[type].end(); ++i) {
704 (*i)->browse_failed(
name, type, domain);
713 AvahiThread::call_handler_all_for_now(
const char *type)
715 if (handlers_.find(type) != handlers_.end()) {
716 std::list<ServiceBrowseHandler *>::iterator i;
717 for (i = handlers_[type].begin(); i != handlers_[type].end(); ++i) {
727 AvahiThread::call_handler_cache_exhausted(
const char *type)
729 if (handlers_.find(type) != handlers_.end()) {
730 std::list<ServiceBrowseHandler *>::iterator i;
731 for (i = handlers_[type].begin(); i != handlers_[type].end(); ++i) {
732 (*i)->cache_exhausted();
751 AvahiThread::browse_callback(AvahiServiceBrowser * b,
752 AvahiIfIndex interface,
753 AvahiProtocol protocol,
754 AvahiBrowserEvent event,
758 AvahiLookupResultFlags flags,
764 case AVAHI_BROWSER_FAILURE:
768 case AVAHI_BROWSER_NEW:
774 if (!(avahi_service_resolver_new(at->client,
782 AvahiThread::resolve_callback,
784 throw NullPointerException(
"Could not instantiate resolver");
788 case AVAHI_BROWSER_REMOVE:
791 at->call_handler_service_removed(
name, type, domain);
794 case AVAHI_BROWSER_ALL_FOR_NOW:
797 at->call_handler_all_for_now(type);
800 case AVAHI_BROWSER_CACHE_EXHAUSTED:
803 at->call_handler_cache_exhausted(type);
826 AvahiThread::resolve_callback(AvahiServiceResolver *r,
827 AvahiIfIndex interface,
828 AVAHI_GCC_UNUSED AvahiProtocol protocol,
829 AvahiResolverEvent event,
833 const char * host_name,
834 const AvahiAddress * address,
836 AvahiStringList * txt,
837 AvahiLookupResultFlags flags,
843 case AVAHI_RESOLVER_FAILURE:
845 at->call_handler_failed(
name, type, domain);
848 case AVAHI_RESOLVER_FOUND:
851 std::list<std::string> txts;
852 AvahiStringList * l = txt;
856 txts.push_back((
char *)avahi_string_list_get_text(l));
857 l = avahi_string_list_get_next(l);
860 at->call_handler_service_added(
861 name, type, domain, host_name, interface, address, port, txts, flags);
866 avahi_service_resolver_free(r);
882 AvahiResolverCallbackData *data =
new AvahiResolverCallbackData(
this, handler);
884 if (pending_hostname_resolves_.find(
name) == pending_hostname_resolves_.end()) {
885 pending_hostname_resolves_[
name] = data;
892 AvahiThread::start_hostname_resolver(
const char *name, AvahiResolverCallbackData *data)
894 AvahiHostNameResolver *resolver;
895 if ((resolver = avahi_host_name_resolver_new(client,
900 AVAHI_LOOKUP_USE_MULTICAST,
901 AvahiThread::host_name_resolver_callback,
904 throw Exception(
"Cannot create Avahi name resolver");
906 running_hostname_resolvers_.push_back(resolver);
911 AvahiThread::start_hostname_resolvers()
913 LockMap<std::string, AvahiResolverCallbackData *>::iterator phrit;
914 for (phrit = pending_hostname_resolves_.begin(); phrit != pending_hostname_resolves_.end();
916 start_hostname_resolver(phrit->first.c_str(), phrit->second);
918 pending_hostname_resolves_.clear();
922 AvahiThread::start_address_resolvers()
924 LockMap<struct ::sockaddr_storage *, AvahiResolverCallbackData *>::iterator parit;
926 for (parit = pending_address_resolves_.begin(); parit != pending_address_resolves_.end();
928 start_address_resolver(parit->first, parit->second);
931 pending_address_resolves_.clear();
944 AvahiResolverHandler *handler)
946 struct ::sockaddr_storage *sstor =
947 (struct ::sockaddr_storage *)malloc(
sizeof(struct ::sockaddr_storage));
948 if (addr->sa_family == AF_INET) {
949 if (addrlen !=
sizeof(sockaddr_in)) {
950 throw Exception(
"Invalid size for IPv4 address struct");
952 memcpy(sstor, addr,
sizeof(sockaddr_in));
953 }
else if (addr->sa_family == AF_INET6) {
954 if (addrlen !=
sizeof(sockaddr_in6)) {
955 throw Exception(
"Invalid size for IPv6 address struct");
957 memcpy(sstor, addr,
sizeof(sockaddr_in6));
959 throw Exception(
"Unknown address family");
961 AvahiResolverCallbackData *data =
new AvahiResolverCallbackData(
this, handler);
963 pending_address_resolves_[sstor] = data;
968 AvahiThread::start_address_resolver(
const struct sockaddr_storage *in_addr,
969 AvahiResolverCallbackData * data)
973 if (in_addr->ss_family == AF_INET) {
974 a.proto = AVAHI_PROTO_INET;
975 a.data.ipv4.address = ((sockaddr_in *)in_addr)->sin_addr.s_addr;
976 }
else if (in_addr->ss_family == AF_INET6) {
977 a.proto = AVAHI_PROTO_INET6;
978 memcpy(&a.data.ipv6.address, &((sockaddr_in6 *)in_addr)->sin6_addr,
sizeof(in6_addr));
980 throw Exception(
"Unknown address family");
983 AvahiAddressResolver *resolver;
984 if ((resolver = avahi_address_resolver_new(client,
988 AVAHI_LOOKUP_USE_MULTICAST,
989 AvahiThread::address_resolver_callback,
992 Exception e(
"Cannot create Avahi address resolver");
993 e.append(
"Avahi error: %s", avahi_strerror(avahi_client_errno(client)));
1005 AvahiThread::remove_hostname_resolver(AvahiHostNameResolver *r)
1015 AvahiThread::remove_address_resolver(AvahiAddressResolver *r)
1024 AvahiThread::host_name_resolver_callback(AvahiHostNameResolver *r,
1025 AvahiIfIndex interface,
1026 AvahiProtocol protocol,
1027 AvahiResolverEvent event,
1029 const AvahiAddress * a,
1030 AvahiLookupResultFlags flags,
1033 AvahiResolverCallbackData *cd =
static_cast<AvahiResolverCallbackData *
>(userdata);
1035 cd->first->remove_hostname_resolver(r);
1036 avahi_host_name_resolver_free(r);
1039 case AVAHI_RESOLVER_FOUND: {
1040 if (protocol == AVAHI_PROTO_INET) {
1041 struct sockaddr_in *res = (
struct sockaddr_in *)malloc(
sizeof(
struct sockaddr_in));
1042 res->sin_family = (
unsigned short)avahi_proto_to_af(protocol);
1043 res->sin_addr.s_addr = a->data.ipv4.address;
1044 cd->second->resolved_name(strdup(
name), (
struct sockaddr *)res,
sizeof(
struct sockaddr_in));
1045 }
else if (protocol == AVAHI_PROTO_INET6) {
1046 struct sockaddr_in6 *res = (
struct sockaddr_in6 *)malloc(
sizeof(
struct sockaddr_in6));
1047 res->sin6_family = (
unsigned short)avahi_proto_to_af(protocol);
1048 memcpy(&res->sin6_addr, &a->data.ipv6.address,
sizeof(in6_addr));
1049 cd->second->resolved_name(strdup(
name), (
struct sockaddr *)res,
sizeof(
struct sockaddr_in6));
1051 cd->second->name_resolution_failed(strdup(
name));
1055 case AVAHI_RESOLVER_FAILURE:
1056 default: cd->second->name_resolution_failed(strdup(
name));
break;
1066 AvahiThread::address_resolver_callback(AvahiAddressResolver * r,
1067 AvahiIfIndex interface,
1068 AvahiProtocol protocol,
1069 AvahiResolverEvent event,
1070 const AvahiAddress * a,
1072 AvahiLookupResultFlags flags,
1075 AvahiResolverCallbackData *cd =
static_cast<AvahiResolverCallbackData *
>(userdata);
1077 cd->first->remove_address_resolver(r);
1078 avahi_address_resolver_free(r);
1080 struct sockaddr *res = NULL;
1081 socklen_t res_size = 0;
1083 if (protocol == AVAHI_PROTO_INET) {
1084 res_size =
sizeof(
struct sockaddr_in);
1085 res = (
struct sockaddr *)malloc(res_size);
1086 sockaddr_in *res_4 = (
struct sockaddr_in *)res;
1087 res_4->sin_family = (
unsigned short)avahi_proto_to_af(protocol);
1088 res_4->sin_addr.s_addr = a->data.ipv4.address;
1089 }
else if (protocol == AVAHI_PROTO_INET6) {
1090 res_size =
sizeof(
struct sockaddr_in6);
1091 res = (
struct sockaddr *)malloc(res_size);
1092 sockaddr_in6 *res_6 = (
struct sockaddr_in6 *)res;
1093 res_6->sin6_family = (
unsigned short)avahi_proto_to_af(protocol);
1094 memcpy(&res_6->sin6_addr, &a->data.ipv6.address,
sizeof(in6_addr));
1098 case AVAHI_RESOLVER_FOUND: cd->second->resolved_address(res, res_size, strdup(
name));
break;
1099 case AVAHI_RESOLVER_FAILURE: cd->second->address_resolution_failed(res, res_size);
break;
1101 default: cd->second->address_resolution_failed(NULL, 0);
break;
1111 AvahiThread::init_done()