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);
1206 register_browse_domain(s);
1207 avahi_update_host_rrs(s->monitor, 0);
1209 if (s->n_host_rr_pending == 0)
1210 server_set_state(s, AVAHI_SERVER_RUNNING);
1213 static void update_fqdn(AvahiServer *s) {
1217 assert(s->host_name);
1218 assert(s->domain_name);
1220 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1223 avahi_free(s->host_name_fqdn);
1224 s->host_name_fqdn = n;
1227 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1231 if (host_name && !avahi_is_valid_host_name(host_name))
1232 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1234 withdraw_host_rrs(s);
1236 avahi_free(s->host_name);
1237 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1238 s->host_name[strcspn(s->host_name, ".")] = 0;
1245 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1247 assert(domain_name);
1249 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1250 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1252 withdraw_host_rrs(s);
1254 avahi_free(s->domain_name);
1255 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
1262 static int valid_server_config(const AvahiServerConfig *sc) {
1264 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1265 return AVAHI_ERR_INVALID_HOST_NAME;
1267 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1268 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1273 static int setup_sockets(AvahiServer *s) {
1276 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1277 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1279 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1280 return AVAHI_ERR_NO_NETWORK;
1282 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1283 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1284 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1285 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1287 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1288 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1290 s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL;
1292 if (s->fd_ipv4 >= 0)
1293 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1294 if (s->fd_ipv6 >= 0)
1295 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1296 if (s->fd_legacy_unicast_ipv4 >= 0)
1297 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1298 if (s->fd_legacy_unicast_ipv6 >= 0)
1299 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1304 AvahiServer *avahi_server_new(AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1308 if ((e = valid_server_config(sc)) < 0) {
1314 if (!(s = avahi_new(AvahiServer, 1))) {
1316 *error = AVAHI_ERR_NO_MEMORY;
1321 s->poll_api = poll_api;
1324 avahi_server_config_copy(&s->config, sc);
1326 avahi_server_config_init(&s->config);
1328 if ((e = setup_sockets(s)) < 0) {
1332 avahi_server_config_free(&s->config);
1338 s->n_host_rr_pending = 0;
1339 s->need_entry_cleanup = 0;
1340 s->need_group_cleanup = 0;
1341 s->need_browser_cleanup = 0;
1343 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1345 s->callback = callback;
1346 s->userdata = userdata;
1348 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1349 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1350 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1352 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1353 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1354 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1355 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1356 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1357 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1358 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1359 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1360 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1362 s->legacy_unicast_reflect_slots = NULL;
1363 s->legacy_unicast_reflect_id = 0;
1366 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1367 s->host_name[strcspn(s->host_name, ".")] = 0;
1368 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
1369 s->host_name_fqdn = NULL;
1372 s->record_list = avahi_record_list_new();
1374 s->state = AVAHI_SERVER_INVALID;
1376 s->monitor = avahi_interface_monitor_new(s);
1377 avahi_interface_monitor_sync(s->monitor);
1379 register_localhost(s);
1381 s->hinfo_entry_group = NULL;
1382 s->browse_domain_entry_group = NULL;
1385 s->error = AVAHI_OK;
1390 void avahi_server_free(AvahiServer* s) {
1394 free_entry(s, s->entries);
1396 avahi_interface_monitor_free(s->monitor);
1399 free_group(s, s->groups);
1403 while (s->dns_server_browsers)
1404 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1405 while (s->host_name_resolvers)
1406 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1407 while (s->address_resolvers)
1408 avahi_s_address_resolver_free(s->address_resolvers);
1409 while (s->domain_browsers)
1410 avahi_s_domain_browser_free(s->domain_browsers);
1411 while (s->service_type_browsers)
1412 avahi_s_service_type_browser_free(s->service_type_browsers);
1413 while (s->service_browsers)
1414 avahi_s_service_browser_free(s->service_browsers);
1415 while (s->service_resolvers)
1416 avahi_s_service_resolver_free(s->service_resolvers);
1417 while (s->record_browsers)
1418 avahi_s_record_browser_destroy(s->record_browsers);
1420 avahi_hashmap_free(s->record_browser_hashmap);
1421 avahi_hashmap_free(s->entries_by_key);
1423 avahi_time_event_queue_free(s->time_event_queue);
1425 avahi_record_list_free(s->record_list);
1428 s->poll_api->watch_free(s->watch_ipv4);
1430 s->poll_api->watch_free(s->watch_ipv6);
1431 if (s->watch_legacy_unicast_ipv4)
1432 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1433 if (s->watch_legacy_unicast_ipv6)
1434 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1436 if (s->fd_ipv4 >= 0)
1438 if (s->fd_ipv6 >= 0)
1440 if (s->fd_legacy_unicast_ipv4 >= 0)
1441 close(s->fd_legacy_unicast_ipv4);
1442 if (s->fd_legacy_unicast_ipv6 >= 0)
1443 close(s->fd_legacy_unicast_ipv6);
1445 avahi_free(s->host_name);
1446 avahi_free(s->domain_name);
1447 avahi_free(s->host_name_fqdn);
1449 avahi_server_config_free(&s->config);
1454 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1460 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1464 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1467 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1470 if (interface <= 0 ||
1471 e->interface <= 0 ||
1472 e->interface == interface ||
1473 protocol == AVAHI_PROTO_UNSPEC ||
1474 e->protocol == AVAHI_PROTO_UNSPEC ||
1475 e->protocol == protocol)
1484 int avahi_server_add(
1486 AvahiSEntryGroup *g,
1487 AvahiIfIndex interface,
1488 AvahiProtocol protocol,
1489 AvahiEntryFlags flags,
1498 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1500 if (avahi_key_is_pattern(r->key))
1501 return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1503 if (!avahi_record_is_valid(r))
1504 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1506 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1507 return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1509 if (!(e = avahi_new(AvahiEntry, 1)))
1510 return avahi_server_set_errno(s, AVAHI_ERR_NO_NETWORK);
1513 e->record = avahi_record_ref(r);
1515 e->interface = interface;
1516 e->protocol = protocol;
1520 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1522 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1524 /* Insert into hash table indexed by name */
1525 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1526 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1527 avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1529 /* Insert into group list */
1531 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1533 avahi_announce_entry(s, e);
1538 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1539 AvahiEntry **e = (AvahiEntry**) state;
1544 *e = g ? g->entries : s->entries;
1546 while (*e && (*e)->dead)
1547 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1552 return avahi_record_ref((*e)->record);
1555 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1561 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1563 for (e = s->entries; e; e = e->entries_next) {
1570 if (!(t = avahi_record_to_string(e->record)))
1571 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1573 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1576 callback(ln, userdata);
1579 avahi_dump_caches(s->monitor, callback, userdata);
1583 int avahi_server_add_ptr(
1585 AvahiSEntryGroup *g,
1586 AvahiIfIndex interface,
1587 AvahiProtocol protocol,
1588 AvahiEntryFlags flags,
1599 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1600 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1602 r->data.ptr.name = avahi_normalize_name(dest);
1603 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1604 avahi_record_unref(r);
1608 int avahi_server_add_address(
1610 AvahiSEntryGroup *g,
1611 AvahiIfIndex interface,
1612 AvahiProtocol protocol,
1613 AvahiEntryFlags flags,
1623 if (!(n = avahi_normalize_name(name)))
1624 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1628 name = s->host_name_fqdn;
1630 if (!avahi_is_valid_domain_name(name)) {
1631 ret = avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1635 if (a->family == AVAHI_PROTO_INET) {
1639 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1640 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1644 r->data.a.address = a->data.ipv4;
1645 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1646 avahi_record_unref(r);
1651 if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1652 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1656 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1657 avahi_free(reverse);
1663 assert(a->family == AVAHI_PROTO_INET6);
1665 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1666 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1670 r->data.aaaa.address = a->data.ipv6;
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_ipv6_arpa(&a->data.ipv6))) {
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);
1688 if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1689 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1693 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1694 avahi_free(reverse);
1704 static int server_add_txt_strlst_nocopy(
1706 AvahiSEntryGroup *g,
1707 AvahiIfIndex interface,
1708 AvahiProtocol protocol,
1709 AvahiEntryFlags flags,
1712 AvahiStringList *strlst) {
1719 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl)))
1720 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1722 r->data.txt.string_list = strlst;
1723 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1724 avahi_record_unref(r);
1729 int avahi_server_add_txt_strlst(
1731 AvahiSEntryGroup *g,
1732 AvahiIfIndex interface,
1733 AvahiProtocol protocol,
1734 AvahiEntryFlags flags,
1737 AvahiStringList *strlst) {
1741 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1744 int avahi_server_add_txt_va(
1746 AvahiSEntryGroup *g,
1747 AvahiIfIndex interface,
1748 AvahiProtocol protocol,
1749 AvahiEntryFlags flags,
1756 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1759 int avahi_server_add_txt(
1761 AvahiSEntryGroup *g,
1762 AvahiIfIndex interface,
1763 AvahiProtocol protocol,
1764 AvahiEntryFlags flags,
1775 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1781 static void escape_service_name(char *d, size_t size, const char *s) {
1786 while (*s && size >= 2) {
1787 if (*s == '.' || *s == '\\') {
1803 static int server_add_service_strlst_nocopy(
1805 AvahiSEntryGroup *g,
1806 AvahiIfIndex interface,
1807 AvahiProtocol protocol,
1813 AvahiStringList *strlst) {
1815 char ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1816 char *t = NULL, *d = NULL, *h = NULL;
1817 AvahiRecord *r = NULL;
1824 if (!avahi_is_valid_service_name(name))
1825 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1827 if (!avahi_is_valid_service_type(type))
1828 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1830 if (domain && !avahi_is_valid_domain_name(domain))
1831 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1833 if (host && !avahi_is_valid_domain_name(host))
1834 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1836 escape_service_name(ename, sizeof(ename), name);
1839 domain = s->domain_name;
1842 host = s->host_name_fqdn;
1844 if (!(d = avahi_normalize_name(domain)) ||
1845 !(t = avahi_normalize_name(type)) ||
1846 !(h = avahi_normalize_name(host))) {
1847 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1851 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1852 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1854 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1857 if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1858 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1862 r->data.srv.priority = 0;
1863 r->data.srv.weight = 0;
1864 r->data.srv.port = port;
1865 r->data.srv.name = h;
1867 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1868 avahi_record_unref(r);
1873 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1879 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1880 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1888 avahi_string_list_free(strlst);
1893 int avahi_server_add_service_strlst(
1895 AvahiSEntryGroup *g,
1896 AvahiIfIndex interface,
1897 AvahiProtocol protocol,
1903 AvahiStringList *strlst) {
1909 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1912 int avahi_server_add_service_va(
1914 AvahiSEntryGroup *g,
1915 AvahiIfIndex interface,
1916 AvahiProtocol protocol,
1928 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1931 int avahi_server_add_service(
1933 AvahiSEntryGroup *g,
1934 AvahiIfIndex interface,
1935 AvahiProtocol protocol,
1951 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1956 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
1957 static const char hex[] = "0123456789abcdef";
1959 const uint8_t *k = p;
1961 while (sl > 1 && pl > 0) {
1962 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1978 int avahi_server_add_dns_server_address(
1980 AvahiSEntryGroup *g,
1981 AvahiIfIndex interface,
1982 AvahiProtocol protocol,
1984 AvahiDNSServerType type,
1985 const AvahiAddress *address,
1986 uint16_t port /** should be 53 */) {
1994 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1995 assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1998 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2000 if (domain && !avahi_is_valid_domain_name(domain))
2001 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2003 if (address->family == AVAHI_PROTO_INET) {
2004 hexstring(n+3, sizeof(n)-3, &address->data, 4);
2005 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2006 r->data.a.address = address->data.ipv4;
2008 hexstring(n+3, sizeof(n)-3, &address->data, 6);
2009 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2010 r->data.aaaa.address = address->data.ipv6;
2014 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2016 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
2017 avahi_record_unref(r);
2022 return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
2025 int avahi_server_add_dns_server_name(
2027 AvahiSEntryGroup *g,
2028 AvahiIfIndex interface,
2029 AvahiProtocol protocol,
2031 AvahiDNSServerType type,
2033 uint16_t port /** should be 53 */) {
2036 char t[256], *d = NULL, *n = NULL;
2041 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2044 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2046 if (!avahi_is_valid_domain_name(name))
2047 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2049 if (domain && !avahi_is_valid_domain_name(domain))
2050 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2054 domain = s->domain_name;
2056 if (!(n = avahi_normalize_name(name)) ||
2057 !(d = avahi_normalize_name(domain))) {
2060 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2063 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2066 if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2068 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2071 r->data.srv.priority = 0;
2072 r->data.srv.weight = 0;
2073 r->data.srv.port = port;
2074 r->data.srv.name = n;
2075 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2076 avahi_record_unref(r);
2081 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2082 AvahiKey *k = userdata;
2088 avahi_interface_post_query(i, k, 0);
2091 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2095 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2098 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2101 if (g->state == state)
2104 assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2109 g->callback(g->server, g, state, g->userdata);
2112 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2113 AvahiSEntryGroup *g;
2117 if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2118 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2123 g->callback = callback;
2124 g->userdata = userdata;
2126 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2128 g->n_register_try = 0;
2129 g->register_time_event = NULL;
2130 g->register_time.tv_sec = 0;
2131 g->register_time.tv_usec = 0;
2132 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2134 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2138 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2144 for (e = g->entries; e; e = e->by_group_next) {
2146 avahi_goodbye_entry(g->server, e, 1);
2151 if (g->register_time_event) {
2152 avahi_time_event_free(g->register_time_event);
2153 g->register_time_event = NULL;
2158 g->server->need_group_cleanup = 1;
2159 g->server->need_entry_cleanup = 1;
2162 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2165 gettimeofday(&g->register_time, NULL);
2167 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2170 avahi_announce_group(g->server, g);
2171 avahi_s_entry_group_check_probed(g, 0);
2175 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2176 AvahiSEntryGroup *g = userdata;
2179 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2181 avahi_time_event_free(g->register_time_event);
2182 g->register_time_event = NULL;
2184 /* Holdoff time passed, so let's start probing */
2185 entry_group_commit_real(g);
2188 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2194 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2195 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2197 g->n_register_try++;
2199 avahi_timeval_add(&g->register_time,
2200 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2201 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2202 AVAHI_RR_HOLDOFF_MSEC));
2204 gettimeofday(&now, NULL);
2206 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2207 /* Holdoff time passed, so let's start probing */
2208 /* avahi_log_debug("Holdoff passed, directly going on."); */
2210 entry_group_commit_real(g);
2212 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2214 /* Holdoff time has not yet passed, so let's wait */
2215 assert(!g->register_time_event);
2216 g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2218 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2224 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2228 if (g->register_time_event) {
2229 avahi_time_event_free(g->register_time_event);
2230 g->register_time_event = NULL;
2233 for (e = g->entries; e; e = e->by_group_next) {
2235 avahi_goodbye_entry(g->server, e, 1);
2240 if (g->register_time_event) {
2241 avahi_time_event_free(g->register_time_event);
2242 g->register_time_event = NULL;
2245 g->server->need_entry_cleanup = 1;
2248 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2251 int avahi_entry_is_commited(AvahiEntry *e) {
2256 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2257 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2260 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2267 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2270 g->userdata = userdata;
2273 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2279 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2283 /* Look for an entry that is not dead */
2284 for (e = g->entries; e; e = e->by_group_next)
2291 const char* avahi_server_get_domain_name(AvahiServer *s) {
2294 return s->domain_name;
2297 const char* avahi_server_get_host_name(AvahiServer *s) {
2300 return s->host_name;
2303 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2306 return s->host_name_fqdn;
2309 void* avahi_server_get_data(AvahiServer *s) {
2315 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2318 s->userdata = userdata;
2321 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2327 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2330 memset(c, 0, sizeof(AvahiServerConfig));
2333 c->host_name = NULL;
2334 c->domain_name = NULL;
2335 c->check_response_ttl = 0;
2336 c->publish_hinfo = 1;
2337 c->publish_addresses = 1;
2338 c->publish_workstation = 1;
2339 c->publish_domain = 1;
2340 c->use_iff_running = 0;
2341 c->enable_reflector = 0;
2347 void avahi_server_config_free(AvahiServerConfig *c) {
2350 avahi_free(c->host_name);
2351 avahi_free(c->domain_name);
2354 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2355 char *d = NULL, *h = NULL;
2360 if (!(h = avahi_strdup(c->host_name)))
2364 if (!(d = avahi_strdup(c->domain_name))) {
2371 ret->domain_name = d;
2376 int avahi_server_errno(AvahiServer *s) {
2382 /* Just for internal use */
2383 int avahi_server_set_errno(AvahiServer *s, int error) {
2386 return s->error = error;