4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <sys/socket.h>
27 #include <arpa/inet.h>
29 #include <sys/utsname.h>
36 #include <avahi-common/domain.h>
37 #include <avahi-common/timeval.h>
38 #include <avahi-common/malloc.h>
39 #include <avahi-common/error.h>
48 #define AVAHI_RR_HOLDOFF_MSEC 1000
49 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
50 #define AVAHI_RR_RATE_LIMIT_COUNT 15
52 static void free_entry(AvahiServer*s, AvahiEntry *e) {
58 avahi_goodbye_entry(s, e, 1, 1);
60 /* Remove from linked list */
61 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
63 /* Remove from hash table indexed by name */
64 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
65 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
67 avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
69 avahi_hashmap_remove(s->entries_by_key, e->record->key);
71 /* Remove from associated group */
73 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
75 avahi_record_unref(e->record);
79 static void free_group(AvahiServer *s, AvahiSEntryGroup *g) {
84 free_entry(s, g->entries);
86 if (g->register_time_event)
87 avahi_time_event_free(g->register_time_event);
89 AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g);
93 static void cleanup_dead(AvahiServer *s) {
96 if (s->need_group_cleanup) {
97 AvahiSEntryGroup *g, *next;
99 for (g = s->groups; g; g = next) {
100 next = g->groups_next;
106 s->need_group_cleanup = 0;
109 if (s->need_entry_cleanup) {
110 AvahiEntry *e, *next;
112 for (e = s->entries; e; e = next) {
113 next = e->entries_next;
119 s->need_entry_cleanup = 0;
122 if (s->need_browser_cleanup)
123 avahi_browser_cleanup(s);
126 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
135 assert(type != AVAHI_DNS_TYPE_ANY);
137 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
140 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
141 if (!e->dead && avahi_entry_is_registered(s, e, i))
142 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
147 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
153 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
154 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
155 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
156 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
157 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
158 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
159 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
164 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
169 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
172 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
180 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
181 /* avahi_free(txt); */
183 if (avahi_key_is_pattern(k)) {
185 /* Handle ANY query */
187 for (e = s->entries; e; e = e->entries_next)
188 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
189 avahi_server_prepare_response(s, i, e, unicast_response, 0);
193 /* Handle all other queries */
195 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
196 if (!e->dead && avahi_entry_is_registered(s, e, i))
197 avahi_server_prepare_response(s, i, e, unicast_response, 0);
201 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
208 for (k = e->group->entries; k; k = k->by_group_next) {
210 avahi_goodbye_entry(s, k, 0, 1);
215 e->group->n_probing = 0;
217 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
219 avahi_goodbye_entry(s, e, 0, 1);
223 s->need_entry_cleanup = 1;
226 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
232 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
234 withdraw_entry(s, e);
237 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
240 int ours = 0, won = 0, lost = 0;
246 t = avahi_record_to_string(record);
248 /* avahi_log_debug("incoming_probe()"); */
250 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
257 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
262 if (avahi_entry_is_probing(s, e, i)) {
274 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
276 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
277 withdraw_rrset(s, record->key);
279 /* avahi_log_debug("Not conflicting probe"); */
285 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique, const AvahiAddress *a) {
286 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
287 AvahiEntry *e, *n, *conflicting_entry = NULL;
294 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
296 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
302 /* Check if the incoming is a goodbye record */
303 if (avahi_record_is_goodbye(record)) {
305 if (avahi_record_equal_no_ttl(e->record, record)) {
309 t = avahi_record_to_string(record);
310 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
311 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
318 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
322 if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
325 /* Either our entry or the other is intended to be unique, so let's check */
327 if (avahi_record_equal_no_ttl(e->record, record)) {
328 ours = 1; /* We have an identical record, so this is no conflict */
330 /* Check wheter there is a TTL conflict */
331 if (record->ttl <= e->record->ttl/2 &&
332 avahi_entry_is_registered(s, e, i)) {
335 t = avahi_record_to_string(record);
337 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
338 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
344 /* There's no need to check the other entries of this RRset */
349 if (avahi_entry_is_registered(s, e, i)) {
351 /* A conflict => we have to return to probe mode */
353 conflicting_entry = e;
355 } else if (avahi_entry_is_probing(s, e, i)) {
357 /* We are currently registering a matching record, but
358 * someone else already claimed it, so let's
361 withdraw_immediately = 1;
366 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
368 if (!ours && conflict) {
373 t = avahi_record_to_string(record);
375 if (withdraw_immediately) {
376 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
377 withdraw_rrset(s, record->key);
379 assert(conflicting_entry);
380 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
381 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
383 /* Local unique records are returned to probing
384 * state. Local shared records are reannounced. */
393 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
394 int *unicast_response = userdata;
398 assert(unicast_response);
400 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
403 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
407 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
410 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
414 assert(!legacy_unicast || (a && port > 0 && p));
416 if (legacy_unicast) {
417 AvahiDnsPacket *reply;
420 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
423 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
425 append_aux_records_to_list(s, i, r, 0);
427 if (avahi_dns_packet_append_record(reply, r, 0, 10))
428 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
430 char *t = avahi_record_to_string(r);
431 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
435 avahi_record_unref(r);
438 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
439 avahi_interface_send_packet_unicast(i, reply, a, port);
441 avahi_dns_packet_free(reply);
444 int unicast_response, flush_cache, auxiliary;
445 AvahiDnsPacket *reply = NULL;
448 /* In case the query packet was truncated never respond
449 immediately, because known answer suppression records might be
450 contained in later packets */
451 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
453 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
455 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
457 append_aux_records_to_list(s, i, r, unicast_response);
459 /* Due to some reasons the record has not been scheduled.
460 * The client requested an unicast response in that
461 * case. Therefore we prepare such a response */
468 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
472 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
474 /* Appending this record succeeded, so incremeant
475 * the specific header field, and return to the caller */
477 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
482 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
485 /* The record is too large for one packet, so create a larger packet */
487 avahi_dns_packet_free(reply);
488 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
489 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
490 size = AVAHI_DNS_PACKET_MAX_SIZE;
492 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
495 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
497 avahi_dns_packet_free(reply);
498 t = avahi_record_to_string(r);
499 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
503 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
506 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
507 avahi_interface_send_packet_unicast(i, reply, a, port);
508 avahi_dns_packet_free(reply);
513 avahi_record_unref(r);
517 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
518 avahi_interface_send_packet_unicast(i, reply, a, port);
519 avahi_dns_packet_free(reply);
523 avahi_record_list_flush(s->record_list);
527 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
534 if (!s->config.enable_reflector)
537 for (j = s->monitor->interfaces; j; j = j->interface_next)
538 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
539 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
542 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
543 AvahiServer *s = userdata;
550 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
554 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
561 if (!s->config.enable_reflector)
564 for (j = s->monitor->interfaces; j; j = j->interface_next)
565 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
566 /* Post the query to other networks */
567 avahi_interface_post_query(j, k, 1);
569 /* Reply from caches of other network. This is needed to
570 * "work around" known answer suppression. */
572 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
576 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
583 if (!s->config.enable_reflector)
586 for (j = s->monitor->interfaces; j; j = j->interface_next)
587 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
588 avahi_interface_post_probe(j, r, 1);
591 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
600 /* avahi_log_debug("query"); */
602 assert(avahi_record_list_is_empty(s->record_list));
604 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
606 /* Handle the questions */
607 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
609 int unicast_response = 0;
611 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
612 avahi_log_warn("Packet too short (1)");
616 if (!legacy_unicast && !from_local_iface) {
617 reflect_query(s, i, key);
618 avahi_cache_start_poof(i->cache, key, a);
621 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
622 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
623 /* Allow our own queries to be suppressed by incoming
624 * queries only when they do not include known answers */
625 avahi_query_scheduler_incoming(i->query_scheduler, key);
627 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
628 avahi_key_unref(key);
631 if (!legacy_unicast) {
633 /* Known Answer Suppression */
634 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
638 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
639 avahi_log_warn("Packet too short (2)");
643 if (handle_conflict(s, i, record, unique, a)) {
644 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
645 avahi_record_list_drop(s->record_list, record);
646 avahi_cache_stop_poof(i->cache, record, a);
649 avahi_record_unref(record);
653 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
657 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
658 avahi_log_warn("Packet too short (3)");
662 if (!avahi_key_is_pattern(record->key)) {
663 if (!from_local_iface)
664 reflect_probe(s, i, record);
665 incoming_probe(s, record, i);
668 avahi_record_unref(record);
672 if (!avahi_record_list_is_empty(s->record_list))
673 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
678 avahi_record_list_flush(s->record_list);
681 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
689 /* avahi_log_debug("response"); */
691 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
692 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
697 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
698 avahi_log_warn("Packet too short (4)");
702 if (!avahi_key_is_pattern(record->key)) {
704 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
705 /* avahi_free(txt); */
707 if (handle_conflict(s, i, record, cache_flush, a)) {
708 if (!from_local_iface)
709 reflect_response(s, i, record, cache_flush);
710 avahi_cache_update(i->cache, record, cache_flush, a);
711 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
715 avahi_record_unref(record);
718 /* If the incoming response contained a conflicting record, some
719 records have been scheduling for sending. We need to flush them
721 if (!avahi_record_list_is_empty(s->record_list))
722 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
725 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
726 unsigned n, idx = (unsigned) -1;
727 AvahiLegacyUnicastReflectSlot *slot;
731 if (!s->legacy_unicast_reflect_slots)
732 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
734 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
735 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
737 if (!s->legacy_unicast_reflect_slots[idx])
741 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
744 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
745 return NULL; /* OOM */
747 s->legacy_unicast_reflect_slots[idx] = slot;
748 slot->id = s->legacy_unicast_reflect_id++;
754 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
760 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
762 assert(s->legacy_unicast_reflect_slots[idx] == slot);
764 avahi_time_event_free(slot->time_event);
767 s->legacy_unicast_reflect_slots[idx] = NULL;
770 static void free_slots(AvahiServer *s) {
774 if (!s->legacy_unicast_reflect_slots)
777 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
778 if (s->legacy_unicast_reflect_slots[idx])
779 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
781 avahi_free(s->legacy_unicast_reflect_slots);
782 s->legacy_unicast_reflect_slots = NULL;
785 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
790 if (!s->legacy_unicast_reflect_slots)
793 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
795 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
798 return s->legacy_unicast_reflect_slots[idx];
801 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
802 AvahiLegacyUnicastReflectSlot *slot = userdata;
806 assert(slot->time_event == e);
808 deallocate_slot(slot->server, slot);
811 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
812 AvahiLegacyUnicastReflectSlot *slot;
820 assert(i->protocol == a->proto);
822 if (!s->config.enable_reflector)
825 /* avahi_log_debug("legacy unicast reflector"); */
827 /* Reflecting legacy unicast queries is a little more complicated
828 than reflecting normal queries, since we must route the
829 responses back to the right client. Therefore we must store
830 some information for finding the right client contact data for
831 response packets. In contrast to normal queries legacy
832 unicast query and response packets are reflected untouched and
833 are not reassembled into larger packets */
835 if (!(slot = allocate_slot(s))) {
836 /* No slot available, we drop this legacy unicast query */
837 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
841 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
844 slot->interface = i->hardware->index;
846 avahi_elapse_time(&slot->elapse_time, 2000, 0);
847 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
849 /* Patch the packet with our new locally generatedt id */
850 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
852 for (j = s->monitor->interfaces; j; j = j->interface_next)
853 if (avahi_interface_is_relevant(j) &&
855 (s->config.reflect_ipv || j->protocol == i->protocol)) {
857 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
858 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
859 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
860 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
864 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
867 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
872 if (!s->config.enable_reflector)
875 avahi_address_from_sockaddr(sa, &a);
877 if (!avahi_address_is_local(s->monitor, &a))
880 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
881 struct sockaddr_in lsa;
882 socklen_t l = sizeof(lsa);
884 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
885 avahi_log_warn("getsockname(): %s", strerror(errno));
887 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
891 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
892 struct sockaddr_in6 lsa;
893 socklen_t l = sizeof(lsa);
895 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
896 avahi_log_warn("getsockname(): %s", strerror(errno));
898 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
904 static int is_mdns_mcast_address(const AvahiAddress *a) {
908 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
909 return avahi_address_cmp(a, &b) == 0;
912 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
914 assert(iface != AVAHI_IF_UNSPEC);
917 /* If it isn't the MDNS port it can't be generated by us */
918 if (port != AVAHI_MDNS_PORT)
921 return avahi_interface_has_address(s->monitor, iface, a);
924 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
928 int from_local_iface = 0;
936 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
937 !avahi_interface_is_relevant(i)) {
938 avahi_log_warn("Recieved packet from invalid interface.");
942 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
944 port = avahi_port_from_sockaddr(sa);
945 avahi_address_from_sockaddr(sa, &a);
947 if (avahi_address_is_ipv4_in_ipv6(&a))
948 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
951 if (originates_from_local_legacy_unicast_socket(s, sa))
952 /* This originates from our local reflector, so let's ignore it */
955 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
956 if (s->config.enable_reflector)
957 from_local_iface = originates_from_local_iface(s, iface, &a, port);
959 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
960 avahi_log_warn("Recieved invalid packet.");
964 if (avahi_dns_packet_is_query(p)) {
965 int legacy_unicast = 0;
967 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
968 avahi_log_warn("Invalid query packet.");
972 if (port != AVAHI_MDNS_PORT) {
975 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
976 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
977 avahi_log_warn("Invalid legacy unicast query packet.");
985 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
987 handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
989 /* avahi_log_debug("Handled query"); */
991 if (port != AVAHI_MDNS_PORT) {
992 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
996 if (ttl != 255 && s->config.check_response_ttl) {
997 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
1001 if (!is_mdns_mcast_address(dest) &&
1002 !avahi_interface_address_on_link(i, &a)) {
1003 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
1007 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
1008 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
1009 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
1010 avahi_log_warn("Invalid response packet.");
1014 handle_response_packet(s, p, i, &a, from_local_iface);
1015 /* avahi_log_debug("Handled response"); */
1019 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface) {
1020 AvahiInterface *i, *j;
1022 AvahiLegacyUnicastReflectSlot *slot;
1029 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
1030 !avahi_interface_is_relevant(i)) {
1031 avahi_log_warn("Recieved packet from invalid interface.");
1035 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
1037 avahi_address_from_sockaddr(sa, &a);
1039 if (avahi_address_is_ipv4_in_ipv6(&a))
1040 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
1043 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
1044 avahi_log_warn("Recieved invalid packet.");
1048 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1049 avahi_log_warn("Recieved legacy unicast response with unknown id");
1053 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1054 !avahi_interface_is_relevant(j))
1057 /* Patch the original ID into this response */
1058 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1060 /* Forward the response to the correct client */
1061 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1063 /* Undo changes to packet */
1064 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1067 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1068 AvahiServer *s = userdata;
1073 struct sockaddr_in sa;
1074 struct sockaddr_in6 sa6;
1079 if (events & AVAHI_WATCH_IN) {
1081 if (fd == s->fd_ipv4) {
1082 dest.proto = AVAHI_PROTO_INET;
1083 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1084 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1085 avahi_dns_packet_free(p);
1087 } else if (fd == s->fd_ipv6) {
1088 dest.proto = AVAHI_PROTO_INET6;
1090 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1091 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1092 avahi_dns_packet_free(p);
1094 } else if (fd == s->fd_legacy_unicast_ipv4) {
1095 dest.proto = AVAHI_PROTO_INET;
1097 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1098 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface);
1099 avahi_dns_packet_free(p);
1101 } else if (fd == s->fd_legacy_unicast_ipv6) {
1102 dest.proto = AVAHI_PROTO_INET6;
1104 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1105 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface);
1106 avahi_dns_packet_free(p);
1115 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1118 if (s->state == state)
1124 s->callback(s, state, s->userdata);
1127 static void withdraw_host_rrs(AvahiServer *s) {
1130 if (s->hinfo_entry_group)
1131 avahi_s_entry_group_reset(s->hinfo_entry_group);
1133 if (s->browse_domain_entry_group)
1134 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1136 avahi_interface_monitor_update_rrs(s->monitor, 1);
1137 s->n_host_rr_pending = 0;
1140 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1143 assert(s->n_host_rr_pending > 0);
1145 if (--s->n_host_rr_pending == 0)
1146 server_set_state(s, AVAHI_SERVER_RUNNING);
1149 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1152 s->n_host_rr_pending ++;
1155 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1159 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1160 s->state == AVAHI_SERVER_REGISTERING)
1161 avahi_server_increase_host_rr_pending(s);
1163 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1164 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1165 withdraw_host_rrs(s);
1166 server_set_state(s, AVAHI_SERVER_COLLISION);
1168 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1169 s->state == AVAHI_SERVER_REGISTERING)
1170 avahi_server_decrease_host_rr_pending(s);
1173 static void register_hinfo(AvahiServer *s) {
1174 struct utsname utsname;
1179 if (!s->config.publish_hinfo)
1182 if (s->hinfo_entry_group)
1183 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1185 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1187 if (!s->hinfo_entry_group) {
1188 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1192 /* Fill in HINFO rr */
1193 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1195 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1196 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1198 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1199 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1203 avahi_record_unref(r);
1206 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1207 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1211 static void register_localhost(AvahiServer *s) {
1215 /* Add localhost entries */
1216 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1217 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1219 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1220 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1223 static void register_browse_domain(AvahiServer *s) {
1226 if (!s->config.publish_domain)
1229 if (s->browse_domain_entry_group)
1230 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1232 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1234 if (!s->browse_domain_entry_group) {
1235 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1239 if (avahi_server_add_ptr(s, s->browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, AVAHI_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name) < 0) {
1240 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1244 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1245 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1248 static void register_stuff(AvahiServer *s) {
1251 server_set_state(s, AVAHI_SERVER_REGISTERING);
1252 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1255 register_browse_domain(s);
1256 avahi_interface_monitor_update_rrs(s->monitor, 0);
1258 s->n_host_rr_pending --;
1260 if (s->n_host_rr_pending == 0)
1261 server_set_state(s, AVAHI_SERVER_RUNNING);
1264 static void update_fqdn(AvahiServer *s) {
1268 assert(s->host_name);
1269 assert(s->domain_name);
1271 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1274 avahi_free(s->host_name_fqdn);
1275 s->host_name_fqdn = n;
1278 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1282 if (host_name && !avahi_is_valid_host_name(host_name))
1283 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1285 withdraw_host_rrs(s);
1287 avahi_free(s->host_name);
1288 s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
1289 s->host_name[strcspn(s->host_name, ".")] = 0;
1296 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1298 assert(domain_name);
1300 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1301 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1303 withdraw_host_rrs(s);
1305 avahi_free(s->domain_name);
1306 s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
1313 static int valid_server_config(const AvahiServerConfig *sc) {
1315 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1316 return AVAHI_ERR_INVALID_HOST_NAME;
1318 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1319 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1324 static int setup_sockets(AvahiServer *s) {
1327 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1328 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1330 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1331 return AVAHI_ERR_NO_NETWORK;
1333 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1334 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1335 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1336 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1338 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1339 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1343 s->watch_legacy_unicast_ipv4 =
1344 s->watch_legacy_unicast_ipv6 = NULL;
1346 if (s->fd_ipv4 >= 0)
1347 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1348 if (s->fd_ipv6 >= 0)
1349 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1351 if (s->fd_legacy_unicast_ipv4 >= 0)
1352 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1353 if (s->fd_legacy_unicast_ipv6 >= 0)
1354 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1359 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1363 if (sc && (e = valid_server_config(sc)) < 0) {
1369 if (!(s = avahi_new(AvahiServer, 1))) {
1371 *error = AVAHI_ERR_NO_MEMORY;
1376 s->poll_api = poll_api;
1379 avahi_server_config_copy(&s->config, sc);
1381 avahi_server_config_init(&s->config);
1383 if ((e = setup_sockets(s)) < 0) {
1387 avahi_server_config_free(&s->config);
1394 s->n_host_rr_pending = 0;
1395 s->need_entry_cleanup = 0;
1396 s->need_group_cleanup = 0;
1397 s->need_browser_cleanup = 0;
1399 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1401 s->callback = callback;
1402 s->userdata = userdata;
1404 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1405 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1406 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1408 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1409 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1410 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1411 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1412 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1413 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1414 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1415 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1416 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1418 s->legacy_unicast_reflect_slots = NULL;
1419 s->legacy_unicast_reflect_id = 0;
1421 if (s->config.enable_wide_area) {
1422 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1423 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1425 s->wide_area_lookup_engine = NULL;
1427 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1430 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1431 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1434 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1435 s->host_name[strcspn(s->host_name, ".")] = 0;
1436 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1437 s->host_name_fqdn = NULL;
1440 s->record_list = avahi_record_list_new();
1442 s->state = AVAHI_SERVER_INVALID;
1444 s->monitor = avahi_interface_monitor_new(s);
1445 avahi_interface_monitor_sync(s->monitor);
1447 register_localhost(s);
1449 s->hinfo_entry_group = NULL;
1450 s->browse_domain_entry_group = NULL;
1453 s->error = AVAHI_OK;
1458 void avahi_server_free(AvahiServer* s) {
1461 /* Remove all browsers */
1463 while (s->dns_server_browsers)
1464 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1465 while (s->host_name_resolvers)
1466 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1467 while (s->address_resolvers)
1468 avahi_s_address_resolver_free(s->address_resolvers);
1469 while (s->domain_browsers)
1470 avahi_s_domain_browser_free(s->domain_browsers);
1471 while (s->service_type_browsers)
1472 avahi_s_service_type_browser_free(s->service_type_browsers);
1473 while (s->service_browsers)
1474 avahi_s_service_browser_free(s->service_browsers);
1475 while (s->service_resolvers)
1476 avahi_s_service_resolver_free(s->service_resolvers);
1477 while (s->record_browsers)
1478 avahi_s_record_browser_destroy(s->record_browsers);
1480 /* Remove all locally rgeistered stuff */
1483 free_entry(s, s->entries);
1485 avahi_interface_monitor_free(s->monitor);
1488 free_group(s, s->groups);
1492 avahi_hashmap_free(s->entries_by_key);
1493 avahi_record_list_free(s->record_list);
1494 avahi_hashmap_free(s->record_browser_hashmap);
1496 if (s->wide_area_lookup_engine)
1497 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1498 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1500 avahi_time_event_queue_free(s->time_event_queue);
1505 s->poll_api->watch_free(s->watch_ipv4);
1507 s->poll_api->watch_free(s->watch_ipv6);
1509 if (s->watch_legacy_unicast_ipv4)
1510 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1511 if (s->watch_legacy_unicast_ipv6)
1512 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1516 if (s->fd_ipv4 >= 0)
1518 if (s->fd_ipv6 >= 0)
1521 if (s->fd_legacy_unicast_ipv4 >= 0)
1522 close(s->fd_legacy_unicast_ipv4);
1523 if (s->fd_legacy_unicast_ipv6 >= 0)
1524 close(s->fd_legacy_unicast_ipv6);
1526 /* Free other stuff */
1528 avahi_free(s->host_name);
1529 avahi_free(s->domain_name);
1530 avahi_free(s->host_name_fqdn);
1532 avahi_server_config_free(&s->config);
1537 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags) {
1543 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1547 if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE))
1550 if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) )
1553 if ((interface <= 0 ||
1554 e->interface <= 0 ||
1555 e->interface == interface) &&
1556 (protocol == AVAHI_PROTO_UNSPEC ||
1557 e->protocol == AVAHI_PROTO_UNSPEC ||
1558 e->protocol == protocol))
1566 int avahi_server_add(
1568 AvahiSEntryGroup *g,
1569 AvahiIfIndex interface,
1570 AvahiProtocol protocol,
1571 AvahiPublishFlags flags,
1577 if (!AVAHI_IF_VALID(interface))
1578 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
1580 if (!AVAHI_PROTO_VALID(protocol))
1581 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
1583 if (!AVAHI_FLAGS_VALID(
1585 AVAHI_PUBLISH_NO_ANNOUNCE|
1586 AVAHI_PUBLISH_NO_PROBE|
1587 AVAHI_PUBLISH_UNIQUE|
1588 AVAHI_PUBLISH_ALLOW_MULTIPLE|
1589 AVAHI_PUBLISH_IS_PROXY|
1590 AVAHI_PUBLISH_UPDATE))
1591 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
1593 if (!avahi_is_valid_domain_name(r->key->name))
1594 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1597 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1599 if (avahi_key_is_pattern(r->key))
1600 return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1602 if (!avahi_record_is_valid(r))
1603 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1605 if (flags & AVAHI_PUBLISH_UPDATE) {
1607 AvahiRecord *old_record;
1610 /* Update and existing record */
1612 /* Find the first matching entry */
1613 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1614 if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol)
1620 /* Hmm, nothing found? */
1622 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1624 /* Update the entry */
1625 old_record = e->record;
1626 e->record = avahi_record_ref(r);
1629 /* Announce our changes when needed */
1630 if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) {
1632 /* Remove the old entry from all caches, if needed */
1633 if (!(e->flags & AVAHI_PUBLISH_UNIQUE))
1634 avahi_goodbye_entry(s, e, 1, 0);
1636 /* Reannounce our updated entry */
1637 avahi_reannounce_entry(s, e);
1640 /* If we were the first entry in the list, we need to update the key */
1642 avahi_hashmap_replace(s->entries_by_key, e->record->key, e);
1644 avahi_record_unref(old_record);
1649 /* Add a new record */
1651 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1652 return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1654 if (!(e = avahi_new(AvahiEntry, 1)))
1655 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1658 e->record = avahi_record_ref(r);
1660 e->interface = interface;
1661 e->protocol = protocol;
1665 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers);
1667 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1669 /* Insert into hash table indexed by name */
1670 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1671 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1672 avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1674 /* Insert into group list */
1676 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1678 avahi_announce_entry(s, e);
1684 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1685 AvahiEntry **e = (AvahiEntry**) state;
1690 *e = g ? g->entries : s->entries;
1692 while (*e && (*e)->dead)
1693 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1698 return avahi_record_ref((*e)->record);
1701 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1707 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1709 for (e = s->entries; e; e = e->entries_next) {
1716 if (!(t = avahi_record_to_string(e->record)))
1717 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1719 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1722 callback(ln, userdata);
1725 avahi_dump_caches(s->monitor, callback, userdata);
1727 if (s->wide_area_lookup_engine)
1728 avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata);
1732 int avahi_server_add_ptr(
1734 AvahiSEntryGroup *g,
1735 AvahiIfIndex interface,
1736 AvahiProtocol protocol,
1737 AvahiPublishFlags flags,
1748 if ((name && !avahi_is_valid_domain_name(name)) || !avahi_is_valid_domain_name(dest))
1749 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1752 name = s->host_name_fqdn;
1754 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1755 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1757 r->data.ptr.name = avahi_normalize_name_strdup(dest);
1758 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1759 avahi_record_unref(r);
1763 int avahi_server_add_address(
1765 AvahiSEntryGroup *g,
1766 AvahiIfIndex interface,
1767 AvahiProtocol protocol,
1768 AvahiPublishFlags flags,
1778 if (!AVAHI_IF_VALID(interface))
1779 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
1781 if (!AVAHI_PROTO_VALID(protocol) || !AVAHI_PROTO_VALID(a->proto))
1782 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
1784 if (!AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_REVERSE|AVAHI_PUBLISH_NO_ANNOUNCE|AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_UPDATE))
1785 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
1787 if (name && !avahi_is_valid_domain_name(name))
1788 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1791 name = s->host_name_fqdn;
1793 if (!(n = avahi_normalize_name_strdup(name)))
1794 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1799 if (a->proto == AVAHI_PROTO_INET) {
1802 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1803 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1807 r->data.a.address = a->data.ipv4;
1808 ret = avahi_server_add(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
1809 avahi_record_unref(r);
1814 if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
1817 if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1818 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1822 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1823 avahi_free(reverse);
1829 assert(a->proto == AVAHI_PROTO_INET6);
1831 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1832 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1836 r->data.aaaa.address = a->data.ipv6;
1837 ret = avahi_server_add(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
1838 avahi_record_unref(r);
1843 if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
1846 if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
1847 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1851 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1852 avahi_free(reverse);
1857 if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1858 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1862 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1863 avahi_free(reverse);
1874 static int server_add_txt_strlst_nocopy(
1876 AvahiSEntryGroup *g,
1877 AvahiIfIndex interface,
1878 AvahiProtocol protocol,
1879 AvahiPublishFlags flags,
1882 AvahiStringList *strlst) {
1889 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) {
1890 avahi_string_list_free(strlst);
1891 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1894 r->data.txt.string_list = strlst;
1895 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1896 avahi_record_unref(r);
1901 int avahi_server_add_txt_strlst(
1903 AvahiSEntryGroup *g,
1904 AvahiIfIndex interface,
1905 AvahiProtocol protocol,
1906 AvahiPublishFlags flags,
1909 AvahiStringList *strlst) {
1913 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1916 int avahi_server_add_txt_va(
1918 AvahiSEntryGroup *g,
1919 AvahiIfIndex interface,
1920 AvahiProtocol protocol,
1921 AvahiPublishFlags flags,
1928 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1931 int avahi_server_add_txt(
1933 AvahiSEntryGroup *g,
1934 AvahiIfIndex interface,
1935 AvahiProtocol protocol,
1936 AvahiPublishFlags flags,
1947 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1953 static AvahiStringList *add_magic_cookie(
1955 AvahiStringList *strlst) {
1959 if (!s->config.add_service_cookie)
1962 if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE))
1963 /* This string list already contains a magic cookie */
1966 return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie);
1969 static int server_add_service_strlst_nocopy(
1971 AvahiSEntryGroup *g,
1972 AvahiIfIndex interface,
1973 AvahiProtocol protocol,
1974 AvahiPublishFlags flags,
1980 AvahiStringList *strlst) {
1982 char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL;
1983 AvahiRecord *r = NULL;
1990 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1991 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1992 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_NO_COOKIE|AVAHI_PUBLISH_IS_PROXY|AVAHI_PUBLISH_UPDATE), AVAHI_ERR_INVALID_FLAGS);
1993 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1994 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1995 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1996 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_domain_name(host), AVAHI_ERR_INVALID_HOST_NAME);
1999 domain = s->domain_name;
2002 host = s->host_name_fqdn;
2004 if (!(h = avahi_normalize_name_strdup(host))) {
2005 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2009 if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
2010 (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 ||
2011 (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) {
2012 avahi_server_set_errno(s, ret);
2016 /* Add service enumeration PTR record */
2018 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, flags & AVAHI_PUBLISH_IS_PROXY, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
2021 /* Add SRV record */
2023 if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2024 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2028 r->data.srv.priority = 0;
2029 r->data.srv.weight = 0;
2030 r->data.srv.port = port;
2031 r->data.srv.name = h;
2033 ret = avahi_server_add(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY) | AVAHI_PUBLISH_UNIQUE, r);
2034 avahi_record_unref(r);
2039 /* Add TXT record */
2041 if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
2042 strlst = add_magic_cookie(s, strlst);
2044 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY) | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
2050 /* Add service type enumeration record */
2052 ret = avahi_server_add_ptr(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY), AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
2056 avahi_string_list_free(strlst);
2062 int avahi_server_add_service_strlst(
2064 AvahiSEntryGroup *g,
2065 AvahiIfIndex interface,
2066 AvahiProtocol protocol,
2067 AvahiPublishFlags flags,
2073 AvahiStringList *strlst) {
2079 return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst));
2082 int avahi_server_add_service_va(
2084 AvahiSEntryGroup *g,
2085 AvahiIfIndex interface,
2086 AvahiProtocol protocol,
2087 AvahiPublishFlags flags,
2099 return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va));
2102 int avahi_server_add_service(
2104 AvahiSEntryGroup *g,
2105 AvahiIfIndex interface,
2106 AvahiProtocol protocol,
2107 AvahiPublishFlags flags,
2119 ret = avahi_server_add_service_va(s, g, interface, protocol, flags, name, type, domain, host, port, va);
2125 static int server_update_service_txt_strlst_nocopy(
2127 AvahiSEntryGroup *g,
2128 AvahiIfIndex interface,
2129 AvahiProtocol protocol,
2130 AvahiPublishFlags flags,
2134 AvahiStringList *strlst) {
2136 char svc_name[AVAHI_DOMAIN_NAME_MAX];
2143 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
2144 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
2145 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_IS_PROXY|AVAHI_PUBLISH_NO_COOKIE), AVAHI_ERR_INVALID_FLAGS);
2146 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
2147 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
2148 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
2151 domain = s->domain_name;
2153 if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) {
2154 avahi_server_set_errno(s, ret);
2158 /* Add TXT record */
2159 if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
2160 strlst = add_magic_cookie(s, strlst);
2162 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, (flags & AVAHI_PUBLISH_IS_PROXY) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst);
2167 avahi_string_list_free(strlst);
2172 int avahi_server_update_service_txt_strlst(
2174 AvahiSEntryGroup *g,
2175 AvahiIfIndex interface,
2176 AvahiProtocol protocol,
2177 AvahiPublishFlags flags,
2181 AvahiStringList *strlst) {
2183 return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst));
2186 /** Update the TXT record for a service with the NULL terminated list of strings of the va_list. */
2187 int avahi_server_update_service_txt_va(
2189 AvahiSEntryGroup *g,
2190 AvahiIfIndex interface,
2191 AvahiProtocol protocol,
2192 AvahiPublishFlags flags,
2198 return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va));
2201 /** Update the TXT record for a service with the NULL termonate list of strings */
2202 int avahi_server_update_service_txt(
2204 AvahiSEntryGroup *g,
2205 AvahiIfIndex interface,
2206 AvahiProtocol protocol,
2207 AvahiPublishFlags flags,
2216 va_start(va, domain);
2217 ret = avahi_server_update_service_txt_va(s, g, interface, protocol, flags, name, type, domain, va);
2223 int avahi_server_add_service_subtype(
2225 AvahiSEntryGroup *g,
2226 AvahiIfIndex interface,
2227 AvahiProtocol protocol,
2228 AvahiPublishFlags flags,
2232 const char *subtype) {
2235 char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX];
2241 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
2242 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
2243 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, 0), AVAHI_ERR_INVALID_FLAGS);
2244 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
2245 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
2246 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
2247 AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE);
2250 domain = s->domain_name;
2252 if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
2253 (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) {
2254 avahi_server_set_errno(s, ret);
2258 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, flags & AVAHI_PUBLISH_IS_PROXY, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
2266 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
2267 static const char hex[] = "0123456789abcdef";
2269 const uint8_t *k = p;
2271 while (sl > 1 && pl > 0) {
2272 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
2288 int avahi_server_add_dns_server_address(
2290 AvahiSEntryGroup *g,
2291 AvahiIfIndex interface,
2292 AvahiProtocol protocol,
2293 AvahiPublishFlags flags,
2295 AvahiDNSServerType type,
2296 const AvahiAddress *address,
2297 uint16_t port /** should be 53 */) {
2306 if (!AVAHI_IF_VALID(interface))
2307 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
2309 if (!AVAHI_PROTO_VALID(protocol) || !AVAHI_PROTO_VALID(address->proto))
2310 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
2312 if (!AVAHI_FLAGS_VALID(flags, 0) || (type != AVAHI_DNS_SERVER_UPDATE && type != AVAHI_DNS_SERVER_RESOLVE))
2313 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
2316 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2318 if (domain && !avahi_is_valid_domain_name(domain))
2319 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2322 domain = s->domain_name;
2324 if (address->proto == AVAHI_PROTO_INET) {
2325 hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address));
2326 snprintf(n, sizeof(n), "ip-%s.%s", h, domain);
2327 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2328 r->data.a.address = address->data.ipv4;
2330 hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address));
2331 snprintf(n, sizeof(n), "ip6-%s.%s", h, domain);
2332 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2333 r->data.aaaa.address = address->data.ipv6;
2337 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2339 ret = avahi_server_add(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
2340 avahi_record_unref(r);
2345 return avahi_server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port);
2348 int avahi_server_add_dns_server_name(
2350 AvahiSEntryGroup *g,
2351 AvahiIfIndex interface,
2352 AvahiProtocol protocol,
2353 AvahiPublishFlags flags,
2355 AvahiDNSServerType type,
2357 uint16_t port /** should be 53 */) {
2360 char t[256], *d = NULL, *n = NULL;
2366 if (!AVAHI_IF_VALID(interface))
2367 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
2369 if (!AVAHI_PROTO_VALID(protocol))
2370 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
2372 if (!AVAHI_FLAGS_VALID(flags, 0) || (type != AVAHI_DNS_SERVER_UPDATE && type != AVAHI_DNS_SERVER_RESOLVE))
2373 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_FLAGS);
2376 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2378 if (!avahi_is_valid_domain_name(name))
2379 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2381 if (domain && !avahi_is_valid_domain_name(domain))
2382 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2385 domain = s->domain_name;
2387 if (!(n = avahi_normalize_name_strdup(name)) ||
2388 !(d = avahi_normalize_name_strdup(domain))) {
2391 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2394 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2397 if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2399 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2402 r->data.srv.priority = 0;
2403 r->data.srv.weight = 0;
2404 r->data.srv.port = port;
2405 r->data.srv.name = n;
2406 ret = avahi_server_add(s, g, interface, protocol, AVAHI_PUBLISH_NULL, r);
2407 avahi_record_unref(r);
2412 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2413 AvahiKey *k = userdata;
2419 avahi_interface_post_query(i, k, 0);
2422 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2426 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2429 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2432 if (g->state == state)
2435 assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2437 if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
2439 /* If the entry group was established for a time longer then
2440 * 5s, reset the establishment trial counter */
2442 if (avahi_age(&g->established_at) > 5000000)
2443 g->n_register_try = 0;
2446 if (state == AVAHI_ENTRY_GROUP_ESTABLISHED)
2448 /* If the entry group is now established, remember the time
2451 gettimeofday(&g->established_at, NULL);
2456 g->callback(g->server, g, state, g->userdata);
2459 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2460 AvahiSEntryGroup *g;
2464 if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2465 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2470 g->callback = callback;
2471 g->userdata = userdata;
2473 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2475 g->n_register_try = 0;
2476 g->register_time_event = NULL;
2477 g->register_time.tv_sec = 0;
2478 g->register_time.tv_usec = 0;
2479 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2481 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2485 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2491 for (e = g->entries; e; e = e->by_group_next) {
2493 avahi_goodbye_entry(g->server, e, 1, 1);
2498 if (g->register_time_event) {
2499 avahi_time_event_free(g->register_time_event);
2500 g->register_time_event = NULL;
2505 g->server->need_group_cleanup = 1;
2506 g->server->need_entry_cleanup = 1;
2509 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2512 gettimeofday(&g->register_time, NULL);
2514 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2517 avahi_announce_group(g->server, g);
2518 avahi_s_entry_group_check_probed(g, 0);
2522 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2523 AvahiSEntryGroup *g = userdata;
2526 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2528 avahi_time_event_free(g->register_time_event);
2529 g->register_time_event = NULL;
2531 /* Holdoff time passed, so let's start probing */
2532 entry_group_commit_real(g);
2535 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2541 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2542 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2544 g->n_register_try++;
2546 avahi_timeval_add(&g->register_time,
2547 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2548 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2549 AVAHI_RR_HOLDOFF_MSEC));
2551 gettimeofday(&now, NULL);
2553 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2554 /* Holdoff time passed, so let's start probing */
2555 /* avahi_log_debug("Holdoff passed, directly going on."); */
2557 entry_group_commit_real(g);
2559 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2561 /* Holdoff time has not yet passed, so let's wait */
2562 assert(!g->register_time_event);
2563 g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2565 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2571 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2575 for (e = g->entries; e; e = e->by_group_next) {
2577 avahi_goodbye_entry(g->server, e, 1, 1);
2581 g->server->need_entry_cleanup = 1;
2583 if (g->register_time_event) {
2584 avahi_time_event_free(g->register_time_event);
2585 g->register_time_event = NULL;
2590 gettimeofday(&g->register_time, NULL);
2592 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2595 int avahi_entry_is_commited(AvahiEntry *e) {
2600 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2601 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2604 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2611 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2614 g->userdata = userdata;
2617 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2623 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2627 /* Look for an entry that is not dead */
2628 for (e = g->entries; e; e = e->by_group_next)
2635 const char* avahi_server_get_domain_name(AvahiServer *s) {
2638 return s->domain_name;
2641 const char* avahi_server_get_host_name(AvahiServer *s) {
2644 return s->host_name;
2647 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2650 return s->host_name_fqdn;
2653 void* avahi_server_get_data(AvahiServer *s) {
2659 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2662 s->userdata = userdata;
2665 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2671 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2674 memset(c, 0, sizeof(AvahiServerConfig));
2677 c->host_name = NULL;
2678 c->domain_name = NULL;
2679 c->check_response_ttl = 0;
2680 c->publish_hinfo = 1;
2681 c->publish_addresses = 1;
2682 c->publish_workstation = 1;
2683 c->publish_domain = 1;
2684 c->use_iff_running = 0;
2685 c->enable_reflector = 0;
2687 c->add_service_cookie = 1;
2688 c->enable_wide_area = 0;
2689 c->n_wide_area_servers = 0;
2690 c->disallow_other_stacks = 0;
2695 void avahi_server_config_free(AvahiServerConfig *c) {
2698 avahi_free(c->host_name);
2699 avahi_free(c->domain_name);
2702 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2703 char *d = NULL, *h = NULL;
2708 if (!(h = avahi_strdup(c->host_name)))
2712 if (!(d = avahi_strdup(c->domain_name))) {
2719 ret->domain_name = d;
2724 int avahi_server_errno(AvahiServer *s) {
2730 /* Just for internal use */
2731 int avahi_server_set_errno(AvahiServer *s, int error) {
2734 return s->error = error;
2737 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
2740 return s->local_service_cookie;
2743 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char*domain) {
2744 AvahiKey *key = NULL;
2754 if (!avahi_is_valid_service_name(name))
2755 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
2757 if (!avahi_is_valid_service_type_strict(type))
2758 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
2760 if (domain && !avahi_is_valid_domain_name(domain))
2761 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2763 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
2764 return avahi_server_set_errno(s, ret);
2766 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
2767 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2771 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) {
2773 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
2774 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
2775 !(e->flags & AVAHI_PUBLISH_IS_PROXY)) {
2781 avahi_key_unref(key);
2786 /** Set the wide area DNS servers */
2787 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
2790 if (!s->wide_area_lookup_engine)
2791 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
2793 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);