+ AvahiSEntryGroup *group;
+ AvahiPublishFlags flags;
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+ AVAHI_LLIST_FIELDS(AvahiService, services);
static void transport_flags_from_domain(AvahiServer *s, AvahiPublishFlags *flags, const char *domain) {
*flags |= AVAHI_PUBLISH_USE_WIDE_AREA;
+static void register_service_reply(DNSServiceRef client, const DNSServiceFlags flags, DNSServiceErrorType errorCode,
+ const char *name, const char *regtype, const char *domain, void *context) {
+ AvahiService *as = context;
+ case kDNSServiceErr_NoError:
+ as->group->n_probing--;
+ if (as->group->n_probing == 0) {
+ avahi_s_entry_group_change_state(as->group, AVAHI_ENTRY_GROUP_ESTABLISHED);
+ case kDNSServiceErr_NameConflict:
+ avahi_s_entry_group_change_state(as->group, AVAHI_ENTRY_GROUP_COLLISION);
+ avahi_s_entry_group_change_state(as->group, AVAHI_ENTRY_GROUP_FAILURE);
+static void register_service_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
+ AvahiService *as = userdata;
+ DNSServiceErrorType ret;
+ assert(events & AVAHI_WATCH_IN);
+ assert (fd == DNSServiceRefSockFD(as->client));
+ ret = DNSServiceProcessResult(as->client);
+ if (ret != kDNSServiceErr_NoError) {
+ as->server->poll_api->watch_free(as->watch);
+ DNSServiceRefDeallocate(as->client);
+ avahi_s_entry_group_change_state(as->group, AVAHI_ENTRY_GROUP_FAILURE);
+static void avahi_service_free(AvahiServer*s, AvahiService *as) {
+ /* Remove from linked list */
+ AVAHI_LLIST_REMOVE(AvahiService, services, s->services, as);
+ /* Remove from associated group */
+ if (as->group && (as->group->services != NULL))
+ AVAHI_LLIST_REMOVE(AvahiService, services, as->group->services, as);
+ avahi_free(as->domain);
+ s->poll_api->watch_free(as->watch);
+ DNSServiceRefDeallocate (as->client);
+ avahi_free(as->txtrecord);
+static void avahi_register_service(AvahiServer *s, AvahiService *as) {
+ DNSServiceErrorType ret;
+ ret = DNSServiceRegister(&as->client,
+ as->interface == AVAHI_IF_UNSPEC ?
+ kDNSServiceInterfaceIndexAny :
+ register_service_reply,
+ if (ret == kDNSServiceErr_NoError) {
+ avahi_s_entry_group_change_state(as->group, AVAHI_ENTRY_GROUP_FAILURE);
+ as->group->n_probing++;
+ as->watch = s->poll_api->watch_new(s->poll_api, DNSServiceRefSockFD(as->client), AVAHI_WATCH_IN, register_service_socket_event, as);
+ if (ret == kDNSServiceErr_NameConflict) {
+ avahi_s_entry_group_change_state(as->group, AVAHI_ENTRY_GROUP_COLLISION);
+ avahi_s_entry_group_change_state(as->group, AVAHI_ENTRY_GROUP_FAILURE);
+static void register_record_reply(DNSServiceRef client, DNSRecordRef recordref, const DNSServiceFlags flags, DNSServiceErrorType errorCode, void *context) {
+ AvahiEntry *e = context;
+ DNSServiceErrorType ret;
+ case kDNSServiceErr_NoError:
+ case kDNSServiceErr_NameConflict:
+ avahi_server_set_errno(e->server, AVAHI_ERR_COLLISION);
+ avahi_server_set_errno(e->server, AVAHI_ERR_FAILURE);
void avahi_entry_free(AvahiServer*s, AvahiEntry *e) {
avahi_goodbye_entry(s, e, 1, 1);
/* Remove from linked list */
AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
avahi_entry_free(s, g->entries);
+ avahi_service_free(s, g->services);
+ if (g->record_connection) {
+ DNSServiceRefDeallocate(g->record_connection);
+ g->record_connection = NULL;
if (g->register_time_event)
avahi_time_event_free(g->register_time_event);
s->need_entry_cleanup = 0;
+ if (s->need_service_cleanup) {
+ AvahiService *as, *next;
+ for (as = s->services; as; as = next) {
+ next = as->services_next;
+ avahi_service_free(s, as);
+ s->need_service_cleanup = 0;
if (s->need_browser_cleanup)
avahi_browser_cleanup(s);
/* Hmm, nothing found? */
+ * Assume that we are updating a service's primary TXT record
+ DNSServiceErrorType ret;
+ uint8_t rdata[AVAHI_DNS_RDATA_MAX];
+ for (as = g->services; as; as = as->services_next) {
+ char svc_name[AVAHI_DOMAIN_NAME_MAX];
+ if ((a_ret = avahi_service_name_join(svc_name, sizeof(svc_name), as->name, as->type, as->domain ? as->domain : s->domain_name)) < 0) {
+ avahi_server_set_errno(s, a_ret);
+ if (!strcmp(svc_name, r->key->name)) {
+ avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
+ if ((l = avahi_rdata_serialize(r, rdata, sizeof(rdata))) == (size_t) -1) {
+ avahi_server_set_errno(s, AVAHI_ERR_FAILURE);
+ ret = DNSServiceUpdateRecord(as->client,
+ if (ret != kDNSServiceErr_NoError) {
+ avahi_server_set_errno(s, AVAHI_ERR_FAILURE);
avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
/* Announce our changes when needed */
if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) {
+ DNSServiceErrorType ret;
+ uint8_t rdata[AVAHI_DNS_RDATA_MAX];
+ if (!g->record_connection) {
+ if (DNSServiceCreateConnection(&g->record_connection) != kDNSServiceErr_NoError) {
+ avahi_entry_free(s, e);
+ avahi_server_set_errno(s, AVAHI_ERR_FAILURE);
+ if ((l = avahi_rdata_serialize(r, rdata, sizeof(rdata))) == (size_t) -1) {
+ avahi_entry_free(s, e);
+ avahi_server_set_errno(s, AVAHI_ERR_FAILURE);
+ ret = DNSServiceUpdateRecord(g->record_connection,
+ if (ret != kDNSServiceErr_NoError) {
+ avahi_entry_free(s, e);
+ avahi_server_set_errno(s, AVAHI_ERR_FAILURE);
/* Remove the old entry from all caches, if needed */
if (!(e->flags & AVAHI_PUBLISH_UNIQUE))
/* Reannounce our updated entry */
avahi_reannounce_entry(s, e);
/* If we were the first entry in the list, we need to update the key */
+ DNSServiceErrorType ret;
+ DNSServiceFlags bflags;
+ uint8_t rdata[AVAHI_DNS_RDATA_MAX];
AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
+ if (!g->record_connection) {
+ if (DNSServiceCreateConnection(&g->record_connection) != kDNSServiceErr_NoError) {
+ avahi_entry_free(s, e);
+ avahi_server_set_errno(s, AVAHI_ERR_FAILURE);
+ if (flags & AVAHI_PUBLISH_ALLOW_MULTIPLE)
+ bflags |= kDNSServiceFlagsShared;
+ bflags |= kDNSServiceFlagsUnique;
+ switch (r->key->type) {
+ case AVAHI_DNS_TYPE_AAAA:
+ record_name = avahi_strdup(r->key->name);
+ record_name = avahi_malloc(strlen(r->key->name) + strlen(s->host_name_fqdn) + 2);
+ strcpy(record_name, r->key->name);
+ strcat(record_name, ".");
+ strcat(record_name, s->host_name_fqdn);
+ if ((l = avahi_rdata_serialize(r, rdata, sizeof(rdata))) == (size_t) -1) {
+ avahi_entry_free(s, e);
+ avahi_server_set_errno(s, AVAHI_ERR_FAILURE);
+ ret = DNSServiceRegisterRecord(g->record_connection,
+ interface == AVAHI_IF_UNSPEC ?
+ kDNSServiceInterfaceIndexAny :
+ if (ret == kDNSServiceErr_NoError) {
+ ret = DNSServiceProcessResult(g->record_connection);
+ if (ret != kDNSServiceErr_NoError || e->recordref == NULL) {
+ avahi_entry_free(s, e);
+ avahi_entry_free(s, e);
+ avahi_server_set_errno(s, AVAHI_ERR_FAILURE);
+ avahi_free(record_name);
avahi_announce_entry(s, e);
AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL;
+ as = avahi_new (AvahiService, 1);
+ as->interface = interface;
+ as->protocol = protocol;
+ as->name = avahi_strdup(name);
+ as->type = avahi_strdup(type);
+ as->domain = avahi_strdup(domain);
+ as->host = avahi_strdup(host);
+ as->txtlen = avahi_string_list_serialize(strlst, NULL, 0);
+ as->txtrecord = avahi_new(uint8_t, as->txtlen);
+ if (as->txtrecord == NULL) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ avahi_string_list_serialize(strlst, as->txtrecord, as->txtlen);
+ AVAHI_LLIST_PREPEND(AvahiService, services, s->services, as);
+ AVAHI_LLIST_PREPEND(AvahiService, services, g->services, as);
host = s->host_name_fqdn;
ret = avahi_server_errno(s);
if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
+ assert(state <= AVAHI_ENTRY_GROUP_FAILURE);
assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
+ AVAHI_LLIST_HEAD_INIT(AvahiService, g->services);
+ g->record_connection = NULL;
AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
for (e = g->entries; e; e = e->by_group_next) {
avahi_goodbye_entry(g->server, e, 1, 1);
+ for (s = g->services; s; s = s->services_next) {
if (g->register_time_event) {
avahi_time_event_free(g->register_time_event);
g->server->need_group_cleanup = 1;
g->server->need_entry_cleanup = 1;
+ g->server->need_service_cleanup = 1;
schedule_cleanup(g->server);
static void entry_group_commit_real(AvahiSEntryGroup *g) {
gettimeofday(&g->register_time, NULL);
+ for (s = g->services; s; s = s->services_next)
+ avahi_register_service(g->server, s);
avahi_announce_group(g->server, g);
avahi_s_entry_group_check_probed(g, 0);
static void entry_group_register_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) {
void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
for (e = g->entries; e; e = e->by_group_next) {
avahi_goodbye_entry(g->server, e, 1, 1);
g->server->need_entry_cleanup = 1;
+ for (s = g->services; s; s = s->services_next) {
+ g->server->need_service_cleanup = 1;
avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
+ for (s = g->services; s; s = s->services_next)
/* Look for an entry that is not dead */
for (e = g->entries; e; e = e->by_group_next)