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 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("Received conflicting probe [%s]. Local host won.", t);
231 avahi_log_debug("Received 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("Received 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("Received 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("Received conflicting record [%s] with local record to be. Withdrawing.", t);
328 withdraw_rrset(s, record->key);
330 assert(conflicting_entry);
331 avahi_log_debug("Received 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 + AVAHI_DNS_PACKET_EXTRA_SIZE /* 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 int im = immediately;
408 /* Only send the response immediately if it contains a
409 * unique entry AND it is not in reply to a truncated
410 * packet AND it is not an auxiliary record AND all other
411 * responses for this record are unique too. */
413 if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list))
416 if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) {
418 /* Due to some reasons the record has not been scheduled.
419 * The client requested an unicast response in that
420 * case. Therefore we prepare such a response */
422 append_aux_records_to_list(s, i, r, unicast_response);
429 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
433 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
435 /* Appending this record succeeded, so incremeant
436 * the specific header field, and return to the caller */
438 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
442 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
445 /* The record is too large for one packet, so create a larger packet */
447 avahi_dns_packet_free(reply);
448 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
450 if (!(reply = avahi_dns_packet_new_reply(p, size + AVAHI_DNS_PACKET_EXTRA_SIZE, 0, 1)))
453 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
455 /* Appending this record succeeded, so incremeant
456 * the specific header field, and return to the caller */
458 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
463 /* We completely fucked up, there's
464 * nothing we can do. The RR just doesn't
465 * fit in. Let's ignore it. */
468 avahi_dns_packet_free(reply);
470 t = avahi_record_to_string(r);
471 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
477 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
478 avahi_interface_send_packet_unicast(i, reply, a, port);
479 avahi_dns_packet_free(reply);
484 avahi_record_unref(r);
488 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
489 avahi_interface_send_packet_unicast(i, reply, a, port);
490 avahi_dns_packet_free(reply);
494 avahi_record_list_flush(s->record_list);
497 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
504 if (!s->config.enable_reflector)
507 for (j = s->monitor->interfaces; j; j = j->interface_next)
508 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
509 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
512 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
513 AvahiServer *s = userdata;
520 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
524 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
531 if (!s->config.enable_reflector)
534 for (j = s->monitor->interfaces; j; j = j->interface_next)
535 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
536 /* Post the query to other networks */
537 avahi_interface_post_query(j, k, 1, NULL);
539 /* Reply from caches of other network. This is needed to
540 * "work around" known answer suppression. */
542 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
546 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
553 if (!s->config.enable_reflector)
556 for (j = s->monitor->interfaces; j; j = j->interface_next)
557 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
558 avahi_interface_post_probe(j, r, 1);
561 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
570 assert(avahi_record_list_is_empty(s->record_list));
572 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
574 /* Handle the questions */
575 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
577 int unicast_response = 0;
579 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
580 avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)");
584 if (!legacy_unicast && !from_local_iface) {
585 reflect_query(s, i, key);
586 if (!unicast_response)
587 avahi_cache_start_poof(i->cache, key, a);
590 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
591 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
592 /* Allow our own queries to be suppressed by incoming
593 * queries only when they do not include known answers */
594 avahi_query_scheduler_incoming(i->query_scheduler, key);
596 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
597 avahi_key_unref(key);
600 if (!legacy_unicast) {
602 /* Known Answer Suppression */
603 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
607 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
608 avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)");
612 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
613 avahi_record_list_drop(s->record_list, record);
614 avahi_cache_stop_poof(i->cache, record, a);
616 avahi_record_unref(record);
620 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
624 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
625 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)");
629 if (!avahi_key_is_pattern(record->key)) {
630 if (!from_local_iface)
631 reflect_probe(s, i, record);
632 incoming_probe(s, record, i);
635 avahi_record_unref(record);
639 if (!avahi_record_list_is_empty(s->record_list))
640 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
645 avahi_record_list_flush(s->record_list);
648 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
656 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
657 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
662 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
663 avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
667 if (!avahi_key_is_pattern(record->key) && !avahi_record_is_link_local_address(record)) {
669 if (handle_conflict(s, i, record, cache_flush)) {
670 if (!from_local_iface)
671 reflect_response(s, i, record, cache_flush);
672 avahi_cache_update(i->cache, record, cache_flush, a);
673 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
677 avahi_record_unref(record);
680 /* If the incoming response contained a conflicting record, some
681 records have been scheduling for sending. We need to flush them
683 if (!avahi_record_list_is_empty(s->record_list))
684 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
687 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
688 unsigned n, idx = (unsigned) -1;
689 AvahiLegacyUnicastReflectSlot *slot;
693 if (!s->legacy_unicast_reflect_slots)
694 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
696 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
697 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
699 if (!s->legacy_unicast_reflect_slots[idx])
703 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
706 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
707 return NULL; /* OOM */
709 s->legacy_unicast_reflect_slots[idx] = slot;
710 slot->id = s->legacy_unicast_reflect_id++;
716 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
722 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
724 assert(s->legacy_unicast_reflect_slots[idx] == slot);
726 avahi_time_event_free(slot->time_event);
729 s->legacy_unicast_reflect_slots[idx] = NULL;
732 static void free_slots(AvahiServer *s) {
736 if (!s->legacy_unicast_reflect_slots)
739 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
740 if (s->legacy_unicast_reflect_slots[idx])
741 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
743 avahi_free(s->legacy_unicast_reflect_slots);
744 s->legacy_unicast_reflect_slots = NULL;
747 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
752 if (!s->legacy_unicast_reflect_slots)
755 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
757 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
760 return s->legacy_unicast_reflect_slots[idx];
763 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
764 AvahiLegacyUnicastReflectSlot *slot = userdata;
768 assert(slot->time_event == e);
770 deallocate_slot(slot->server, slot);
773 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
774 AvahiLegacyUnicastReflectSlot *slot;
782 assert(i->protocol == a->proto);
784 if (!s->config.enable_reflector)
787 /* Reflecting legacy unicast queries is a little more complicated
788 than reflecting normal queries, since we must route the
789 responses back to the right client. Therefore we must store
790 some information for finding the right client contact data for
791 response packets. In contrast to normal queries legacy
792 unicast query and response packets are reflected untouched and
793 are not reassembled into larger packets */
795 if (!(slot = allocate_slot(s))) {
796 /* No slot available, we drop this legacy unicast query */
797 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
801 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
804 slot->interface = i->hardware->index;
806 avahi_elapse_time(&slot->elapse_time, 2000, 0);
807 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
809 /* Patch the packet with our new locally generatet id */
810 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
812 for (j = s->monitor->interfaces; j; j = j->interface_next)
815 (s->config.reflect_ipv || j->protocol == i->protocol)) {
817 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
818 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
819 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
820 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
824 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
827 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
832 if (!s->config.enable_reflector)
835 if (!avahi_address_is_local(s->monitor, address))
838 if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
839 struct sockaddr_in lsa;
840 socklen_t l = sizeof(lsa);
842 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
843 avahi_log_warn("getsockname(): %s", strerror(errno));
845 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
849 if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
850 struct sockaddr_in6 lsa;
851 socklen_t l = sizeof(lsa);
853 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
854 avahi_log_warn("getsockname(): %s", strerror(errno));
856 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
862 static int is_mdns_mcast_address(const AvahiAddress *a) {
866 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
867 return avahi_address_cmp(a, &b) == 0;
870 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
872 assert(iface != AVAHI_IF_UNSPEC);
875 /* If it isn't the MDNS port it can't be generated by us */
876 if (port != AVAHI_MDNS_PORT)
879 return avahi_interface_has_address(s->monitor, iface, a);
882 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
884 int from_local_iface = 0;
891 assert(src_address->proto == dst_address->proto);
893 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
895 avahi_log_warn("Received packet from invalid interface.");
900 /* This fixes RHBZ #475394 */
901 avahi_log_warn("Received packet from invalid source port %u.", (unsigned) port);
905 if (avahi_address_is_ipv4_in_ipv6(src_address))
906 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
909 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
910 /* This originates from our local reflector, so let's ignore it */
913 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
914 if (s->config.enable_reflector)
915 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
917 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
918 avahi_log_warn("Received invalid packet.");
922 if (avahi_dns_packet_is_query(p)) {
923 int legacy_unicast = 0;
925 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
926 avahi_log_warn("Invalid query packet.");
930 if (port != AVAHI_MDNS_PORT) {
933 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
934 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
935 avahi_log_warn("Invalid legacy unicast query packet.");
943 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
945 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
948 char t[AVAHI_ADDRESS_STR_MAX];
950 if (port != AVAHI_MDNS_PORT) {
951 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);
955 if (ttl != 255 && s->config.check_response_ttl) {
956 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);
960 if (!is_mdns_mcast_address(dst_address) &&
961 !avahi_interface_address_on_link(i, src_address)) {
963 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);
967 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
968 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
969 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
971 avahi_log_warn("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address));
975 handle_response_packet(s, p, i, src_address, from_local_iface);
979 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
981 AvahiLegacyUnicastReflectSlot *slot;
986 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
987 avahi_log_warn("Received invalid packet.");
991 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
992 avahi_log_warn("Received legacy unicast response with unknown id");
996 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1000 /* Patch the original ID into this response */
1001 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1003 /* Forward the response to the correct client */
1004 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1006 /* Undo changes to packet */
1007 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1010 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1011 AvahiServer *s = userdata;
1012 AvahiAddress dest, src;
1013 AvahiDnsPacket *p = NULL;
1020 assert(events & AVAHI_WATCH_IN);
1022 if (fd == s->fd_ipv4) {
1023 dest.proto = src.proto = AVAHI_PROTO_INET;
1024 p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1026 assert(fd == s->fd_ipv6);
1027 dest.proto = src.proto = AVAHI_PROTO_INET6;
1028 p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1032 if (iface == AVAHI_IF_UNSPEC)
1033 iface = avahi_find_interface_for_address(s->monitor, &dest);
1035 if (iface != AVAHI_IF_UNSPEC)
1036 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1038 avahi_log_error("Incoming packet received on address that isn't local.");
1040 avahi_dns_packet_free(p);
1042 avahi_cleanup_dead_entries(s);
1046 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1047 AvahiServer *s = userdata;
1048 AvahiDnsPacket *p = NULL;
1052 assert(events & AVAHI_WATCH_IN);
1054 if (fd == s->fd_legacy_unicast_ipv4)
1055 p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1057 assert(fd == s->fd_legacy_unicast_ipv6);
1058 p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1062 dispatch_legacy_unicast_packet(s, p);
1063 avahi_dns_packet_free(p);
1065 avahi_cleanup_dead_entries(s);
1069 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1072 if (s->state == state)
1077 avahi_interface_monitor_update_rrs(s->monitor, 0);
1080 s->callback(s, state, s->userdata);
1083 static void withdraw_host_rrs(AvahiServer *s) {
1086 if (s->hinfo_entry_group)
1087 avahi_s_entry_group_reset(s->hinfo_entry_group);
1089 if (s->browse_domain_entry_group)
1090 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1092 avahi_interface_monitor_update_rrs(s->monitor, 1);
1093 s->n_host_rr_pending = 0;
1096 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1099 assert(s->n_host_rr_pending > 0);
1101 if (--s->n_host_rr_pending == 0)
1102 server_set_state(s, AVAHI_SERVER_RUNNING);
1105 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1109 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1110 s->state == AVAHI_SERVER_REGISTERING)
1111 s->n_host_rr_pending ++;
1113 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1114 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1115 withdraw_host_rrs(s);
1116 server_set_state(s, AVAHI_SERVER_COLLISION);
1118 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1119 s->state == AVAHI_SERVER_REGISTERING)
1120 avahi_server_decrease_host_rr_pending(s);
1123 static void register_hinfo(AvahiServer *s) {
1124 struct utsname utsname;
1129 if (!s->config.publish_hinfo)
1132 if (s->hinfo_entry_group)
1133 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1135 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1137 if (!s->hinfo_entry_group) {
1138 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1142 /* Fill in HINFO rr */
1143 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1145 if (uname(&utsname) < 0)
1146 avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1149 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1150 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1152 avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1154 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1155 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1160 avahi_record_unref(r);
1163 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1164 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1168 static void register_localhost(AvahiServer *s) {
1172 /* Add localhost entries */
1173 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1174 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1176 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1177 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1180 static void register_browse_domain(AvahiServer *s) {
1183 if (!s->config.publish_domain)
1186 if (avahi_domain_equal(s->domain_name, "local"))
1189 if (s->browse_domain_entry_group)
1190 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1192 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1194 if (!s->browse_domain_entry_group) {
1195 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1199 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) {
1200 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1204 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1205 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1208 static void register_stuff(AvahiServer *s) {
1211 server_set_state(s, AVAHI_SERVER_REGISTERING);
1212 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1215 register_browse_domain(s);
1216 avahi_interface_monitor_update_rrs(s->monitor, 0);
1218 s->n_host_rr_pending --;
1220 if (s->n_host_rr_pending == 0)
1221 server_set_state(s, AVAHI_SERVER_RUNNING);
1224 static void update_fqdn(AvahiServer *s) {
1228 assert(s->host_name);
1229 assert(s->domain_name);
1231 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1234 avahi_free(s->host_name_fqdn);
1235 s->host_name_fqdn = n;
1238 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1242 AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1245 hn = avahi_get_host_name_strdup();
1246 hn[strcspn(hn, ".")] = 0;
1250 if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
1252 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1255 withdraw_host_rrs(s);
1257 avahi_free(s->host_name);
1258 s->host_name = hn ? hn : avahi_strdup(host_name);
1266 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1270 AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1273 dn = avahi_strdup("local");
1277 if (avahi_domain_equal(s->domain_name, domain_name)) {
1279 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1282 withdraw_host_rrs(s);
1284 avahi_free(s->domain_name);
1285 s->domain_name = avahi_normalize_name_strdup(domain_name);
1294 static int valid_server_config(const AvahiServerConfig *sc) {
1299 if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1300 return AVAHI_ERR_INVALID_CONFIG;
1302 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1303 return AVAHI_ERR_INVALID_HOST_NAME;
1305 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1306 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1308 for (l = sc->browse_domains; l; l = l->next)
1309 if (!avahi_is_valid_domain_name((char*) l->text))
1310 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1315 static int setup_sockets(AvahiServer *s) {
1318 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1319 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1321 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1322 return AVAHI_ERR_NO_NETWORK;
1324 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1325 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1326 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1327 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1329 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1330 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1334 s->watch_legacy_unicast_ipv4 =
1335 s->watch_legacy_unicast_ipv6 = NULL;
1337 if (s->fd_ipv4 >= 0)
1338 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1339 if (s->fd_ipv6 >= 0)
1340 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1342 if (s->fd_legacy_unicast_ipv4 >= 0)
1343 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);
1344 if (s->fd_legacy_unicast_ipv6 >= 0)
1345 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);
1350 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1354 if (sc && (e = valid_server_config(sc)) < 0) {
1360 if (!(s = avahi_new(AvahiServer, 1))) {
1362 *error = AVAHI_ERR_NO_MEMORY;
1367 s->poll_api = poll_api;
1370 avahi_server_config_copy(&s->config, sc);
1372 avahi_server_config_init(&s->config);
1374 if ((e = setup_sockets(s)) < 0) {
1378 avahi_server_config_free(&s->config);
1384 s->n_host_rr_pending = 0;
1385 s->need_entry_cleanup = 0;
1386 s->need_group_cleanup = 0;
1387 s->need_browser_cleanup = 0;
1388 s->hinfo_entry_group = NULL;
1389 s->browse_domain_entry_group = NULL;
1390 s->error = AVAHI_OK;
1391 s->state = AVAHI_SERVER_INVALID;
1393 s->callback = callback;
1394 s->userdata = userdata;
1396 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1398 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1399 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1400 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1402 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1403 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1404 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1405 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1406 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1407 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1408 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1409 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1410 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1412 s->legacy_unicast_reflect_slots = NULL;
1413 s->legacy_unicast_reflect_id = 0;
1415 s->record_list = avahi_record_list_new();
1418 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1419 s->host_name[strcspn(s->host_name, ".")] = 0;
1420 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1421 s->host_name_fqdn = NULL;
1425 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1426 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1428 if (s->config.enable_wide_area) {
1429 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1430 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1432 s->wide_area_lookup_engine = NULL;
1434 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1436 s->monitor = avahi_interface_monitor_new(s);
1437 avahi_interface_monitor_sync(s->monitor);
1439 register_localhost(s);
1445 void avahi_server_free(AvahiServer* s) {
1448 /* Remove all browsers */
1450 while (s->dns_server_browsers)
1451 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1452 while (s->host_name_resolvers)
1453 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1454 while (s->address_resolvers)
1455 avahi_s_address_resolver_free(s->address_resolvers);
1456 while (s->domain_browsers)
1457 avahi_s_domain_browser_free(s->domain_browsers);
1458 while (s->service_type_browsers)
1459 avahi_s_service_type_browser_free(s->service_type_browsers);
1460 while (s->service_browsers)
1461 avahi_s_service_browser_free(s->service_browsers);
1462 while (s->service_resolvers)
1463 avahi_s_service_resolver_free(s->service_resolvers);
1464 while (s->record_browsers)
1465 avahi_s_record_browser_destroy(s->record_browsers);
1467 /* Remove all locally rgeistered stuff */
1470 avahi_entry_free(s, s->entries);
1472 avahi_interface_monitor_free(s->monitor);
1475 avahi_entry_group_free(s, s->groups);
1479 avahi_hashmap_free(s->entries_by_key);
1480 avahi_record_list_free(s->record_list);
1481 avahi_hashmap_free(s->record_browser_hashmap);
1483 if (s->wide_area_lookup_engine)
1484 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1485 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1487 avahi_time_event_queue_free(s->time_event_queue);
1492 s->poll_api->watch_free(s->watch_ipv4);
1494 s->poll_api->watch_free(s->watch_ipv6);
1496 if (s->watch_legacy_unicast_ipv4)
1497 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1498 if (s->watch_legacy_unicast_ipv6)
1499 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1503 if (s->fd_ipv4 >= 0)
1505 if (s->fd_ipv6 >= 0)
1508 if (s->fd_legacy_unicast_ipv4 >= 0)
1509 close(s->fd_legacy_unicast_ipv4);
1510 if (s->fd_legacy_unicast_ipv6 >= 0)
1511 close(s->fd_legacy_unicast_ipv6);
1513 /* Free other stuff */
1515 avahi_free(s->host_name);
1516 avahi_free(s->domain_name);
1517 avahi_free(s->host_name_fqdn);
1519 avahi_server_config_free(&s->config);
1524 const char* avahi_server_get_domain_name(AvahiServer *s) {
1527 return s->domain_name;
1530 const char* avahi_server_get_host_name(AvahiServer *s) {
1533 return s->host_name;
1536 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1539 return s->host_name_fqdn;
1542 void* avahi_server_get_data(AvahiServer *s) {
1548 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1551 s->userdata = userdata;
1554 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1560 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1563 memset(c, 0, sizeof(AvahiServerConfig));
1566 c->allow_interfaces = NULL;
1567 c->deny_interfaces = NULL;
1568 c->host_name = NULL;
1569 c->domain_name = NULL;
1570 c->check_response_ttl = 0;
1571 c->publish_hinfo = 1;
1572 c->publish_addresses = 1;
1573 c->publish_workstation = 1;
1574 c->publish_domain = 1;
1575 c->use_iff_running = 0;
1576 c->enable_reflector = 0;
1578 c->add_service_cookie = 0;
1579 c->enable_wide_area = 0;
1580 c->n_wide_area_servers = 0;
1581 c->disallow_other_stacks = 0;
1582 c->browse_domains = NULL;
1583 c->disable_publishing = 0;
1584 c->allow_point_to_point = 0;
1585 c->publish_aaaa_on_ipv4 = 1;
1586 c->publish_a_on_ipv6 = 0;
1591 void avahi_server_config_free(AvahiServerConfig *c) {
1594 avahi_free(c->host_name);
1595 avahi_free(c->domain_name);
1596 avahi_string_list_free(c->browse_domains);
1597 avahi_string_list_free(c->allow_interfaces);
1598 avahi_string_list_free(c->deny_interfaces);
1601 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1602 char *d = NULL, *h = NULL;
1603 AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL;
1608 if (!(h = avahi_strdup(c->host_name)))
1612 if (!(d = avahi_strdup(c->domain_name))) {
1617 if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1623 if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) {
1624 avahi_string_list_free(browse);
1630 if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) {
1631 avahi_string_list_free(allow);
1632 avahi_string_list_free(browse);
1640 ret->domain_name = d;
1641 ret->browse_domains = browse;
1642 ret->allow_interfaces = allow;
1643 ret->deny_interfaces = deny;
1648 int avahi_server_errno(AvahiServer *s) {
1654 /* Just for internal use */
1655 int avahi_server_set_errno(AvahiServer *s, int error) {
1658 return s->error = error;
1661 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1664 return s->local_service_cookie;
1667 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1673 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1675 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1676 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1677 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1684 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) {
1685 AvahiKey *key = NULL;
1688 char n[AVAHI_DOMAIN_NAME_MAX];
1695 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1696 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1697 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1698 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1699 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1701 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1702 return avahi_server_set_errno(s, ret);
1704 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1705 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1707 e = find_entry(s, interface, protocol, key);
1708 avahi_key_unref(key);
1711 *ret_group = e->group;
1715 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1718 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1719 AvahiKey *key = NULL;
1725 if (!s->host_name_fqdn)
1728 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1731 e = find_entry(s, interface, protocol, key);
1732 avahi_key_unref(key);
1737 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1740 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1746 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1748 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1749 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1750 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1751 avahi_record_equal_no_ttl(record, e->record))
1757 /** Set the wide area DNS servers */
1758 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1761 if (!s->wide_area_lookup_engine)
1762 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1764 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1768 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1774 /** Set the browsing domains */
1775 int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1780 for (l = s->config.browse_domains; l; l = l->next)
1781 if (!avahi_is_valid_domain_name((char*) l->text))
1782 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1784 avahi_string_list_free(s->config.browse_domains);
1785 s->config.browse_domains = avahi_string_list_copy(domains);