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) {
301 /* Check if the incoming is a goodbye record */
302 if (avahi_record_is_goodbye(record)) {
304 if (avahi_record_equal_no_ttl(e->record, record)) {
308 t = avahi_record_to_string(record);
309 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
310 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
317 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
321 if (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique)
324 /* Either our entry or the other is intended to be unique, so let's check */
326 if (avahi_record_equal_no_ttl(e->record, record)) {
327 ours = 1; /* We have an identical record, so this is no conflict */
329 /* Check wheter there is a TTL conflict */
330 if (record->ttl <= e->record->ttl/2 &&
331 avahi_entry_is_registered(s, e, i)) {
334 t = avahi_record_to_string(record);
336 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
337 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
343 /* There's no need to check the other entries of this RRset */
348 if (avahi_entry_is_registered(s, e, i)) {
350 /* A conflict => we have to return to probe mode */
352 conflicting_entry = e;
354 } else if (avahi_entry_is_probing(s, e, i)) {
356 /* We are currently registering a matching record, but
357 * someone else already claimed it, so let's
360 withdraw_immediately = 1;
365 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
367 if (!ours && conflict) {
372 t = avahi_record_to_string(record);
374 if (withdraw_immediately) {
375 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
376 withdraw_rrset(s, record->key);
378 assert(conflicting_entry);
379 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
380 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
382 /* Local unique records are returned to probing
383 * state. Local shared records are reannounced. */
392 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
393 int *unicast_response = userdata;
397 assert(unicast_response);
399 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
402 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
406 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
409 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
413 assert(!legacy_unicast || (a && port > 0 && p));
415 if (legacy_unicast) {
416 AvahiDnsPacket *reply;
419 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
422 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
424 append_aux_records_to_list(s, i, r, 0);
426 if (avahi_dns_packet_append_record(reply, r, 0, 10))
427 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
429 char *t = avahi_record_to_string(r);
430 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
434 avahi_record_unref(r);
437 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
438 avahi_interface_send_packet_unicast(i, reply, a, port);
440 avahi_dns_packet_free(reply);
443 int unicast_response, flush_cache, auxiliary;
444 AvahiDnsPacket *reply = NULL;
447 /* In case the query packet was truncated never respond
448 immediately, because known answer suppression records might be
449 contained in later packets */
450 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
452 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
454 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
456 append_aux_records_to_list(s, i, r, unicast_response);
458 /* Due to some reasons the record has not been scheduled.
459 * The client requested an unicast response in that
460 * case. Therefore we prepare such a response */
467 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
471 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
473 /* Appending this record succeeded, so incremeant
474 * the specific header field, and return to the caller */
476 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
481 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
484 /* The record is too large for one packet, so create a larger packet */
486 avahi_dns_packet_free(reply);
487 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
488 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
489 size = AVAHI_DNS_PACKET_MAX_SIZE;
491 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
494 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
496 avahi_dns_packet_free(reply);
497 t = avahi_record_to_string(r);
498 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
502 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
505 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
506 avahi_interface_send_packet_unicast(i, reply, a, port);
507 avahi_dns_packet_free(reply);
512 avahi_record_unref(r);
516 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
517 avahi_interface_send_packet_unicast(i, reply, a, port);
518 avahi_dns_packet_free(reply);
522 avahi_record_list_flush(s->record_list);
526 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
533 if (!s->config.enable_reflector)
536 for (j = s->monitor->interfaces; j; j = j->interface_next)
537 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
538 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
541 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
542 AvahiServer *s = userdata;
549 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
553 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
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 /* Post the query to other networks */
566 avahi_interface_post_query(j, k, 1);
568 /* Reply from caches of other network. This is needed to
569 * "work around" known answer suppression. */
571 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
575 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
582 if (!s->config.enable_reflector)
585 for (j = s->monitor->interfaces; j; j = j->interface_next)
586 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
587 avahi_interface_post_probe(j, r, 1);
590 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
599 /* avahi_log_debug("query"); */
601 assert(avahi_record_list_is_empty(s->record_list));
603 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
605 /* Handle the questions */
606 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
608 int unicast_response = 0;
610 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
611 avahi_log_warn("Packet too short (1)");
615 if (!legacy_unicast && !from_local_iface)
616 reflect_query(s, i, key);
618 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
619 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
620 /* Allow our own queries to be suppressed by incoming
621 * queries only when they do not include known answers */
622 avahi_query_scheduler_incoming(i->query_scheduler, key);
624 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
625 avahi_key_unref(key);
628 if (!legacy_unicast) {
630 /* Known Answer Suppression */
631 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
635 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
636 avahi_log_warn("Packet too short (2)");
640 if (handle_conflict(s, i, record, unique, a)) {
641 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
642 avahi_record_list_drop(s->record_list, record);
645 avahi_record_unref(record);
649 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
653 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
654 avahi_log_warn("Packet too short (3)");
658 if (!avahi_key_is_pattern(record->key)) {
659 if (!from_local_iface)
660 reflect_probe(s, i, record);
661 incoming_probe(s, record, i);
664 avahi_record_unref(record);
668 if (!avahi_record_list_is_empty(s->record_list))
669 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
674 avahi_record_list_flush(s->record_list);
677 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
685 /* avahi_log_debug("response"); */
687 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
688 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
693 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
694 avahi_log_warn("Packet too short (4)");
698 if (!avahi_key_is_pattern(record->key)) {
700 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
701 /* avahi_free(txt); */
703 if (handle_conflict(s, i, record, cache_flush, a)) {
704 if (!from_local_iface)
705 reflect_response(s, i, record, cache_flush);
706 avahi_cache_update(i->cache, record, cache_flush, a);
707 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
711 avahi_record_unref(record);
714 /* If the incoming response contained a conflicting record, some
715 records have been scheduling for sending. We need to flush them
717 if (!avahi_record_list_is_empty(s->record_list))
718 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
721 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
722 unsigned n, idx = (unsigned) -1;
723 AvahiLegacyUnicastReflectSlot *slot;
727 if (!s->legacy_unicast_reflect_slots)
728 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
730 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
731 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
733 if (!s->legacy_unicast_reflect_slots[idx])
737 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
740 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
741 return NULL; /* OOM */
743 s->legacy_unicast_reflect_slots[idx] = slot;
744 slot->id = s->legacy_unicast_reflect_id++;
750 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
756 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
758 assert(s->legacy_unicast_reflect_slots[idx] == slot);
760 avahi_time_event_free(slot->time_event);
763 s->legacy_unicast_reflect_slots[idx] = NULL;
766 static void free_slots(AvahiServer *s) {
770 if (!s->legacy_unicast_reflect_slots)
773 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
774 if (s->legacy_unicast_reflect_slots[idx])
775 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
777 avahi_free(s->legacy_unicast_reflect_slots);
778 s->legacy_unicast_reflect_slots = NULL;
781 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
786 if (!s->legacy_unicast_reflect_slots)
789 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
791 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
794 return s->legacy_unicast_reflect_slots[idx];
797 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
798 AvahiLegacyUnicastReflectSlot *slot = userdata;
802 assert(slot->time_event == e);
804 deallocate_slot(slot->server, slot);
807 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
808 AvahiLegacyUnicastReflectSlot *slot;
816 assert(i->protocol == a->family);
818 if (!s->config.enable_reflector)
821 /* avahi_log_debug("legacy unicast reflector"); */
823 /* Reflecting legacy unicast queries is a little more complicated
824 than reflecting normal queries, since we must route the
825 responses back to the right client. Therefore we must store
826 some information for finding the right client contact data for
827 response packets. In contrast to normal queries legacy
828 unicast query and response packets are reflected untouched and
829 are not reassembled into larger packets */
831 if (!(slot = allocate_slot(s))) {
832 /* No slot available, we drop this legacy unicast query */
833 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
837 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
840 slot->interface = i->hardware->index;
842 avahi_elapse_time(&slot->elapse_time, 2000, 0);
843 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
845 /* Patch the packet with our new locally generatedt id */
846 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
848 for (j = s->monitor->interfaces; j; j = j->interface_next)
849 if (avahi_interface_relevant(j) &&
851 (s->config.reflect_ipv || j->protocol == i->protocol)) {
853 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
854 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
855 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
856 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
860 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
863 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
868 if (!s->config.enable_reflector)
871 avahi_address_from_sockaddr(sa, &a);
873 if (!avahi_address_is_local(s->monitor, &a))
876 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
877 struct sockaddr_in lsa;
878 socklen_t l = sizeof(lsa);
880 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
881 avahi_log_warn("getsockname(): %s", strerror(errno));
883 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
887 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
888 struct sockaddr_in6 lsa;
889 socklen_t l = sizeof(lsa);
891 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
892 avahi_log_warn("getsockname(): %s", strerror(errno));
894 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
900 static int is_mdns_mcast_address(const AvahiAddress *a) {
904 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
905 return avahi_address_cmp(a, &b) == 0;
908 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
910 assert(iface != AVAHI_IF_UNSPEC);
913 /* If it isn't the MDNS port it can't be generated by us */
914 if (port != AVAHI_MDNS_PORT)
917 return avahi_interface_has_address(s->monitor, iface, a);
920 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
924 int from_local_iface = 0;
932 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
933 !avahi_interface_relevant(i)) {
934 avahi_log_warn("Recieved packet from invalid interface.");
938 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
940 port = avahi_port_from_sockaddr(sa);
941 avahi_address_from_sockaddr(sa, &a);
943 if (avahi_address_is_ipv4_in_ipv6(&a))
944 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
947 if (originates_from_local_legacy_unicast_socket(s, sa))
948 /* This originates from our local reflector, so let's ignore it */
951 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
952 if (s->config.enable_reflector)
953 from_local_iface = originates_from_local_iface(s, iface, &a, port);
955 if (avahi_dns_packet_is_valid(p) < 0) {
956 avahi_log_warn("Recieved invalid packet.");
960 if (avahi_dns_packet_is_query(p)) {
961 int legacy_unicast = 0;
963 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
964 avahi_log_warn("Invalid query packet.");
968 if (port != AVAHI_MDNS_PORT) {
971 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
972 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
973 avahi_log_warn("Invalid legacy unicast query packet.");
981 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
983 handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
985 /* avahi_log_debug("Handled query"); */
987 if (port != AVAHI_MDNS_PORT) {
988 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
992 if (ttl != 255 && s->config.check_response_ttl) {
993 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
997 if (!is_mdns_mcast_address(dest) &&
998 !avahi_interface_address_on_link(i, &a)) {
999 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
1003 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
1004 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
1005 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
1006 avahi_log_warn("Invalid response packet.");
1010 handle_response_packet(s, p, i, &a, from_local_iface);
1011 /* avahi_log_debug("Handled response"); */
1015 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, int ttl) {
1016 AvahiInterface *i, *j;
1019 AvahiLegacyUnicastReflectSlot *slot;
1026 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
1027 !avahi_interface_relevant(i)) {
1028 avahi_log_warn("Recieved packet from invalid interface.");
1032 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
1034 port = avahi_port_from_sockaddr(sa);
1035 avahi_address_from_sockaddr(sa, &a);
1037 if (avahi_address_is_ipv4_in_ipv6(&a))
1038 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
1041 if (avahi_dns_packet_is_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
1042 avahi_log_warn("Recieved invalid packet.");
1046 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1047 avahi_log_warn("Recieved legacy unicast response with unknown id");
1051 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
1052 !avahi_interface_relevant(j))
1055 /* Patch the original ID into this response */
1056 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1058 /* Forward the response to the correct client */
1059 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1061 /* Undo changes to packet */
1062 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1065 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1066 AvahiServer *s = userdata;
1071 struct sockaddr_in sa;
1072 struct sockaddr_in6 sa6;
1077 if (events & AVAHI_WATCH_IN) {
1079 if (fd == s->fd_ipv4) {
1080 dest.family = AVAHI_PROTO_INET;
1081 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1082 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1083 avahi_dns_packet_free(p);
1085 } else if (fd == s->fd_ipv6) {
1086 dest.family = AVAHI_PROTO_INET6;
1088 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1089 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1090 avahi_dns_packet_free(p);
1092 } else if (fd == s->fd_legacy_unicast_ipv4) {
1093 dest.family = AVAHI_PROTO_INET;
1095 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1096 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1097 avahi_dns_packet_free(p);
1099 } else if (fd == s->fd_legacy_unicast_ipv6) {
1100 dest.family = AVAHI_PROTO_INET6;
1102 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1103 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1104 avahi_dns_packet_free(p);
1114 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1117 if (s->state == state)
1123 s->callback(s, state, s->userdata);
1126 static void withdraw_host_rrs(AvahiServer *s) {
1129 if (s->hinfo_entry_group)
1130 avahi_s_entry_group_reset(s->hinfo_entry_group);
1132 if (s->browse_domain_entry_group)
1133 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1135 avahi_update_host_rrs(s->monitor, 1);
1136 s->n_host_rr_pending = 0;
1139 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1142 assert(s->n_host_rr_pending > 0);
1144 if (--s->n_host_rr_pending == 0)
1145 server_set_state(s, AVAHI_SERVER_RUNNING);
1148 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1151 s->n_host_rr_pending ++;
1154 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1158 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1159 s->state == AVAHI_SERVER_REGISTERING)
1160 avahi_server_increase_host_rr_pending(s);
1162 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1163 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1164 withdraw_host_rrs(s);
1165 server_set_state(s, AVAHI_SERVER_COLLISION);
1167 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1168 s->state == AVAHI_SERVER_REGISTERING)
1169 avahi_server_decrease_host_rr_pending(s);
1172 static void register_hinfo(AvahiServer *s) {
1173 struct utsname utsname;
1178 if (!s->config.publish_hinfo)
1181 if (s->hinfo_entry_group)
1182 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1184 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1186 if (!s->hinfo_entry_group) {
1187 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1191 /* Fill in HINFO rr */
1192 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1194 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1195 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1197 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r) < 0) {
1198 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1202 avahi_record_unref(r);
1205 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1206 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1210 static void register_localhost(AvahiServer *s) {
1214 /* Add localhost entries */
1215 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1216 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1218 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1219 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1222 static void register_browse_domain(AvahiServer *s) {
1225 if (!s->config.publish_domain)
1228 if (s->browse_domain_entry_group)
1229 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1231 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1233 if (!s->browse_domain_entry_group) {
1234 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1238 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) {
1239 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1243 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1244 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1247 static void register_stuff(AvahiServer *s) {
1250 server_set_state(s, AVAHI_SERVER_REGISTERING);
1251 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1254 register_browse_domain(s);
1255 avahi_update_host_rrs(s->monitor, 0);
1257 s->n_host_rr_pending --;
1259 if (s->n_host_rr_pending == 0)
1260 server_set_state(s, AVAHI_SERVER_RUNNING);
1263 static void update_fqdn(AvahiServer *s) {
1267 assert(s->host_name);
1268 assert(s->domain_name);
1270 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1273 avahi_free(s->host_name_fqdn);
1274 s->host_name_fqdn = n;
1277 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1281 if (host_name && !avahi_is_valid_host_name(host_name))
1282 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1284 withdraw_host_rrs(s);
1286 avahi_free(s->host_name);
1287 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1288 s->host_name[strcspn(s->host_name, ".")] = 0;
1295 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1297 assert(domain_name);
1299 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1300 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1302 withdraw_host_rrs(s);
1304 avahi_free(s->domain_name);
1305 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
1312 static int valid_server_config(const AvahiServerConfig *sc) {
1314 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1315 return AVAHI_ERR_INVALID_HOST_NAME;
1317 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1318 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1323 static int setup_sockets(AvahiServer *s) {
1326 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1327 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1329 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1330 return AVAHI_ERR_NO_NETWORK;
1332 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1333 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1334 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1335 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1337 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1338 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1340 s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL;
1342 if (s->fd_ipv4 >= 0)
1343 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1344 if (s->fd_ipv6 >= 0)
1345 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1346 if (s->fd_legacy_unicast_ipv4 >= 0)
1347 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1348 if (s->fd_legacy_unicast_ipv6 >= 0)
1349 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1354 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1358 if ((e = valid_server_config(sc)) < 0) {
1364 if (!(s = avahi_new(AvahiServer, 1))) {
1366 *error = AVAHI_ERR_NO_MEMORY;
1371 s->poll_api = poll_api;
1374 avahi_server_config_copy(&s->config, sc);
1376 avahi_server_config_init(&s->config);
1378 if ((e = setup_sockets(s)) < 0) {
1382 avahi_server_config_free(&s->config);
1388 s->n_host_rr_pending = 0;
1389 s->need_entry_cleanup = 0;
1390 s->need_group_cleanup = 0;
1391 s->need_browser_cleanup = 0;
1393 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1395 s->callback = callback;
1396 s->userdata = userdata;
1398 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1399 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1400 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1402 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1403 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1404 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1405 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1406 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1407 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1408 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1409 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1410 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1412 s->legacy_unicast_reflect_slots = NULL;
1413 s->legacy_unicast_reflect_id = 0;
1416 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1417 s->host_name[strcspn(s->host_name, ".")] = 0;
1418 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
1419 s->host_name_fqdn = NULL;
1422 s->record_list = avahi_record_list_new();
1424 s->state = AVAHI_SERVER_INVALID;
1426 s->monitor = avahi_interface_monitor_new(s);
1427 avahi_interface_monitor_sync(s->monitor);
1429 register_localhost(s);
1431 s->hinfo_entry_group = NULL;
1432 s->browse_domain_entry_group = NULL;
1435 s->error = AVAHI_OK;
1440 void avahi_server_free(AvahiServer* s) {
1444 free_entry(s, s->entries);
1446 avahi_interface_monitor_free(s->monitor);
1449 free_group(s, s->groups);
1453 while (s->dns_server_browsers)
1454 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1455 while (s->host_name_resolvers)
1456 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1457 while (s->address_resolvers)
1458 avahi_s_address_resolver_free(s->address_resolvers);
1459 while (s->domain_browsers)
1460 avahi_s_domain_browser_free(s->domain_browsers);
1461 while (s->service_type_browsers)
1462 avahi_s_service_type_browser_free(s->service_type_browsers);
1463 while (s->service_browsers)
1464 avahi_s_service_browser_free(s->service_browsers);
1465 while (s->service_resolvers)
1466 avahi_s_service_resolver_free(s->service_resolvers);
1467 while (s->record_browsers)
1468 avahi_s_record_browser_destroy(s->record_browsers);
1470 avahi_hashmap_free(s->record_browser_hashmap);
1471 avahi_hashmap_free(s->entries_by_key);
1473 avahi_time_event_queue_free(s->time_event_queue);
1475 avahi_record_list_free(s->record_list);
1478 s->poll_api->watch_free(s->watch_ipv4);
1480 s->poll_api->watch_free(s->watch_ipv6);
1481 if (s->watch_legacy_unicast_ipv4)
1482 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1483 if (s->watch_legacy_unicast_ipv6)
1484 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1486 if (s->fd_ipv4 >= 0)
1488 if (s->fd_ipv6 >= 0)
1490 if (s->fd_legacy_unicast_ipv4 >= 0)
1491 close(s->fd_legacy_unicast_ipv4);
1492 if (s->fd_legacy_unicast_ipv6 >= 0)
1493 close(s->fd_legacy_unicast_ipv6);
1495 avahi_free(s->host_name);
1496 avahi_free(s->domain_name);
1497 avahi_free(s->host_name_fqdn);
1499 avahi_server_config_free(&s->config);
1504 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1510 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1514 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1517 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1520 if ((interface <= 0 ||
1521 e->interface <= 0 ||
1522 e->interface == interface) &&
1523 (protocol == AVAHI_PROTO_UNSPEC ||
1524 e->protocol == AVAHI_PROTO_UNSPEC ||
1525 e->protocol == protocol))
1533 int avahi_server_add(
1535 AvahiSEntryGroup *g,
1536 AvahiIfIndex interface,
1537 AvahiProtocol protocol,
1538 AvahiEntryFlags flags,
1547 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1549 if (avahi_key_is_pattern(r->key))
1550 return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1552 if (!avahi_record_is_valid(r))
1553 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1555 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1556 return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1558 if (!(e = avahi_new(AvahiEntry, 1)))
1559 return avahi_server_set_errno(s, AVAHI_ERR_NO_NETWORK);
1562 e->record = avahi_record_ref(r);
1564 e->interface = interface;
1565 e->protocol = protocol;
1569 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1571 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1573 /* Insert into hash table indexed by name */
1574 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1575 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1576 avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1578 /* Insert into group list */
1580 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1582 avahi_announce_entry(s, e);
1587 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1588 AvahiEntry **e = (AvahiEntry**) state;
1593 *e = g ? g->entries : s->entries;
1595 while (*e && (*e)->dead)
1596 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1601 return avahi_record_ref((*e)->record);
1604 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1610 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1612 for (e = s->entries; e; e = e->entries_next) {
1619 if (!(t = avahi_record_to_string(e->record)))
1620 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1622 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1625 callback(ln, userdata);
1628 avahi_dump_caches(s->monitor, callback, userdata);
1632 int avahi_server_add_ptr(
1634 AvahiSEntryGroup *g,
1635 AvahiIfIndex interface,
1636 AvahiProtocol protocol,
1637 AvahiEntryFlags flags,
1648 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1649 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1651 r->data.ptr.name = avahi_normalize_name(dest);
1652 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1653 avahi_record_unref(r);
1657 int avahi_server_add_address(
1659 AvahiSEntryGroup *g,
1660 AvahiIfIndex interface,
1661 AvahiProtocol protocol,
1662 AvahiEntryFlags flags,
1672 if (!(n = avahi_normalize_name(name)))
1673 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1677 name = s->host_name_fqdn;
1679 if (!avahi_is_valid_domain_name(name)) {
1680 ret = avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1684 if (a->family == AVAHI_PROTO_INET) {
1688 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1689 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1693 r->data.a.address = a->data.ipv4;
1694 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1695 avahi_record_unref(r);
1700 if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1701 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1705 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1706 avahi_free(reverse);
1712 assert(a->family == AVAHI_PROTO_INET6);
1714 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1715 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1719 r->data.aaaa.address = a->data.ipv6;
1720 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1721 avahi_record_unref(r);
1726 if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
1727 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1731 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1732 avahi_free(reverse);
1737 if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1738 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1742 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1743 avahi_free(reverse);
1753 static int server_add_txt_strlst_nocopy(
1755 AvahiSEntryGroup *g,
1756 AvahiIfIndex interface,
1757 AvahiProtocol protocol,
1758 AvahiEntryFlags flags,
1761 AvahiStringList *strlst) {
1768 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl)))
1769 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1771 r->data.txt.string_list = strlst;
1772 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1773 avahi_record_unref(r);
1778 int avahi_server_add_txt_strlst(
1780 AvahiSEntryGroup *g,
1781 AvahiIfIndex interface,
1782 AvahiProtocol protocol,
1783 AvahiEntryFlags flags,
1786 AvahiStringList *strlst) {
1790 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1793 int avahi_server_add_txt_va(
1795 AvahiSEntryGroup *g,
1796 AvahiIfIndex interface,
1797 AvahiProtocol protocol,
1798 AvahiEntryFlags flags,
1805 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1808 int avahi_server_add_txt(
1810 AvahiSEntryGroup *g,
1811 AvahiIfIndex interface,
1812 AvahiProtocol protocol,
1813 AvahiEntryFlags flags,
1824 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1830 static void escape_service_name(char *d, size_t size, const char *s) {
1835 while (*s && size >= 2) {
1836 if (*s == '.' || *s == '\\') {
1852 static int server_add_service_strlst_nocopy(
1854 AvahiSEntryGroup *g,
1855 AvahiIfIndex interface,
1856 AvahiProtocol protocol,
1862 AvahiStringList *strlst) {
1864 char ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1865 char *t = NULL, *d = NULL, *h = NULL;
1866 AvahiRecord *r = NULL;
1873 if (!avahi_is_valid_service_name(name))
1874 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1876 if (!avahi_is_valid_service_type(type))
1877 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1879 if (domain && !avahi_is_valid_domain_name(domain))
1880 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1882 if (host && !avahi_is_valid_domain_name(host))
1883 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1885 escape_service_name(ename, sizeof(ename), name);
1888 domain = s->domain_name;
1891 host = s->host_name_fqdn;
1893 if (!(d = avahi_normalize_name(domain)) ||
1894 !(t = avahi_normalize_name(type)) ||
1895 !(h = avahi_normalize_name(host))) {
1896 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1900 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1901 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1903 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1906 if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1907 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1911 r->data.srv.priority = 0;
1912 r->data.srv.weight = 0;
1913 r->data.srv.port = port;
1914 r->data.srv.name = h;
1916 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1917 avahi_record_unref(r);
1922 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1928 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1929 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1937 avahi_string_list_free(strlst);
1942 int avahi_server_add_service_strlst(
1944 AvahiSEntryGroup *g,
1945 AvahiIfIndex interface,
1946 AvahiProtocol protocol,
1952 AvahiStringList *strlst) {
1958 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1961 int avahi_server_add_service_va(
1963 AvahiSEntryGroup *g,
1964 AvahiIfIndex interface,
1965 AvahiProtocol protocol,
1977 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1980 int avahi_server_add_service(
1982 AvahiSEntryGroup *g,
1983 AvahiIfIndex interface,
1984 AvahiProtocol protocol,
2000 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
2005 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
2006 static const char hex[] = "0123456789abcdef";
2008 const uint8_t *k = p;
2010 while (sl > 1 && pl > 0) {
2011 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
2027 int avahi_server_add_dns_server_address(
2029 AvahiSEntryGroup *g,
2030 AvahiIfIndex interface,
2031 AvahiProtocol protocol,
2033 AvahiDNSServerType type,
2034 const AvahiAddress *address,
2035 uint16_t port /** should be 53 */) {
2043 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2044 assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
2047 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2049 if (domain && !avahi_is_valid_domain_name(domain))
2050 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2052 if (address->family == AVAHI_PROTO_INET) {
2053 hexstring(n+3, sizeof(n)-3, &address->data, 4);
2054 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2055 r->data.a.address = address->data.ipv4;
2057 hexstring(n+3, sizeof(n)-3, &address->data, 6);
2058 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2059 r->data.aaaa.address = address->data.ipv6;
2063 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2065 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
2066 avahi_record_unref(r);
2071 return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
2074 int avahi_server_add_dns_server_name(
2076 AvahiSEntryGroup *g,
2077 AvahiIfIndex interface,
2078 AvahiProtocol protocol,
2080 AvahiDNSServerType type,
2082 uint16_t port /** should be 53 */) {
2085 char t[256], *d = NULL, *n = NULL;
2090 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2093 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2095 if (!avahi_is_valid_domain_name(name))
2096 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2098 if (domain && !avahi_is_valid_domain_name(domain))
2099 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2103 domain = s->domain_name;
2105 if (!(n = avahi_normalize_name(name)) ||
2106 !(d = avahi_normalize_name(domain))) {
2109 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2112 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2115 if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2117 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2120 r->data.srv.priority = 0;
2121 r->data.srv.weight = 0;
2122 r->data.srv.port = port;
2123 r->data.srv.name = n;
2124 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2125 avahi_record_unref(r);
2130 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2131 AvahiKey *k = userdata;
2137 avahi_interface_post_query(i, k, 0);
2140 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2144 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2147 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2150 if (g->state == state)
2153 assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2158 g->callback(g->server, g, state, g->userdata);
2161 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2162 AvahiSEntryGroup *g;
2166 if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2167 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2172 g->callback = callback;
2173 g->userdata = userdata;
2175 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2177 g->n_register_try = 0;
2178 g->register_time_event = NULL;
2179 g->register_time.tv_sec = 0;
2180 g->register_time.tv_usec = 0;
2181 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2183 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2187 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2193 for (e = g->entries; e; e = e->by_group_next) {
2195 avahi_goodbye_entry(g->server, e, 1);
2200 if (g->register_time_event) {
2201 avahi_time_event_free(g->register_time_event);
2202 g->register_time_event = NULL;
2207 g->server->need_group_cleanup = 1;
2208 g->server->need_entry_cleanup = 1;
2211 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2214 gettimeofday(&g->register_time, NULL);
2216 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2219 avahi_announce_group(g->server, g);
2220 avahi_s_entry_group_check_probed(g, 0);
2224 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2225 AvahiSEntryGroup *g = userdata;
2228 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2230 avahi_time_event_free(g->register_time_event);
2231 g->register_time_event = NULL;
2233 /* Holdoff time passed, so let's start probing */
2234 entry_group_commit_real(g);
2237 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2243 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2244 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2246 g->n_register_try++;
2248 avahi_timeval_add(&g->register_time,
2249 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2250 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2251 AVAHI_RR_HOLDOFF_MSEC));
2253 gettimeofday(&now, NULL);
2255 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2256 /* Holdoff time passed, so let's start probing */
2257 /* avahi_log_debug("Holdoff passed, directly going on."); */
2259 entry_group_commit_real(g);
2261 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2263 /* Holdoff time has not yet passed, so let's wait */
2264 assert(!g->register_time_event);
2265 g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2267 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2273 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2277 if (g->register_time_event) {
2278 avahi_time_event_free(g->register_time_event);
2279 g->register_time_event = NULL;
2282 for (e = g->entries; e; e = e->by_group_next) {
2284 avahi_goodbye_entry(g->server, e, 1);
2289 if (g->register_time_event) {
2290 avahi_time_event_free(g->register_time_event);
2291 g->register_time_event = NULL;
2294 g->server->need_entry_cleanup = 1;
2297 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2300 int avahi_entry_is_commited(AvahiEntry *e) {
2305 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2306 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2309 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2316 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2319 g->userdata = userdata;
2322 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2328 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2332 /* Look for an entry that is not dead */
2333 for (e = g->entries; e; e = e->by_group_next)
2340 const char* avahi_server_get_domain_name(AvahiServer *s) {
2343 return s->domain_name;
2346 const char* avahi_server_get_host_name(AvahiServer *s) {
2349 return s->host_name;
2352 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2355 return s->host_name_fqdn;
2358 void* avahi_server_get_data(AvahiServer *s) {
2364 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2367 s->userdata = userdata;
2370 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2376 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2379 memset(c, 0, sizeof(AvahiServerConfig));
2382 c->host_name = NULL;
2383 c->domain_name = NULL;
2384 c->check_response_ttl = 0;
2385 c->publish_hinfo = 1;
2386 c->publish_addresses = 1;
2387 c->publish_workstation = 1;
2388 c->publish_domain = 1;
2389 c->use_iff_running = 0;
2390 c->enable_reflector = 0;
2396 void avahi_server_config_free(AvahiServerConfig *c) {
2399 avahi_free(c->host_name);
2400 avahi_free(c->domain_name);
2403 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2404 char *d = NULL, *h = NULL;
2409 if (!(h = avahi_strdup(c->host_name)))
2413 if (!(d = avahi_strdup(c->domain_name))) {
2420 ret->domain_name = d;
2425 int avahi_server_errno(AvahiServer *s) {
2431 /* Just for internal use */
2432 int avahi_server_set_errno(AvahiServer *s, int error) {
2435 return s->error = error;