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>
46 #define AVAHI_RR_HOLDOFF_MSEC 1000
47 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
48 #define AVAHI_RR_RATE_LIMIT_COUNT 15
50 static void free_entry(AvahiServer*s, AvahiEntry *e) {
56 avahi_goodbye_entry(s, e, 1);
58 /* Remove from linked list */
59 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
61 /* Remove from hash table indexed by name */
62 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
63 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
65 avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
67 avahi_hashmap_remove(s->entries_by_key, e->record->key);
69 /* Remove from associated group */
71 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
73 avahi_record_unref(e->record);
77 static void free_group(AvahiServer *s, AvahiSEntryGroup *g) {
82 free_entry(s, g->entries);
84 if (g->register_time_event)
85 avahi_time_event_free(g->register_time_event);
87 AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g);
91 static void cleanup_dead(AvahiServer *s) {
94 if (s->need_group_cleanup) {
95 AvahiSEntryGroup *g, *next;
97 for (g = s->groups; g; g = next) {
98 next = g->groups_next;
104 s->need_group_cleanup = 0;
107 if (s->need_entry_cleanup) {
108 AvahiEntry *e, *next;
110 for (e = s->entries; e; e = next) {
111 next = e->entries_next;
117 s->need_entry_cleanup = 0;
120 if (s->need_browser_cleanup)
121 avahi_browser_cleanup(s);
124 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) {
133 assert(type != AVAHI_DNS_TYPE_ANY);
135 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
138 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
139 if (!e->dead && avahi_entry_is_registered(s, e, i))
140 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
145 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) {
151 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
152 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
153 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
154 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
155 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
156 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
157 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
162 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
167 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
170 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
178 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
179 /* avahi_free(txt); */
181 if (avahi_key_is_pattern(k)) {
183 /* Handle ANY query */
185 for (e = s->entries; e; e = e->entries_next)
186 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
187 avahi_server_prepare_response(s, i, e, unicast_response, 0);
191 /* Handle all other queries */
193 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
194 if (!e->dead && avahi_entry_is_registered(s, e, i))
195 avahi_server_prepare_response(s, i, e, unicast_response, 0);
199 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
206 for (k = e->group->entries; k; k = k->by_group_next) {
208 avahi_goodbye_entry(s, k, 0);
213 e->group->n_probing = 0;
215 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
217 avahi_goodbye_entry(s, e, 0);
221 s->need_entry_cleanup = 1;
224 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
230 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
232 withdraw_entry(s, e);
235 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
238 int ours = 0, won = 0, lost = 0;
244 t = avahi_record_to_string(record);
246 /* avahi_log_debug("incoming_probe()"); */
248 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
255 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
260 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) {
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)");
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 /* Known Answer Suppression */
606 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
610 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
611 avahi_log_warn("Packet too short (2)");
615 if (handle_conflict(s, i, record, unique, a)) {
616 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
617 avahi_record_list_drop(s->record_list, record);
620 avahi_record_unref(record);
624 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
628 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
629 avahi_log_warn("Packet too short (3)");
633 if (!avahi_key_is_pattern(record->key)) {
634 reflect_probe(s, i, record);
635 incoming_probe(s, record, i);
638 avahi_record_unref(record);
641 if (!avahi_record_list_is_empty(s->record_list))
642 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
647 avahi_record_list_flush(s->record_list);
650 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
658 /* avahi_log_debug("response"); */
660 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
661 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
666 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
667 avahi_log_warn("Packet too short (4)");
671 if (!avahi_key_is_pattern(record->key)) {
673 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
674 /* avahi_free(txt); */
676 if (handle_conflict(s, i, record, cache_flush, a)) {
677 reflect_response(s, i, record, cache_flush);
678 avahi_cache_update(i->cache, record, cache_flush, a);
679 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
683 avahi_record_unref(record);
686 /* If the incoming response contained a conflicting record, some
687 records have been scheduling for sending. We need to flush them
689 if (!avahi_record_list_is_empty(s->record_list))
690 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
693 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
694 unsigned n, idx = (unsigned) -1;
695 AvahiLegacyUnicastReflectSlot *slot;
699 if (!s->legacy_unicast_reflect_slots)
700 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
702 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
703 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
705 if (!s->legacy_unicast_reflect_slots[idx])
709 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
712 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
713 return NULL; /* OOM */
715 slot = s->legacy_unicast_reflect_slots[idx];
716 slot->id = s->legacy_unicast_reflect_id++;
721 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
727 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
729 assert(s->legacy_unicast_reflect_slots[idx] == slot);
731 avahi_time_event_free(slot->time_event);
734 s->legacy_unicast_reflect_slots[idx] = NULL;
737 static void free_slots(AvahiServer *s) {
741 if (!s->legacy_unicast_reflect_slots)
744 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
745 if (s->legacy_unicast_reflect_slots[idx])
746 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
748 avahi_free(s->legacy_unicast_reflect_slots);
749 s->legacy_unicast_reflect_slots = NULL;
752 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
757 if (!s->legacy_unicast_reflect_slots)
760 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
762 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
765 return s->legacy_unicast_reflect_slots[idx];
768 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
769 AvahiLegacyUnicastReflectSlot *slot = userdata;
773 assert(slot->time_event == e);
775 deallocate_slot(slot->server, slot);
778 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
779 AvahiLegacyUnicastReflectSlot *slot;
787 assert(i->protocol == a->family);
789 if (!s->config.enable_reflector)
792 /* avahi_log_debug("legacy unicast reflector"); */
794 /* Reflecting legacy unicast queries is a little more complicated
795 than reflecting normal queries, since we must route the
796 responses back to the right client. Therefore we must store
797 some information for finding the right client contact data for
798 response packets. In contrast to normal queries legacy
799 unicast query and response packets are reflected untouched and
800 are not reassembled into larger packets */
802 if (!(slot = allocate_slot(s))) {
803 /* No slot available, we drop this legacy unicast query */
804 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
808 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
811 slot->interface = i->hardware->index;
813 avahi_elapse_time(&slot->elapse_time, 2000, 0);
814 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
816 /* Patch the packet with our new locally generatedt id */
817 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
819 for (j = s->monitor->interfaces; j; j = j->interface_next)
820 if (avahi_interface_relevant(j) &&
822 (s->config.reflect_ipv || j->protocol == i->protocol)) {
824 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
825 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
826 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
827 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
831 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
834 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
839 if (!s->config.enable_reflector)
842 avahi_address_from_sockaddr(sa, &a);
844 if (!avahi_address_is_local(s->monitor, &a))
847 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
848 struct sockaddr_in lsa;
849 socklen_t l = sizeof(lsa);
851 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
852 avahi_log_warn("getsockname(): %s", strerror(errno));
854 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
858 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
859 struct sockaddr_in6 lsa;
860 socklen_t l = sizeof(lsa);
862 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
863 avahi_log_warn("getsockname(): %s", strerror(errno));
865 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
871 static int is_mdns_mcast_address(const AvahiAddress *a) {
875 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
876 return avahi_address_cmp(a, &b) == 0;
879 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
890 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
891 !avahi_interface_relevant(i)) {
892 avahi_log_warn("Recieved packet from invalid interface.");
896 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
898 port = avahi_port_from_sockaddr(sa);
899 avahi_address_from_sockaddr(sa, &a);
901 if (avahi_address_is_ipv4_in_ipv6(&a))
902 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
905 if (originates_from_local_legacy_unicast_socket(s, sa))
906 /* This originates from our local reflector, so let's ignore it */
909 if (avahi_dns_packet_is_valid(p) < 0) {
910 avahi_log_warn("Recieved invalid packet.");
914 if (avahi_dns_packet_is_query(p)) {
915 int legacy_unicast = 0;
917 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
918 avahi_log_warn("Invalid query packet.");
922 if (port != AVAHI_MDNS_PORT) {
925 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
926 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
927 avahi_log_warn("Invalid legacy unicast query packet.");
935 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
937 handle_query_packet(s, p, i, &a, port, legacy_unicast);
939 /* avahi_log_debug("Handled query"); */
941 if (port != AVAHI_MDNS_PORT) {
942 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
946 if (ttl != 255 && s->config.check_response_ttl) {
947 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
951 if (!is_mdns_mcast_address(dest) &&
952 !avahi_interface_address_on_link(i, &a)) {
953 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
957 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
958 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
959 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
960 avahi_log_warn("Invalid response packet.");
964 handle_response_packet(s, p, i, &a);
965 /* avahi_log_debug("Handled response"); */
969 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, int ttl) {
970 AvahiInterface *i, *j;
973 AvahiLegacyUnicastReflectSlot *slot;
980 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
981 !avahi_interface_relevant(i)) {
982 avahi_log_warn("Recieved packet from invalid interface.");
986 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
988 port = avahi_port_from_sockaddr(sa);
989 avahi_address_from_sockaddr(sa, &a);
991 if (avahi_address_is_ipv4_in_ipv6(&a))
992 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
995 if (avahi_dns_packet_is_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
996 avahi_log_warn("Recieved invalid packet.");
1000 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1001 avahi_log_warn("Recieved legacy unicast response with unknown id");
1005 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
1006 !avahi_interface_relevant(j))
1009 /* Patch the original ID into this response */
1010 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1012 /* Forward the response to the correct client */
1013 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1015 /* Undo changes to packet */
1016 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1019 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1020 AvahiServer *s = userdata;
1025 struct sockaddr_in sa;
1026 struct sockaddr_in6 sa6;
1031 if (events & AVAHI_WATCH_IN) {
1033 if (fd == s->fd_ipv4) {
1034 dest.family = AVAHI_PROTO_INET;
1035 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1036 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1037 avahi_dns_packet_free(p);
1039 } else if (fd == s->fd_ipv6) {
1040 dest.family = AVAHI_PROTO_INET6;
1042 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1043 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1044 avahi_dns_packet_free(p);
1046 } else if (fd == s->fd_legacy_unicast_ipv4) {
1047 dest.family = AVAHI_PROTO_INET;
1049 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1050 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1051 avahi_dns_packet_free(p);
1053 } else if (fd == s->fd_legacy_unicast_ipv6) {
1054 dest.family = AVAHI_PROTO_INET6;
1056 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1057 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1058 avahi_dns_packet_free(p);
1068 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1071 if (s->state == state)
1077 s->callback(s, state, s->userdata);
1080 static void withdraw_host_rrs(AvahiServer *s) {
1083 if (s->hinfo_entry_group)
1084 avahi_s_entry_group_reset(s->hinfo_entry_group);
1086 if (s->browse_domain_entry_group)
1087 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1089 avahi_update_host_rrs(s->monitor, 1);
1090 s->n_host_rr_pending = 0;
1093 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1096 assert(s->n_host_rr_pending > 0);
1098 if (--s->n_host_rr_pending == 0)
1099 server_set_state(s, AVAHI_SERVER_RUNNING);
1102 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1105 s->n_host_rr_pending ++;
1108 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1112 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1113 s->state == AVAHI_SERVER_REGISTERING)
1114 avahi_server_increase_host_rr_pending(s);
1116 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1117 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1118 withdraw_host_rrs(s);
1119 server_set_state(s, AVAHI_SERVER_COLLISION);
1121 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1122 s->state == AVAHI_SERVER_REGISTERING)
1123 avahi_server_decrease_host_rr_pending(s);
1126 static void register_hinfo(AvahiServer *s) {
1127 struct utsname utsname;
1132 if (!s->config.publish_hinfo)
1135 if (s->hinfo_entry_group)
1136 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1138 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1140 if (!s->hinfo_entry_group) {
1141 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1145 /* Fill in HINFO rr */
1146 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1148 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1149 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1151 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r) < 0) {
1152 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1156 avahi_record_unref(r);
1159 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1160 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1164 static void register_localhost(AvahiServer *s) {
1168 /* Add localhost entries */
1169 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1170 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1172 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1173 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1176 static void register_browse_domain(AvahiServer *s) {
1179 if (!s->config.publish_domain)
1182 if (s->browse_domain_entry_group)
1183 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1185 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1187 if (!s->browse_domain_entry_group) {
1188 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1192 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) {
1193 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1197 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1198 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1201 static void register_stuff(AvahiServer *s) {
1204 server_set_state(s, AVAHI_SERVER_REGISTERING);
1205 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1208 register_browse_domain(s);
1209 avahi_update_host_rrs(s->monitor, 0);
1211 s->n_host_rr_pending --;
1213 if (s->n_host_rr_pending == 0)
1214 server_set_state(s, AVAHI_SERVER_RUNNING);
1217 static void update_fqdn(AvahiServer *s) {
1221 assert(s->host_name);
1222 assert(s->domain_name);
1224 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1227 avahi_free(s->host_name_fqdn);
1228 s->host_name_fqdn = n;
1231 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1235 if (host_name && !avahi_is_valid_host_name(host_name))
1236 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1238 withdraw_host_rrs(s);
1240 avahi_free(s->host_name);
1241 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1242 s->host_name[strcspn(s->host_name, ".")] = 0;
1249 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1251 assert(domain_name);
1253 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1254 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1256 withdraw_host_rrs(s);
1258 avahi_free(s->domain_name);
1259 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
1266 static int valid_server_config(const AvahiServerConfig *sc) {
1268 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1269 return AVAHI_ERR_INVALID_HOST_NAME;
1271 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1272 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1277 static int setup_sockets(AvahiServer *s) {
1280 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1281 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1283 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1284 return AVAHI_ERR_NO_NETWORK;
1286 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1287 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1288 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1289 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1291 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1292 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1294 s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL;
1296 if (s->fd_ipv4 >= 0)
1297 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1298 if (s->fd_ipv6 >= 0)
1299 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1300 if (s->fd_legacy_unicast_ipv4 >= 0)
1301 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1302 if (s->fd_legacy_unicast_ipv6 >= 0)
1303 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1308 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1312 if ((e = valid_server_config(sc)) < 0) {
1318 if (!(s = avahi_new(AvahiServer, 1))) {
1320 *error = AVAHI_ERR_NO_MEMORY;
1325 s->poll_api = poll_api;
1328 avahi_server_config_copy(&s->config, sc);
1330 avahi_server_config_init(&s->config);
1332 if ((e = setup_sockets(s)) < 0) {
1336 avahi_server_config_free(&s->config);
1342 s->n_host_rr_pending = 0;
1343 s->need_entry_cleanup = 0;
1344 s->need_group_cleanup = 0;
1345 s->need_browser_cleanup = 0;
1347 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1349 s->callback = callback;
1350 s->userdata = userdata;
1352 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1353 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1354 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1356 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1357 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1358 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1359 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1360 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1361 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1362 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1363 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1364 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1366 s->legacy_unicast_reflect_slots = NULL;
1367 s->legacy_unicast_reflect_id = 0;
1370 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1371 s->host_name[strcspn(s->host_name, ".")] = 0;
1372 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
1373 s->host_name_fqdn = NULL;
1376 s->record_list = avahi_record_list_new();
1378 s->state = AVAHI_SERVER_INVALID;
1380 s->monitor = avahi_interface_monitor_new(s);
1381 avahi_interface_monitor_sync(s->monitor);
1383 register_localhost(s);
1385 s->hinfo_entry_group = NULL;
1386 s->browse_domain_entry_group = NULL;
1389 s->error = AVAHI_OK;
1394 void avahi_server_free(AvahiServer* s) {
1398 free_entry(s, s->entries);
1400 avahi_interface_monitor_free(s->monitor);
1403 free_group(s, s->groups);
1407 while (s->dns_server_browsers)
1408 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1409 while (s->host_name_resolvers)
1410 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1411 while (s->address_resolvers)
1412 avahi_s_address_resolver_free(s->address_resolvers);
1413 while (s->domain_browsers)
1414 avahi_s_domain_browser_free(s->domain_browsers);
1415 while (s->service_type_browsers)
1416 avahi_s_service_type_browser_free(s->service_type_browsers);
1417 while (s->service_browsers)
1418 avahi_s_service_browser_free(s->service_browsers);
1419 while (s->service_resolvers)
1420 avahi_s_service_resolver_free(s->service_resolvers);
1421 while (s->record_browsers)
1422 avahi_s_record_browser_destroy(s->record_browsers);
1424 avahi_hashmap_free(s->record_browser_hashmap);
1425 avahi_hashmap_free(s->entries_by_key);
1427 avahi_time_event_queue_free(s->time_event_queue);
1429 avahi_record_list_free(s->record_list);
1432 s->poll_api->watch_free(s->watch_ipv4);
1434 s->poll_api->watch_free(s->watch_ipv6);
1435 if (s->watch_legacy_unicast_ipv4)
1436 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1437 if (s->watch_legacy_unicast_ipv6)
1438 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1440 if (s->fd_ipv4 >= 0)
1442 if (s->fd_ipv6 >= 0)
1444 if (s->fd_legacy_unicast_ipv4 >= 0)
1445 close(s->fd_legacy_unicast_ipv4);
1446 if (s->fd_legacy_unicast_ipv6 >= 0)
1447 close(s->fd_legacy_unicast_ipv6);
1449 avahi_free(s->host_name);
1450 avahi_free(s->domain_name);
1451 avahi_free(s->host_name_fqdn);
1453 avahi_server_config_free(&s->config);
1458 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1464 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1468 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1471 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1474 if (interface <= 0 ||
1475 e->interface <= 0 ||
1476 e->interface == interface ||
1477 protocol == AVAHI_PROTO_UNSPEC ||
1478 e->protocol == AVAHI_PROTO_UNSPEC ||
1479 e->protocol == protocol)
1488 int avahi_server_add(
1490 AvahiSEntryGroup *g,
1491 AvahiIfIndex interface,
1492 AvahiProtocol protocol,
1493 AvahiEntryFlags flags,
1502 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1504 if (avahi_key_is_pattern(r->key))
1505 return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1507 if (!avahi_record_is_valid(r))
1508 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1510 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1511 return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1513 if (!(e = avahi_new(AvahiEntry, 1)))
1514 return avahi_server_set_errno(s, AVAHI_ERR_NO_NETWORK);
1517 e->record = avahi_record_ref(r);
1519 e->interface = interface;
1520 e->protocol = protocol;
1524 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1526 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1528 /* Insert into hash table indexed by name */
1529 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1530 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1531 avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1533 /* Insert into group list */
1535 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1537 avahi_announce_entry(s, e);
1542 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1543 AvahiEntry **e = (AvahiEntry**) state;
1548 *e = g ? g->entries : s->entries;
1550 while (*e && (*e)->dead)
1551 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1556 return avahi_record_ref((*e)->record);
1559 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1565 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1567 for (e = s->entries; e; e = e->entries_next) {
1574 if (!(t = avahi_record_to_string(e->record)))
1575 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1577 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1580 callback(ln, userdata);
1583 avahi_dump_caches(s->monitor, callback, userdata);
1587 int avahi_server_add_ptr(
1589 AvahiSEntryGroup *g,
1590 AvahiIfIndex interface,
1591 AvahiProtocol protocol,
1592 AvahiEntryFlags flags,
1603 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1604 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1606 r->data.ptr.name = avahi_normalize_name(dest);
1607 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1608 avahi_record_unref(r);
1612 int avahi_server_add_address(
1614 AvahiSEntryGroup *g,
1615 AvahiIfIndex interface,
1616 AvahiProtocol protocol,
1617 AvahiEntryFlags flags,
1627 if (!(n = avahi_normalize_name(name)))
1628 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1632 name = s->host_name_fqdn;
1634 if (!avahi_is_valid_domain_name(name)) {
1635 ret = avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1639 if (a->family == AVAHI_PROTO_INET) {
1643 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1644 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1648 r->data.a.address = a->data.ipv4;
1649 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1650 avahi_record_unref(r);
1655 if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1656 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1660 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1661 avahi_free(reverse);
1667 assert(a->family == AVAHI_PROTO_INET6);
1669 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1670 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1674 r->data.aaaa.address = a->data.ipv6;
1675 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1676 avahi_record_unref(r);
1681 if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
1682 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1686 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1687 avahi_free(reverse);
1692 if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1693 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1697 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1698 avahi_free(reverse);
1708 static int server_add_txt_strlst_nocopy(
1710 AvahiSEntryGroup *g,
1711 AvahiIfIndex interface,
1712 AvahiProtocol protocol,
1713 AvahiEntryFlags flags,
1716 AvahiStringList *strlst) {
1723 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl)))
1724 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1726 r->data.txt.string_list = strlst;
1727 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1728 avahi_record_unref(r);
1733 int avahi_server_add_txt_strlst(
1735 AvahiSEntryGroup *g,
1736 AvahiIfIndex interface,
1737 AvahiProtocol protocol,
1738 AvahiEntryFlags flags,
1741 AvahiStringList *strlst) {
1745 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1748 int avahi_server_add_txt_va(
1750 AvahiSEntryGroup *g,
1751 AvahiIfIndex interface,
1752 AvahiProtocol protocol,
1753 AvahiEntryFlags flags,
1760 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1763 int avahi_server_add_txt(
1765 AvahiSEntryGroup *g,
1766 AvahiIfIndex interface,
1767 AvahiProtocol protocol,
1768 AvahiEntryFlags flags,
1779 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1785 static void escape_service_name(char *d, size_t size, const char *s) {
1790 while (*s && size >= 2) {
1791 if (*s == '.' || *s == '\\') {
1807 static int server_add_service_strlst_nocopy(
1809 AvahiSEntryGroup *g,
1810 AvahiIfIndex interface,
1811 AvahiProtocol protocol,
1817 AvahiStringList *strlst) {
1819 char ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1820 char *t = NULL, *d = NULL, *h = NULL;
1821 AvahiRecord *r = NULL;
1828 if (!avahi_is_valid_service_name(name))
1829 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1831 if (!avahi_is_valid_service_type(type))
1832 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1834 if (domain && !avahi_is_valid_domain_name(domain))
1835 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1837 if (host && !avahi_is_valid_domain_name(host))
1838 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1840 escape_service_name(ename, sizeof(ename), name);
1843 domain = s->domain_name;
1846 host = s->host_name_fqdn;
1848 if (!(d = avahi_normalize_name(domain)) ||
1849 !(t = avahi_normalize_name(type)) ||
1850 !(h = avahi_normalize_name(host))) {
1851 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1855 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1856 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1858 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1861 if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1862 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1866 r->data.srv.priority = 0;
1867 r->data.srv.weight = 0;
1868 r->data.srv.port = port;
1869 r->data.srv.name = h;
1871 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1872 avahi_record_unref(r);
1877 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1883 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1884 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1892 avahi_string_list_free(strlst);
1897 int avahi_server_add_service_strlst(
1899 AvahiSEntryGroup *g,
1900 AvahiIfIndex interface,
1901 AvahiProtocol protocol,
1907 AvahiStringList *strlst) {
1913 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1916 int avahi_server_add_service_va(
1918 AvahiSEntryGroup *g,
1919 AvahiIfIndex interface,
1920 AvahiProtocol protocol,
1932 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1935 int avahi_server_add_service(
1937 AvahiSEntryGroup *g,
1938 AvahiIfIndex interface,
1939 AvahiProtocol protocol,
1955 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1960 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
1961 static const char hex[] = "0123456789abcdef";
1963 const uint8_t *k = p;
1965 while (sl > 1 && pl > 0) {
1966 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1982 int avahi_server_add_dns_server_address(
1984 AvahiSEntryGroup *g,
1985 AvahiIfIndex interface,
1986 AvahiProtocol protocol,
1988 AvahiDNSServerType type,
1989 const AvahiAddress *address,
1990 uint16_t port /** should be 53 */) {
1998 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1999 assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
2002 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2004 if (domain && !avahi_is_valid_domain_name(domain))
2005 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2007 if (address->family == AVAHI_PROTO_INET) {
2008 hexstring(n+3, sizeof(n)-3, &address->data, 4);
2009 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2010 r->data.a.address = address->data.ipv4;
2012 hexstring(n+3, sizeof(n)-3, &address->data, 6);
2013 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2014 r->data.aaaa.address = address->data.ipv6;
2018 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2020 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
2021 avahi_record_unref(r);
2026 return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
2029 int avahi_server_add_dns_server_name(
2031 AvahiSEntryGroup *g,
2032 AvahiIfIndex interface,
2033 AvahiProtocol protocol,
2035 AvahiDNSServerType type,
2037 uint16_t port /** should be 53 */) {
2040 char t[256], *d = NULL, *n = NULL;
2045 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2048 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2050 if (!avahi_is_valid_domain_name(name))
2051 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2053 if (domain && !avahi_is_valid_domain_name(domain))
2054 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2058 domain = s->domain_name;
2060 if (!(n = avahi_normalize_name(name)) ||
2061 !(d = avahi_normalize_name(domain))) {
2064 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2067 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2070 if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2072 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2075 r->data.srv.priority = 0;
2076 r->data.srv.weight = 0;
2077 r->data.srv.port = port;
2078 r->data.srv.name = n;
2079 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2080 avahi_record_unref(r);
2085 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2086 AvahiKey *k = userdata;
2092 avahi_interface_post_query(i, k, 0);
2095 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2099 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2102 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2105 if (g->state == state)
2108 assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2113 g->callback(g->server, g, state, g->userdata);
2116 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2117 AvahiSEntryGroup *g;
2121 if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2122 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2127 g->callback = callback;
2128 g->userdata = userdata;
2130 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2132 g->n_register_try = 0;
2133 g->register_time_event = NULL;
2134 g->register_time.tv_sec = 0;
2135 g->register_time.tv_usec = 0;
2136 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2138 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2142 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2148 for (e = g->entries; e; e = e->by_group_next) {
2150 avahi_goodbye_entry(g->server, e, 1);
2155 if (g->register_time_event) {
2156 avahi_time_event_free(g->register_time_event);
2157 g->register_time_event = NULL;
2162 g->server->need_group_cleanup = 1;
2163 g->server->need_entry_cleanup = 1;
2166 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2169 gettimeofday(&g->register_time, NULL);
2171 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2174 avahi_announce_group(g->server, g);
2175 avahi_s_entry_group_check_probed(g, 0);
2179 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2180 AvahiSEntryGroup *g = userdata;
2183 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2185 avahi_time_event_free(g->register_time_event);
2186 g->register_time_event = NULL;
2188 /* Holdoff time passed, so let's start probing */
2189 entry_group_commit_real(g);
2192 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2198 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2199 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2201 g->n_register_try++;
2203 avahi_timeval_add(&g->register_time,
2204 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2205 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2206 AVAHI_RR_HOLDOFF_MSEC));
2208 gettimeofday(&now, NULL);
2210 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2211 /* Holdoff time passed, so let's start probing */
2212 /* avahi_log_debug("Holdoff passed, directly going on."); */
2214 entry_group_commit_real(g);
2216 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2218 /* Holdoff time has not yet passed, so let's wait */
2219 assert(!g->register_time_event);
2220 g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2222 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2228 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2232 if (g->register_time_event) {
2233 avahi_time_event_free(g->register_time_event);
2234 g->register_time_event = NULL;
2237 for (e = g->entries; e; e = e->by_group_next) {
2239 avahi_goodbye_entry(g->server, e, 1);
2244 if (g->register_time_event) {
2245 avahi_time_event_free(g->register_time_event);
2246 g->register_time_event = NULL;
2249 g->server->need_entry_cleanup = 1;
2252 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2255 int avahi_entry_is_commited(AvahiEntry *e) {
2260 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2261 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2264 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2271 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2274 g->userdata = userdata;
2277 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2283 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2287 /* Look for an entry that is not dead */
2288 for (e = g->entries; e; e = e->by_group_next)
2295 const char* avahi_server_get_domain_name(AvahiServer *s) {
2298 return s->domain_name;
2301 const char* avahi_server_get_host_name(AvahiServer *s) {
2304 return s->host_name;
2307 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2310 return s->host_name_fqdn;
2313 void* avahi_server_get_data(AvahiServer *s) {
2319 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2322 s->userdata = userdata;
2325 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2331 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2334 memset(c, 0, sizeof(AvahiServerConfig));
2337 c->host_name = NULL;
2338 c->domain_name = NULL;
2339 c->check_response_ttl = 0;
2340 c->publish_hinfo = 1;
2341 c->publish_addresses = 1;
2342 c->publish_workstation = 1;
2343 c->publish_domain = 1;
2344 c->use_iff_running = 0;
2345 c->enable_reflector = 0;
2351 void avahi_server_config_free(AvahiServerConfig *c) {
2354 avahi_free(c->host_name);
2355 avahi_free(c->domain_name);
2358 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2359 char *d = NULL, *h = NULL;
2364 if (!(h = avahi_strdup(c->host_name)))
2368 if (!(d = avahi_strdup(c->domain_name))) {
2375 ret->domain_name = d;
2380 int avahi_server_errno(AvahiServer *s) {
2386 /* Just for internal use */
2387 int avahi_server_set_errno(AvahiServer *s, int error) {
2390 return s->error = error;