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);
688 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 address lengths different");
693 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 getaddrinfo failed");
697 fprintf(stderr,
"AvahiThread::call_handler_service_added: IPv6 inet_ntop failed");
700 s = (
struct sockaddr *)sin;
705 if ( __handlers.find(type) != __handlers.end() ) {
706 std::list<ServiceBrowseHandler *>::iterator i;
707 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
708 (*i)->service_added(name, type, domain, host_name, ifname,
709 (
struct sockaddr *)s, slen, port, txt, (
int)flags);
722 AvahiThread::call_handler_failed(
const char *name,
726 if ( __handlers.find(type) != __handlers.end() ) {
727 std::list<ServiceBrowseHandler *>::iterator i;
728 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
729 (*i)->browse_failed(name, type, domain);
739 AvahiThread::call_handler_all_for_now(
const char *type)
741 if ( __handlers.find(type) != __handlers.end() ) {
742 std::list<ServiceBrowseHandler *>::iterator i;
743 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
754 AvahiThread::call_handler_cache_exhausted(
const char *type)
756 if ( __handlers.find(type) != __handlers.end() ) {
757 std::list<ServiceBrowseHandler *>::iterator i;
758 for ( i = __handlers[type].begin(); i != __handlers[type].end(); ++i) {
759 (*i)->cache_exhausted();
779 AvahiThread::browse_callback( AvahiServiceBrowser *b,
780 AvahiIfIndex interface,
781 AvahiProtocol protocol,
782 AvahiBrowserEvent event,
786 AvahiLookupResultFlags flags,
792 case AVAHI_BROWSER_FAILURE:
796 case AVAHI_BROWSER_NEW:
802 if (!(avahi_service_resolver_new(at->client, interface, protocol,
803 name, type, domain, protocol, (AvahiLookupFlags)0,
804 AvahiThread::resolve_callback, instance))) {
809 case AVAHI_BROWSER_REMOVE:
812 at->call_handler_service_removed(name, type, domain);
815 case AVAHI_BROWSER_ALL_FOR_NOW:
818 at->call_handler_all_for_now(type);
821 case AVAHI_BROWSER_CACHE_EXHAUSTED:
824 at->call_handler_cache_exhausted(type);
849 AvahiThread::resolve_callback( AvahiServiceResolver *r,
850 AvahiIfIndex interface,
851 AVAHI_GCC_UNUSED AvahiProtocol protocol,
852 AvahiResolverEvent event,
856 const char *host_name,
857 const AvahiAddress *address,
859 AvahiStringList *txt,
860 AvahiLookupResultFlags flags,
866 case AVAHI_RESOLVER_FAILURE:
868 at->call_handler_failed(name, type, domain);
871 case AVAHI_RESOLVER_FOUND:
874 std::list< std::string > txts;
875 AvahiStringList *l = txt;
879 txts.push_back((
char *)avahi_string_list_get_text(l));
880 l = avahi_string_list_get_next( l );
883 at->call_handler_service_added(name, type, domain, host_name, interface, address, port, txts, flags);
888 avahi_service_resolver_free(r);
906 AvahiResolverCallbackData *data =
new AvahiResolverCallbackData(
this, handler);
908 if ( __pending_hostname_resolves.find(name) == __pending_hostname_resolves.end()) {
909 __pending_hostname_resolves[
name] = data;
917 AvahiThread::start_hostname_resolver(
const char *name, AvahiResolverCallbackData *data)
919 AvahiHostNameResolver *resolver;
920 if ( (resolver = avahi_host_name_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
921 name, service_protocol,
922 AVAHI_LOOKUP_USE_MULTICAST,
923 AvahiThread::host_name_resolver_callback,
925 throw Exception(
"Cannot create Avahi name resolver");
927 __running_hostname_resolvers.push_back(resolver);
934 AvahiThread::start_hostname_resolvers()
937 for (phrit = __pending_hostname_resolves.begin(); phrit != __pending_hostname_resolves.end(); ++phrit) {
938 start_hostname_resolver(phrit->first.c_str(), phrit->second);
940 __pending_hostname_resolves.clear();
945 AvahiThread::start_address_resolvers()
949 for (parit = __pending_address_resolves.begin(); parit != __pending_address_resolves.end(); ++parit) {
950 start_address_resolver(parit->first, parit->second);
953 __pending_address_resolves.clear();
968 struct ::sockaddr_storage *sstor =
969 (struct ::sockaddr_storage *)malloc(
sizeof(struct ::sockaddr_storage));
970 if (addr->sa_family == AF_INET) {
971 if (addrlen !=
sizeof(sockaddr_in)) {
972 throw Exception(
"Invalid size for IPv4 address struct");
974 memcpy(sstor, addr,
sizeof(sockaddr_in));
975 }
else if (addr->sa_family == AF_INET6) {
976 if (addrlen !=
sizeof(sockaddr_in6)) {
977 throw Exception(
"Invalid size for IPv6 address struct");
979 memcpy(sstor, addr,
sizeof(sockaddr_in6));
981 throw Exception(
"Unknown address family");
983 AvahiResolverCallbackData *data =
new AvahiResolverCallbackData(
this, handler);
985 __pending_address_resolves[sstor] = data;
991 AvahiThread::start_address_resolver(
const struct sockaddr_storage *in_addr, AvahiResolverCallbackData *data)
995 if (in_addr->ss_family == AF_INET) {
996 a.proto = AVAHI_PROTO_INET;
997 a.data.ipv4.address = ((sockaddr_in *)in_addr)->sin_addr.s_addr;
998 }
else if (in_addr->ss_family == AF_INET6) {
999 a.proto = AVAHI_PROTO_INET6;
1000 memcpy(&a.data.ipv6.address, &((sockaddr_in6 *)in_addr)->sin6_addr,
sizeof(in6_addr));
1002 throw Exception(
"Unknown address family");
1005 AvahiAddressResolver *resolver;
1006 if ( (resolver = avahi_address_resolver_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
1007 &a, AVAHI_LOOKUP_USE_MULTICAST,
1008 AvahiThread::address_resolver_callback,
1010 Exception e(
"Cannot create Avahi address resolver");
1011 e.
append(
"Avahi error: %s", avahi_strerror(avahi_client_errno(client)));
1024 AvahiThread::remove_hostname_resolver(AvahiHostNameResolver *r)
1035 AvahiThread::remove_address_resolver(AvahiAddressResolver *r)
1045 AvahiThread::host_name_resolver_callback(AvahiHostNameResolver *r,
1046 AvahiIfIndex interface,
1047 AvahiProtocol protocol,
1048 AvahiResolverEvent event,
1050 const AvahiAddress *a,
1051 AvahiLookupResultFlags flags,
1054 AvahiResolverCallbackData *cd =
static_cast<AvahiResolverCallbackData *
>(userdata);
1056 cd->first->remove_hostname_resolver(r);
1057 avahi_host_name_resolver_free(r);
1060 case AVAHI_RESOLVER_FOUND:
1062 if (protocol == AVAHI_PROTO_INET) {
1063 struct sockaddr_in *res = (
struct sockaddr_in *)malloc(
sizeof(
struct sockaddr_in));
1064 res->sin_family = (
unsigned short)avahi_proto_to_af(protocol);
1065 res->sin_addr.s_addr = a->data.ipv4.address;
1066 cd->second->resolved_name(strdup(name), (
struct sockaddr *)res,
sizeof(
struct sockaddr_in));
1067 }
else if (protocol == AVAHI_PROTO_INET6) {
1068 struct sockaddr_in6 *res = (
struct sockaddr_in6 *)malloc(
sizeof(
struct sockaddr_in6));
1069 res->sin6_family = (
unsigned short)avahi_proto_to_af(protocol);
1070 memcpy(&res->sin6_addr, &a->data.ipv6.address,
sizeof(in6_addr));
1071 cd->second->resolved_name(strdup(name), (
struct sockaddr *)res,
sizeof(
struct sockaddr_in6));
1073 cd->second->name_resolution_failed(strdup(name));
1078 case AVAHI_RESOLVER_FAILURE:
1080 cd->second->name_resolution_failed(strdup(name));
1092 AvahiThread::address_resolver_callback(AvahiAddressResolver *r,
1093 AvahiIfIndex interface,
1094 AvahiProtocol protocol,
1095 AvahiResolverEvent event,
1096 const AvahiAddress *a,
1098 AvahiLookupResultFlags flags,
1101 AvahiResolverCallbackData *cd =
static_cast<AvahiResolverCallbackData *
>(userdata);
1103 cd->first->remove_address_resolver(r);
1104 avahi_address_resolver_free(r);
1106 struct sockaddr *res = NULL;
1107 socklen_t res_size = 0;
1109 if (protocol == AVAHI_PROTO_INET) {
1110 res_size =
sizeof(
struct sockaddr_in);
1111 res = (
struct sockaddr *)malloc(res_size);
1112 sockaddr_in *res_4 = (
struct sockaddr_in *)res;
1113 res_4->sin_family = (
unsigned short)avahi_proto_to_af(protocol);
1114 res_4->sin_addr.s_addr = a->data.ipv4.address;
1115 }
else if (protocol == AVAHI_PROTO_INET6) {
1116 res_size =
sizeof(
struct sockaddr_in6);
1117 res = (
struct sockaddr *)malloc(res_size);
1118 sockaddr_in6 *res_6 = (
struct sockaddr_in6 *)res;
1119 res_6->sin6_family = (
unsigned short)avahi_proto_to_af(protocol);
1120 memcpy(&res_6->sin6_addr, &a->data.ipv6.address,
sizeof(in6_addr));
1124 case AVAHI_RESOLVER_FOUND:
1125 cd->second->resolved_address(res, res_size, strdup(name));
1127 case AVAHI_RESOLVER_FAILURE:
1128 cd->second->address_resolution_failed(res, res_size);
1132 cd->second->address_resolution_failed(NULL, 0);
1144 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.