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>
35 #include <avahi-common/domain.h>
36 #include <avahi-common/timeval.h>
37 #include <avahi-common/malloc.h>
38 #include <avahi-common/error.h>
47 #define AVAHI_RR_HOLDOFF_MSEC 1000
48 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
49 #define AVAHI_RR_RATE_LIMIT_COUNT 15
51 static void free_entry(AvahiServer*s, AvahiEntry *e) {
57 avahi_goodbye_entry(s, e, 1);
59 /* Remove from linked list */
60 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
62 /* Remove from hash table indexed by name */
63 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
64 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
66 avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
68 avahi_hashmap_remove(s->entries_by_key, e->record->key);
70 /* Remove from associated group */
72 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
74 avahi_record_unref(e->record);
78 static void free_group(AvahiServer *s, AvahiSEntryGroup *g) {
83 free_entry(s, g->entries);
85 if (g->register_time_event)
86 avahi_time_event_free(g->register_time_event);
88 AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g);
92 static void cleanup_dead(AvahiServer *s) {
95 if (s->need_group_cleanup) {
96 AvahiSEntryGroup *g, *next;
98 for (g = s->groups; g; g = next) {
99 next = g->groups_next;
105 s->need_group_cleanup = 0;
108 if (s->need_entry_cleanup) {
109 AvahiEntry *e, *next;
111 for (e = s->entries; e; e = next) {
112 next = e->entries_next;
118 s->need_entry_cleanup = 0;
121 if (s->need_browser_cleanup)
122 avahi_browser_cleanup(s);
125 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) {
134 assert(type != AVAHI_DNS_TYPE_ANY);
136 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
139 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
140 if (!e->dead && avahi_entry_is_registered(s, e, i))
141 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
146 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) {
152 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
153 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
154 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
155 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
156 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
157 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
158 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
163 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
168 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
171 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
179 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
180 /* avahi_free(txt); */
182 if (avahi_key_is_pattern(k)) {
184 /* Handle ANY query */
186 for (e = s->entries; e; e = e->entries_next)
187 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
188 avahi_server_prepare_response(s, i, e, unicast_response, 0);
192 /* Handle all other queries */
194 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
195 if (!e->dead && avahi_entry_is_registered(s, e, i))
196 avahi_server_prepare_response(s, i, e, unicast_response, 0);
200 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
207 for (k = e->group->entries; k; k = k->by_group_next) {
209 avahi_goodbye_entry(s, k, 0);
214 e->group->n_probing = 0;
216 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
218 avahi_goodbye_entry(s, e, 0);
222 s->need_entry_cleanup = 1;
225 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
231 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
233 withdraw_entry(s, e);
236 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
239 int ours = 0, won = 0, lost = 0;
245 t = avahi_record_to_string(record);
247 /* avahi_log_debug("incoming_probe()"); */
249 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
256 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
261 if (avahi_entry_is_probing(s, e, i)) {
273 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
275 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
276 withdraw_rrset(s, record->key);
278 /* avahi_log_debug("Not conflicting probe"); */
284 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique, const AvahiAddress *a) {
285 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
286 AvahiEntry *e, *n, *conflicting_entry = NULL;
293 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
295 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
298 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
301 /* Either our entry or the other is intended to be unique, so let's check */
303 if (avahi_record_equal_no_ttl(e->record, record)) {
304 ours = 1; /* We have an identical record, so this is no conflict */
306 /* Check wheter there is a TTL conflict */
307 if (record->ttl <= e->record->ttl/2 &&
308 avahi_entry_is_registered(s, e, i)) {
311 t = avahi_record_to_string(record);
313 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
314 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
320 /* There's no need to check the other entries of this RRset */
325 if (avahi_entry_is_registered(s, e, i)) {
327 /* A conflict => we have to return to probe mode */
329 conflicting_entry = e;
331 } else if (avahi_entry_is_probing(s, e, i)) {
333 /* We are currently registering a matching record, but
334 * someone else already claimed it, so let's
337 withdraw_immediately = 1;
342 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
344 if (!ours && conflict) {
349 t = avahi_record_to_string(record);
351 if (withdraw_immediately) {
352 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
353 withdraw_rrset(s, record->key);
355 assert(conflicting_entry);
356 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
357 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
359 /* Local unique records are returned to probing
360 * state. Local shared records are reannounced. */
369 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
370 int *unicast_response = userdata;
374 assert(unicast_response);
376 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
379 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
383 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
386 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
390 assert(!legacy_unicast || (a && port > 0 && p));
392 if (legacy_unicast) {
393 AvahiDnsPacket *reply;
396 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
399 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
401 append_aux_records_to_list(s, i, r, 0);
403 if (avahi_dns_packet_append_record(reply, r, 0, 10))
404 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
406 char *t = avahi_record_to_string(r);
407 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
411 avahi_record_unref(r);
414 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
415 avahi_interface_send_packet_unicast(i, reply, a, port);
417 avahi_dns_packet_free(reply);
420 int unicast_response, flush_cache, auxiliary;
421 AvahiDnsPacket *reply = NULL;
424 /* In case the query packet was truncated never respond
425 immediately, because known answer suppression records might be
426 contained in later packets */
427 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
429 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
431 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
433 append_aux_records_to_list(s, i, r, unicast_response);
435 /* Due to some reasons the record has not been scheduled.
436 * The client requested an unicast response in that
437 * case. Therefore we prepare such a response */
444 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
448 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
450 /* Appending this record succeeded, so incremeant
451 * the specific header field, and return to the caller */
453 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
458 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
461 /* The record is too large for one packet, so create a larger packet */
463 avahi_dns_packet_free(reply);
464 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
465 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
466 size = AVAHI_DNS_PACKET_MAX_SIZE;
468 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
471 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
473 avahi_dns_packet_free(reply);
474 t = avahi_record_to_string(r);
475 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
479 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
482 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
483 avahi_interface_send_packet_unicast(i, reply, a, port);
484 avahi_dns_packet_free(reply);
489 avahi_record_unref(r);
493 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
494 avahi_interface_send_packet_unicast(i, reply, a, port);
495 avahi_dns_packet_free(reply);
499 avahi_record_list_flush(s->record_list);
503 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
510 if (!s->config.enable_reflector)
513 for (j = s->monitor->interfaces; j; j = j->interface_next)
514 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
515 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
518 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
519 AvahiServer *s = userdata;
526 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
530 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
537 if (!s->config.enable_reflector)
540 for (j = s->monitor->interfaces; j; j = j->interface_next)
541 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
542 /* Post the query to other networks */
543 avahi_interface_post_query(j, k, 1);
545 /* Reply from caches of other network. This is needed to
546 * "work around" known answer suppression. */
548 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
552 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
559 if (!s->config.enable_reflector)
562 for (j = s->monitor->interfaces; j; j = j->interface_next)
563 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
564 avahi_interface_post_probe(j, r, 1);
567 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
576 /* avahi_log_debug("query"); */
578 assert(avahi_record_list_is_empty(s->record_list));
580 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
582 /* Handle the questions */
583 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
585 int unicast_response = 0;
587 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
588 avahi_log_warn("Packet too short (1)");
592 if (!legacy_unicast && !from_local_iface)
593 reflect_query(s, i, key);
595 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
596 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
597 /* Allow our own queries to be suppressed by incoming
598 * queries only when they do not include known answers */
599 avahi_query_scheduler_incoming(i->query_scheduler, key);
601 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
602 avahi_key_unref(key);
605 if (!legacy_unicast) {
607 /* Known Answer Suppression */
608 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
612 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
613 avahi_log_warn("Packet too short (2)");
617 if (handle_conflict(s, i, record, unique, a)) {
618 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
619 avahi_record_list_drop(s->record_list, record);
622 avahi_record_unref(record);
626 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
630 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
631 avahi_log_warn("Packet too short (3)");
635 if (!avahi_key_is_pattern(record->key)) {
636 if (!from_local_iface)
637 reflect_probe(s, i, record);
638 incoming_probe(s, record, i);
641 avahi_record_unref(record);
645 if (!avahi_record_list_is_empty(s->record_list))
646 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
651 avahi_record_list_flush(s->record_list);
654 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
662 /* avahi_log_debug("response"); */
664 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
665 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
670 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
671 avahi_log_warn("Packet too short (4)");
675 if (!avahi_key_is_pattern(record->key)) {
677 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
678 /* avahi_free(txt); */
680 if (handle_conflict(s, i, record, cache_flush, a)) {
681 if (!from_local_iface)
682 reflect_response(s, i, record, cache_flush);
683 avahi_cache_update(i->cache, record, cache_flush, a);
684 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
688 avahi_record_unref(record);
691 /* If the incoming response contained a conflicting record, some
692 records have been scheduling for sending. We need to flush them
694 if (!avahi_record_list_is_empty(s->record_list))
695 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
698 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
699 unsigned n, idx = (unsigned) -1;
700 AvahiLegacyUnicastReflectSlot *slot;
704 if (!s->legacy_unicast_reflect_slots)
705 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
707 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
708 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
710 if (!s->legacy_unicast_reflect_slots[idx])
714 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
717 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
718 return NULL; /* OOM */
720 s->legacy_unicast_reflect_slots[idx] = slot;
721 slot->id = s->legacy_unicast_reflect_id++;
727 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
733 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
735 assert(s->legacy_unicast_reflect_slots[idx] == slot);
737 avahi_time_event_free(slot->time_event);
740 s->legacy_unicast_reflect_slots[idx] = NULL;
743 static void free_slots(AvahiServer *s) {
747 if (!s->legacy_unicast_reflect_slots)
750 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
751 if (s->legacy_unicast_reflect_slots[idx])
752 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
754 avahi_free(s->legacy_unicast_reflect_slots);
755 s->legacy_unicast_reflect_slots = NULL;
758 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
763 if (!s->legacy_unicast_reflect_slots)
766 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
768 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
771 return s->legacy_unicast_reflect_slots[idx];
774 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
775 AvahiLegacyUnicastReflectSlot *slot = userdata;
779 assert(slot->time_event == e);
781 deallocate_slot(slot->server, slot);
784 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
785 AvahiLegacyUnicastReflectSlot *slot;
793 assert(i->protocol == a->family);
795 if (!s->config.enable_reflector)
798 /* avahi_log_debug("legacy unicast reflector"); */
800 /* Reflecting legacy unicast queries is a little more complicated
801 than reflecting normal queries, since we must route the
802 responses back to the right client. Therefore we must store
803 some information for finding the right client contact data for
804 response packets. In contrast to normal queries legacy
805 unicast query and response packets are reflected untouched and
806 are not reassembled into larger packets */
808 if (!(slot = allocate_slot(s))) {
809 /* No slot available, we drop this legacy unicast query */
810 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
814 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
817 slot->interface = i->hardware->index;
819 avahi_elapse_time(&slot->elapse_time, 2000, 0);
820 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
822 /* Patch the packet with our new locally generatedt id */
823 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
825 for (j = s->monitor->interfaces; j; j = j->interface_next)
826 if (avahi_interface_relevant(j) &&
828 (s->config.reflect_ipv || j->protocol == i->protocol)) {
830 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
831 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
832 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
833 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
837 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
840 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
845 if (!s->config.enable_reflector)
848 avahi_address_from_sockaddr(sa, &a);
850 if (!avahi_address_is_local(s->monitor, &a))
853 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
854 struct sockaddr_in lsa;
855 socklen_t l = sizeof(lsa);
857 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
858 avahi_log_warn("getsockname(): %s", strerror(errno));
860 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
864 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
865 struct sockaddr_in6 lsa;
866 socklen_t l = sizeof(lsa);
868 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
869 avahi_log_warn("getsockname(): %s", strerror(errno));
871 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
877 static int is_mdns_mcast_address(const AvahiAddress *a) {
881 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
882 return avahi_address_cmp(a, &b) == 0;
885 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
887 assert(iface != AVAHI_IF_UNSPEC);
890 /* If it isn't the MDNS port it can't be generated by us */
891 if (port != AVAHI_MDNS_PORT)
894 return avahi_interface_has_address(s->monitor, iface, a);
897 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
901 int from_local_iface = 0;
909 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
910 !avahi_interface_relevant(i)) {
911 avahi_log_warn("Recieved packet from invalid interface.");
915 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
917 port = avahi_port_from_sockaddr(sa);
918 avahi_address_from_sockaddr(sa, &a);
920 if (avahi_address_is_ipv4_in_ipv6(&a))
921 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
924 if (originates_from_local_legacy_unicast_socket(s, sa))
925 /* This originates from our local reflector, so let's ignore it */
928 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
929 if (s->config.enable_reflector)
930 from_local_iface = originates_from_local_iface(s, iface, &a, port);
932 if (avahi_dns_packet_is_valid(p) < 0) {
933 avahi_log_warn("Recieved invalid packet.");
937 if (avahi_dns_packet_is_query(p)) {
938 int legacy_unicast = 0;
940 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
941 avahi_log_warn("Invalid query packet.");
945 if (port != AVAHI_MDNS_PORT) {
948 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
949 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
950 avahi_log_warn("Invalid legacy unicast query packet.");
958 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
960 handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
962 /* avahi_log_debug("Handled query"); */
964 if (port != AVAHI_MDNS_PORT) {
965 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
969 if (ttl != 255 && s->config.check_response_ttl) {
970 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
974 if (!is_mdns_mcast_address(dest) &&
975 !avahi_interface_address_on_link(i, &a)) {
976 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
980 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
981 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
982 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
983 avahi_log_warn("Invalid response packet.");
987 handle_response_packet(s, p, i, &a, from_local_iface);
988 /* avahi_log_debug("Handled response"); */
992 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, int ttl) {
993 AvahiInterface *i, *j;
996 AvahiLegacyUnicastReflectSlot *slot;
1003 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
1004 !avahi_interface_relevant(i)) {
1005 avahi_log_warn("Recieved packet from invalid interface.");
1009 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
1011 port = avahi_port_from_sockaddr(sa);
1012 avahi_address_from_sockaddr(sa, &a);
1014 if (avahi_address_is_ipv4_in_ipv6(&a))
1015 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
1018 if (avahi_dns_packet_is_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
1019 avahi_log_warn("Recieved invalid packet.");
1023 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1024 avahi_log_warn("Recieved legacy unicast response with unknown id");
1028 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
1029 !avahi_interface_relevant(j))
1032 /* Patch the original ID into this response */
1033 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1035 /* Forward the response to the correct client */
1036 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1038 /* Undo changes to packet */
1039 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1042 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1043 AvahiServer *s = userdata;
1048 struct sockaddr_in sa;
1049 struct sockaddr_in6 sa6;
1054 if (events & AVAHI_WATCH_IN) {
1056 if (fd == s->fd_ipv4) {
1057 dest.family = AVAHI_PROTO_INET;
1058 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1059 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1060 avahi_dns_packet_free(p);
1062 } else if (fd == s->fd_ipv6) {
1063 dest.family = AVAHI_PROTO_INET6;
1065 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1066 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1067 avahi_dns_packet_free(p);
1069 } else if (fd == s->fd_legacy_unicast_ipv4) {
1070 dest.family = AVAHI_PROTO_INET;
1072 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1073 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1074 avahi_dns_packet_free(p);
1076 } else if (fd == s->fd_legacy_unicast_ipv6) {
1077 dest.family = AVAHI_PROTO_INET6;
1079 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1080 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1081 avahi_dns_packet_free(p);
1091 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1094 if (s->state == state)
1100 s->callback(s, state, s->userdata);
1103 static void withdraw_host_rrs(AvahiServer *s) {
1106 if (s->hinfo_entry_group)
1107 avahi_s_entry_group_reset(s->hinfo_entry_group);
1109 if (s->browse_domain_entry_group)
1110 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1112 avahi_update_host_rrs(s->monitor, 1);
1113 s->n_host_rr_pending = 0;
1116 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1119 assert(s->n_host_rr_pending > 0);
1121 if (--s->n_host_rr_pending == 0)
1122 server_set_state(s, AVAHI_SERVER_RUNNING);
1125 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1128 s->n_host_rr_pending ++;
1131 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1135 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1136 s->state == AVAHI_SERVER_REGISTERING)
1137 avahi_server_increase_host_rr_pending(s);
1139 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1140 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1141 withdraw_host_rrs(s);
1142 server_set_state(s, AVAHI_SERVER_COLLISION);
1144 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1145 s->state == AVAHI_SERVER_REGISTERING)
1146 avahi_server_decrease_host_rr_pending(s);
1149 static void register_hinfo(AvahiServer *s) {
1150 struct utsname utsname;
1155 if (!s->config.publish_hinfo)
1158 if (s->hinfo_entry_group)
1159 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1161 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1163 if (!s->hinfo_entry_group) {
1164 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1168 /* Fill in HINFO rr */
1169 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1171 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1172 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1174 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r) < 0) {
1175 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1179 avahi_record_unref(r);
1182 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1183 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1187 static void register_localhost(AvahiServer *s) {
1191 /* Add localhost entries */
1192 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1193 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1195 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1196 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1199 static void register_browse_domain(AvahiServer *s) {
1202 if (!s->config.publish_domain)
1205 if (s->browse_domain_entry_group)
1206 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1208 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1210 if (!s->browse_domain_entry_group) {
1211 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1215 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) {
1216 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1220 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1221 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1224 static void register_stuff(AvahiServer *s) {
1227 server_set_state(s, AVAHI_SERVER_REGISTERING);
1228 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1231 register_browse_domain(s);
1232 avahi_update_host_rrs(s->monitor, 0);
1234 s->n_host_rr_pending --;
1236 if (s->n_host_rr_pending == 0)
1237 server_set_state(s, AVAHI_SERVER_RUNNING);
1240 static void update_fqdn(AvahiServer *s) {
1244 assert(s->host_name);
1245 assert(s->domain_name);
1247 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1250 avahi_free(s->host_name_fqdn);
1251 s->host_name_fqdn = n;
1254 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1258 if (host_name && !avahi_is_valid_host_name(host_name))
1259 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1261 withdraw_host_rrs(s);
1263 avahi_free(s->host_name);
1264 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1265 s->host_name[strcspn(s->host_name, ".")] = 0;
1272 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1274 assert(domain_name);
1276 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1277 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1279 withdraw_host_rrs(s);
1281 avahi_free(s->domain_name);
1282 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
1289 static int valid_server_config(const AvahiServerConfig *sc) {
1291 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1292 return AVAHI_ERR_INVALID_HOST_NAME;
1294 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1295 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1300 static int setup_sockets(AvahiServer *s) {
1303 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1304 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1306 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1307 return AVAHI_ERR_NO_NETWORK;
1309 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1310 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1311 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1312 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1314 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1315 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1317 s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL;
1319 if (s->fd_ipv4 >= 0)
1320 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1321 if (s->fd_ipv6 >= 0)
1322 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1323 if (s->fd_legacy_unicast_ipv4 >= 0)
1324 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1325 if (s->fd_legacy_unicast_ipv6 >= 0)
1326 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1331 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1335 if ((e = valid_server_config(sc)) < 0) {
1341 if (!(s = avahi_new(AvahiServer, 1))) {
1343 *error = AVAHI_ERR_NO_MEMORY;
1348 s->poll_api = poll_api;
1351 avahi_server_config_copy(&s->config, sc);
1353 avahi_server_config_init(&s->config);
1355 if ((e = setup_sockets(s)) < 0) {
1359 avahi_server_config_free(&s->config);
1365 s->n_host_rr_pending = 0;
1366 s->need_entry_cleanup = 0;
1367 s->need_group_cleanup = 0;
1368 s->need_browser_cleanup = 0;
1370 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1372 s->callback = callback;
1373 s->userdata = userdata;
1375 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1376 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1377 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1379 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1380 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1381 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1382 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1383 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1384 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1385 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1386 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1387 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1389 s->legacy_unicast_reflect_slots = NULL;
1390 s->legacy_unicast_reflect_id = 0;
1393 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1394 s->host_name[strcspn(s->host_name, ".")] = 0;
1395 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
1396 s->host_name_fqdn = NULL;
1399 s->record_list = avahi_record_list_new();
1401 s->state = AVAHI_SERVER_INVALID;
1403 s->monitor = avahi_interface_monitor_new(s);
1404 avahi_interface_monitor_sync(s->monitor);
1406 register_localhost(s);
1408 s->hinfo_entry_group = NULL;
1409 s->browse_domain_entry_group = NULL;
1412 s->error = AVAHI_OK;
1417 void avahi_server_free(AvahiServer* s) {
1421 free_entry(s, s->entries);
1423 avahi_interface_monitor_free(s->monitor);
1426 free_group(s, s->groups);
1430 while (s->dns_server_browsers)
1431 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1432 while (s->host_name_resolvers)
1433 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1434 while (s->address_resolvers)
1435 avahi_s_address_resolver_free(s->address_resolvers);
1436 while (s->domain_browsers)
1437 avahi_s_domain_browser_free(s->domain_browsers);
1438 while (s->service_type_browsers)
1439 avahi_s_service_type_browser_free(s->service_type_browsers);
1440 while (s->service_browsers)
1441 avahi_s_service_browser_free(s->service_browsers);
1442 while (s->service_resolvers)
1443 avahi_s_service_resolver_free(s->service_resolvers);
1444 while (s->record_browsers)
1445 avahi_s_record_browser_destroy(s->record_browsers);
1447 avahi_hashmap_free(s->record_browser_hashmap);
1448 avahi_hashmap_free(s->entries_by_key);
1450 avahi_time_event_queue_free(s->time_event_queue);
1452 avahi_record_list_free(s->record_list);
1455 s->poll_api->watch_free(s->watch_ipv4);
1457 s->poll_api->watch_free(s->watch_ipv6);
1458 if (s->watch_legacy_unicast_ipv4)
1459 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1460 if (s->watch_legacy_unicast_ipv6)
1461 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1463 if (s->fd_ipv4 >= 0)
1465 if (s->fd_ipv6 >= 0)
1467 if (s->fd_legacy_unicast_ipv4 >= 0)
1468 close(s->fd_legacy_unicast_ipv4);
1469 if (s->fd_legacy_unicast_ipv6 >= 0)
1470 close(s->fd_legacy_unicast_ipv6);
1472 avahi_free(s->host_name);
1473 avahi_free(s->domain_name);
1474 avahi_free(s->host_name_fqdn);
1476 avahi_server_config_free(&s->config);
1481 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1487 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1491 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1494 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1497 if ((interface <= 0 ||
1498 e->interface <= 0 ||
1499 e->interface == interface) &&
1500 (protocol == AVAHI_PROTO_UNSPEC ||
1501 e->protocol == AVAHI_PROTO_UNSPEC ||
1502 e->protocol == protocol))
1510 int avahi_server_add(
1512 AvahiSEntryGroup *g,
1513 AvahiIfIndex interface,
1514 AvahiProtocol protocol,
1515 AvahiEntryFlags flags,
1524 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1526 if (avahi_key_is_pattern(r->key))
1527 return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1529 if (!avahi_record_is_valid(r))
1530 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1532 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1533 return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1535 if (!(e = avahi_new(AvahiEntry, 1)))
1536 return avahi_server_set_errno(s, AVAHI_ERR_NO_NETWORK);
1539 e->record = avahi_record_ref(r);
1541 e->interface = interface;
1542 e->protocol = protocol;
1546 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1548 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1550 /* Insert into hash table indexed by name */
1551 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1552 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1553 avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1555 /* Insert into group list */
1557 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1559 avahi_announce_entry(s, e);
1564 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1565 AvahiEntry **e = (AvahiEntry**) state;
1570 *e = g ? g->entries : s->entries;
1572 while (*e && (*e)->dead)
1573 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1578 return avahi_record_ref((*e)->record);
1581 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1587 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1589 for (e = s->entries; e; e = e->entries_next) {
1596 if (!(t = avahi_record_to_string(e->record)))
1597 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1599 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1602 callback(ln, userdata);
1605 avahi_dump_caches(s->monitor, callback, userdata);
1609 int avahi_server_add_ptr(
1611 AvahiSEntryGroup *g,
1612 AvahiIfIndex interface,
1613 AvahiProtocol protocol,
1614 AvahiEntryFlags flags,
1625 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1626 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1628 r->data.ptr.name = avahi_normalize_name(dest);
1629 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1630 avahi_record_unref(r);
1634 int avahi_server_add_address(
1636 AvahiSEntryGroup *g,
1637 AvahiIfIndex interface,
1638 AvahiProtocol protocol,
1639 AvahiEntryFlags flags,
1649 if (!(n = avahi_normalize_name(name)))
1650 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1654 name = s->host_name_fqdn;
1656 if (!avahi_is_valid_domain_name(name)) {
1657 ret = avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1661 if (a->family == AVAHI_PROTO_INET) {
1665 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1666 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1670 r->data.a.address = a->data.ipv4;
1671 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1672 avahi_record_unref(r);
1677 if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1678 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1682 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1683 avahi_free(reverse);
1689 assert(a->family == AVAHI_PROTO_INET6);
1691 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1692 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1696 r->data.aaaa.address = a->data.ipv6;
1697 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1698 avahi_record_unref(r);
1703 if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
1704 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1708 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1709 avahi_free(reverse);
1714 if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1715 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1719 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1720 avahi_free(reverse);
1730 static int server_add_txt_strlst_nocopy(
1732 AvahiSEntryGroup *g,
1733 AvahiIfIndex interface,
1734 AvahiProtocol protocol,
1735 AvahiEntryFlags flags,
1738 AvahiStringList *strlst) {
1745 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl)))
1746 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1748 r->data.txt.string_list = strlst;
1749 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1750 avahi_record_unref(r);
1755 int avahi_server_add_txt_strlst(
1757 AvahiSEntryGroup *g,
1758 AvahiIfIndex interface,
1759 AvahiProtocol protocol,
1760 AvahiEntryFlags flags,
1763 AvahiStringList *strlst) {
1767 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1770 int avahi_server_add_txt_va(
1772 AvahiSEntryGroup *g,
1773 AvahiIfIndex interface,
1774 AvahiProtocol protocol,
1775 AvahiEntryFlags flags,
1782 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1785 int avahi_server_add_txt(
1787 AvahiSEntryGroup *g,
1788 AvahiIfIndex interface,
1789 AvahiProtocol protocol,
1790 AvahiEntryFlags flags,
1801 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1807 static void escape_service_name(char *d, size_t size, const char *s) {
1812 while (*s && size >= 2) {
1813 if (*s == '.' || *s == '\\') {
1829 static int server_add_service_strlst_nocopy(
1831 AvahiSEntryGroup *g,
1832 AvahiIfIndex interface,
1833 AvahiProtocol protocol,
1839 AvahiStringList *strlst) {
1841 char ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1842 char *t = NULL, *d = NULL, *h = NULL;
1843 AvahiRecord *r = NULL;
1850 if (!avahi_is_valid_service_name(name))
1851 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1853 if (!avahi_is_valid_service_type(type))
1854 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1856 if (domain && !avahi_is_valid_domain_name(domain))
1857 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1859 if (host && !avahi_is_valid_domain_name(host))
1860 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1862 escape_service_name(ename, sizeof(ename), name);
1865 domain = s->domain_name;
1868 host = s->host_name_fqdn;
1870 if (!(d = avahi_normalize_name(domain)) ||
1871 !(t = avahi_normalize_name(type)) ||
1872 !(h = avahi_normalize_name(host))) {
1873 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1877 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1878 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1880 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1883 if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1884 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1888 r->data.srv.priority = 0;
1889 r->data.srv.weight = 0;
1890 r->data.srv.port = port;
1891 r->data.srv.name = h;
1893 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1894 avahi_record_unref(r);
1899 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1905 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1906 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1914 avahi_string_list_free(strlst);
1919 int avahi_server_add_service_strlst(
1921 AvahiSEntryGroup *g,
1922 AvahiIfIndex interface,
1923 AvahiProtocol protocol,
1929 AvahiStringList *strlst) {
1935 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1938 int avahi_server_add_service_va(
1940 AvahiSEntryGroup *g,
1941 AvahiIfIndex interface,
1942 AvahiProtocol protocol,
1954 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1957 int avahi_server_add_service(
1959 AvahiSEntryGroup *g,
1960 AvahiIfIndex interface,
1961 AvahiProtocol protocol,
1977 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1982 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
1983 static const char hex[] = "0123456789abcdef";
1985 const uint8_t *k = p;
1987 while (sl > 1 && pl > 0) {
1988 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
2004 int avahi_server_add_dns_server_address(
2006 AvahiSEntryGroup *g,
2007 AvahiIfIndex interface,
2008 AvahiProtocol protocol,
2010 AvahiDNSServerType type,
2011 const AvahiAddress *address,
2012 uint16_t port /** should be 53 */) {
2020 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2021 assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
2024 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2026 if (domain && !avahi_is_valid_domain_name(domain))
2027 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2029 if (address->family == AVAHI_PROTO_INET) {
2030 hexstring(n+3, sizeof(n)-3, &address->data, 4);
2031 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2032 r->data.a.address = address->data.ipv4;
2034 hexstring(n+3, sizeof(n)-3, &address->data, 6);
2035 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2036 r->data.aaaa.address = address->data.ipv6;
2040 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2042 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
2043 avahi_record_unref(r);
2048 return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
2051 int avahi_server_add_dns_server_name(
2053 AvahiSEntryGroup *g,
2054 AvahiIfIndex interface,
2055 AvahiProtocol protocol,
2057 AvahiDNSServerType type,
2059 uint16_t port /** should be 53 */) {
2062 char t[256], *d = NULL, *n = NULL;
2067 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2070 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2072 if (!avahi_is_valid_domain_name(name))
2073 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2075 if (domain && !avahi_is_valid_domain_name(domain))
2076 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2080 domain = s->domain_name;
2082 if (!(n = avahi_normalize_name(name)) ||
2083 !(d = avahi_normalize_name(domain))) {
2086 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2089 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2092 if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2094 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2097 r->data.srv.priority = 0;
2098 r->data.srv.weight = 0;
2099 r->data.srv.port = port;
2100 r->data.srv.name = n;
2101 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2102 avahi_record_unref(r);
2107 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2108 AvahiKey *k = userdata;
2114 avahi_interface_post_query(i, k, 0);
2117 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2121 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2124 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2127 if (g->state == state)
2130 assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2135 g->callback(g->server, g, state, g->userdata);
2138 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2139 AvahiSEntryGroup *g;
2143 if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2144 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2149 g->callback = callback;
2150 g->userdata = userdata;
2152 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2154 g->n_register_try = 0;
2155 g->register_time_event = NULL;
2156 g->register_time.tv_sec = 0;
2157 g->register_time.tv_usec = 0;
2158 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2160 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2164 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2170 for (e = g->entries; e; e = e->by_group_next) {
2172 avahi_goodbye_entry(g->server, e, 1);
2177 if (g->register_time_event) {
2178 avahi_time_event_free(g->register_time_event);
2179 g->register_time_event = NULL;
2184 g->server->need_group_cleanup = 1;
2185 g->server->need_entry_cleanup = 1;
2188 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2191 gettimeofday(&g->register_time, NULL);
2193 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2196 avahi_announce_group(g->server, g);
2197 avahi_s_entry_group_check_probed(g, 0);
2201 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2202 AvahiSEntryGroup *g = userdata;
2205 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2207 avahi_time_event_free(g->register_time_event);
2208 g->register_time_event = NULL;
2210 /* Holdoff time passed, so let's start probing */
2211 entry_group_commit_real(g);
2214 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2220 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2221 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2223 g->n_register_try++;
2225 avahi_timeval_add(&g->register_time,
2226 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2227 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2228 AVAHI_RR_HOLDOFF_MSEC));
2230 gettimeofday(&now, NULL);
2232 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2233 /* Holdoff time passed, so let's start probing */
2234 /* avahi_log_debug("Holdoff passed, directly going on."); */
2236 entry_group_commit_real(g);
2238 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2240 /* Holdoff time has not yet passed, so let's wait */
2241 assert(!g->register_time_event);
2242 g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2244 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2250 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2254 if (g->register_time_event) {
2255 avahi_time_event_free(g->register_time_event);
2256 g->register_time_event = NULL;
2259 for (e = g->entries; e; e = e->by_group_next) {
2261 avahi_goodbye_entry(g->server, e, 1);
2266 if (g->register_time_event) {
2267 avahi_time_event_free(g->register_time_event);
2268 g->register_time_event = NULL;
2271 g->server->need_entry_cleanup = 1;
2274 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2277 int avahi_entry_is_commited(AvahiEntry *e) {
2282 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2283 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2286 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2293 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2296 g->userdata = userdata;
2299 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2305 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2309 /* Look for an entry that is not dead */
2310 for (e = g->entries; e; e = e->by_group_next)
2317 const char* avahi_server_get_domain_name(AvahiServer *s) {
2320 return s->domain_name;
2323 const char* avahi_server_get_host_name(AvahiServer *s) {
2326 return s->host_name;
2329 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2332 return s->host_name_fqdn;
2335 void* avahi_server_get_data(AvahiServer *s) {
2341 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2344 s->userdata = userdata;
2347 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2353 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2356 memset(c, 0, sizeof(AvahiServerConfig));
2359 c->host_name = NULL;
2360 c->domain_name = NULL;
2361 c->check_response_ttl = 0;
2362 c->publish_hinfo = 1;
2363 c->publish_addresses = 1;
2364 c->publish_workstation = 1;
2365 c->publish_domain = 1;
2366 c->use_iff_running = 0;
2367 c->enable_reflector = 0;
2373 void avahi_server_config_free(AvahiServerConfig *c) {
2376 avahi_free(c->host_name);
2377 avahi_free(c->domain_name);
2380 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2381 char *d = NULL, *h = NULL;
2386 if (!(h = avahi_strdup(c->host_name)))
2390 if (!(d = avahi_strdup(c->domain_name))) {
2397 ret->domain_name = d;
2402 int avahi_server_errno(AvahiServer *s) {
2408 /* Just for internal use */
2409 int avahi_server_set_errno(AvahiServer *s, int error) {
2412 return s->error = error;