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"
52 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) {
58 if (type == AVAHI_DNS_TYPE_ANY) {
61 for (e = s->entries; e; e = e->entries_next)
63 avahi_entry_is_registered(s, e, i) &&
64 e->record->key->clazz == AVAHI_DNS_CLASS_IN &&
65 avahi_domain_equal(name, e->record->key->name))
66 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
72 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
75 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
76 if (!e->dead && avahi_entry_is_registered(s, e, i))
77 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
83 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) {
89 /* Call the specified callback far all records referenced by the one specified in *r */
91 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
92 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
93 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
94 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
95 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
96 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
97 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
98 } else if (r->key->type == AVAHI_DNS_TYPE_CNAME)
99 enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata);
103 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
108 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
111 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
116 /* Push all records that match the specified key to the record list */
118 if (avahi_key_is_pattern(k)) {
121 /* Handle ANY query */
123 for (e = s->entries; e; e = e->entries_next)
124 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
125 avahi_server_prepare_response(s, i, e, unicast_response, 0);
130 /* Handle all other queries */
132 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
133 if (!e->dead && avahi_entry_is_registered(s, e, i))
134 avahi_server_prepare_response(s, i, e, unicast_response, 0);
137 /* Look for CNAME records */
139 if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY)
140 && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) {
144 if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME)))
147 avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response);
148 avahi_key_unref(cname_key);
152 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
156 /* Withdraw the specified entry, and if is part of an entry group,
157 * put that into COLLISION state */
165 for (k = e->group->entries; k; k = k->by_group_next)
167 avahi_goodbye_entry(s, k, 0, 1);
171 e->group->n_probing = 0;
173 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
175 avahi_goodbye_entry(s, e, 0, 1);
179 s->need_entry_cleanup = 1;
182 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
188 /* Withdraw an entry RRSset */
190 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
191 withdraw_entry(s, e);
194 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
196 int ours = 0, won = 0, lost = 0;
202 /* Handle incoming probes and check if they conflict our own probes */
204 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
211 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
216 if (avahi_entry_is_probing(s, e, i)) {
226 char *t = avahi_record_to_string(record);
229 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
231 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
232 withdraw_rrset(s, record->key);
239 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) {
240 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
241 AvahiEntry *e, *n, *conflicting_entry = NULL;
247 /* Check whether an incoming record conflicts with one of our own */
249 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
255 /* Check if the incoming is a goodbye record */
256 if (avahi_record_is_goodbye(record)) {
258 if (avahi_record_equal_no_ttl(e->record, record)) {
262 t = avahi_record_to_string(record);
263 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
264 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
271 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
275 if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
278 /* Either our entry or the other is intended to be unique, so let's check */
280 if (avahi_record_equal_no_ttl(e->record, record)) {
281 ours = 1; /* We have an identical record, so this is no conflict */
283 /* Check wheter there is a TTL conflict */
284 if (record->ttl <= e->record->ttl/2 &&
285 avahi_entry_is_registered(s, e, i)) {
288 t = avahi_record_to_string(record);
290 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
291 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
297 /* There's no need to check the other entries of this RRset */
302 if (avahi_entry_is_registered(s, e, i)) {
304 /* A conflict => we have to return to probe mode */
306 conflicting_entry = e;
308 } else if (avahi_entry_is_probing(s, e, i)) {
310 /* We are currently registering a matching record, but
311 * someone else already claimed it, so let's
314 withdraw_immediately = 1;
319 if (!ours && conflict) {
324 t = avahi_record_to_string(record);
326 if (withdraw_immediately) {
327 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
328 withdraw_rrset(s, record->key);
330 assert(conflicting_entry);
331 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
332 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
334 /* Local unique records are returned to probing
335 * state. Local shared records are reannounced. */
344 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
345 int *unicast_response = userdata;
349 assert(unicast_response);
351 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
354 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
358 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
361 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
365 assert(!legacy_unicast || (a && port > 0 && p));
367 if (legacy_unicast) {
368 AvahiDnsPacket *reply;
371 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
374 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
376 append_aux_records_to_list(s, i, r, 0);
378 if (avahi_dns_packet_append_record(reply, r, 0, 10))
379 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
381 char *t = avahi_record_to_string(r);
382 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
386 avahi_record_unref(r);
389 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
390 avahi_interface_send_packet_unicast(i, reply, a, port);
392 avahi_dns_packet_free(reply);
395 int unicast_response, flush_cache, auxiliary;
396 AvahiDnsPacket *reply = NULL;
399 /* In case the query packet was truncated never respond
400 immediately, because known answer suppression records might be
401 contained in later packets */
402 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
404 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
406 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
408 append_aux_records_to_list(s, i, r, unicast_response);
410 /* Due to some reasons the record has not been scheduled.
411 * The client requested an unicast response in that
412 * case. Therefore we prepare such a response */
419 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
423 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
425 /* Appending this record succeeded, so incremeant
426 * the specific header field, and return to the caller */
428 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
433 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
436 /* The record is too large for one packet, so create a larger packet */
438 avahi_dns_packet_free(reply);
439 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
440 if (size > AVAHI_DNS_PACKET_SIZE_MAX)
441 size = AVAHI_DNS_PACKET_SIZE_MAX;
443 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
446 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
448 avahi_dns_packet_free(reply);
449 t = avahi_record_to_string(r);
450 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
454 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
457 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
458 avahi_interface_send_packet_unicast(i, reply, a, port);
459 avahi_dns_packet_free(reply);
464 avahi_record_unref(r);
468 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
469 avahi_interface_send_packet_unicast(i, reply, a, port);
470 avahi_dns_packet_free(reply);
474 avahi_record_list_flush(s->record_list);
478 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
485 if (!s->config.enable_reflector)
488 for (j = s->monitor->interfaces; j; j = j->interface_next)
489 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
490 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
493 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
494 AvahiServer *s = userdata;
501 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
505 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
512 if (!s->config.enable_reflector)
515 for (j = s->monitor->interfaces; j; j = j->interface_next)
516 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
517 /* Post the query to other networks */
518 avahi_interface_post_query(j, k, 1);
520 /* Reply from caches of other network. This is needed to
521 * "work around" known answer suppression. */
523 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
527 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
534 if (!s->config.enable_reflector)
537 for (j = s->monitor->interfaces; j; j = j->interface_next)
538 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
539 avahi_interface_post_probe(j, r, 1);
542 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
551 /* avahi_log_debug("query"); */
553 assert(avahi_record_list_is_empty(s->record_list));
555 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
557 /* Handle the questions */
558 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
560 int unicast_response = 0;
562 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
563 avahi_log_warn("Packet too short (1)");
567 if (!legacy_unicast && !from_local_iface) {
568 reflect_query(s, i, key);
569 avahi_cache_start_poof(i->cache, key, a);
572 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
573 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
574 /* Allow our own queries to be suppressed by incoming
575 * queries only when they do not include known answers */
576 avahi_query_scheduler_incoming(i->query_scheduler, key);
578 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
579 avahi_key_unref(key);
582 if (!legacy_unicast) {
584 /* Known Answer Suppression */
585 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
589 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
590 avahi_log_warn("Packet too short (2)");
594 if (handle_conflict(s, i, record, unique)) {
595 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
596 avahi_record_list_drop(s->record_list, record);
597 avahi_cache_stop_poof(i->cache, record, a);
600 avahi_record_unref(record);
604 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
608 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
609 avahi_log_warn("Packet too short (3)");
613 if (!avahi_key_is_pattern(record->key)) {
614 if (!from_local_iface)
615 reflect_probe(s, i, record);
616 incoming_probe(s, record, i);
619 avahi_record_unref(record);
623 if (!avahi_record_list_is_empty(s->record_list))
624 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
629 avahi_record_list_flush(s->record_list);
632 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
640 /* avahi_log_debug("response"); */
642 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
643 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
648 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
649 avahi_log_warn("Packet too short (4)");
653 if (!avahi_key_is_pattern(record->key)) {
655 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
656 /* avahi_free(txt); */
658 if (handle_conflict(s, i, record, cache_flush)) {
659 if (!from_local_iface)
660 reflect_response(s, i, record, cache_flush);
661 avahi_cache_update(i->cache, record, cache_flush, a);
662 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
666 avahi_record_unref(record);
669 /* If the incoming response contained a conflicting record, some
670 records have been scheduling for sending. We need to flush them
672 if (!avahi_record_list_is_empty(s->record_list))
673 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
676 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
677 unsigned n, idx = (unsigned) -1;
678 AvahiLegacyUnicastReflectSlot *slot;
682 if (!s->legacy_unicast_reflect_slots)
683 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
685 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
686 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
688 if (!s->legacy_unicast_reflect_slots[idx])
692 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
695 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
696 return NULL; /* OOM */
698 s->legacy_unicast_reflect_slots[idx] = slot;
699 slot->id = s->legacy_unicast_reflect_id++;
705 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
711 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
713 assert(s->legacy_unicast_reflect_slots[idx] == slot);
715 avahi_time_event_free(slot->time_event);
718 s->legacy_unicast_reflect_slots[idx] = NULL;
721 static void free_slots(AvahiServer *s) {
725 if (!s->legacy_unicast_reflect_slots)
728 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
729 if (s->legacy_unicast_reflect_slots[idx])
730 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
732 avahi_free(s->legacy_unicast_reflect_slots);
733 s->legacy_unicast_reflect_slots = NULL;
736 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
741 if (!s->legacy_unicast_reflect_slots)
744 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
746 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
749 return s->legacy_unicast_reflect_slots[idx];
752 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
753 AvahiLegacyUnicastReflectSlot *slot = userdata;
757 assert(slot->time_event == e);
759 deallocate_slot(slot->server, slot);
762 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
763 AvahiLegacyUnicastReflectSlot *slot;
771 assert(i->protocol == a->proto);
773 if (!s->config.enable_reflector)
776 /* avahi_log_debug("legacy unicast reflector"); */
778 /* Reflecting legacy unicast queries is a little more complicated
779 than reflecting normal queries, since we must route the
780 responses back to the right client. Therefore we must store
781 some information for finding the right client contact data for
782 response packets. In contrast to normal queries legacy
783 unicast query and response packets are reflected untouched and
784 are not reassembled into larger packets */
786 if (!(slot = allocate_slot(s))) {
787 /* No slot available, we drop this legacy unicast query */
788 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
792 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
795 slot->interface = i->hardware->index;
797 avahi_elapse_time(&slot->elapse_time, 2000, 0);
798 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
800 /* Patch the packet with our new locally generatedt id */
801 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
803 for (j = s->monitor->interfaces; j; j = j->interface_next)
804 if (avahi_interface_is_relevant(j) &&
806 (s->config.reflect_ipv || j->protocol == i->protocol)) {
808 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
809 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
810 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
811 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
815 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
818 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
823 if (!s->config.enable_reflector)
826 avahi_address_from_sockaddr(sa, &a);
828 if (!avahi_address_is_local(s->monitor, &a))
831 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
832 struct sockaddr_in lsa;
833 socklen_t l = sizeof(lsa);
835 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
836 avahi_log_warn("getsockname(): %s", strerror(errno));
838 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
842 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
843 struct sockaddr_in6 lsa;
844 socklen_t l = sizeof(lsa);
846 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
847 avahi_log_warn("getsockname(): %s", strerror(errno));
849 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
855 static int is_mdns_mcast_address(const AvahiAddress *a) {
859 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
860 return avahi_address_cmp(a, &b) == 0;
863 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
865 assert(iface != AVAHI_IF_UNSPEC);
868 /* If it isn't the MDNS port it can't be generated by us */
869 if (port != AVAHI_MDNS_PORT)
872 return avahi_interface_has_address(s->monitor, iface, a);
875 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
879 int from_local_iface = 0;
887 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
888 !avahi_interface_is_relevant(i)) {
889 avahi_log_warn("Recieved packet from invalid interface.");
893 /* avahi_log_debug("new packet received on interface '%s.%i'.", i->hardware->name, i->protocol); */
895 port = avahi_port_from_sockaddr(sa);
896 avahi_address_from_sockaddr(sa, &a);
898 if (avahi_address_is_ipv4_in_ipv6(&a))
899 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
902 if (originates_from_local_legacy_unicast_socket(s, sa))
903 /* This originates from our local reflector, so let's ignore it */
906 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
907 if (s->config.enable_reflector)
908 from_local_iface = originates_from_local_iface(s, iface, &a, port);
910 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
911 avahi_log_warn("Recieved invalid packet.");
915 if (avahi_dns_packet_is_query(p)) {
916 int legacy_unicast = 0;
918 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
919 avahi_log_warn("Invalid query packet.");
923 if (port != AVAHI_MDNS_PORT) {
926 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
927 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
928 avahi_log_warn("Invalid legacy unicast query packet.");
936 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
938 handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
940 /* avahi_log_debug("Handled query"); */
942 if (port != AVAHI_MDNS_PORT) {
943 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
947 if (ttl != 255 && s->config.check_response_ttl) {
948 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
952 if (!is_mdns_mcast_address(dest) &&
953 !avahi_interface_address_on_link(i, &a)) {
954 avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
958 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
959 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
960 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
961 avahi_log_warn("Invalid response packet.");
965 handle_response_packet(s, p, i, &a, from_local_iface);
966 /* avahi_log_debug("Handled response"); */
970 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface) {
971 AvahiInterface *i, *j;
973 AvahiLegacyUnicastReflectSlot *slot;
980 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
981 !avahi_interface_is_relevant(i)) {
982 avahi_log_warn("Recieved packet from invalid interface.");
986 /* avahi_log_debug("new legacy unicast packet received on interface '%s.%i'.", i->hardware->name, i->protocol); */
988 avahi_address_from_sockaddr(sa, &a);
990 if (avahi_address_is_ipv4_in_ipv6(&a))
991 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
994 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
995 avahi_log_warn("Recieved invalid packet.");
999 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1000 avahi_log_warn("Recieved legacy unicast response with unknown id");
1004 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1005 !avahi_interface_is_relevant(j))
1008 /* Patch the original ID into this response */
1009 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1011 /* Forward the response to the correct client */
1012 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1014 /* Undo changes to packet */
1015 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1018 static void cleanup_dead(AvahiServer *s) {
1021 avahi_cleanup_dead_entries(s);
1022 avahi_browser_cleanup(s);
1025 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1026 AvahiServer *s = userdata;
1031 struct sockaddr_in sa;
1032 struct sockaddr_in6 sa6;
1037 if (events & AVAHI_WATCH_IN) {
1039 if (fd == s->fd_ipv4) {
1040 dest.proto = AVAHI_PROTO_INET;
1041 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1042 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1043 avahi_dns_packet_free(p);
1045 } else if (fd == s->fd_ipv6) {
1046 dest.proto = AVAHI_PROTO_INET6;
1048 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1049 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1050 avahi_dns_packet_free(p);
1052 } else if (fd == s->fd_legacy_unicast_ipv4) {
1053 dest.proto = AVAHI_PROTO_INET;
1055 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1056 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface);
1057 avahi_dns_packet_free(p);
1059 } else if (fd == s->fd_legacy_unicast_ipv6) {
1060 dest.proto = AVAHI_PROTO_INET6;
1062 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1063 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface);
1064 avahi_dns_packet_free(p);
1073 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1076 if (s->state == state)
1082 s->callback(s, state, s->userdata);
1085 static void withdraw_host_rrs(AvahiServer *s) {
1088 if (s->hinfo_entry_group)
1089 avahi_s_entry_group_reset(s->hinfo_entry_group);
1091 if (s->browse_domain_entry_group)
1092 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1094 avahi_interface_monitor_update_rrs(s->monitor, 1);
1095 s->n_host_rr_pending = 0;
1098 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1101 assert(s->n_host_rr_pending > 0);
1103 if (--s->n_host_rr_pending == 0)
1104 server_set_state(s, AVAHI_SERVER_RUNNING);
1107 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1111 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1112 s->state == AVAHI_SERVER_REGISTERING)
1113 s->n_host_rr_pending ++;
1115 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1116 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1117 withdraw_host_rrs(s);
1118 server_set_state(s, AVAHI_SERVER_COLLISION);
1120 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1121 s->state == AVAHI_SERVER_REGISTERING)
1122 avahi_server_decrease_host_rr_pending(s);
1125 static void register_hinfo(AvahiServer *s) {
1126 struct utsname utsname;
1131 if (!s->config.publish_hinfo)
1134 if (s->hinfo_entry_group)
1135 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1137 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1139 if (!s->hinfo_entry_group) {
1140 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1144 /* Fill in HINFO rr */
1145 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1147 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1148 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1150 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1151 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1155 avahi_record_unref(r);
1158 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1159 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1163 static void register_localhost(AvahiServer *s) {
1167 /* Add localhost entries */
1168 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1169 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1171 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1172 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1175 static void register_browse_domain(AvahiServer *s) {
1178 if (!s->config.publish_domain)
1181 if (s->browse_domain_entry_group)
1182 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1184 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1186 if (!s->browse_domain_entry_group) {
1187 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1191 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) {
1192 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1196 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1197 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1200 static void register_stuff(AvahiServer *s) {
1203 server_set_state(s, AVAHI_SERVER_REGISTERING);
1204 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1207 register_browse_domain(s);
1208 avahi_interface_monitor_update_rrs(s->monitor, 0);
1210 s->n_host_rr_pending --;
1212 if (s->n_host_rr_pending == 0)
1213 server_set_state(s, AVAHI_SERVER_RUNNING);
1216 static void update_fqdn(AvahiServer *s) {
1220 assert(s->host_name);
1221 assert(s->domain_name);
1223 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1226 avahi_free(s->host_name_fqdn);
1227 s->host_name_fqdn = n;
1230 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1234 if (host_name && !avahi_is_valid_host_name(host_name))
1235 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1237 withdraw_host_rrs(s);
1239 avahi_free(s->host_name);
1240 s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
1241 s->host_name[strcspn(s->host_name, ".")] = 0;
1248 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1250 assert(domain_name);
1252 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1253 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1255 withdraw_host_rrs(s);
1257 avahi_free(s->domain_name);
1258 s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
1265 static int valid_server_config(const AvahiServerConfig *sc) {
1267 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1268 return AVAHI_ERR_INVALID_HOST_NAME;
1270 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1271 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1276 static int setup_sockets(AvahiServer *s) {
1279 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1280 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1282 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1283 return AVAHI_ERR_NO_NETWORK;
1285 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1286 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1287 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1288 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1290 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1291 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1295 s->watch_legacy_unicast_ipv4 =
1296 s->watch_legacy_unicast_ipv6 = NULL;
1298 if (s->fd_ipv4 >= 0)
1299 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1300 if (s->fd_ipv6 >= 0)
1301 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1303 if (s->fd_legacy_unicast_ipv4 >= 0)
1304 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1305 if (s->fd_legacy_unicast_ipv6 >= 0)
1306 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1311 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1315 if (sc && (e = valid_server_config(sc)) < 0) {
1321 if (!(s = avahi_new(AvahiServer, 1))) {
1323 *error = AVAHI_ERR_NO_MEMORY;
1328 s->poll_api = poll_api;
1331 avahi_server_config_copy(&s->config, sc);
1333 avahi_server_config_init(&s->config);
1335 if ((e = setup_sockets(s)) < 0) {
1339 avahi_server_config_free(&s->config);
1345 s->n_host_rr_pending = 0;
1346 s->need_entry_cleanup = 0;
1347 s->need_group_cleanup = 0;
1348 s->need_browser_cleanup = 0;
1349 s->hinfo_entry_group = NULL;
1350 s->browse_domain_entry_group = NULL;
1351 s->error = AVAHI_OK;
1352 s->state = AVAHI_SERVER_INVALID;
1354 s->callback = callback;
1355 s->userdata = userdata;
1357 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1359 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1360 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1361 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1363 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1364 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1365 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1366 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1367 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1368 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1369 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1370 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1371 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1373 s->legacy_unicast_reflect_slots = NULL;
1374 s->legacy_unicast_reflect_id = 0;
1376 s->record_list = avahi_record_list_new();
1379 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1380 s->host_name[strcspn(s->host_name, ".")] = 0;
1381 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1382 s->host_name_fqdn = NULL;
1386 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1387 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1389 if (s->config.enable_wide_area) {
1390 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1391 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1393 s->wide_area_lookup_engine = NULL;
1395 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1397 s->monitor = avahi_interface_monitor_new(s);
1398 avahi_interface_monitor_sync(s->monitor);
1400 register_localhost(s);
1406 void avahi_server_free(AvahiServer* s) {
1409 /* Remove all browsers */
1411 while (s->dns_server_browsers)
1412 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1413 while (s->host_name_resolvers)
1414 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1415 while (s->address_resolvers)
1416 avahi_s_address_resolver_free(s->address_resolvers);
1417 while (s->domain_browsers)
1418 avahi_s_domain_browser_free(s->domain_browsers);
1419 while (s->service_type_browsers)
1420 avahi_s_service_type_browser_free(s->service_type_browsers);
1421 while (s->service_browsers)
1422 avahi_s_service_browser_free(s->service_browsers);
1423 while (s->service_resolvers)
1424 avahi_s_service_resolver_free(s->service_resolvers);
1425 while (s->record_browsers)
1426 avahi_s_record_browser_destroy(s->record_browsers);
1428 /* Remove all locally rgeistered stuff */
1431 avahi_entry_free(s, s->entries);
1433 avahi_interface_monitor_free(s->monitor);
1436 avahi_entry_group_free(s, s->groups);
1440 avahi_hashmap_free(s->entries_by_key);
1441 avahi_record_list_free(s->record_list);
1442 avahi_hashmap_free(s->record_browser_hashmap);
1444 if (s->wide_area_lookup_engine)
1445 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1446 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1448 avahi_time_event_queue_free(s->time_event_queue);
1453 s->poll_api->watch_free(s->watch_ipv4);
1455 s->poll_api->watch_free(s->watch_ipv6);
1457 if (s->watch_legacy_unicast_ipv4)
1458 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1459 if (s->watch_legacy_unicast_ipv6)
1460 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1464 if (s->fd_ipv4 >= 0)
1466 if (s->fd_ipv6 >= 0)
1469 if (s->fd_legacy_unicast_ipv4 >= 0)
1470 close(s->fd_legacy_unicast_ipv4);
1471 if (s->fd_legacy_unicast_ipv6 >= 0)
1472 close(s->fd_legacy_unicast_ipv6);
1474 /* Free other stuff */
1476 avahi_free(s->host_name);
1477 avahi_free(s->domain_name);
1478 avahi_free(s->host_name_fqdn);
1480 avahi_server_config_free(&s->config);
1485 const char* avahi_server_get_domain_name(AvahiServer *s) {
1488 return s->domain_name;
1491 const char* avahi_server_get_host_name(AvahiServer *s) {
1494 return s->host_name;
1497 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1500 return s->host_name_fqdn;
1503 void* avahi_server_get_data(AvahiServer *s) {
1509 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1512 s->userdata = userdata;
1515 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1521 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1524 memset(c, 0, sizeof(AvahiServerConfig));
1527 c->host_name = NULL;
1528 c->domain_name = NULL;
1529 c->check_response_ttl = 0;
1530 c->publish_hinfo = 1;
1531 c->publish_addresses = 1;
1532 c->publish_workstation = 1;
1533 c->publish_domain = 1;
1534 c->use_iff_running = 0;
1535 c->enable_reflector = 0;
1537 c->add_service_cookie = 1;
1538 c->enable_wide_area = 0;
1539 c->n_wide_area_servers = 0;
1540 c->disallow_other_stacks = 0;
1545 void avahi_server_config_free(AvahiServerConfig *c) {
1548 avahi_free(c->host_name);
1549 avahi_free(c->domain_name);
1552 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1553 char *d = NULL, *h = NULL;
1558 if (!(h = avahi_strdup(c->host_name)))
1562 if (!(d = avahi_strdup(c->domain_name))) {
1569 ret->domain_name = d;
1574 int avahi_server_errno(AvahiServer *s) {
1580 /* Just for internal use */
1581 int avahi_server_set_errno(AvahiServer *s, int error) {
1584 return s->error = error;
1587 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1590 return s->local_service_cookie;
1593 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1599 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1601 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1602 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1603 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1610 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) {
1611 AvahiKey *key = NULL;
1614 char n[AVAHI_DOMAIN_NAME_MAX];
1621 if (!AVAHI_IF_VALID(interface))
1622 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
1624 if (!AVAHI_IF_VALID(protocol))
1625 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
1627 if (!avahi_is_valid_service_name(name))
1628 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1630 if (!avahi_is_valid_service_type_strict(type))
1631 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1633 if (domain && !avahi_is_valid_domain_name(domain))
1634 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1636 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1637 return avahi_server_set_errno(s, ret);
1639 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1640 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1642 e = find_entry(s, interface, protocol, key);
1643 avahi_key_unref(key);
1646 *ret_group = e->group;
1650 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1653 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1654 AvahiKey *key = NULL;
1660 if (!s->host_name_fqdn)
1663 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1666 e = find_entry(s, interface, protocol, key);
1667 avahi_key_unref(key);
1672 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1675 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1681 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1683 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1684 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1685 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1686 avahi_record_equal_no_ttl(record, e->record))
1692 /** Set the wide area DNS servers */
1693 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1696 if (!s->wide_area_lookup_engine)
1697 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1699 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);