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/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
31 #include <sys/utsname.h>
38 #include <avahi-common/domain.h>
39 #include <avahi-common/timeval.h>
40 #include <avahi-common/malloc.h>
41 #include <avahi-common/error.h>
49 #include "dns-srv-rr.h"
50 #include "addr-util.h"
51 #include "domain-util.h"
54 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) {
60 if (type == AVAHI_DNS_TYPE_ANY) {
63 for (e = s->entries; e; e = e->entries_next)
65 avahi_entry_is_registered(s, e, i) &&
66 e->record->key->clazz == AVAHI_DNS_CLASS_IN &&
67 avahi_domain_equal(name, e->record->key->name))
68 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
74 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
77 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
78 if (!e->dead && avahi_entry_is_registered(s, e, i))
79 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
85 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) {
91 /* Call the specified callback far all records referenced by the one specified in *r */
93 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
94 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
95 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
96 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
97 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
98 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
99 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
100 } else if (r->key->type == AVAHI_DNS_TYPE_CNAME)
101 enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata);
105 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
110 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
113 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
118 /* Push all records that match the specified key to the record list */
120 if (avahi_key_is_pattern(k)) {
123 /* Handle ANY query */
125 for (e = s->entries; e; e = e->entries_next)
126 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
127 avahi_server_prepare_response(s, i, e, unicast_response, 0);
132 /* Handle all other queries */
134 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
135 if (!e->dead && avahi_entry_is_registered(s, e, i))
136 avahi_server_prepare_response(s, i, e, unicast_response, 0);
139 /* Look for CNAME records */
141 if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY)
142 && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) {
146 if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME)))
149 avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response);
150 avahi_key_unref(cname_key);
154 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
158 /* Withdraw the specified entry, and if is part of an entry group,
159 * put that into COLLISION state */
167 for (k = e->group->entries; k; k = k->by_group_next)
169 avahi_goodbye_entry(s, k, 0, 1);
173 e->group->n_probing = 0;
175 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
177 avahi_goodbye_entry(s, e, 0, 1);
181 s->need_entry_cleanup = 1;
184 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
190 /* Withdraw an entry RRSset */
192 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
193 withdraw_entry(s, e);
196 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
198 int ours = 0, won = 0, lost = 0;
204 /* Handle incoming probes and check if they conflict our own probes */
206 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
213 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
218 if (avahi_entry_is_probing(s, e, i)) {
228 char *t = avahi_record_to_string(record);
231 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
233 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
234 withdraw_rrset(s, record->key);
241 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) {
242 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
243 AvahiEntry *e, *n, *conflicting_entry = NULL;
249 /* Check whether an incoming record conflicts with one of our own */
251 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
257 /* Check if the incoming is a goodbye record */
258 if (avahi_record_is_goodbye(record)) {
260 if (avahi_record_equal_no_ttl(e->record, record)) {
264 t = avahi_record_to_string(record);
265 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
266 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
273 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
277 if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
280 /* Either our entry or the other is intended to be unique, so let's check */
282 if (avahi_record_equal_no_ttl(e->record, record)) {
283 ours = 1; /* We have an identical record, so this is no conflict */
285 /* Check wheter there is a TTL conflict */
286 if (record->ttl <= e->record->ttl/2 &&
287 avahi_entry_is_registered(s, e, i)) {
290 t = avahi_record_to_string(record);
292 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
293 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
299 /* There's no need to check the other entries of this RRset */
304 if (avahi_entry_is_registered(s, e, i)) {
306 /* A conflict => we have to return to probe mode */
308 conflicting_entry = e;
310 } else if (avahi_entry_is_probing(s, e, i)) {
312 /* We are currently registering a matching record, but
313 * someone else already claimed it, so let's
316 withdraw_immediately = 1;
321 if (!ours && conflict) {
326 t = avahi_record_to_string(record);
328 if (withdraw_immediately) {
329 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
330 withdraw_rrset(s, record->key);
332 assert(conflicting_entry);
333 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
334 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
336 /* Local unique records are returned to probing
337 * state. Local shared records are reannounced. */
346 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
347 int *unicast_response = userdata;
351 assert(unicast_response);
353 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
356 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
360 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
363 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
367 assert(!legacy_unicast || (a && port > 0 && p));
369 if (legacy_unicast) {
370 AvahiDnsPacket *reply;
373 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
376 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
378 append_aux_records_to_list(s, i, r, 0);
380 if (avahi_dns_packet_append_record(reply, r, 0, 10))
381 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
383 char *t = avahi_record_to_string(r);
384 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
388 avahi_record_unref(r);
391 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
392 avahi_interface_send_packet_unicast(i, reply, a, port);
394 avahi_dns_packet_free(reply);
397 int unicast_response, flush_cache, auxiliary;
398 AvahiDnsPacket *reply = NULL;
401 /* In case the query packet was truncated never respond
402 immediately, because known answer suppression records might be
403 contained in later packets */
404 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
406 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
408 int im = immediately;
410 /* Only send the response immediately if it contains a
411 * unique entry AND it is not in reply to a truncated
412 * packet AND it is not an auxiliary record AND all other
413 * responses for this record are unique too. */
415 if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list))
418 if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) {
420 /* Due to some reasons the record has not been scheduled.
421 * The client requested an unicast response in that
422 * case. Therefore we prepare such a response */
424 append_aux_records_to_list(s, i, r, unicast_response);
431 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
435 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
437 /* Appending this record succeeded, so incremeant
438 * the specific header field, and return to the caller */
440 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
445 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
448 /* The record is too large for one packet, so create a larger packet */
450 avahi_dns_packet_free(reply);
451 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
452 if (size > AVAHI_DNS_PACKET_SIZE_MAX)
453 size = AVAHI_DNS_PACKET_SIZE_MAX;
455 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
458 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
460 avahi_dns_packet_free(reply);
461 t = avahi_record_to_string(r);
462 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
466 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
469 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
470 avahi_interface_send_packet_unicast(i, reply, a, port);
471 avahi_dns_packet_free(reply);
476 avahi_record_unref(r);
480 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
481 avahi_interface_send_packet_unicast(i, reply, a, port);
482 avahi_dns_packet_free(reply);
486 avahi_record_list_flush(s->record_list);
489 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
496 if (!s->config.enable_reflector)
499 for (j = s->monitor->interfaces; j; j = j->interface_next)
500 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
501 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
504 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
505 AvahiServer *s = userdata;
512 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
516 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
523 if (!s->config.enable_reflector)
526 for (j = s->monitor->interfaces; j; j = j->interface_next)
527 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
528 /* Post the query to other networks */
529 avahi_interface_post_query(j, k, 1, NULL);
531 /* Reply from caches of other network. This is needed to
532 * "work around" known answer suppression. */
534 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
538 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
545 if (!s->config.enable_reflector)
548 for (j = s->monitor->interfaces; j; j = j->interface_next)
549 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
550 avahi_interface_post_probe(j, r, 1);
553 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
562 assert(avahi_record_list_is_empty(s->record_list));
564 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
566 /* Handle the questions */
567 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
569 int unicast_response = 0;
571 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
572 avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe an UTF8 problem?)");
576 if (!legacy_unicast && !from_local_iface) {
577 reflect_query(s, i, key);
578 avahi_cache_start_poof(i->cache, key, a);
581 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
582 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
583 /* Allow our own queries to be suppressed by incoming
584 * queries only when they do not include known answers */
585 avahi_query_scheduler_incoming(i->query_scheduler, key);
587 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
588 avahi_key_unref(key);
591 if (!legacy_unicast) {
593 /* Known Answer Suppression */
594 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
598 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
599 avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe an UTF8 problem?)");
603 if (handle_conflict(s, i, record, unique)) {
604 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
605 avahi_record_list_drop(s->record_list, record);
606 avahi_cache_stop_poof(i->cache, record, a);
609 avahi_record_unref(record);
613 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
617 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
618 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe an UTF8 problem?)");
622 if (!avahi_key_is_pattern(record->key)) {
623 if (!from_local_iface)
624 reflect_probe(s, i, record);
625 incoming_probe(s, record, i);
628 avahi_record_unref(record);
632 if (!avahi_record_list_is_empty(s->record_list))
633 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
638 avahi_record_list_flush(s->record_list);
641 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
649 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
650 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
655 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
656 avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe an UTF8 problem?)");
660 if (!avahi_key_is_pattern(record->key)) {
662 if (handle_conflict(s, i, record, cache_flush)) {
663 if (!from_local_iface)
664 reflect_response(s, i, record, cache_flush);
665 avahi_cache_update(i->cache, record, cache_flush, a);
666 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
670 avahi_record_unref(record);
673 /* If the incoming response contained a conflicting record, some
674 records have been scheduling for sending. We need to flush them
676 if (!avahi_record_list_is_empty(s->record_list))
677 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
680 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
681 unsigned n, idx = (unsigned) -1;
682 AvahiLegacyUnicastReflectSlot *slot;
686 if (!s->legacy_unicast_reflect_slots)
687 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
689 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
690 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
692 if (!s->legacy_unicast_reflect_slots[idx])
696 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
699 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
700 return NULL; /* OOM */
702 s->legacy_unicast_reflect_slots[idx] = slot;
703 slot->id = s->legacy_unicast_reflect_id++;
709 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
715 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
717 assert(s->legacy_unicast_reflect_slots[idx] == slot);
719 avahi_time_event_free(slot->time_event);
722 s->legacy_unicast_reflect_slots[idx] = NULL;
725 static void free_slots(AvahiServer *s) {
729 if (!s->legacy_unicast_reflect_slots)
732 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
733 if (s->legacy_unicast_reflect_slots[idx])
734 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
736 avahi_free(s->legacy_unicast_reflect_slots);
737 s->legacy_unicast_reflect_slots = NULL;
740 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
745 if (!s->legacy_unicast_reflect_slots)
748 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
750 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
753 return s->legacy_unicast_reflect_slots[idx];
756 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
757 AvahiLegacyUnicastReflectSlot *slot = userdata;
761 assert(slot->time_event == e);
763 deallocate_slot(slot->server, slot);
766 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
767 AvahiLegacyUnicastReflectSlot *slot;
775 assert(i->protocol == a->proto);
777 if (!s->config.enable_reflector)
780 /* Reflecting legacy unicast queries is a little more complicated
781 than reflecting normal queries, since we must route the
782 responses back to the right client. Therefore we must store
783 some information for finding the right client contact data for
784 response packets. In contrast to normal queries legacy
785 unicast query and response packets are reflected untouched and
786 are not reassembled into larger packets */
788 if (!(slot = allocate_slot(s))) {
789 /* No slot available, we drop this legacy unicast query */
790 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
794 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
797 slot->interface = i->hardware->index;
799 avahi_elapse_time(&slot->elapse_time, 2000, 0);
800 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
802 /* Patch the packet with our new locally generatet id */
803 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
805 for (j = s->monitor->interfaces; j; j = j->interface_next)
808 (s->config.reflect_ipv || j->protocol == i->protocol)) {
810 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
811 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
812 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
813 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
817 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
820 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
825 if (!s->config.enable_reflector)
828 if (!avahi_address_is_local(s->monitor, address))
831 if (address->proto == AVAHI_PROTO_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 == port;
842 if (address->proto == AVAHI_PROTO_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 == 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 AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
877 int from_local_iface = 0;
884 assert(src_address->proto == dst_address->proto);
886 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
888 avahi_log_warn("Recieved packet from invalid interface.");
892 if (avahi_address_is_ipv4_in_ipv6(src_address))
893 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
896 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
897 /* This originates from our local reflector, so let's ignore it */
900 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
901 if (s->config.enable_reflector)
902 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
904 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
905 avahi_log_warn("Recieved invalid packet.");
909 if (avahi_dns_packet_is_query(p)) {
910 int legacy_unicast = 0;
912 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
913 avahi_log_warn("Invalid query packet.");
917 if (port != AVAHI_MDNS_PORT) {
920 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
921 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
922 avahi_log_warn("Invalid legacy unicast query packet.");
930 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
932 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
935 if (port != AVAHI_MDNS_PORT) {
936 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
940 if (ttl != 255 && s->config.check_response_ttl) {
941 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
945 if (!is_mdns_mcast_address(dst_address) &&
946 !avahi_interface_address_on_link(i, src_address)) {
947 avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
951 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
952 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
953 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
954 avahi_log_warn("Invalid response packet.");
958 handle_response_packet(s, p, i, src_address, from_local_iface);
962 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
964 AvahiLegacyUnicastReflectSlot *slot;
969 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
970 avahi_log_warn("Recieved invalid packet.");
974 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
975 avahi_log_warn("Recieved legacy unicast response with unknown id");
979 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
983 /* Patch the original ID into this response */
984 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
986 /* Forward the response to the correct client */
987 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
989 /* Undo changes to packet */
990 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
993 static void cleanup_dead(AvahiServer *s) {
996 avahi_cleanup_dead_entries(s);
997 avahi_browser_cleanup(s);
1000 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1001 AvahiServer *s = userdata;
1002 AvahiAddress dest, src;
1003 AvahiDnsPacket *p = NULL;
1010 assert(events & AVAHI_WATCH_IN);
1012 if (fd == s->fd_ipv4) {
1013 dest.proto = src.proto = AVAHI_PROTO_INET;
1014 p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1016 assert(fd == s->fd_ipv6);
1017 dest.proto = src.proto = AVAHI_PROTO_INET6;
1018 p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1022 if (iface == AVAHI_IF_UNSPEC)
1023 iface = avahi_find_interface_for_address(s->monitor, &dest);
1025 if (iface != AVAHI_IF_UNSPEC)
1026 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1028 avahi_log_error("Incoming packet recieved on address that isn't local.");
1030 avahi_dns_packet_free(p);
1036 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1037 AvahiServer *s = userdata;
1038 AvahiDnsPacket *p = NULL;
1042 assert(events & AVAHI_WATCH_IN);
1044 if (fd == s->fd_legacy_unicast_ipv4)
1045 p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1047 assert(fd == s->fd_legacy_unicast_ipv6);
1048 p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1052 dispatch_legacy_unicast_packet(s, p);
1053 avahi_dns_packet_free(p);
1059 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1062 if (s->state == state)
1067 avahi_interface_monitor_update_rrs(s->monitor, 0);
1070 s->callback(s, state, s->userdata);
1073 static void withdraw_host_rrs(AvahiServer *s) {
1076 if (s->hinfo_entry_group)
1077 avahi_s_entry_group_reset(s->hinfo_entry_group);
1079 if (s->browse_domain_entry_group)
1080 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1082 avahi_interface_monitor_update_rrs(s->monitor, 1);
1083 s->n_host_rr_pending = 0;
1086 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1089 assert(s->n_host_rr_pending > 0);
1091 if (--s->n_host_rr_pending == 0)
1092 server_set_state(s, AVAHI_SERVER_RUNNING);
1095 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1099 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1100 s->state == AVAHI_SERVER_REGISTERING)
1101 s->n_host_rr_pending ++;
1103 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1104 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1105 withdraw_host_rrs(s);
1106 server_set_state(s, AVAHI_SERVER_COLLISION);
1108 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1109 s->state == AVAHI_SERVER_REGISTERING)
1110 avahi_server_decrease_host_rr_pending(s);
1113 static void register_hinfo(AvahiServer *s) {
1114 struct utsname utsname;
1119 if (!s->config.publish_hinfo)
1122 if (s->hinfo_entry_group)
1123 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1125 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1127 if (!s->hinfo_entry_group) {
1128 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1132 /* Fill in HINFO rr */
1133 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1135 if (uname(&utsname) < 0)
1136 avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1139 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1140 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1142 avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1144 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1145 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1150 avahi_record_unref(r);
1153 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1154 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1158 static void register_localhost(AvahiServer *s) {
1162 /* Add localhost entries */
1163 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1164 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1166 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1167 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1170 static void register_browse_domain(AvahiServer *s) {
1173 if (!s->config.publish_domain)
1176 if (avahi_domain_equal(s->domain_name, "local"))
1179 if (s->browse_domain_entry_group)
1180 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1182 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1184 if (!s->browse_domain_entry_group) {
1185 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1189 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) {
1190 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1194 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1195 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1198 static void register_stuff(AvahiServer *s) {
1201 server_set_state(s, AVAHI_SERVER_REGISTERING);
1202 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1205 register_browse_domain(s);
1206 avahi_interface_monitor_update_rrs(s->monitor, 0);
1208 s->n_host_rr_pending --;
1210 if (s->n_host_rr_pending == 0)
1211 server_set_state(s, AVAHI_SERVER_RUNNING);
1214 static void update_fqdn(AvahiServer *s) {
1218 assert(s->host_name);
1219 assert(s->domain_name);
1221 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1224 avahi_free(s->host_name_fqdn);
1225 s->host_name_fqdn = n;
1228 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1232 AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1235 hn = avahi_get_host_name_strdup();
1236 hn[strcspn(hn, ".")] = 0;
1240 if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
1242 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1245 withdraw_host_rrs(s);
1247 avahi_free(s->host_name);
1248 s->host_name = hn ? hn : avahi_strdup(host_name);
1256 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1260 AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1263 dn = avahi_strdup("local");
1267 if (avahi_domain_equal(s->domain_name, domain_name)) {
1269 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1272 withdraw_host_rrs(s);
1274 avahi_free(s->domain_name);
1275 s->domain_name = avahi_normalize_name_strdup(domain_name);
1284 static int valid_server_config(const AvahiServerConfig *sc) {
1286 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1287 return AVAHI_ERR_INVALID_HOST_NAME;
1289 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1290 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1295 static int setup_sockets(AvahiServer *s) {
1298 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1299 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1301 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1302 return AVAHI_ERR_NO_NETWORK;
1304 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1305 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1306 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1307 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1309 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1310 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1314 s->watch_legacy_unicast_ipv4 =
1315 s->watch_legacy_unicast_ipv6 = NULL;
1317 if (s->fd_ipv4 >= 0)
1318 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1319 if (s->fd_ipv6 >= 0)
1320 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1322 if (s->fd_legacy_unicast_ipv4 >= 0)
1323 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, legacy_unicast_socket_event, s);
1324 if (s->fd_legacy_unicast_ipv6 >= 0)
1325 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, legacy_unicast_socket_event, s);
1330 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1334 if (sc && (e = valid_server_config(sc)) < 0) {
1340 if (!(s = avahi_new(AvahiServer, 1))) {
1342 *error = AVAHI_ERR_NO_MEMORY;
1347 s->poll_api = poll_api;
1350 avahi_server_config_copy(&s->config, sc);
1352 avahi_server_config_init(&s->config);
1354 if ((e = setup_sockets(s)) < 0) {
1358 avahi_server_config_free(&s->config);
1364 s->n_host_rr_pending = 0;
1365 s->need_entry_cleanup = 0;
1366 s->need_group_cleanup = 0;
1367 s->need_browser_cleanup = 0;
1368 s->hinfo_entry_group = NULL;
1369 s->browse_domain_entry_group = NULL;
1370 s->error = AVAHI_OK;
1371 s->state = AVAHI_SERVER_INVALID;
1373 s->callback = callback;
1374 s->userdata = userdata;
1376 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1378 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1379 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1380 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1382 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1383 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1384 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1385 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1386 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1387 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1388 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1389 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1390 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1392 s->legacy_unicast_reflect_slots = NULL;
1393 s->legacy_unicast_reflect_id = 0;
1395 s->record_list = avahi_record_list_new();
1398 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1399 s->host_name[strcspn(s->host_name, ".")] = 0;
1400 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1401 s->host_name_fqdn = NULL;
1405 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1406 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1408 if (s->config.enable_wide_area) {
1409 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1410 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1412 s->wide_area_lookup_engine = NULL;
1414 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1416 s->monitor = avahi_interface_monitor_new(s);
1417 avahi_interface_monitor_sync(s->monitor);
1419 register_localhost(s);
1425 void avahi_server_free(AvahiServer* s) {
1428 /* Remove all browsers */
1430 while (s->dns_server_browsers)
1431 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1432 while (s->host_name_resolvers)
1433 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1434 while (s->address_resolvers)
1435 avahi_s_address_resolver_free(s->address_resolvers);
1436 while (s->domain_browsers)
1437 avahi_s_domain_browser_free(s->domain_browsers);
1438 while (s->service_type_browsers)
1439 avahi_s_service_type_browser_free(s->service_type_browsers);
1440 while (s->service_browsers)
1441 avahi_s_service_browser_free(s->service_browsers);
1442 while (s->service_resolvers)
1443 avahi_s_service_resolver_free(s->service_resolvers);
1444 while (s->record_browsers)
1445 avahi_s_record_browser_destroy(s->record_browsers);
1447 /* Remove all locally rgeistered stuff */
1450 avahi_entry_free(s, s->entries);
1452 avahi_interface_monitor_free(s->monitor);
1455 avahi_entry_group_free(s, s->groups);
1459 avahi_hashmap_free(s->entries_by_key);
1460 avahi_record_list_free(s->record_list);
1461 avahi_hashmap_free(s->record_browser_hashmap);
1463 if (s->wide_area_lookup_engine)
1464 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1465 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1467 avahi_time_event_queue_free(s->time_event_queue);
1472 s->poll_api->watch_free(s->watch_ipv4);
1474 s->poll_api->watch_free(s->watch_ipv6);
1476 if (s->watch_legacy_unicast_ipv4)
1477 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1478 if (s->watch_legacy_unicast_ipv6)
1479 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1483 if (s->fd_ipv4 >= 0)
1485 if (s->fd_ipv6 >= 0)
1488 if (s->fd_legacy_unicast_ipv4 >= 0)
1489 close(s->fd_legacy_unicast_ipv4);
1490 if (s->fd_legacy_unicast_ipv6 >= 0)
1491 close(s->fd_legacy_unicast_ipv6);
1493 /* Free other stuff */
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 const char* avahi_server_get_domain_name(AvahiServer *s) {
1507 return s->domain_name;
1510 const char* avahi_server_get_host_name(AvahiServer *s) {
1513 return s->host_name;
1516 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1519 return s->host_name_fqdn;
1522 void* avahi_server_get_data(AvahiServer *s) {
1528 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1531 s->userdata = userdata;
1534 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1540 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1543 memset(c, 0, sizeof(AvahiServerConfig));
1546 c->host_name = NULL;
1547 c->domain_name = NULL;
1548 c->check_response_ttl = 0;
1549 c->publish_hinfo = 1;
1550 c->publish_addresses = 1;
1551 c->publish_workstation = 1;
1552 c->publish_domain = 1;
1553 c->use_iff_running = 0;
1554 c->enable_reflector = 0;
1556 c->add_service_cookie = 1;
1557 c->enable_wide_area = 0;
1558 c->n_wide_area_servers = 0;
1559 c->disallow_other_stacks = 0;
1560 c->browse_domains = NULL;
1561 c->disable_publishing = 0;
1562 c->allow_point_to_point = 0;
1563 c->publish_aaaa_on_ipv4 = 1;
1564 c->publish_a_on_ipv6 = 0;
1569 void avahi_server_config_free(AvahiServerConfig *c) {
1572 avahi_free(c->host_name);
1573 avahi_free(c->domain_name);
1574 avahi_string_list_free(c->browse_domains);
1577 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1578 char *d = NULL, *h = NULL;
1579 AvahiStringList *l = NULL;
1584 if (!(h = avahi_strdup(c->host_name)))
1588 if (!(d = avahi_strdup(c->domain_name))) {
1593 if (!(l = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1601 ret->domain_name = d;
1602 ret->browse_domains = l;
1607 int avahi_server_errno(AvahiServer *s) {
1613 /* Just for internal use */
1614 int avahi_server_set_errno(AvahiServer *s, int error) {
1617 return s->error = error;
1620 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1623 return s->local_service_cookie;
1626 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1632 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1634 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1635 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1636 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1643 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) {
1644 AvahiKey *key = NULL;
1647 char n[AVAHI_DOMAIN_NAME_MAX];
1654 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1655 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1656 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1657 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1658 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1660 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1661 return avahi_server_set_errno(s, ret);
1663 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1664 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1666 e = find_entry(s, interface, protocol, key);
1667 avahi_key_unref(key);
1670 *ret_group = e->group;
1674 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1677 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1678 AvahiKey *key = NULL;
1684 if (!s->host_name_fqdn)
1687 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1690 e = find_entry(s, interface, protocol, key);
1691 avahi_key_unref(key);
1696 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1699 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1705 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1707 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1708 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1709 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1710 avahi_record_equal_no_ttl(record, e->record))
1716 /** Set the wide area DNS servers */
1717 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1720 if (!s->wide_area_lookup_engine)
1721 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1723 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1727 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {