2 This file is part of avahi.
4 avahi is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of the
7 License, or (at your option) any later version.
9 avahi is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12 Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with avahi; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.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 #define AVAHI_DEFAULT_CACHE_ENTRIES_MAX 4096
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("Received conflicting probe [%s]. Local host won.", t);
233 avahi_log_debug("Received 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("Received 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("Received 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("Received conflicting record [%s] with local record to be. Withdrawing.", t);
330 withdraw_rrset(s, record->key);
332 assert(conflicting_entry);
333 avahi_log_debug("Received 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 + AVAHI_DNS_PACKET_EXTRA_SIZE /* 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);
444 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
447 /* The record is too large for one packet, so create a larger packet */
449 avahi_dns_packet_free(reply);
450 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
452 if (!(reply = avahi_dns_packet_new_reply(p, size + AVAHI_DNS_PACKET_EXTRA_SIZE, 0, 1)))
455 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
457 /* Appending this record succeeded, so incremeant
458 * the specific header field, and return to the caller */
460 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
465 /* We completely fucked up, there's
466 * nothing we can do. The RR just doesn't
467 * fit in. Let's ignore it. */
470 avahi_dns_packet_free(reply);
472 t = avahi_record_to_string(r);
473 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
479 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
480 avahi_interface_send_packet_unicast(i, reply, a, port);
481 avahi_dns_packet_free(reply);
486 avahi_record_unref(r);
490 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
491 avahi_interface_send_packet_unicast(i, reply, a, port);
492 avahi_dns_packet_free(reply);
496 avahi_record_list_flush(s->record_list);
499 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
506 if (!s->config.enable_reflector)
509 for (j = s->monitor->interfaces; j; j = j->interface_next)
510 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
511 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
514 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
515 AvahiServer *s = userdata;
523 /* Don't reflect cache entry with ipv6 link-local addresses. */
525 if ((r->key->type == AVAHI_DNS_TYPE_AAAA) &&
526 (r->data.aaaa.address.address[0] == 0xFE) &&
527 (r->data.aaaa.address.address[1] == 0x80))
530 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
534 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
541 if (!s->config.enable_reflector)
544 for (j = s->monitor->interfaces; j; j = j->interface_next)
545 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
546 /* Post the query to other networks */
547 avahi_interface_post_query(j, k, 1, NULL);
549 /* Reply from caches of other network. This is needed to
550 * "work around" known answer suppression. */
552 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
556 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
563 if (!s->config.enable_reflector)
566 for (j = s->monitor->interfaces; j; j = j->interface_next)
567 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
568 avahi_interface_post_probe(j, r, 1);
571 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
580 assert(avahi_record_list_is_empty(s->record_list));
582 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
584 /* Handle the questions */
585 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
587 int unicast_response = 0;
589 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
590 avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)");
594 if (!legacy_unicast && !from_local_iface) {
595 reflect_query(s, i, key);
596 if (!unicast_response)
597 avahi_cache_start_poof(i->cache, key, a);
600 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
601 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
602 /* Allow our own queries to be suppressed by incoming
603 * queries only when they do not include known answers */
604 avahi_query_scheduler_incoming(i->query_scheduler, key);
606 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
607 avahi_key_unref(key);
610 if (!legacy_unicast) {
612 /* Known Answer Suppression */
613 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); 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 known answer record. (Maybe a UTF-8 problem?)");
622 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
623 avahi_record_list_drop(s->record_list, record);
624 avahi_cache_stop_poof(i->cache, record, a);
626 avahi_record_unref(record);
630 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
634 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
635 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)");
639 if (!avahi_key_is_pattern(record->key)) {
640 if (!from_local_iface)
641 reflect_probe(s, i, record);
642 incoming_probe(s, record, i);
645 avahi_record_unref(record);
649 if (!avahi_record_list_is_empty(s->record_list))
650 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
655 avahi_record_list_flush(s->record_list);
658 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
666 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
667 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
671 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
672 avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
676 if (!avahi_key_is_pattern(record->key)) {
678 if (handle_conflict(s, i, record, cache_flush)) {
679 if (!from_local_iface && !avahi_record_is_link_local_address(record))
680 reflect_response(s, i, record, cache_flush);
681 avahi_cache_update(i->cache, record, cache_flush, a);
682 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
686 avahi_record_unref(record);
689 /* If the incoming response contained a conflicting record, some
690 records have been scheduled for sending. We need to flush them
692 if (!avahi_record_list_is_empty(s->record_list))
693 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
696 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
697 unsigned n, idx = (unsigned) -1;
698 AvahiLegacyUnicastReflectSlot *slot;
702 if (!s->legacy_unicast_reflect_slots)
703 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
705 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
706 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
708 if (!s->legacy_unicast_reflect_slots[idx])
712 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
715 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
716 return NULL; /* OOM */
718 s->legacy_unicast_reflect_slots[idx] = slot;
719 slot->id = s->legacy_unicast_reflect_id++;
725 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
731 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
733 assert(s->legacy_unicast_reflect_slots[idx] == slot);
735 avahi_time_event_free(slot->time_event);
738 s->legacy_unicast_reflect_slots[idx] = NULL;
741 static void free_slots(AvahiServer *s) {
745 if (!s->legacy_unicast_reflect_slots)
748 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
749 if (s->legacy_unicast_reflect_slots[idx])
750 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
752 avahi_free(s->legacy_unicast_reflect_slots);
753 s->legacy_unicast_reflect_slots = NULL;
756 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
761 if (!s->legacy_unicast_reflect_slots)
764 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
766 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
769 return s->legacy_unicast_reflect_slots[idx];
772 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
773 AvahiLegacyUnicastReflectSlot *slot = userdata;
777 assert(slot->time_event == e);
779 deallocate_slot(slot->server, slot);
782 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
783 AvahiLegacyUnicastReflectSlot *slot;
791 assert(i->protocol == a->proto);
793 if (!s->config.enable_reflector)
796 /* Reflecting legacy unicast queries is a little more complicated
797 than reflecting normal queries, since we must route the
798 responses back to the right client. Therefore we must store
799 some information for finding the right client contact data for
800 response packets. In contrast to normal queries legacy
801 unicast query and response packets are reflected untouched and
802 are not reassembled into larger packets */
804 if (!(slot = allocate_slot(s))) {
805 /* No slot available, we drop this legacy unicast query */
806 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
810 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
813 slot->interface = i->hardware->index;
815 avahi_elapse_time(&slot->elapse_time, 2000, 0);
816 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
818 /* Patch the packet with our new locally generatet id */
819 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
821 for (j = s->monitor->interfaces; j; j = j->interface_next)
824 (s->config.reflect_ipv || j->protocol == i->protocol)) {
826 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
827 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
828 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
829 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
833 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
836 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
841 if (!s->config.enable_reflector)
844 if (!avahi_address_is_local(s->monitor, address))
847 if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
848 struct sockaddr_in lsa;
849 socklen_t l = sizeof(lsa);
851 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
852 avahi_log_warn("getsockname(): %s", strerror(errno));
854 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
858 if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
859 struct sockaddr_in6 lsa;
860 socklen_t l = sizeof(lsa);
862 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
863 avahi_log_warn("getsockname(): %s", strerror(errno));
865 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
871 static int is_mdns_mcast_address(const AvahiAddress *a) {
875 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
876 return avahi_address_cmp(a, &b) == 0;
879 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
881 assert(iface != AVAHI_IF_UNSPEC);
884 /* If it isn't the MDNS port it can't be generated by us */
885 if (port != AVAHI_MDNS_PORT)
888 return avahi_interface_has_address(s->monitor, iface, a);
891 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
893 int from_local_iface = 0;
900 assert(src_address->proto == dst_address->proto);
902 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
904 avahi_log_warn("Received packet from invalid interface.");
909 /* This fixes RHBZ #475394 */
910 avahi_log_warn("Received packet from invalid source port %u.", (unsigned) port);
914 if (avahi_address_is_ipv4_in_ipv6(src_address))
915 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
918 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
919 /* This originates from our local reflector, so let's ignore it */
922 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
923 if (s->config.enable_reflector)
924 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
926 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
927 avahi_log_warn("Received invalid packet.");
931 if (avahi_dns_packet_is_query(p)) {
932 int legacy_unicast = 0;
934 /* For queries EDNS0 might allow ARCOUNT != 0. We ignore the
935 * AR section completely here, so far. Until the day we add
938 if (port != AVAHI_MDNS_PORT) {
941 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
942 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
943 avahi_log_warn("Invalid legacy unicast query packet.");
951 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
953 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
956 char t[AVAHI_ADDRESS_STR_MAX];
958 if (port != AVAHI_MDNS_PORT) {
959 avahi_log_warn("Received response from host %s with invalid source port %u on interface '%s.%i'", avahi_address_snprint(t, sizeof(t), src_address), port, i->hardware->name, i->protocol);
963 if (ttl != 255 && s->config.check_response_ttl) {
964 avahi_log_warn("Received response from host %s with invalid TTL %u on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), ttl, i->hardware->name, i->protocol);
968 if (!is_mdns_mcast_address(dst_address) &&
969 !avahi_interface_address_on_link(i, src_address)) {
971 avahi_log_warn("Received non-local response from host %s on interface '%s.%i'.", avahi_address_snprint(t, sizeof(t), src_address), i->hardware->name, i->protocol);
975 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
976 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
977 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
979 avahi_log_warn("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address));
983 handle_response_packet(s, p, i, src_address, from_local_iface);
987 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
989 AvahiLegacyUnicastReflectSlot *slot;
994 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
995 avahi_log_warn("Received invalid packet.");
999 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1000 avahi_log_warn("Received legacy unicast response with unknown id");
1004 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
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 mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1019 AvahiServer *s = userdata;
1020 AvahiAddress dest, src;
1021 AvahiDnsPacket *p = NULL;
1028 assert(events & AVAHI_WATCH_IN);
1030 if (fd == s->fd_ipv4) {
1031 dest.proto = src.proto = AVAHI_PROTO_INET;
1032 p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1034 assert(fd == s->fd_ipv6);
1035 dest.proto = src.proto = AVAHI_PROTO_INET6;
1036 p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1040 if (iface == AVAHI_IF_UNSPEC)
1041 iface = avahi_find_interface_for_address(s->monitor, &dest);
1043 if (iface != AVAHI_IF_UNSPEC)
1044 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1046 avahi_log_error("Incoming packet received on address that isn't local.");
1048 avahi_dns_packet_free(p);
1050 avahi_cleanup_dead_entries(s);
1054 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1055 AvahiServer *s = userdata;
1056 AvahiDnsPacket *p = NULL;
1060 assert(events & AVAHI_WATCH_IN);
1062 if (fd == s->fd_legacy_unicast_ipv4)
1063 p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1065 assert(fd == s->fd_legacy_unicast_ipv6);
1066 p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1070 dispatch_legacy_unicast_packet(s, p);
1071 avahi_dns_packet_free(p);
1073 avahi_cleanup_dead_entries(s);
1077 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1080 if (s->state == state)
1085 avahi_interface_monitor_update_rrs(s->monitor, 0);
1088 s->callback(s, state, s->userdata);
1091 static void withdraw_host_rrs(AvahiServer *s) {
1094 if (s->hinfo_entry_group)
1095 avahi_s_entry_group_reset(s->hinfo_entry_group);
1097 if (s->browse_domain_entry_group)
1098 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1100 avahi_interface_monitor_update_rrs(s->monitor, 1);
1101 s->n_host_rr_pending = 0;
1104 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1107 assert(s->n_host_rr_pending > 0);
1109 if (--s->n_host_rr_pending == 0)
1110 server_set_state(s, AVAHI_SERVER_RUNNING);
1113 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1117 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1118 s->state == AVAHI_SERVER_REGISTERING)
1119 s->n_host_rr_pending ++;
1121 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1122 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1123 withdraw_host_rrs(s);
1124 server_set_state(s, AVAHI_SERVER_COLLISION);
1126 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1127 s->state == AVAHI_SERVER_REGISTERING)
1128 avahi_server_decrease_host_rr_pending(s);
1131 static void register_hinfo(AvahiServer *s) {
1132 struct utsname utsname;
1137 if (!s->config.publish_hinfo)
1140 if (s->hinfo_entry_group)
1141 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1143 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1145 if (!s->hinfo_entry_group) {
1146 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1150 /* Fill in HINFO rr */
1151 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1153 if (uname(&utsname) < 0)
1154 avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1157 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1158 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1160 avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1162 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1163 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1168 avahi_record_unref(r);
1171 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1172 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1176 static void register_localhost(AvahiServer *s) {
1180 /* Add localhost entries */
1181 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1182 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1184 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1185 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1188 static void register_browse_domain(AvahiServer *s) {
1191 if (!s->config.publish_domain)
1194 if (avahi_domain_equal(s->domain_name, "local"))
1197 if (s->browse_domain_entry_group)
1198 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1200 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1202 if (!s->browse_domain_entry_group) {
1203 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1207 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) {
1208 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1212 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1213 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1216 static void register_stuff(AvahiServer *s) {
1219 server_set_state(s, AVAHI_SERVER_REGISTERING);
1220 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1223 register_browse_domain(s);
1224 avahi_interface_monitor_update_rrs(s->monitor, 0);
1226 assert(s->n_host_rr_pending > 0);
1227 s->n_host_rr_pending --;
1229 if (s->n_host_rr_pending == 0)
1230 server_set_state(s, AVAHI_SERVER_RUNNING);
1233 static void update_fqdn(AvahiServer *s) {
1237 assert(s->host_name);
1238 assert(s->domain_name);
1240 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1243 avahi_free(s->host_name_fqdn);
1244 s->host_name_fqdn = n;
1247 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1251 AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1254 hn = avahi_get_host_name_strdup();
1256 hn = avahi_normalize_name_strdup(host_name);
1258 hn[strcspn(hn, ".")] = 0;
1260 if (avahi_domain_equal(s->host_name, hn) && s->state != AVAHI_SERVER_COLLISION) {
1262 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1265 withdraw_host_rrs(s);
1267 avahi_free(s->host_name);
1276 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1280 AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1283 dn = avahi_strdup("local");
1285 dn = avahi_normalize_name_strdup(domain_name);
1287 if (avahi_domain_equal(s->domain_name, domain_name)) {
1289 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1292 withdraw_host_rrs(s);
1294 avahi_free(s->domain_name);
1295 s->domain_name = dn;
1304 static int valid_server_config(const AvahiServerConfig *sc) {
1309 if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1310 return AVAHI_ERR_INVALID_CONFIG;
1312 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1313 return AVAHI_ERR_INVALID_HOST_NAME;
1315 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1316 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1318 for (l = sc->browse_domains; l; l = l->next)
1319 if (!avahi_is_valid_domain_name((char*) l->text))
1320 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1325 static int setup_sockets(AvahiServer *s) {
1328 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1329 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1331 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1332 return AVAHI_ERR_NO_NETWORK;
1334 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1335 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1336 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1337 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1339 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1340 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1344 s->watch_legacy_unicast_ipv4 =
1345 s->watch_legacy_unicast_ipv6 = NULL;
1347 if (s->fd_ipv4 >= 0)
1348 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1349 if (s->fd_ipv6 >= 0)
1350 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1352 if (s->fd_legacy_unicast_ipv4 >= 0)
1353 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);
1354 if (s->fd_legacy_unicast_ipv6 >= 0)
1355 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);
1360 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1364 if (sc && (e = valid_server_config(sc)) < 0) {
1370 if (!(s = avahi_new(AvahiServer, 1))) {
1372 *error = AVAHI_ERR_NO_MEMORY;
1377 s->poll_api = poll_api;
1380 avahi_server_config_copy(&s->config, sc);
1382 avahi_server_config_init(&s->config);
1384 if ((e = setup_sockets(s)) < 0) {
1388 avahi_server_config_free(&s->config);
1394 s->n_host_rr_pending = 0;
1395 s->need_entry_cleanup = 0;
1396 s->need_group_cleanup = 0;
1397 s->need_browser_cleanup = 0;
1398 s->cleanup_time_event = NULL;
1399 s->hinfo_entry_group = NULL;
1400 s->browse_domain_entry_group = NULL;
1401 s->error = AVAHI_OK;
1402 s->state = AVAHI_SERVER_INVALID;
1404 s->callback = callback;
1405 s->userdata = userdata;
1407 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1409 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1410 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1411 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1413 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1414 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1415 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1416 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1417 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1418 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1419 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1420 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1421 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1423 s->legacy_unicast_reflect_slots = NULL;
1424 s->legacy_unicast_reflect_id = 0;
1426 s->record_list = avahi_record_list_new();
1429 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1430 s->host_name[strcspn(s->host_name, ".")] = 0;
1431 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1432 s->host_name_fqdn = NULL;
1436 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1437 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1439 if (s->config.enable_wide_area) {
1440 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1441 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1443 s->wide_area_lookup_engine = NULL;
1445 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1447 s->monitor = avahi_interface_monitor_new(s);
1448 avahi_interface_monitor_sync(s->monitor);
1450 register_localhost(s);
1456 void avahi_server_free(AvahiServer* s) {
1459 /* Remove all browsers */
1461 while (s->dns_server_browsers)
1462 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1463 while (s->host_name_resolvers)
1464 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1465 while (s->address_resolvers)
1466 avahi_s_address_resolver_free(s->address_resolvers);
1467 while (s->domain_browsers)
1468 avahi_s_domain_browser_free(s->domain_browsers);
1469 while (s->service_type_browsers)
1470 avahi_s_service_type_browser_free(s->service_type_browsers);
1471 while (s->service_browsers)
1472 avahi_s_service_browser_free(s->service_browsers);
1473 while (s->service_resolvers)
1474 avahi_s_service_resolver_free(s->service_resolvers);
1475 while (s->record_browsers)
1476 avahi_s_record_browser_destroy(s->record_browsers);
1478 /* Remove all locally rgeistered stuff */
1481 avahi_entry_free(s, s->entries);
1483 avahi_interface_monitor_free(s->monitor);
1486 avahi_entry_group_free(s, s->groups);
1490 avahi_hashmap_free(s->entries_by_key);
1491 avahi_record_list_free(s->record_list);
1492 avahi_hashmap_free(s->record_browser_hashmap);
1494 if (s->wide_area_lookup_engine)
1495 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1496 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1498 if (s->cleanup_time_event)
1499 avahi_time_event_free(s->cleanup_time_event);
1501 avahi_time_event_queue_free(s->time_event_queue);
1506 s->poll_api->watch_free(s->watch_ipv4);
1508 s->poll_api->watch_free(s->watch_ipv6);
1510 if (s->watch_legacy_unicast_ipv4)
1511 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1512 if (s->watch_legacy_unicast_ipv6)
1513 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1517 if (s->fd_ipv4 >= 0)
1519 if (s->fd_ipv6 >= 0)
1522 if (s->fd_legacy_unicast_ipv4 >= 0)
1523 close(s->fd_legacy_unicast_ipv4);
1524 if (s->fd_legacy_unicast_ipv6 >= 0)
1525 close(s->fd_legacy_unicast_ipv6);
1527 /* Free other stuff */
1529 avahi_free(s->host_name);
1530 avahi_free(s->domain_name);
1531 avahi_free(s->host_name_fqdn);
1533 avahi_server_config_free(&s->config);
1538 const char* avahi_server_get_domain_name(AvahiServer *s) {
1541 return s->domain_name;
1544 const char* avahi_server_get_host_name(AvahiServer *s) {
1547 return s->host_name;
1550 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1553 return s->host_name_fqdn;
1556 void* avahi_server_get_data(AvahiServer *s) {
1562 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1565 s->userdata = userdata;
1568 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1574 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1577 memset(c, 0, sizeof(AvahiServerConfig));
1580 c->allow_interfaces = NULL;
1581 c->deny_interfaces = NULL;
1582 c->host_name = NULL;
1583 c->domain_name = NULL;
1584 c->check_response_ttl = 0;
1585 c->publish_hinfo = 0;
1586 c->publish_addresses = 1;
1587 c->publish_no_reverse = 0;
1588 c->publish_workstation = 0;
1589 c->publish_domain = 1;
1590 c->use_iff_running = 0;
1591 c->enable_reflector = 0;
1593 c->add_service_cookie = 0;
1594 c->enable_wide_area = 0;
1595 c->n_wide_area_servers = 0;
1596 c->disallow_other_stacks = 0;
1597 c->browse_domains = NULL;
1598 c->disable_publishing = 0;
1599 c->allow_point_to_point = 0;
1600 c->publish_aaaa_on_ipv4 = 1;
1601 c->publish_a_on_ipv6 = 0;
1602 c->n_cache_entries_max = AVAHI_DEFAULT_CACHE_ENTRIES_MAX;
1603 c->ratelimit_interval = 0;
1604 c->ratelimit_burst = 0;
1609 void avahi_server_config_free(AvahiServerConfig *c) {
1612 avahi_free(c->host_name);
1613 avahi_free(c->domain_name);
1614 avahi_string_list_free(c->browse_domains);
1615 avahi_string_list_free(c->allow_interfaces);
1616 avahi_string_list_free(c->deny_interfaces);
1619 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1620 char *d = NULL, *h = NULL;
1621 AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL;
1626 if (!(h = avahi_strdup(c->host_name)))
1630 if (!(d = avahi_strdup(c->domain_name))) {
1635 if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1641 if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) {
1642 avahi_string_list_free(browse);
1648 if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) {
1649 avahi_string_list_free(allow);
1650 avahi_string_list_free(browse);
1658 ret->domain_name = d;
1659 ret->browse_domains = browse;
1660 ret->allow_interfaces = allow;
1661 ret->deny_interfaces = deny;
1666 int avahi_server_errno(AvahiServer *s) {
1672 /* Just for internal use */
1673 int avahi_server_set_errno(AvahiServer *s, int error) {
1676 return s->error = error;
1679 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1682 return s->local_service_cookie;
1685 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1691 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1693 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1694 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1695 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1702 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) {
1703 AvahiKey *key = NULL;
1706 char n[AVAHI_DOMAIN_NAME_MAX];
1713 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1714 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1715 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1716 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1717 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1719 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1720 return avahi_server_set_errno(s, ret);
1722 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1723 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1725 e = find_entry(s, interface, protocol, key);
1726 avahi_key_unref(key);
1729 *ret_group = e->group;
1733 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1736 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1737 AvahiKey *key = NULL;
1743 if (!s->host_name_fqdn)
1746 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1749 e = find_entry(s, interface, protocol, key);
1750 avahi_key_unref(key);
1755 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1758 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1764 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1766 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1767 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1768 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1769 avahi_record_equal_no_ttl(record, e->record))
1775 /** Set the wide area DNS servers */
1776 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1779 if (!s->wide_area_lookup_engine)
1780 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1782 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1786 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1792 /** Set the browsing domains */
1793 int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1798 for (l = s->config.browse_domains; l; l = l->next)
1799 if (!avahi_is_valid_domain_name((char*) l->text))
1800 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1802 avahi_string_list_free(s->config.browse_domains);
1803 s->config.browse_domains = avahi_string_list_copy(domains);