4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <sys/socket.h>
27 #include <arpa/inet.h>
29 #include <sys/utsname.h>
35 #include <avahi-common/domain.h>
36 #include <avahi-common/timeval.h>
37 #include <avahi-common/malloc.h>
38 #include <avahi-common/error.h>
47 #define AVAHI_RR_HOLDOFF_MSEC 1000
48 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
49 #define AVAHI_RR_RATE_LIMIT_COUNT 15
51 static void free_entry(AvahiServer*s, AvahiEntry *e) {
57 avahi_goodbye_entry(s, e, 1);
59 /* Remove from linked list */
60 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
62 /* Remove from hash table indexed by name */
63 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
64 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
66 avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
68 avahi_hashmap_remove(s->entries_by_key, e->record->key);
70 /* Remove from associated group */
72 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
74 avahi_record_unref(e->record);
78 static void free_group(AvahiServer *s, AvahiSEntryGroup *g) {
83 free_entry(s, g->entries);
85 if (g->register_time_event)
86 avahi_time_event_free(g->register_time_event);
88 AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g);
92 static void cleanup_dead(AvahiServer *s) {
95 if (s->need_group_cleanup) {
96 AvahiSEntryGroup *g, *next;
98 for (g = s->groups; g; g = next) {
99 next = g->groups_next;
105 s->need_group_cleanup = 0;
108 if (s->need_entry_cleanup) {
109 AvahiEntry *e, *next;
111 for (e = s->entries; e; e = next) {
112 next = e->entries_next;
118 s->need_entry_cleanup = 0;
121 if (s->need_browser_cleanup)
122 avahi_browser_cleanup(s);
125 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
134 assert(type != AVAHI_DNS_TYPE_ANY);
136 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
139 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
140 if (!e->dead && avahi_entry_is_registered(s, e, i))
141 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
146 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
152 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
153 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
154 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
155 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
156 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
157 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
158 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
163 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
168 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
171 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
179 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
180 /* avahi_free(txt); */
182 if (avahi_key_is_pattern(k)) {
184 /* Handle ANY query */
186 for (e = s->entries; e; e = e->entries_next)
187 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
188 avahi_server_prepare_response(s, i, e, unicast_response, 0);
192 /* Handle all other queries */
194 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
195 if (!e->dead && avahi_entry_is_registered(s, e, i))
196 avahi_server_prepare_response(s, i, e, unicast_response, 0);
200 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
207 for (k = e->group->entries; k; k = k->by_group_next) {
209 avahi_goodbye_entry(s, k, 0);
214 e->group->n_probing = 0;
216 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
218 avahi_goodbye_entry(s, e, 0);
222 s->need_entry_cleanup = 1;
225 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
231 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
233 withdraw_entry(s, e);
236 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
239 int ours = 0, won = 0, lost = 0;
245 t = avahi_record_to_string(record);
247 /* avahi_log_debug("incoming_probe()"); */
249 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
256 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
261 if (avahi_entry_is_probing(s, e, i)) {
274 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
276 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
277 withdraw_rrset(s, record->key);
279 /* avahi_log_debug("Not conflicting probe"); */
285 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique, const AvahiAddress *a) {
286 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
287 AvahiEntry *e, *n, *conflicting_entry = NULL;
294 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
296 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
299 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
302 /* Either our entry or the other is intended to be unique, so let's check */
304 if (avahi_record_equal_no_ttl(e->record, record)) {
305 ours = 1; /* We have an identical record, so this is no conflict */
307 /* Check wheter there is a TTL conflict */
308 if (record->ttl <= e->record->ttl/2 &&
309 avahi_entry_is_registered(s, e, i)) {
312 t = avahi_record_to_string(record);
314 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
315 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
321 /* There's no need to check the other entries of this RRset */
326 if (avahi_entry_is_registered(s, e, i)) {
328 /* A conflict => we have to return to probe mode */
330 conflicting_entry = e;
332 } else if (avahi_entry_is_probing(s, e, i)) {
334 /* We are currently registering a matching record, but
335 * someone else already claimed it, so let's
338 withdraw_immediately = 1;
343 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
345 if (!ours && conflict) {
350 t = avahi_record_to_string(record);
352 if (withdraw_immediately) {
353 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
354 withdraw_rrset(s, record->key);
356 assert(conflicting_entry);
357 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
358 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
360 /* Local unique records are returned to probing
361 * state. Local shared records are reannounced. */
370 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
371 int *unicast_response = userdata;
375 assert(unicast_response);
377 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
380 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
384 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
387 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
391 assert(!legacy_unicast || (a && port > 0 && p));
393 if (legacy_unicast) {
394 AvahiDnsPacket *reply;
397 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
400 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
402 append_aux_records_to_list(s, i, r, 0);
404 if (avahi_dns_packet_append_record(reply, r, 0, 10))
405 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
407 char *t = avahi_record_to_string(r);
408 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
412 avahi_record_unref(r);
415 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
416 avahi_interface_send_packet_unicast(i, reply, a, port);
418 avahi_dns_packet_free(reply);
421 int unicast_response, flush_cache, auxiliary;
422 AvahiDnsPacket *reply = NULL;
425 /* In case the query packet was truncated never respond
426 immediately, because known answer suppression records might be
427 contained in later packets */
428 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
430 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
432 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
434 append_aux_records_to_list(s, i, r, unicast_response);
436 /* Due to some reasons the record has not been scheduled.
437 * The client requested an unicast response in that
438 * case. Therefore we prepare such a response */
445 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
449 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
451 /* Appending this record succeeded, so incremeant
452 * the specific header field, and return to the caller */
454 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
459 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
462 /* The record is too large for one packet, so create a larger packet */
464 avahi_dns_packet_free(reply);
465 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
466 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
467 size = AVAHI_DNS_PACKET_MAX_SIZE;
469 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
472 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
474 avahi_dns_packet_free(reply);
475 t = avahi_record_to_string(r);
476 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
480 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
483 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
484 avahi_interface_send_packet_unicast(i, reply, a, port);
485 avahi_dns_packet_free(reply);
490 avahi_record_unref(r);
494 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
495 avahi_interface_send_packet_unicast(i, reply, a, port);
496 avahi_dns_packet_free(reply);
500 avahi_record_list_flush(s->record_list);
504 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
511 if (!s->config.enable_reflector)
514 for (j = s->monitor->interfaces; j; j = j->interface_next)
515 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
516 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
519 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
520 AvahiServer *s = userdata;
527 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
531 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
538 if (!s->config.enable_reflector)
541 for (j = s->monitor->interfaces; j; j = j->interface_next)
542 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
543 /* Post the query to other networks */
544 avahi_interface_post_query(j, k, 1);
546 /* Reply from caches of other network. This is needed to
547 * "work around" known answer suppression. */
549 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
553 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
560 if (!s->config.enable_reflector)
563 for (j = s->monitor->interfaces; j; j = j->interface_next)
564 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
565 avahi_interface_post_probe(j, r, 1);
568 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast) {
577 /* avahi_log_debug("query"); */
579 assert(avahi_record_list_is_empty(s->record_list));
581 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
583 /* Handle the questions */
584 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
586 int unicast_response = 0;
588 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
589 avahi_log_warn("Packet too short (1)");
594 reflect_query(s, i, key);
596 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
597 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
598 /* Allow our own queries to be suppressed by incoming
599 * queries only when they do not include known answers */
600 avahi_query_scheduler_incoming(i->query_scheduler, key);
602 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
603 avahi_key_unref(key);
606 /* Known Answer Suppression */
607 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
611 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
612 avahi_log_warn("Packet too short (2)");
616 if (handle_conflict(s, i, record, unique, a)) {
617 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
618 avahi_record_list_drop(s->record_list, record);
621 avahi_record_unref(record);
625 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
629 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
630 avahi_log_warn("Packet too short (3)");
634 if (!avahi_key_is_pattern(record->key)) {
635 reflect_probe(s, i, record);
636 incoming_probe(s, record, i);
639 avahi_record_unref(record);
642 if (!avahi_record_list_is_empty(s->record_list))
643 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
648 avahi_record_list_flush(s->record_list);
651 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
659 /* avahi_log_debug("response"); */
661 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
662 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
667 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
668 avahi_log_warn("Packet too short (4)");
672 if (!avahi_key_is_pattern(record->key)) {
674 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
675 /* avahi_free(txt); */
677 if (handle_conflict(s, i, record, cache_flush, a)) {
678 reflect_response(s, i, record, cache_flush);
679 avahi_cache_update(i->cache, record, cache_flush, a);
680 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
684 avahi_record_unref(record);
687 /* If the incoming response contained a conflicting record, some
688 records have been scheduling for sending. We need to flush them
690 if (!avahi_record_list_is_empty(s->record_list))
691 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
694 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
695 unsigned n, idx = (unsigned) -1;
696 AvahiLegacyUnicastReflectSlot *slot;
700 if (!s->legacy_unicast_reflect_slots)
701 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
703 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
704 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
706 if (!s->legacy_unicast_reflect_slots[idx])
710 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
713 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
714 return NULL; /* OOM */
716 s->legacy_unicast_reflect_slots[idx] = slot;
717 slot->id = s->legacy_unicast_reflect_id++;
723 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
729 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
731 assert(s->legacy_unicast_reflect_slots[idx] == slot);
733 avahi_time_event_free(slot->time_event);
736 s->legacy_unicast_reflect_slots[idx] = NULL;
739 static void free_slots(AvahiServer *s) {
743 if (!s->legacy_unicast_reflect_slots)
746 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
747 if (s->legacy_unicast_reflect_slots[idx])
748 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
750 avahi_free(s->legacy_unicast_reflect_slots);
751 s->legacy_unicast_reflect_slots = NULL;
754 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
759 if (!s->legacy_unicast_reflect_slots)
762 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
764 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
767 return s->legacy_unicast_reflect_slots[idx];
770 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
771 AvahiLegacyUnicastReflectSlot *slot = userdata;
775 assert(slot->time_event == e);
777 deallocate_slot(slot->server, slot);
780 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
781 AvahiLegacyUnicastReflectSlot *slot;
789 assert(i->protocol == a->family);
791 if (!s->config.enable_reflector)
794 /* avahi_log_debug("legacy unicast reflector"); */
796 /* Reflecting legacy unicast queries is a little more complicated
797 than reflecting normal queries, since we must route the
798 responses back to the right client. Therefore we must store
799 some information for finding the right client contact data for
800 response packets. In contrast to normal queries legacy
801 unicast query and response packets are reflected untouched and
802 are not reassembled into larger packets */
804 if (!(slot = allocate_slot(s))) {
805 /* No slot available, we drop this legacy unicast query */
806 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
810 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
813 slot->interface = i->hardware->index;
815 avahi_elapse_time(&slot->elapse_time, 2000, 0);
816 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
818 /* Patch the packet with our new locally generatedt id */
819 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
821 for (j = s->monitor->interfaces; j; j = j->interface_next)
822 if (avahi_interface_relevant(j) &&
824 (s->config.reflect_ipv || j->protocol == i->protocol)) {
826 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
827 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
828 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
829 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
833 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
836 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
841 if (!s->config.enable_reflector)
844 avahi_address_from_sockaddr(sa, &a);
846 if (!avahi_address_is_local(s->monitor, &a))
849 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
850 struct sockaddr_in lsa;
851 socklen_t l = sizeof(lsa);
853 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
854 avahi_log_warn("getsockname(): %s", strerror(errno));
856 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
860 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
861 struct sockaddr_in6 lsa;
862 socklen_t l = sizeof(lsa);
864 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
865 avahi_log_warn("getsockname(): %s", strerror(errno));
867 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
873 static int is_mdns_mcast_address(const AvahiAddress *a) {
877 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
878 return avahi_address_cmp(a, &b) == 0;
881 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
892 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
893 !avahi_interface_relevant(i)) {
894 avahi_log_warn("Recieved packet from invalid interface.");
898 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
900 port = avahi_port_from_sockaddr(sa);
901 avahi_address_from_sockaddr(sa, &a);
903 if (avahi_address_is_ipv4_in_ipv6(&a))
904 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
907 if (originates_from_local_legacy_unicast_socket(s, sa))
908 /* This originates from our local reflector, so let's ignore it */
911 if (avahi_dns_packet_is_valid(p) < 0) {
912 avahi_log_warn("Recieved invalid packet.");
916 if (avahi_dns_packet_is_query(p)) {
917 int legacy_unicast = 0;
919 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
920 avahi_log_warn("Invalid query packet.");
924 if (port != AVAHI_MDNS_PORT) {
927 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
928 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
929 avahi_log_warn("Invalid legacy unicast query packet.");
937 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
939 handle_query_packet(s, p, i, &a, port, legacy_unicast);
941 /* avahi_log_debug("Handled query"); */
943 if (port != AVAHI_MDNS_PORT) {
944 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
948 if (ttl != 255 && s->config.check_response_ttl) {
949 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
953 if (!is_mdns_mcast_address(dest) &&
954 !avahi_interface_address_on_link(i, &a)) {
955 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
959 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
960 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
961 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
962 avahi_log_warn("Invalid response packet.");
966 handle_response_packet(s, p, i, &a);
967 /* avahi_log_debug("Handled response"); */
971 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, int ttl) {
972 AvahiInterface *i, *j;
975 AvahiLegacyUnicastReflectSlot *slot;
982 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
983 !avahi_interface_relevant(i)) {
984 avahi_log_warn("Recieved packet from invalid interface.");
988 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
990 port = avahi_port_from_sockaddr(sa);
991 avahi_address_from_sockaddr(sa, &a);
993 if (avahi_address_is_ipv4_in_ipv6(&a))
994 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
997 if (avahi_dns_packet_is_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
998 avahi_log_warn("Recieved invalid packet.");
1002 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1003 avahi_log_warn("Recieved legacy unicast response with unknown id");
1007 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
1008 !avahi_interface_relevant(j))
1011 /* Patch the original ID into this response */
1012 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1014 /* Forward the response to the correct client */
1015 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1017 /* Undo changes to packet */
1018 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1021 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1022 AvahiServer *s = userdata;
1027 struct sockaddr_in sa;
1028 struct sockaddr_in6 sa6;
1033 if (events & AVAHI_WATCH_IN) {
1035 if (fd == s->fd_ipv4) {
1036 dest.family = AVAHI_PROTO_INET;
1037 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1038 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1039 avahi_dns_packet_free(p);
1041 } else if (fd == s->fd_ipv6) {
1042 dest.family = AVAHI_PROTO_INET6;
1044 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1045 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1046 avahi_dns_packet_free(p);
1048 } else if (fd == s->fd_legacy_unicast_ipv4) {
1049 dest.family = AVAHI_PROTO_INET;
1051 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1052 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1053 avahi_dns_packet_free(p);
1055 } else if (fd == s->fd_legacy_unicast_ipv6) {
1056 dest.family = AVAHI_PROTO_INET6;
1058 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1059 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1060 avahi_dns_packet_free(p);
1070 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1073 if (s->state == state)
1079 s->callback(s, state, s->userdata);
1082 static void withdraw_host_rrs(AvahiServer *s) {
1085 if (s->hinfo_entry_group)
1086 avahi_s_entry_group_reset(s->hinfo_entry_group);
1088 if (s->browse_domain_entry_group)
1089 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1091 avahi_update_host_rrs(s->monitor, 1);
1092 s->n_host_rr_pending = 0;
1095 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1098 assert(s->n_host_rr_pending > 0);
1100 if (--s->n_host_rr_pending == 0)
1101 server_set_state(s, AVAHI_SERVER_RUNNING);
1104 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1107 s->n_host_rr_pending ++;
1110 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1114 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1115 s->state == AVAHI_SERVER_REGISTERING)
1116 avahi_server_increase_host_rr_pending(s);
1118 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1119 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1120 withdraw_host_rrs(s);
1121 server_set_state(s, AVAHI_SERVER_COLLISION);
1123 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1124 s->state == AVAHI_SERVER_REGISTERING)
1125 avahi_server_decrease_host_rr_pending(s);
1128 static void register_hinfo(AvahiServer *s) {
1129 struct utsname utsname;
1134 if (!s->config.publish_hinfo)
1137 if (s->hinfo_entry_group)
1138 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1140 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1142 if (!s->hinfo_entry_group) {
1143 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1147 /* Fill in HINFO rr */
1148 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1150 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1151 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1153 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r) < 0) {
1154 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1158 avahi_record_unref(r);
1161 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1162 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1166 static void register_localhost(AvahiServer *s) {
1170 /* Add localhost entries */
1171 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1172 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1174 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1175 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1178 static void register_browse_domain(AvahiServer *s) {
1181 if (!s->config.publish_domain)
1184 if (s->browse_domain_entry_group)
1185 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1187 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1189 if (!s->browse_domain_entry_group) {
1190 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1194 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) {
1195 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1199 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1200 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1203 static void register_stuff(AvahiServer *s) {
1206 server_set_state(s, AVAHI_SERVER_REGISTERING);
1207 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1210 register_browse_domain(s);
1211 avahi_update_host_rrs(s->monitor, 0);
1213 s->n_host_rr_pending --;
1215 if (s->n_host_rr_pending == 0)
1216 server_set_state(s, AVAHI_SERVER_RUNNING);
1219 static void update_fqdn(AvahiServer *s) {
1223 assert(s->host_name);
1224 assert(s->domain_name);
1226 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1229 avahi_free(s->host_name_fqdn);
1230 s->host_name_fqdn = n;
1233 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1237 if (host_name && !avahi_is_valid_host_name(host_name))
1238 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1240 withdraw_host_rrs(s);
1242 avahi_free(s->host_name);
1243 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1244 s->host_name[strcspn(s->host_name, ".")] = 0;
1251 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1253 assert(domain_name);
1255 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1256 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1258 withdraw_host_rrs(s);
1260 avahi_free(s->domain_name);
1261 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
1268 static int valid_server_config(const AvahiServerConfig *sc) {
1270 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1271 return AVAHI_ERR_INVALID_HOST_NAME;
1273 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1274 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1279 static int setup_sockets(AvahiServer *s) {
1282 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1283 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1285 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1286 return AVAHI_ERR_NO_NETWORK;
1288 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1289 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1290 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1291 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1293 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1294 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1296 s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL;
1298 if (s->fd_ipv4 >= 0)
1299 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1300 if (s->fd_ipv6 >= 0)
1301 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1302 if (s->fd_legacy_unicast_ipv4 >= 0)
1303 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1304 if (s->fd_legacy_unicast_ipv6 >= 0)
1305 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1310 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1314 if ((e = valid_server_config(sc)) < 0) {
1320 if (!(s = avahi_new(AvahiServer, 1))) {
1322 *error = AVAHI_ERR_NO_MEMORY;
1327 s->poll_api = poll_api;
1330 avahi_server_config_copy(&s->config, sc);
1332 avahi_server_config_init(&s->config);
1334 if ((e = setup_sockets(s)) < 0) {
1338 avahi_server_config_free(&s->config);
1344 s->n_host_rr_pending = 0;
1345 s->need_entry_cleanup = 0;
1346 s->need_group_cleanup = 0;
1347 s->need_browser_cleanup = 0;
1349 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1351 s->callback = callback;
1352 s->userdata = userdata;
1354 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1355 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1356 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1358 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1359 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1360 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1361 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1362 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1363 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1364 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1365 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1366 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1368 s->legacy_unicast_reflect_slots = NULL;
1369 s->legacy_unicast_reflect_id = 0;
1372 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1373 s->host_name[strcspn(s->host_name, ".")] = 0;
1374 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
1375 s->host_name_fqdn = NULL;
1378 s->record_list = avahi_record_list_new();
1380 s->state = AVAHI_SERVER_INVALID;
1382 s->monitor = avahi_interface_monitor_new(s);
1383 avahi_interface_monitor_sync(s->monitor);
1385 register_localhost(s);
1387 s->hinfo_entry_group = NULL;
1388 s->browse_domain_entry_group = NULL;
1391 s->error = AVAHI_OK;
1396 void avahi_server_free(AvahiServer* s) {
1400 free_entry(s, s->entries);
1402 avahi_interface_monitor_free(s->monitor);
1405 free_group(s, s->groups);
1409 while (s->dns_server_browsers)
1410 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1411 while (s->host_name_resolvers)
1412 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1413 while (s->address_resolvers)
1414 avahi_s_address_resolver_free(s->address_resolvers);
1415 while (s->domain_browsers)
1416 avahi_s_domain_browser_free(s->domain_browsers);
1417 while (s->service_type_browsers)
1418 avahi_s_service_type_browser_free(s->service_type_browsers);
1419 while (s->service_browsers)
1420 avahi_s_service_browser_free(s->service_browsers);
1421 while (s->service_resolvers)
1422 avahi_s_service_resolver_free(s->service_resolvers);
1423 while (s->record_browsers)
1424 avahi_s_record_browser_destroy(s->record_browsers);
1426 avahi_hashmap_free(s->record_browser_hashmap);
1427 avahi_hashmap_free(s->entries_by_key);
1429 avahi_time_event_queue_free(s->time_event_queue);
1431 avahi_record_list_free(s->record_list);
1434 s->poll_api->watch_free(s->watch_ipv4);
1436 s->poll_api->watch_free(s->watch_ipv6);
1437 if (s->watch_legacy_unicast_ipv4)
1438 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1439 if (s->watch_legacy_unicast_ipv6)
1440 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1442 if (s->fd_ipv4 >= 0)
1444 if (s->fd_ipv6 >= 0)
1446 if (s->fd_legacy_unicast_ipv4 >= 0)
1447 close(s->fd_legacy_unicast_ipv4);
1448 if (s->fd_legacy_unicast_ipv6 >= 0)
1449 close(s->fd_legacy_unicast_ipv6);
1451 avahi_free(s->host_name);
1452 avahi_free(s->domain_name);
1453 avahi_free(s->host_name_fqdn);
1455 avahi_server_config_free(&s->config);
1460 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1466 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1470 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1473 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1476 if ((interface <= 0 ||
1477 e->interface <= 0 ||
1478 e->interface == interface) &&
1479 (protocol == AVAHI_PROTO_UNSPEC ||
1480 e->protocol == AVAHI_PROTO_UNSPEC ||
1481 e->protocol == protocol))
1489 int avahi_server_add(
1491 AvahiSEntryGroup *g,
1492 AvahiIfIndex interface,
1493 AvahiProtocol protocol,
1494 AvahiEntryFlags flags,
1503 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1505 if (avahi_key_is_pattern(r->key))
1506 return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1508 if (!avahi_record_is_valid(r))
1509 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1511 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1512 return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1514 if (!(e = avahi_new(AvahiEntry, 1)))
1515 return avahi_server_set_errno(s, AVAHI_ERR_NO_NETWORK);
1518 e->record = avahi_record_ref(r);
1520 e->interface = interface;
1521 e->protocol = protocol;
1525 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1527 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1529 /* Insert into hash table indexed by name */
1530 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1531 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1532 avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1534 /* Insert into group list */
1536 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1538 avahi_announce_entry(s, e);
1543 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1544 AvahiEntry **e = (AvahiEntry**) state;
1549 *e = g ? g->entries : s->entries;
1551 while (*e && (*e)->dead)
1552 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1557 return avahi_record_ref((*e)->record);
1560 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1566 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1568 for (e = s->entries; e; e = e->entries_next) {
1575 if (!(t = avahi_record_to_string(e->record)))
1576 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1578 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1581 callback(ln, userdata);
1584 avahi_dump_caches(s->monitor, callback, userdata);
1588 int avahi_server_add_ptr(
1590 AvahiSEntryGroup *g,
1591 AvahiIfIndex interface,
1592 AvahiProtocol protocol,
1593 AvahiEntryFlags flags,
1604 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1605 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1607 r->data.ptr.name = avahi_normalize_name(dest);
1608 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1609 avahi_record_unref(r);
1613 int avahi_server_add_address(
1615 AvahiSEntryGroup *g,
1616 AvahiIfIndex interface,
1617 AvahiProtocol protocol,
1618 AvahiEntryFlags flags,
1628 if (!(n = avahi_normalize_name(name)))
1629 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1633 name = s->host_name_fqdn;
1635 if (!avahi_is_valid_domain_name(name)) {
1636 ret = avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1640 if (a->family == AVAHI_PROTO_INET) {
1644 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1645 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1649 r->data.a.address = a->data.ipv4;
1650 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1651 avahi_record_unref(r);
1656 if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1657 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1661 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1662 avahi_free(reverse);
1668 assert(a->family == AVAHI_PROTO_INET6);
1670 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1671 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1675 r->data.aaaa.address = a->data.ipv6;
1676 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1677 avahi_record_unref(r);
1682 if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
1683 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1687 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1688 avahi_free(reverse);
1693 if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1694 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1698 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1699 avahi_free(reverse);
1709 static int server_add_txt_strlst_nocopy(
1711 AvahiSEntryGroup *g,
1712 AvahiIfIndex interface,
1713 AvahiProtocol protocol,
1714 AvahiEntryFlags flags,
1717 AvahiStringList *strlst) {
1724 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl)))
1725 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1727 r->data.txt.string_list = strlst;
1728 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1729 avahi_record_unref(r);
1734 int avahi_server_add_txt_strlst(
1736 AvahiSEntryGroup *g,
1737 AvahiIfIndex interface,
1738 AvahiProtocol protocol,
1739 AvahiEntryFlags flags,
1742 AvahiStringList *strlst) {
1746 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1749 int avahi_server_add_txt_va(
1751 AvahiSEntryGroup *g,
1752 AvahiIfIndex interface,
1753 AvahiProtocol protocol,
1754 AvahiEntryFlags flags,
1761 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1764 int avahi_server_add_txt(
1766 AvahiSEntryGroup *g,
1767 AvahiIfIndex interface,
1768 AvahiProtocol protocol,
1769 AvahiEntryFlags flags,
1780 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1786 static void escape_service_name(char *d, size_t size, const char *s) {
1791 while (*s && size >= 2) {
1792 if (*s == '.' || *s == '\\') {
1808 static int server_add_service_strlst_nocopy(
1810 AvahiSEntryGroup *g,
1811 AvahiIfIndex interface,
1812 AvahiProtocol protocol,
1818 AvahiStringList *strlst) {
1820 char ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1821 char *t = NULL, *d = NULL, *h = NULL;
1822 AvahiRecord *r = NULL;
1829 if (!avahi_is_valid_service_name(name))
1830 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1832 if (!avahi_is_valid_service_type(type))
1833 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1835 if (domain && !avahi_is_valid_domain_name(domain))
1836 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1838 if (host && !avahi_is_valid_domain_name(host))
1839 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1841 escape_service_name(ename, sizeof(ename), name);
1844 domain = s->domain_name;
1847 host = s->host_name_fqdn;
1849 if (!(d = avahi_normalize_name(domain)) ||
1850 !(t = avahi_normalize_name(type)) ||
1851 !(h = avahi_normalize_name(host))) {
1852 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1856 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1857 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1859 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1862 if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1863 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1867 r->data.srv.priority = 0;
1868 r->data.srv.weight = 0;
1869 r->data.srv.port = port;
1870 r->data.srv.name = h;
1872 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1873 avahi_record_unref(r);
1878 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1884 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1885 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1893 avahi_string_list_free(strlst);
1898 int avahi_server_add_service_strlst(
1900 AvahiSEntryGroup *g,
1901 AvahiIfIndex interface,
1902 AvahiProtocol protocol,
1908 AvahiStringList *strlst) {
1914 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1917 int avahi_server_add_service_va(
1919 AvahiSEntryGroup *g,
1920 AvahiIfIndex interface,
1921 AvahiProtocol protocol,
1933 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1936 int avahi_server_add_service(
1938 AvahiSEntryGroup *g,
1939 AvahiIfIndex interface,
1940 AvahiProtocol protocol,
1956 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1961 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
1962 static const char hex[] = "0123456789abcdef";
1964 const uint8_t *k = p;
1966 while (sl > 1 && pl > 0) {
1967 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1983 int avahi_server_add_dns_server_address(
1985 AvahiSEntryGroup *g,
1986 AvahiIfIndex interface,
1987 AvahiProtocol protocol,
1989 AvahiDNSServerType type,
1990 const AvahiAddress *address,
1991 uint16_t port /** should be 53 */) {
1999 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2000 assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
2003 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2005 if (domain && !avahi_is_valid_domain_name(domain))
2006 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2008 if (address->family == AVAHI_PROTO_INET) {
2009 hexstring(n+3, sizeof(n)-3, &address->data, 4);
2010 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2011 r->data.a.address = address->data.ipv4;
2013 hexstring(n+3, sizeof(n)-3, &address->data, 6);
2014 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2015 r->data.aaaa.address = address->data.ipv6;
2019 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2021 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
2022 avahi_record_unref(r);
2027 return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
2030 int avahi_server_add_dns_server_name(
2032 AvahiSEntryGroup *g,
2033 AvahiIfIndex interface,
2034 AvahiProtocol protocol,
2036 AvahiDNSServerType type,
2038 uint16_t port /** should be 53 */) {
2041 char t[256], *d = NULL, *n = NULL;
2046 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2049 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2051 if (!avahi_is_valid_domain_name(name))
2052 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2054 if (domain && !avahi_is_valid_domain_name(domain))
2055 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2059 domain = s->domain_name;
2061 if (!(n = avahi_normalize_name(name)) ||
2062 !(d = avahi_normalize_name(domain))) {
2065 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2068 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2071 if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2073 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2076 r->data.srv.priority = 0;
2077 r->data.srv.weight = 0;
2078 r->data.srv.port = port;
2079 r->data.srv.name = n;
2080 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2081 avahi_record_unref(r);
2086 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2087 AvahiKey *k = userdata;
2093 avahi_interface_post_query(i, k, 0);
2096 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2100 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2103 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2106 if (g->state == state)
2109 assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2114 g->callback(g->server, g, state, g->userdata);
2117 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2118 AvahiSEntryGroup *g;
2122 if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2123 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2128 g->callback = callback;
2129 g->userdata = userdata;
2131 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2133 g->n_register_try = 0;
2134 g->register_time_event = NULL;
2135 g->register_time.tv_sec = 0;
2136 g->register_time.tv_usec = 0;
2137 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2139 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2143 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2149 for (e = g->entries; e; e = e->by_group_next) {
2151 avahi_goodbye_entry(g->server, e, 1);
2156 if (g->register_time_event) {
2157 avahi_time_event_free(g->register_time_event);
2158 g->register_time_event = NULL;
2163 g->server->need_group_cleanup = 1;
2164 g->server->need_entry_cleanup = 1;
2167 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2170 gettimeofday(&g->register_time, NULL);
2172 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2175 avahi_announce_group(g->server, g);
2176 avahi_s_entry_group_check_probed(g, 0);
2180 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2181 AvahiSEntryGroup *g = userdata;
2184 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2186 avahi_time_event_free(g->register_time_event);
2187 g->register_time_event = NULL;
2189 /* Holdoff time passed, so let's start probing */
2190 entry_group_commit_real(g);
2193 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2199 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2200 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2202 g->n_register_try++;
2204 avahi_timeval_add(&g->register_time,
2205 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2206 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2207 AVAHI_RR_HOLDOFF_MSEC));
2209 gettimeofday(&now, NULL);
2211 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2212 /* Holdoff time passed, so let's start probing */
2213 /* avahi_log_debug("Holdoff passed, directly going on."); */
2215 entry_group_commit_real(g);
2217 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2219 /* Holdoff time has not yet passed, so let's wait */
2220 assert(!g->register_time_event);
2221 g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2223 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2229 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2233 if (g->register_time_event) {
2234 avahi_time_event_free(g->register_time_event);
2235 g->register_time_event = NULL;
2238 for (e = g->entries; e; e = e->by_group_next) {
2240 avahi_goodbye_entry(g->server, e, 1);
2245 if (g->register_time_event) {
2246 avahi_time_event_free(g->register_time_event);
2247 g->register_time_event = NULL;
2250 g->server->need_entry_cleanup = 1;
2253 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2256 int avahi_entry_is_commited(AvahiEntry *e) {
2261 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2262 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2265 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2272 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2275 g->userdata = userdata;
2278 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2284 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2288 /* Look for an entry that is not dead */
2289 for (e = g->entries; e; e = e->by_group_next)
2296 const char* avahi_server_get_domain_name(AvahiServer *s) {
2299 return s->domain_name;
2302 const char* avahi_server_get_host_name(AvahiServer *s) {
2305 return s->host_name;
2308 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2311 return s->host_name_fqdn;
2314 void* avahi_server_get_data(AvahiServer *s) {
2320 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2323 s->userdata = userdata;
2326 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2332 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2335 memset(c, 0, sizeof(AvahiServerConfig));
2338 c->host_name = NULL;
2339 c->domain_name = NULL;
2340 c->check_response_ttl = 0;
2341 c->publish_hinfo = 1;
2342 c->publish_addresses = 1;
2343 c->publish_workstation = 1;
2344 c->publish_domain = 1;
2345 c->use_iff_running = 0;
2346 c->enable_reflector = 0;
2352 void avahi_server_config_free(AvahiServerConfig *c) {
2355 avahi_free(c->host_name);
2356 avahi_free(c->domain_name);
2359 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2360 char *d = NULL, *h = NULL;
2365 if (!(h = avahi_strdup(c->host_name)))
2369 if (!(d = avahi_strdup(c->domain_name))) {
2376 ret->domain_name = d;
2381 int avahi_server_errno(AvahiServer *s) {
2387 /* Just for internal use */
2388 int avahi_server_set_errno(AvahiServer *s, int error) {
2391 return s->error = error;