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>
36 #include <avahi-common/domain.h>
37 #include <avahi-common/timeval.h>
38 #include <avahi-common/malloc.h>
39 #include <avahi-common/error.h>
47 #include "dns-srv-rr.h"
48 #include "addr-util.h"
49 #include "domain-util.h"
51 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) {
57 if (type == AVAHI_DNS_TYPE_ANY) {
60 for (e = s->entries; e; e = e->entries_next)
62 avahi_entry_is_registered(s, e, i) &&
63 e->record->key->clazz == AVAHI_DNS_CLASS_IN &&
64 avahi_domain_equal(name, e->record->key->name))
65 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
71 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
74 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
75 if (!e->dead && avahi_entry_is_registered(s, e, i))
76 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
82 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) {
88 /* Call the specified callback far all records referenced by the one specified in *r */
90 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
91 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
92 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
93 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
94 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
95 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
96 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
97 } else if (r->key->type == AVAHI_DNS_TYPE_CNAME)
98 enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata);
102 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
107 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
110 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
115 /* Push all records that match the specified key to the record list */
117 if (avahi_key_is_pattern(k)) {
120 /* Handle ANY query */
122 for (e = s->entries; e; e = e->entries_next)
123 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
124 avahi_server_prepare_response(s, i, e, unicast_response, 0);
129 /* Handle all other queries */
131 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
132 if (!e->dead && avahi_entry_is_registered(s, e, i))
133 avahi_server_prepare_response(s, i, e, unicast_response, 0);
136 /* Look for CNAME records */
138 if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY)
139 && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) {
143 if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME)))
146 avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response);
147 avahi_key_unref(cname_key);
151 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
155 /* Withdraw the specified entry, and if is part of an entry group,
156 * put that into COLLISION state */
164 for (k = e->group->entries; k; k = k->by_group_next)
166 avahi_goodbye_entry(s, k, 0, 1);
170 e->group->n_probing = 0;
172 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
174 avahi_goodbye_entry(s, e, 0, 1);
178 s->need_entry_cleanup = 1;
181 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
187 /* Withdraw an entry RRSset */
189 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
190 withdraw_entry(s, e);
193 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
195 int ours = 0, won = 0, lost = 0;
201 /* Handle incoming probes and check if they conflict our own probes */
203 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
210 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
215 if (avahi_entry_is_probing(s, e, i)) {
225 char *t = avahi_record_to_string(record);
228 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
230 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
231 withdraw_rrset(s, record->key);
238 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique, const AvahiAddress *a) {
239 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
240 AvahiEntry *e, *n, *conflicting_entry = NULL;
246 /* Check whether an incoming record conflicts with one of our own */
248 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
254 /* Check if the incoming is a goodbye record */
255 if (avahi_record_is_goodbye(record)) {
257 if (avahi_record_equal_no_ttl(e->record, record)) {
261 t = avahi_record_to_string(record);
262 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
263 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
270 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
274 if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
277 /* Either our entry or the other is intended to be unique, so let's check */
279 if (avahi_record_equal_no_ttl(e->record, record)) {
280 ours = 1; /* We have an identical record, so this is no conflict */
282 /* Check wheter there is a TTL conflict */
283 if (record->ttl <= e->record->ttl/2 &&
284 avahi_entry_is_registered(s, e, i)) {
287 t = avahi_record_to_string(record);
289 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
290 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
296 /* There's no need to check the other entries of this RRset */
301 if (avahi_entry_is_registered(s, e, i)) {
303 /* A conflict => we have to return to probe mode */
305 conflicting_entry = e;
307 } else if (avahi_entry_is_probing(s, e, i)) {
309 /* We are currently registering a matching record, but
310 * someone else already claimed it, so let's
313 withdraw_immediately = 1;
318 if (!ours && conflict) {
323 t = avahi_record_to_string(record);
325 if (withdraw_immediately) {
326 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
327 withdraw_rrset(s, record->key);
329 assert(conflicting_entry);
330 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
331 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
333 /* Local unique records are returned to probing
334 * state. Local shared records are reannounced. */
343 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
344 int *unicast_response = userdata;
348 assert(unicast_response);
350 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
353 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
357 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
360 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
364 assert(!legacy_unicast || (a && port > 0 && p));
366 if (legacy_unicast) {
367 AvahiDnsPacket *reply;
370 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
373 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
375 append_aux_records_to_list(s, i, r, 0);
377 if (avahi_dns_packet_append_record(reply, r, 0, 10))
378 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
380 char *t = avahi_record_to_string(r);
381 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
385 avahi_record_unref(r);
388 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
389 avahi_interface_send_packet_unicast(i, reply, a, port);
391 avahi_dns_packet_free(reply);
394 int unicast_response, flush_cache, auxiliary;
395 AvahiDnsPacket *reply = NULL;
398 /* In case the query packet was truncated never respond
399 immediately, because known answer suppression records might be
400 contained in later packets */
401 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
403 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
405 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
407 append_aux_records_to_list(s, i, r, unicast_response);
409 /* Due to some reasons the record has not been scheduled.
410 * The client requested an unicast response in that
411 * case. Therefore we prepare such a response */
418 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
422 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
424 /* Appending this record succeeded, so incremeant
425 * the specific header field, and return to the caller */
427 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
432 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
435 /* The record is too large for one packet, so create a larger packet */
437 avahi_dns_packet_free(reply);
438 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
439 if (size > AVAHI_DNS_PACKET_SIZE_MAX)
440 size = AVAHI_DNS_PACKET_SIZE_MAX;
442 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
445 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
447 avahi_dns_packet_free(reply);
448 t = avahi_record_to_string(r);
449 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
453 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
456 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
457 avahi_interface_send_packet_unicast(i, reply, a, port);
458 avahi_dns_packet_free(reply);
463 avahi_record_unref(r);
467 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
468 avahi_interface_send_packet_unicast(i, reply, a, port);
469 avahi_dns_packet_free(reply);
473 avahi_record_list_flush(s->record_list);
477 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
484 if (!s->config.enable_reflector)
487 for (j = s->monitor->interfaces; j; j = j->interface_next)
488 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
489 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
492 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
493 AvahiServer *s = userdata;
500 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
504 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
511 if (!s->config.enable_reflector)
514 for (j = s->monitor->interfaces; j; j = j->interface_next)
515 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
516 /* Post the query to other networks */
517 avahi_interface_post_query(j, k, 1);
519 /* Reply from caches of other network. This is needed to
520 * "work around" known answer suppression. */
522 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
526 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
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_probe(j, r, 1);
541 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
550 /* avahi_log_debug("query"); */
552 assert(avahi_record_list_is_empty(s->record_list));
554 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
556 /* Handle the questions */
557 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
559 int unicast_response = 0;
561 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
562 avahi_log_warn("Packet too short (1)");
566 if (!legacy_unicast && !from_local_iface) {
567 reflect_query(s, i, key);
568 avahi_cache_start_poof(i->cache, key, a);
571 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
572 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
573 /* Allow our own queries to be suppressed by incoming
574 * queries only when they do not include known answers */
575 avahi_query_scheduler_incoming(i->query_scheduler, key);
577 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
578 avahi_key_unref(key);
581 if (!legacy_unicast) {
583 /* Known Answer Suppression */
584 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
588 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
589 avahi_log_warn("Packet too short (2)");
593 if (handle_conflict(s, i, record, unique, a)) {
594 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
595 avahi_record_list_drop(s->record_list, record);
596 avahi_cache_stop_poof(i->cache, record, a);
599 avahi_record_unref(record);
603 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
607 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
608 avahi_log_warn("Packet too short (3)");
612 if (!avahi_key_is_pattern(record->key)) {
613 if (!from_local_iface)
614 reflect_probe(s, i, record);
615 incoming_probe(s, record, i);
618 avahi_record_unref(record);
622 if (!avahi_record_list_is_empty(s->record_list))
623 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
628 avahi_record_list_flush(s->record_list);
631 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
639 /* avahi_log_debug("response"); */
641 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
642 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
647 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
648 avahi_log_warn("Packet too short (4)");
652 if (!avahi_key_is_pattern(record->key)) {
654 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
655 /* avahi_free(txt); */
657 if (handle_conflict(s, i, record, cache_flush, a)) {
658 if (!from_local_iface)
659 reflect_response(s, i, record, cache_flush);
660 avahi_cache_update(i->cache, record, cache_flush, a);
661 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
665 avahi_record_unref(record);
668 /* If the incoming response contained a conflicting record, some
669 records have been scheduling for sending. We need to flush them
671 if (!avahi_record_list_is_empty(s->record_list))
672 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
675 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
676 unsigned n, idx = (unsigned) -1;
677 AvahiLegacyUnicastReflectSlot *slot;
681 if (!s->legacy_unicast_reflect_slots)
682 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
684 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
685 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
687 if (!s->legacy_unicast_reflect_slots[idx])
691 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
694 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
695 return NULL; /* OOM */
697 s->legacy_unicast_reflect_slots[idx] = slot;
698 slot->id = s->legacy_unicast_reflect_id++;
704 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
710 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
712 assert(s->legacy_unicast_reflect_slots[idx] == slot);
714 avahi_time_event_free(slot->time_event);
717 s->legacy_unicast_reflect_slots[idx] = NULL;
720 static void free_slots(AvahiServer *s) {
724 if (!s->legacy_unicast_reflect_slots)
727 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
728 if (s->legacy_unicast_reflect_slots[idx])
729 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
731 avahi_free(s->legacy_unicast_reflect_slots);
732 s->legacy_unicast_reflect_slots = NULL;
735 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
740 if (!s->legacy_unicast_reflect_slots)
743 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
745 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
748 return s->legacy_unicast_reflect_slots[idx];
751 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
752 AvahiLegacyUnicastReflectSlot *slot = userdata;
756 assert(slot->time_event == e);
758 deallocate_slot(slot->server, slot);
761 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
762 AvahiLegacyUnicastReflectSlot *slot;
770 assert(i->protocol == a->proto);
772 if (!s->config.enable_reflector)
775 /* avahi_log_debug("legacy unicast reflector"); */
777 /* Reflecting legacy unicast queries is a little more complicated
778 than reflecting normal queries, since we must route the
779 responses back to the right client. Therefore we must store
780 some information for finding the right client contact data for
781 response packets. In contrast to normal queries legacy
782 unicast query and response packets are reflected untouched and
783 are not reassembled into larger packets */
785 if (!(slot = allocate_slot(s))) {
786 /* No slot available, we drop this legacy unicast query */
787 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
791 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
794 slot->interface = i->hardware->index;
796 avahi_elapse_time(&slot->elapse_time, 2000, 0);
797 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
799 /* Patch the packet with our new locally generatedt id */
800 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
802 for (j = s->monitor->interfaces; j; j = j->interface_next)
803 if (avahi_interface_is_relevant(j) &&
805 (s->config.reflect_ipv || j->protocol == i->protocol)) {
807 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
808 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
809 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
810 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
814 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
817 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
822 if (!s->config.enable_reflector)
825 avahi_address_from_sockaddr(sa, &a);
827 if (!avahi_address_is_local(s->monitor, &a))
830 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
831 struct sockaddr_in lsa;
832 socklen_t l = sizeof(lsa);
834 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
835 avahi_log_warn("getsockname(): %s", strerror(errno));
837 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
841 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
842 struct sockaddr_in6 lsa;
843 socklen_t l = sizeof(lsa);
845 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
846 avahi_log_warn("getsockname(): %s", strerror(errno));
848 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
854 static int is_mdns_mcast_address(const AvahiAddress *a) {
858 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
859 return avahi_address_cmp(a, &b) == 0;
862 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
864 assert(iface != AVAHI_IF_UNSPEC);
867 /* If it isn't the MDNS port it can't be generated by us */
868 if (port != AVAHI_MDNS_PORT)
871 return avahi_interface_has_address(s->monitor, iface, a);
874 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
878 int from_local_iface = 0;
886 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
887 !avahi_interface_is_relevant(i)) {
888 avahi_log_warn("Recieved packet from invalid interface.");
892 /* avahi_log_debug("new packet received on interface '%s.%i'.", i->hardware->name, i->protocol); */
894 port = avahi_port_from_sockaddr(sa);
895 avahi_address_from_sockaddr(sa, &a);
897 if (avahi_address_is_ipv4_in_ipv6(&a))
898 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
901 if (originates_from_local_legacy_unicast_socket(s, sa))
902 /* This originates from our local reflector, so let's ignore it */
905 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
906 if (s->config.enable_reflector)
907 from_local_iface = originates_from_local_iface(s, iface, &a, port);
909 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
910 avahi_log_warn("Recieved invalid packet.");
914 if (avahi_dns_packet_is_query(p)) {
915 int legacy_unicast = 0;
917 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
918 avahi_log_warn("Invalid query packet.");
922 if (port != AVAHI_MDNS_PORT) {
925 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
926 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
927 avahi_log_warn("Invalid legacy unicast query packet.");
935 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
937 handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
939 /* avahi_log_debug("Handled query"); */
941 if (port != AVAHI_MDNS_PORT) {
942 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
946 if (ttl != 255 && s->config.check_response_ttl) {
947 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
951 if (!is_mdns_mcast_address(dest) &&
952 !avahi_interface_address_on_link(i, &a)) {
953 avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
957 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
958 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
959 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
960 avahi_log_warn("Invalid response packet.");
964 handle_response_packet(s, p, i, &a, from_local_iface);
965 /* avahi_log_debug("Handled response"); */
969 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface) {
970 AvahiInterface *i, *j;
972 AvahiLegacyUnicastReflectSlot *slot;
979 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
980 !avahi_interface_is_relevant(i)) {
981 avahi_log_warn("Recieved packet from invalid interface.");
985 /* avahi_log_debug("new legacy unicast packet received on interface '%s.%i'.", i->hardware->name, i->protocol); */
987 avahi_address_from_sockaddr(sa, &a);
989 if (avahi_address_is_ipv4_in_ipv6(&a))
990 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
993 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
994 avahi_log_warn("Recieved invalid packet.");
998 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
999 avahi_log_warn("Recieved legacy unicast response with unknown id");
1003 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1004 !avahi_interface_is_relevant(j))
1007 /* Patch the original ID into this response */
1008 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1010 /* Forward the response to the correct client */
1011 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1013 /* Undo changes to packet */
1014 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1017 static void cleanup_dead(AvahiServer *s) {
1020 avahi_cleanup_dead_entries(s);
1021 avahi_browser_cleanup(s);
1024 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1025 AvahiServer *s = userdata;
1030 struct sockaddr_in sa;
1031 struct sockaddr_in6 sa6;
1036 if (events & AVAHI_WATCH_IN) {
1038 if (fd == s->fd_ipv4) {
1039 dest.proto = AVAHI_PROTO_INET;
1040 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1041 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1042 avahi_dns_packet_free(p);
1044 } else if (fd == s->fd_ipv6) {
1045 dest.proto = AVAHI_PROTO_INET6;
1047 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1048 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1049 avahi_dns_packet_free(p);
1051 } else if (fd == s->fd_legacy_unicast_ipv4) {
1052 dest.proto = AVAHI_PROTO_INET;
1054 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1055 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface);
1056 avahi_dns_packet_free(p);
1058 } else if (fd == s->fd_legacy_unicast_ipv6) {
1059 dest.proto = AVAHI_PROTO_INET6;
1061 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1062 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface);
1063 avahi_dns_packet_free(p);
1072 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1075 if (s->state == state)
1081 s->callback(s, state, s->userdata);
1084 static void withdraw_host_rrs(AvahiServer *s) {
1087 if (s->hinfo_entry_group)
1088 avahi_s_entry_group_reset(s->hinfo_entry_group);
1090 if (s->browse_domain_entry_group)
1091 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1093 avahi_interface_monitor_update_rrs(s->monitor, 1);
1094 s->n_host_rr_pending = 0;
1097 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1100 assert(s->n_host_rr_pending > 0);
1102 if (--s->n_host_rr_pending == 0)
1103 server_set_state(s, AVAHI_SERVER_RUNNING);
1106 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1110 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1111 s->state == AVAHI_SERVER_REGISTERING)
1112 s->n_host_rr_pending ++;
1114 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1115 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1116 withdraw_host_rrs(s);
1117 server_set_state(s, AVAHI_SERVER_COLLISION);
1119 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1120 s->state == AVAHI_SERVER_REGISTERING)
1121 avahi_server_decrease_host_rr_pending(s);
1124 static void register_hinfo(AvahiServer *s) {
1125 struct utsname utsname;
1130 if (!s->config.publish_hinfo)
1133 if (s->hinfo_entry_group)
1134 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1136 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1138 if (!s->hinfo_entry_group) {
1139 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1143 /* Fill in HINFO rr */
1144 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1146 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1147 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1149 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1150 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1154 avahi_record_unref(r);
1157 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1158 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1162 static void register_localhost(AvahiServer *s) {
1166 /* Add localhost entries */
1167 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1168 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1170 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1171 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1174 static void register_browse_domain(AvahiServer *s) {
1177 if (!s->config.publish_domain)
1180 if (s->browse_domain_entry_group)
1181 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1183 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1185 if (!s->browse_domain_entry_group) {
1186 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1190 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) {
1191 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1195 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1196 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1199 static void register_stuff(AvahiServer *s) {
1202 server_set_state(s, AVAHI_SERVER_REGISTERING);
1203 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1206 register_browse_domain(s);
1207 avahi_interface_monitor_update_rrs(s->monitor, 0);
1209 s->n_host_rr_pending --;
1211 if (s->n_host_rr_pending == 0)
1212 server_set_state(s, AVAHI_SERVER_RUNNING);
1215 static void update_fqdn(AvahiServer *s) {
1219 assert(s->host_name);
1220 assert(s->domain_name);
1222 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1225 avahi_free(s->host_name_fqdn);
1226 s->host_name_fqdn = n;
1229 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1233 if (host_name && !avahi_is_valid_host_name(host_name))
1234 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1236 withdraw_host_rrs(s);
1238 avahi_free(s->host_name);
1239 s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
1240 s->host_name[strcspn(s->host_name, ".")] = 0;
1247 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1249 assert(domain_name);
1251 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1252 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1254 withdraw_host_rrs(s);
1256 avahi_free(s->domain_name);
1257 s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
1264 static int valid_server_config(const AvahiServerConfig *sc) {
1266 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1267 return AVAHI_ERR_INVALID_HOST_NAME;
1269 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1270 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1275 static int setup_sockets(AvahiServer *s) {
1278 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1279 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1281 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1282 return AVAHI_ERR_NO_NETWORK;
1284 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1285 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1286 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1287 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1289 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1290 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1294 s->watch_legacy_unicast_ipv4 =
1295 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);
1302 if (s->fd_legacy_unicast_ipv4 >= 0)
1303 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1304 if (s->fd_legacy_unicast_ipv6 >= 0)
1305 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1310 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1314 if (sc && (e = valid_server_config(sc)) < 0) {
1320 if (!(s = avahi_new(AvahiServer, 1))) {
1322 *error = AVAHI_ERR_NO_MEMORY;
1327 s->poll_api = poll_api;
1330 avahi_server_config_copy(&s->config, sc);
1332 avahi_server_config_init(&s->config);
1334 if ((e = setup_sockets(s)) < 0) {
1338 avahi_server_config_free(&s->config);
1344 s->n_host_rr_pending = 0;
1345 s->need_entry_cleanup = 0;
1346 s->need_group_cleanup = 0;
1347 s->need_browser_cleanup = 0;
1348 s->hinfo_entry_group = NULL;
1349 s->browse_domain_entry_group = NULL;
1350 s->error = AVAHI_OK;
1351 s->state = AVAHI_SERVER_INVALID;
1353 s->callback = callback;
1354 s->userdata = userdata;
1356 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1358 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1359 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1360 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1362 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1363 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1364 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1365 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1366 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1367 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1368 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1369 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1370 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1372 s->legacy_unicast_reflect_slots = NULL;
1373 s->legacy_unicast_reflect_id = 0;
1375 s->record_list = avahi_record_list_new();
1378 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1379 s->host_name[strcspn(s->host_name, ".")] = 0;
1380 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1381 s->host_name_fqdn = NULL;
1385 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1386 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1388 if (s->config.enable_wide_area) {
1389 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1390 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1392 s->wide_area_lookup_engine = NULL;
1394 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1396 s->monitor = avahi_interface_monitor_new(s);
1397 avahi_interface_monitor_sync(s->monitor);
1399 register_localhost(s);
1405 void avahi_server_free(AvahiServer* s) {
1408 /* Remove all browsers */
1410 while (s->dns_server_browsers)
1411 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1412 while (s->host_name_resolvers)
1413 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1414 while (s->address_resolvers)
1415 avahi_s_address_resolver_free(s->address_resolvers);
1416 while (s->domain_browsers)
1417 avahi_s_domain_browser_free(s->domain_browsers);
1418 while (s->service_type_browsers)
1419 avahi_s_service_type_browser_free(s->service_type_browsers);
1420 while (s->service_browsers)
1421 avahi_s_service_browser_free(s->service_browsers);
1422 while (s->service_resolvers)
1423 avahi_s_service_resolver_free(s->service_resolvers);
1424 while (s->record_browsers)
1425 avahi_s_record_browser_destroy(s->record_browsers);
1427 /* Remove all locally rgeistered stuff */
1430 avahi_entry_free(s, s->entries);
1432 avahi_interface_monitor_free(s->monitor);
1435 avahi_entry_group_free(s, s->groups);
1439 avahi_hashmap_free(s->entries_by_key);
1440 avahi_record_list_free(s->record_list);
1441 avahi_hashmap_free(s->record_browser_hashmap);
1443 if (s->wide_area_lookup_engine)
1444 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1445 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1447 avahi_time_event_queue_free(s->time_event_queue);
1452 s->poll_api->watch_free(s->watch_ipv4);
1454 s->poll_api->watch_free(s->watch_ipv6);
1456 if (s->watch_legacy_unicast_ipv4)
1457 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1458 if (s->watch_legacy_unicast_ipv6)
1459 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1463 if (s->fd_ipv4 >= 0)
1465 if (s->fd_ipv6 >= 0)
1468 if (s->fd_legacy_unicast_ipv4 >= 0)
1469 close(s->fd_legacy_unicast_ipv4);
1470 if (s->fd_legacy_unicast_ipv6 >= 0)
1471 close(s->fd_legacy_unicast_ipv6);
1473 /* Free other stuff */
1475 avahi_free(s->host_name);
1476 avahi_free(s->domain_name);
1477 avahi_free(s->host_name_fqdn);
1479 avahi_server_config_free(&s->config);
1484 const char* avahi_server_get_domain_name(AvahiServer *s) {
1487 return s->domain_name;
1490 const char* avahi_server_get_host_name(AvahiServer *s) {
1493 return s->host_name;
1496 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1499 return s->host_name_fqdn;
1502 void* avahi_server_get_data(AvahiServer *s) {
1508 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1511 s->userdata = userdata;
1514 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1520 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1523 memset(c, 0, sizeof(AvahiServerConfig));
1526 c->host_name = NULL;
1527 c->domain_name = NULL;
1528 c->check_response_ttl = 0;
1529 c->publish_hinfo = 1;
1530 c->publish_addresses = 1;
1531 c->publish_workstation = 1;
1532 c->publish_domain = 1;
1533 c->use_iff_running = 0;
1534 c->enable_reflector = 0;
1536 c->add_service_cookie = 1;
1537 c->enable_wide_area = 0;
1538 c->n_wide_area_servers = 0;
1539 c->disallow_other_stacks = 0;
1544 void avahi_server_config_free(AvahiServerConfig *c) {
1547 avahi_free(c->host_name);
1548 avahi_free(c->domain_name);
1551 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1552 char *d = NULL, *h = NULL;
1557 if (!(h = avahi_strdup(c->host_name)))
1561 if (!(d = avahi_strdup(c->domain_name))) {
1568 ret->domain_name = d;
1573 int avahi_server_errno(AvahiServer *s) {
1579 /* Just for internal use */
1580 int avahi_server_set_errno(AvahiServer *s, int error) {
1583 return s->error = error;
1586 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1589 return s->local_service_cookie;
1592 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1598 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1600 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1601 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1602 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1609 int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group) {
1610 AvahiKey *key = NULL;
1613 char n[AVAHI_DOMAIN_NAME_MAX];
1620 if (!AVAHI_IF_VALID(interface))
1621 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
1623 if (!AVAHI_IF_VALID(protocol))
1624 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
1626 if (!avahi_is_valid_service_name(name))
1627 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1629 if (!avahi_is_valid_service_type_strict(type))
1630 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1632 if (domain && !avahi_is_valid_domain_name(domain))
1633 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1635 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1636 return avahi_server_set_errno(s, ret);
1638 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1639 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1641 e = find_entry(s, interface, protocol, key);
1642 avahi_key_unref(key);
1645 *ret_group = e->group;
1649 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1652 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1653 AvahiKey *key = NULL;
1659 if (!s->host_name_fqdn)
1662 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1665 e = find_entry(s, interface, protocol, key);
1666 avahi_key_unref(key);
1671 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1674 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1680 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1682 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1683 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1684 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1685 avahi_record_equal_no_ttl(record, e->record))
1691 /** Set the wide area DNS servers */
1692 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1695 if (!s->wide_area_lookup_engine)
1696 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1698 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);