4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <sys/socket.h>
27 #include <arpa/inet.h>
29 #include <sys/utsname.h>
35 #include <avahi-common/domain.h>
36 #include <avahi-common/timeval.h>
37 #include <avahi-common/malloc.h>
38 #include <avahi-common/error.h>
47 #define AVAHI_RR_HOLDOFF_MSEC 1000
48 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
49 #define AVAHI_RR_RATE_LIMIT_COUNT 15
51 static void free_entry(AvahiServer*s, AvahiEntry *e) {
57 avahi_goodbye_entry(s, e, 1);
59 /* Remove from linked list */
60 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
62 /* Remove from hash table indexed by name */
63 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
64 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
66 avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
68 avahi_hashmap_remove(s->entries_by_key, e->record->key);
70 /* Remove from associated group */
72 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
74 avahi_record_unref(e->record);
78 static void free_group(AvahiServer *s, AvahiSEntryGroup *g) {
83 free_entry(s, g->entries);
85 if (g->register_time_event)
86 avahi_time_event_free(g->register_time_event);
88 AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g);
92 static void cleanup_dead(AvahiServer *s) {
95 if (s->need_group_cleanup) {
96 AvahiSEntryGroup *g, *next;
98 for (g = s->groups; g; g = next) {
99 next = g->groups_next;
105 s->need_group_cleanup = 0;
108 if (s->need_entry_cleanup) {
109 AvahiEntry *e, *next;
111 for (e = s->entries; e; e = next) {
112 next = e->entries_next;
118 s->need_entry_cleanup = 0;
121 if (s->need_browser_cleanup)
122 avahi_browser_cleanup(s);
125 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
134 assert(type != AVAHI_DNS_TYPE_ANY);
136 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
139 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
140 if (!e->dead && avahi_entry_is_registered(s, e, i))
141 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
146 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
152 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
153 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
154 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
155 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
156 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
157 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
158 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
163 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
168 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
171 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
179 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
180 /* avahi_free(txt); */
182 if (avahi_key_is_pattern(k)) {
184 /* Handle ANY query */
186 for (e = s->entries; e; e = e->entries_next)
187 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
188 avahi_server_prepare_response(s, i, e, unicast_response, 0);
192 /* Handle all other queries */
194 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
195 if (!e->dead && avahi_entry_is_registered(s, e, i))
196 avahi_server_prepare_response(s, i, e, unicast_response, 0);
200 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
207 for (k = e->group->entries; k; k = k->by_group_next) {
209 avahi_goodbye_entry(s, k, 0);
214 e->group->n_probing = 0;
216 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
218 avahi_goodbye_entry(s, e, 0);
222 s->need_entry_cleanup = 1;
225 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
231 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
233 withdraw_entry(s, e);
236 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
239 int ours = 0, won = 0, lost = 0;
245 t = avahi_record_to_string(record);
247 /* avahi_log_debug("incoming_probe()"); */
249 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
256 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
261 if (avahi_entry_is_probing(s, e, i)) {
273 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
275 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
276 withdraw_rrset(s, record->key);
278 /* avahi_log_debug("Not conflicting probe"); */
284 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique, const AvahiAddress *a) {
285 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
286 AvahiEntry *e, *n, *conflicting_entry = NULL;
293 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
295 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
298 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
301 /* Either our entry or the other is intended to be unique, so let's check */
303 if (avahi_record_equal_no_ttl(e->record, record)) {
304 ours = 1; /* We have an identical record, so this is no conflict */
306 /* Check wheter there is a TTL conflict */
307 if (record->ttl <= e->record->ttl/2 &&
308 avahi_entry_is_registered(s, e, i)) {
311 t = avahi_record_to_string(record);
313 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
314 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
320 /* There's no need to check the other entries of this RRset */
325 if (avahi_entry_is_registered(s, e, i)) {
327 /* A conflict => we have to return to probe mode */
329 conflicting_entry = e;
331 } else if (avahi_entry_is_probing(s, e, i)) {
333 /* We are currently registering a matching record, but
334 * someone else already claimed it, so let's
337 withdraw_immediately = 1;
342 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
344 if (!ours && conflict) {
349 t = avahi_record_to_string(record);
351 if (withdraw_immediately) {
352 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
353 withdraw_rrset(s, record->key);
355 assert(conflicting_entry);
356 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
357 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
359 /* Local unique records are returned to probing
360 * state. Local shared records are reannounced. */
369 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
370 int *unicast_response = userdata;
374 assert(unicast_response);
376 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
379 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
383 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
386 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
390 assert(!legacy_unicast || (a && port > 0 && p));
392 if (legacy_unicast) {
393 AvahiDnsPacket *reply;
396 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
399 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
401 append_aux_records_to_list(s, i, r, 0);
403 if (avahi_dns_packet_append_record(reply, r, 0, 10))
404 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
406 char *t = avahi_record_to_string(r);
407 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
411 avahi_record_unref(r);
414 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
415 avahi_interface_send_packet_unicast(i, reply, a, port);
417 avahi_dns_packet_free(reply);
420 int unicast_response, flush_cache, auxiliary;
421 AvahiDnsPacket *reply = NULL;
424 /* In case the query packet was truncated never respond
425 immediately, because known answer suppression records might be
426 contained in later packets */
427 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
429 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
431 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
433 append_aux_records_to_list(s, i, r, unicast_response);
435 /* Due to some reasons the record has not been scheduled.
436 * The client requested an unicast response in that
437 * case. Therefore we prepare such a response */
444 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
448 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
450 /* Appending this record succeeded, so incremeant
451 * the specific header field, and return to the caller */
453 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
458 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
461 /* The record is too large for one packet, so create a larger packet */
463 avahi_dns_packet_free(reply);
464 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
465 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
466 size = AVAHI_DNS_PACKET_MAX_SIZE;
468 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
471 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
473 avahi_dns_packet_free(reply);
474 t = avahi_record_to_string(r);
475 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
479 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
482 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
483 avahi_interface_send_packet_unicast(i, reply, a, port);
484 avahi_dns_packet_free(reply);
489 avahi_record_unref(r);
493 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
494 avahi_interface_send_packet_unicast(i, reply, a, port);
495 avahi_dns_packet_free(reply);
499 avahi_record_list_flush(s->record_list);
503 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
510 if (!s->config.enable_reflector)
513 for (j = s->monitor->interfaces; j; j = j->interface_next)
514 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
515 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
518 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
519 AvahiServer *s = userdata;
526 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
530 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
537 if (!s->config.enable_reflector)
540 for (j = s->monitor->interfaces; j; j = j->interface_next)
541 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
542 /* Post the query to other networks */
543 avahi_interface_post_query(j, k, 1);
545 /* Reply from caches of other network. This is needed to
546 * "work around" known answer suppression. */
548 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
552 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
559 if (!s->config.enable_reflector)
562 for (j = s->monitor->interfaces; j; j = j->interface_next)
563 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
564 avahi_interface_post_probe(j, r, 1);
567 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast) {
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 s->legacy_unicast_reflect_slots[idx] = slot;
716 slot->id = s->legacy_unicast_reflect_id++;
722 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
728 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
730 assert(s->legacy_unicast_reflect_slots[idx] == slot);
732 avahi_time_event_free(slot->time_event);
735 s->legacy_unicast_reflect_slots[idx] = NULL;
738 static void free_slots(AvahiServer *s) {
742 if (!s->legacy_unicast_reflect_slots)
745 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
746 if (s->legacy_unicast_reflect_slots[idx])
747 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
749 avahi_free(s->legacy_unicast_reflect_slots);
750 s->legacy_unicast_reflect_slots = NULL;
753 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
758 if (!s->legacy_unicast_reflect_slots)
761 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
763 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
766 return s->legacy_unicast_reflect_slots[idx];
769 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
770 AvahiLegacyUnicastReflectSlot *slot = userdata;
774 assert(slot->time_event == e);
776 deallocate_slot(slot->server, slot);
779 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
780 AvahiLegacyUnicastReflectSlot *slot;
788 assert(i->protocol == a->family);
790 if (!s->config.enable_reflector)
793 /* avahi_log_debug("legacy unicast reflector"); */
795 /* Reflecting legacy unicast queries is a little more complicated
796 than reflecting normal queries, since we must route the
797 responses back to the right client. Therefore we must store
798 some information for finding the right client contact data for
799 response packets. In contrast to normal queries legacy
800 unicast query and response packets are reflected untouched and
801 are not reassembled into larger packets */
803 if (!(slot = allocate_slot(s))) {
804 /* No slot available, we drop this legacy unicast query */
805 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
809 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
812 slot->interface = i->hardware->index;
814 avahi_elapse_time(&slot->elapse_time, 2000, 0);
815 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
817 /* Patch the packet with our new locally generatedt id */
818 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
820 for (j = s->monitor->interfaces; j; j = j->interface_next)
821 if (avahi_interface_relevant(j) &&
823 (s->config.reflect_ipv || j->protocol == i->protocol)) {
825 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
826 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
827 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
828 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
832 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
835 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
840 if (!s->config.enable_reflector)
843 avahi_address_from_sockaddr(sa, &a);
845 if (!avahi_address_is_local(s->monitor, &a))
848 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
849 struct sockaddr_in lsa;
850 socklen_t l = sizeof(lsa);
852 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
853 avahi_log_warn("getsockname(): %s", strerror(errno));
855 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
859 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
860 struct sockaddr_in6 lsa;
861 socklen_t l = sizeof(lsa);
863 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
864 avahi_log_warn("getsockname(): %s", strerror(errno));
866 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
872 static int is_mdns_mcast_address(const AvahiAddress *a) {
876 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
877 return avahi_address_cmp(a, &b) == 0;
880 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
891 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
892 !avahi_interface_relevant(i)) {
893 avahi_log_warn("Recieved packet from invalid interface.");
897 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
899 port = avahi_port_from_sockaddr(sa);
900 avahi_address_from_sockaddr(sa, &a);
902 if (avahi_address_is_ipv4_in_ipv6(&a))
903 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
906 if (originates_from_local_legacy_unicast_socket(s, sa))
907 /* This originates from our local reflector, so let's ignore it */
910 if (avahi_dns_packet_is_valid(p) < 0) {
911 avahi_log_warn("Recieved invalid packet.");
915 if (avahi_dns_packet_is_query(p)) {
916 int legacy_unicast = 0;
918 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
919 avahi_log_warn("Invalid query packet.");
923 if (port != AVAHI_MDNS_PORT) {
926 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
927 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
928 avahi_log_warn("Invalid legacy unicast query packet.");
936 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
938 handle_query_packet(s, p, i, &a, port, legacy_unicast);
940 /* avahi_log_debug("Handled query"); */
942 if (port != AVAHI_MDNS_PORT) {
943 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
947 if (ttl != 255 && s->config.check_response_ttl) {
948 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
952 if (!is_mdns_mcast_address(dest) &&
953 !avahi_interface_address_on_link(i, &a)) {
954 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
958 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
959 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
960 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
961 avahi_log_warn("Invalid response packet.");
965 handle_response_packet(s, p, i, &a);
966 /* avahi_log_debug("Handled response"); */
970 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, int ttl) {
971 AvahiInterface *i, *j;
974 AvahiLegacyUnicastReflectSlot *slot;
981 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
982 !avahi_interface_relevant(i)) {
983 avahi_log_warn("Recieved packet from invalid interface.");
987 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
989 port = avahi_port_from_sockaddr(sa);
990 avahi_address_from_sockaddr(sa, &a);
992 if (avahi_address_is_ipv4_in_ipv6(&a))
993 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
996 if (avahi_dns_packet_is_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
997 avahi_log_warn("Recieved invalid packet.");
1001 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1002 avahi_log_warn("Recieved legacy unicast response with unknown id");
1006 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
1007 !avahi_interface_relevant(j))
1010 /* Patch the original ID into this response */
1011 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1013 /* Forward the response to the correct client */
1014 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1016 /* Undo changes to packet */
1017 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1020 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1021 AvahiServer *s = userdata;
1026 struct sockaddr_in sa;
1027 struct sockaddr_in6 sa6;
1032 if (events & AVAHI_WATCH_IN) {
1034 if (fd == s->fd_ipv4) {
1035 dest.family = AVAHI_PROTO_INET;
1036 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1037 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1038 avahi_dns_packet_free(p);
1040 } else if (fd == s->fd_ipv6) {
1041 dest.family = AVAHI_PROTO_INET6;
1043 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1044 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1045 avahi_dns_packet_free(p);
1047 } else if (fd == s->fd_legacy_unicast_ipv4) {
1048 dest.family = AVAHI_PROTO_INET;
1050 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1051 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1052 avahi_dns_packet_free(p);
1054 } else if (fd == s->fd_legacy_unicast_ipv6) {
1055 dest.family = AVAHI_PROTO_INET6;
1057 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1058 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1059 avahi_dns_packet_free(p);
1069 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1072 if (s->state == state)
1078 s->callback(s, state, s->userdata);
1081 static void withdraw_host_rrs(AvahiServer *s) {
1084 if (s->hinfo_entry_group)
1085 avahi_s_entry_group_reset(s->hinfo_entry_group);
1087 if (s->browse_domain_entry_group)
1088 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1090 avahi_update_host_rrs(s->monitor, 1);
1091 s->n_host_rr_pending = 0;
1094 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1097 assert(s->n_host_rr_pending > 0);
1099 if (--s->n_host_rr_pending == 0)
1100 server_set_state(s, AVAHI_SERVER_RUNNING);
1103 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1106 s->n_host_rr_pending ++;
1109 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1113 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1114 s->state == AVAHI_SERVER_REGISTERING)
1115 avahi_server_increase_host_rr_pending(s);
1117 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1118 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1119 withdraw_host_rrs(s);
1120 server_set_state(s, AVAHI_SERVER_COLLISION);
1122 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1123 s->state == AVAHI_SERVER_REGISTERING)
1124 avahi_server_decrease_host_rr_pending(s);
1127 static void register_hinfo(AvahiServer *s) {
1128 struct utsname utsname;
1133 if (!s->config.publish_hinfo)
1136 if (s->hinfo_entry_group)
1137 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1139 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1141 if (!s->hinfo_entry_group) {
1142 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1146 /* Fill in HINFO rr */
1147 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1149 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1150 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1152 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r) < 0) {
1153 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1157 avahi_record_unref(r);
1160 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1161 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1165 static void register_localhost(AvahiServer *s) {
1169 /* Add localhost entries */
1170 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1171 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1173 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1174 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1177 static void register_browse_domain(AvahiServer *s) {
1180 if (!s->config.publish_domain)
1183 if (s->browse_domain_entry_group)
1184 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1186 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1188 if (!s->browse_domain_entry_group) {
1189 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1193 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) {
1194 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1198 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1199 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1202 static void register_stuff(AvahiServer *s) {
1205 server_set_state(s, AVAHI_SERVER_REGISTERING);
1206 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1209 register_browse_domain(s);
1210 avahi_update_host_rrs(s->monitor, 0);
1212 s->n_host_rr_pending --;
1214 if (s->n_host_rr_pending == 0)
1215 server_set_state(s, AVAHI_SERVER_RUNNING);
1218 static void update_fqdn(AvahiServer *s) {
1222 assert(s->host_name);
1223 assert(s->domain_name);
1225 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1228 avahi_free(s->host_name_fqdn);
1229 s->host_name_fqdn = n;
1232 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1236 if (host_name && !avahi_is_valid_host_name(host_name))
1237 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1239 withdraw_host_rrs(s);
1241 avahi_free(s->host_name);
1242 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1243 s->host_name[strcspn(s->host_name, ".")] = 0;
1250 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1252 assert(domain_name);
1254 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1255 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1257 withdraw_host_rrs(s);
1259 avahi_free(s->domain_name);
1260 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
1267 static int valid_server_config(const AvahiServerConfig *sc) {
1269 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1270 return AVAHI_ERR_INVALID_HOST_NAME;
1272 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1273 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1278 static int setup_sockets(AvahiServer *s) {
1281 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1282 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1284 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1285 return AVAHI_ERR_NO_NETWORK;
1287 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1288 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1289 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1290 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1292 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1293 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1295 s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL;
1297 if (s->fd_ipv4 >= 0)
1298 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1299 if (s->fd_ipv6 >= 0)
1300 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1301 if (s->fd_legacy_unicast_ipv4 >= 0)
1302 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1303 if (s->fd_legacy_unicast_ipv6 >= 0)
1304 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1309 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1313 if ((e = valid_server_config(sc)) < 0) {
1319 if (!(s = avahi_new(AvahiServer, 1))) {
1321 *error = AVAHI_ERR_NO_MEMORY;
1326 s->poll_api = poll_api;
1329 avahi_server_config_copy(&s->config, sc);
1331 avahi_server_config_init(&s->config);
1333 if ((e = setup_sockets(s)) < 0) {
1337 avahi_server_config_free(&s->config);
1343 s->n_host_rr_pending = 0;
1344 s->need_entry_cleanup = 0;
1345 s->need_group_cleanup = 0;
1346 s->need_browser_cleanup = 0;
1348 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1350 s->callback = callback;
1351 s->userdata = userdata;
1353 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1354 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1355 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1357 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1358 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1359 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1360 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1361 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1362 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1363 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1364 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1365 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1367 s->legacy_unicast_reflect_slots = NULL;
1368 s->legacy_unicast_reflect_id = 0;
1371 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1372 s->host_name[strcspn(s->host_name, ".")] = 0;
1373 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
1374 s->host_name_fqdn = NULL;
1377 s->record_list = avahi_record_list_new();
1379 s->state = AVAHI_SERVER_INVALID;
1381 s->monitor = avahi_interface_monitor_new(s);
1382 avahi_interface_monitor_sync(s->monitor);
1384 register_localhost(s);
1386 s->hinfo_entry_group = NULL;
1387 s->browse_domain_entry_group = NULL;
1390 s->error = AVAHI_OK;
1395 void avahi_server_free(AvahiServer* s) {
1399 free_entry(s, s->entries);
1401 avahi_interface_monitor_free(s->monitor);
1404 free_group(s, s->groups);
1408 while (s->dns_server_browsers)
1409 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1410 while (s->host_name_resolvers)
1411 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1412 while (s->address_resolvers)
1413 avahi_s_address_resolver_free(s->address_resolvers);
1414 while (s->domain_browsers)
1415 avahi_s_domain_browser_free(s->domain_browsers);
1416 while (s->service_type_browsers)
1417 avahi_s_service_type_browser_free(s->service_type_browsers);
1418 while (s->service_browsers)
1419 avahi_s_service_browser_free(s->service_browsers);
1420 while (s->service_resolvers)
1421 avahi_s_service_resolver_free(s->service_resolvers);
1422 while (s->record_browsers)
1423 avahi_s_record_browser_destroy(s->record_browsers);
1425 avahi_hashmap_free(s->record_browser_hashmap);
1426 avahi_hashmap_free(s->entries_by_key);
1428 avahi_time_event_queue_free(s->time_event_queue);
1430 avahi_record_list_free(s->record_list);
1433 s->poll_api->watch_free(s->watch_ipv4);
1435 s->poll_api->watch_free(s->watch_ipv6);
1436 if (s->watch_legacy_unicast_ipv4)
1437 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1438 if (s->watch_legacy_unicast_ipv6)
1439 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1441 if (s->fd_ipv4 >= 0)
1443 if (s->fd_ipv6 >= 0)
1445 if (s->fd_legacy_unicast_ipv4 >= 0)
1446 close(s->fd_legacy_unicast_ipv4);
1447 if (s->fd_legacy_unicast_ipv6 >= 0)
1448 close(s->fd_legacy_unicast_ipv6);
1450 avahi_free(s->host_name);
1451 avahi_free(s->domain_name);
1452 avahi_free(s->host_name_fqdn);
1454 avahi_server_config_free(&s->config);
1459 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1465 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1469 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1472 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1475 if ((interface <= 0 ||
1476 e->interface <= 0 ||
1477 e->interface == interface) &&
1478 (protocol == AVAHI_PROTO_UNSPEC ||
1479 e->protocol == AVAHI_PROTO_UNSPEC ||
1480 e->protocol == protocol))
1488 int avahi_server_add(
1490 AvahiSEntryGroup *g,
1491 AvahiIfIndex interface,
1492 AvahiProtocol protocol,
1493 AvahiEntryFlags flags,
1502 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1504 if (avahi_key_is_pattern(r->key))
1505 return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1507 if (!avahi_record_is_valid(r))
1508 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1510 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1511 return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1513 if (!(e = avahi_new(AvahiEntry, 1)))
1514 return avahi_server_set_errno(s, AVAHI_ERR_NO_NETWORK);
1517 e->record = avahi_record_ref(r);
1519 e->interface = interface;
1520 e->protocol = protocol;
1524 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1526 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1528 /* Insert into hash table indexed by name */
1529 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1530 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1531 avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1533 /* Insert into group list */
1535 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1537 avahi_announce_entry(s, e);
1542 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1543 AvahiEntry **e = (AvahiEntry**) state;
1548 *e = g ? g->entries : s->entries;
1550 while (*e && (*e)->dead)
1551 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1556 return avahi_record_ref((*e)->record);
1559 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1565 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1567 for (e = s->entries; e; e = e->entries_next) {
1574 if (!(t = avahi_record_to_string(e->record)))
1575 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1577 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1580 callback(ln, userdata);
1583 avahi_dump_caches(s->monitor, callback, userdata);
1587 int avahi_server_add_ptr(
1589 AvahiSEntryGroup *g,
1590 AvahiIfIndex interface,
1591 AvahiProtocol protocol,
1592 AvahiEntryFlags flags,
1603 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1604 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1606 r->data.ptr.name = avahi_normalize_name(dest);
1607 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1608 avahi_record_unref(r);
1612 int avahi_server_add_address(
1614 AvahiSEntryGroup *g,
1615 AvahiIfIndex interface,
1616 AvahiProtocol protocol,
1617 AvahiEntryFlags flags,
1627 if (!(n = avahi_normalize_name(name)))
1628 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1632 name = s->host_name_fqdn;
1634 if (!avahi_is_valid_domain_name(name)) {
1635 ret = avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1639 if (a->family == AVAHI_PROTO_INET) {
1643 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1644 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1648 r->data.a.address = a->data.ipv4;
1649 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1650 avahi_record_unref(r);
1655 if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1656 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1660 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1661 avahi_free(reverse);
1667 assert(a->family == AVAHI_PROTO_INET6);
1669 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1670 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1674 r->data.aaaa.address = a->data.ipv6;
1675 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1676 avahi_record_unref(r);
1681 if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
1682 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1686 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1687 avahi_free(reverse);
1692 if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1693 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1697 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1698 avahi_free(reverse);
1708 static int server_add_txt_strlst_nocopy(
1710 AvahiSEntryGroup *g,
1711 AvahiIfIndex interface,
1712 AvahiProtocol protocol,
1713 AvahiEntryFlags flags,
1716 AvahiStringList *strlst) {
1723 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl)))
1724 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1726 r->data.txt.string_list = strlst;
1727 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1728 avahi_record_unref(r);
1733 int avahi_server_add_txt_strlst(
1735 AvahiSEntryGroup *g,
1736 AvahiIfIndex interface,
1737 AvahiProtocol protocol,
1738 AvahiEntryFlags flags,
1741 AvahiStringList *strlst) {
1745 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1748 int avahi_server_add_txt_va(
1750 AvahiSEntryGroup *g,
1751 AvahiIfIndex interface,
1752 AvahiProtocol protocol,
1753 AvahiEntryFlags flags,
1760 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1763 int avahi_server_add_txt(
1765 AvahiSEntryGroup *g,
1766 AvahiIfIndex interface,
1767 AvahiProtocol protocol,
1768 AvahiEntryFlags flags,
1779 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1785 static void escape_service_name(char *d, size_t size, const char *s) {
1790 while (*s && size >= 2) {
1791 if (*s == '.' || *s == '\\') {
1807 static int server_add_service_strlst_nocopy(
1809 AvahiSEntryGroup *g,
1810 AvahiIfIndex interface,
1811 AvahiProtocol protocol,
1817 AvahiStringList *strlst) {
1819 char ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1820 char *t = NULL, *d = NULL, *h = NULL;
1821 AvahiRecord *r = NULL;
1828 if (!avahi_is_valid_service_name(name))
1829 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1831 if (!avahi_is_valid_service_type(type))
1832 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1834 if (domain && !avahi_is_valid_domain_name(domain))
1835 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1837 if (host && !avahi_is_valid_domain_name(host))
1838 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1840 escape_service_name(ename, sizeof(ename), name);
1843 domain = s->domain_name;
1846 host = s->host_name_fqdn;
1848 if (!(d = avahi_normalize_name(domain)) ||
1849 !(t = avahi_normalize_name(type)) ||
1850 !(h = avahi_normalize_name(host))) {
1851 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1855 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1856 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1858 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1861 if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1862 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1866 r->data.srv.priority = 0;
1867 r->data.srv.weight = 0;
1868 r->data.srv.port = port;
1869 r->data.srv.name = h;
1871 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1872 avahi_record_unref(r);
1877 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1883 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1884 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1892 avahi_string_list_free(strlst);
1897 int avahi_server_add_service_strlst(
1899 AvahiSEntryGroup *g,
1900 AvahiIfIndex interface,
1901 AvahiProtocol protocol,
1907 AvahiStringList *strlst) {
1913 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1916 int avahi_server_add_service_va(
1918 AvahiSEntryGroup *g,
1919 AvahiIfIndex interface,
1920 AvahiProtocol protocol,
1932 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1935 int avahi_server_add_service(
1937 AvahiSEntryGroup *g,
1938 AvahiIfIndex interface,
1939 AvahiProtocol protocol,
1955 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1960 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
1961 static const char hex[] = "0123456789abcdef";
1963 const uint8_t *k = p;
1965 while (sl > 1 && pl > 0) {
1966 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1982 int avahi_server_add_dns_server_address(
1984 AvahiSEntryGroup *g,
1985 AvahiIfIndex interface,
1986 AvahiProtocol protocol,
1988 AvahiDNSServerType type,
1989 const AvahiAddress *address,
1990 uint16_t port /** should be 53 */) {
1998 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1999 assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
2002 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2004 if (domain && !avahi_is_valid_domain_name(domain))
2005 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2007 if (address->family == AVAHI_PROTO_INET) {
2008 hexstring(n+3, sizeof(n)-3, &address->data, 4);
2009 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2010 r->data.a.address = address->data.ipv4;
2012 hexstring(n+3, sizeof(n)-3, &address->data, 6);
2013 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2014 r->data.aaaa.address = address->data.ipv6;
2018 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2020 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
2021 avahi_record_unref(r);
2026 return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
2029 int avahi_server_add_dns_server_name(
2031 AvahiSEntryGroup *g,
2032 AvahiIfIndex interface,
2033 AvahiProtocol protocol,
2035 AvahiDNSServerType type,
2037 uint16_t port /** should be 53 */) {
2040 char t[256], *d = NULL, *n = NULL;
2045 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2048 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2050 if (!avahi_is_valid_domain_name(name))
2051 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2053 if (domain && !avahi_is_valid_domain_name(domain))
2054 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2058 domain = s->domain_name;
2060 if (!(n = avahi_normalize_name(name)) ||
2061 !(d = avahi_normalize_name(domain))) {
2064 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2067 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2070 if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2072 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2075 r->data.srv.priority = 0;
2076 r->data.srv.weight = 0;
2077 r->data.srv.port = port;
2078 r->data.srv.name = n;
2079 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2080 avahi_record_unref(r);
2085 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2086 AvahiKey *k = userdata;
2092 avahi_interface_post_query(i, k, 0);
2095 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2099 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2102 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2105 if (g->state == state)
2108 assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2113 g->callback(g->server, g, state, g->userdata);
2116 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2117 AvahiSEntryGroup *g;
2121 if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2122 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2127 g->callback = callback;
2128 g->userdata = userdata;
2130 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2132 g->n_register_try = 0;
2133 g->register_time_event = NULL;
2134 g->register_time.tv_sec = 0;
2135 g->register_time.tv_usec = 0;
2136 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2138 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2142 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2148 for (e = g->entries; e; e = e->by_group_next) {
2150 avahi_goodbye_entry(g->server, e, 1);
2155 if (g->register_time_event) {
2156 avahi_time_event_free(g->register_time_event);
2157 g->register_time_event = NULL;
2162 g->server->need_group_cleanup = 1;
2163 g->server->need_entry_cleanup = 1;
2166 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2169 gettimeofday(&g->register_time, NULL);
2171 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2174 avahi_announce_group(g->server, g);
2175 avahi_s_entry_group_check_probed(g, 0);
2179 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2180 AvahiSEntryGroup *g = userdata;
2183 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2185 avahi_time_event_free(g->register_time_event);
2186 g->register_time_event = NULL;
2188 /* Holdoff time passed, so let's start probing */
2189 entry_group_commit_real(g);
2192 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2198 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2199 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2201 g->n_register_try++;
2203 avahi_timeval_add(&g->register_time,
2204 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2205 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2206 AVAHI_RR_HOLDOFF_MSEC));
2208 gettimeofday(&now, NULL);
2210 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2211 /* Holdoff time passed, so let's start probing */
2212 /* avahi_log_debug("Holdoff passed, directly going on."); */
2214 entry_group_commit_real(g);
2216 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2218 /* Holdoff time has not yet passed, so let's wait */
2219 assert(!g->register_time_event);
2220 g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2222 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2228 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2232 if (g->register_time_event) {
2233 avahi_time_event_free(g->register_time_event);
2234 g->register_time_event = NULL;
2237 for (e = g->entries; e; e = e->by_group_next) {
2239 avahi_goodbye_entry(g->server, e, 1);
2244 if (g->register_time_event) {
2245 avahi_time_event_free(g->register_time_event);
2246 g->register_time_event = NULL;
2249 g->server->need_entry_cleanup = 1;
2252 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2255 int avahi_entry_is_commited(AvahiEntry *e) {
2260 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2261 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2264 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2271 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2274 g->userdata = userdata;
2277 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2283 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2287 /* Look for an entry that is not dead */
2288 for (e = g->entries; e; e = e->by_group_next)
2295 const char* avahi_server_get_domain_name(AvahiServer *s) {
2298 return s->domain_name;
2301 const char* avahi_server_get_host_name(AvahiServer *s) {
2304 return s->host_name;
2307 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2310 return s->host_name_fqdn;
2313 void* avahi_server_get_data(AvahiServer *s) {
2319 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2322 s->userdata = userdata;
2325 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2331 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2334 memset(c, 0, sizeof(AvahiServerConfig));
2337 c->host_name = NULL;
2338 c->domain_name = NULL;
2339 c->check_response_ttl = 0;
2340 c->publish_hinfo = 1;
2341 c->publish_addresses = 1;
2342 c->publish_workstation = 1;
2343 c->publish_domain = 1;
2344 c->use_iff_running = 0;
2345 c->enable_reflector = 0;
2351 void avahi_server_config_free(AvahiServerConfig *c) {
2354 avahi_free(c->host_name);
2355 avahi_free(c->domain_name);
2358 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2359 char *d = NULL, *h = NULL;
2364 if (!(h = avahi_strdup(c->host_name)))
2368 if (!(d = avahi_strdup(c->domain_name))) {
2375 ret->domain_name = d;
2380 int avahi_server_errno(AvahiServer *s) {
2386 /* Just for internal use */
2387 int avahi_server_set_errno(AvahiServer *s, int error) {
2390 return s->error = error;