4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
31 #include <sys/utsname.h>
38 #include <avahi-common/domain.h>
39 #include <avahi-common/timeval.h>
40 #include <avahi-common/malloc.h>
41 #include <avahi-common/error.h>
49 #include "dns-srv-rr.h"
50 #include "addr-util.h"
51 #include "domain-util.h"
54 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
60 if (type == AVAHI_DNS_TYPE_ANY) {
63 for (e = s->entries; e; e = e->entries_next)
65 avahi_entry_is_registered(s, e, i) &&
66 e->record->key->clazz == AVAHI_DNS_CLASS_IN &&
67 avahi_domain_equal(name, e->record->key->name))
68 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
74 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
77 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
78 if (!e->dead && avahi_entry_is_registered(s, e, i))
79 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
85 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
91 /* Call the specified callback far all records referenced by the one specified in *r */
93 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
94 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
95 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
96 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
97 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
98 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
99 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
100 } else if (r->key->type == AVAHI_DNS_TYPE_CNAME)
101 enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata);
105 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
110 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
113 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
118 /* Push all records that match the specified key to the record list */
120 if (avahi_key_is_pattern(k)) {
123 /* Handle ANY query */
125 for (e = s->entries; e; e = e->entries_next)
126 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
127 avahi_server_prepare_response(s, i, e, unicast_response, 0);
132 /* Handle all other queries */
134 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
135 if (!e->dead && avahi_entry_is_registered(s, e, i))
136 avahi_server_prepare_response(s, i, e, unicast_response, 0);
139 /* Look for CNAME records */
141 if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY)
142 && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) {
146 if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME)))
149 avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response);
150 avahi_key_unref(cname_key);
154 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
158 /* Withdraw the specified entry, and if is part of an entry group,
159 * put that into COLLISION state */
167 for (k = e->group->entries; k; k = k->by_group_next)
169 avahi_goodbye_entry(s, k, 0, 1);
173 e->group->n_probing = 0;
175 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
177 avahi_goodbye_entry(s, e, 0, 1);
181 s->need_entry_cleanup = 1;
184 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
190 /* Withdraw an entry RRSset */
192 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
193 withdraw_entry(s, e);
196 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
198 int ours = 0, won = 0, lost = 0;
204 /* Handle incoming probes and check if they conflict our own probes */
206 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
213 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
218 if (avahi_entry_is_probing(s, e, i)) {
228 char *t = avahi_record_to_string(record);
231 avahi_log_debug("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;
522 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
526 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
533 if (!s->config.enable_reflector)
536 for (j = s->monitor->interfaces; j; j = j->interface_next)
537 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
538 /* Post the query to other networks */
539 avahi_interface_post_query(j, k, 1, NULL);
541 /* Reply from caches of other network. This is needed to
542 * "work around" known answer suppression. */
544 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
548 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
555 if (!s->config.enable_reflector)
558 for (j = s->monitor->interfaces; j; j = j->interface_next)
559 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
560 avahi_interface_post_probe(j, r, 1);
563 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
572 assert(avahi_record_list_is_empty(s->record_list));
574 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
576 /* Handle the questions */
577 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
579 int unicast_response = 0;
581 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
582 avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe a UTF-8 problem?)");
586 if (!legacy_unicast && !from_local_iface) {
587 reflect_query(s, i, key);
588 if (!unicast_response)
589 avahi_cache_start_poof(i->cache, key, a);
592 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
593 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
594 /* Allow our own queries to be suppressed by incoming
595 * queries only when they do not include known answers */
596 avahi_query_scheduler_incoming(i->query_scheduler, key);
598 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
599 avahi_key_unref(key);
602 if (!legacy_unicast) {
604 /* Known Answer Suppression */
605 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
609 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
610 avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)");
614 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
615 avahi_record_list_drop(s->record_list, record);
616 avahi_cache_stop_poof(i->cache, record, a);
618 avahi_record_unref(record);
622 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
626 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
627 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)");
631 if (!avahi_key_is_pattern(record->key)) {
632 if (!from_local_iface)
633 reflect_probe(s, i, record);
634 incoming_probe(s, record, i);
637 avahi_record_unref(record);
641 if (!avahi_record_list_is_empty(s->record_list))
642 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
647 avahi_record_list_flush(s->record_list);
650 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
658 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
659 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
664 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
665 avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
669 if (!avahi_key_is_pattern(record->key)) {
671 if (handle_conflict(s, i, record, cache_flush)) {
672 if (!from_local_iface)
673 reflect_response(s, i, record, cache_flush);
674 avahi_cache_update(i->cache, record, cache_flush, a);
675 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
679 avahi_record_unref(record);
682 /* If the incoming response contained a conflicting record, some
683 records have been scheduling for sending. We need to flush them
685 if (!avahi_record_list_is_empty(s->record_list))
686 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
689 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
690 unsigned n, idx = (unsigned) -1;
691 AvahiLegacyUnicastReflectSlot *slot;
695 if (!s->legacy_unicast_reflect_slots)
696 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
698 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
699 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
701 if (!s->legacy_unicast_reflect_slots[idx])
705 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
708 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
709 return NULL; /* OOM */
711 s->legacy_unicast_reflect_slots[idx] = slot;
712 slot->id = s->legacy_unicast_reflect_id++;
718 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
724 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
726 assert(s->legacy_unicast_reflect_slots[idx] == slot);
728 avahi_time_event_free(slot->time_event);
731 s->legacy_unicast_reflect_slots[idx] = NULL;
734 static void free_slots(AvahiServer *s) {
738 if (!s->legacy_unicast_reflect_slots)
741 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
742 if (s->legacy_unicast_reflect_slots[idx])
743 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
745 avahi_free(s->legacy_unicast_reflect_slots);
746 s->legacy_unicast_reflect_slots = NULL;
749 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
754 if (!s->legacy_unicast_reflect_slots)
757 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
759 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
762 return s->legacy_unicast_reflect_slots[idx];
765 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
766 AvahiLegacyUnicastReflectSlot *slot = userdata;
770 assert(slot->time_event == e);
772 deallocate_slot(slot->server, slot);
775 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
776 AvahiLegacyUnicastReflectSlot *slot;
784 assert(i->protocol == a->proto);
786 if (!s->config.enable_reflector)
789 /* Reflecting legacy unicast queries is a little more complicated
790 than reflecting normal queries, since we must route the
791 responses back to the right client. Therefore we must store
792 some information for finding the right client contact data for
793 response packets. In contrast to normal queries legacy
794 unicast query and response packets are reflected untouched and
795 are not reassembled into larger packets */
797 if (!(slot = allocate_slot(s))) {
798 /* No slot available, we drop this legacy unicast query */
799 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
803 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
806 slot->interface = i->hardware->index;
808 avahi_elapse_time(&slot->elapse_time, 2000, 0);
809 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
811 /* Patch the packet with our new locally generatet id */
812 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
814 for (j = s->monitor->interfaces; j; j = j->interface_next)
817 (s->config.reflect_ipv || j->protocol == i->protocol)) {
819 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
820 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
821 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
822 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
826 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
829 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
834 if (!s->config.enable_reflector)
837 if (!avahi_address_is_local(s->monitor, address))
840 if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
841 struct sockaddr_in lsa;
842 socklen_t l = sizeof(lsa);
844 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
845 avahi_log_warn("getsockname(): %s", strerror(errno));
847 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
851 if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
852 struct sockaddr_in6 lsa;
853 socklen_t l = sizeof(lsa);
855 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
856 avahi_log_warn("getsockname(): %s", strerror(errno));
858 return avahi_port_from_sockaddr((struct sockaddr*) &lsa) == port;
864 static int is_mdns_mcast_address(const AvahiAddress *a) {
868 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
869 return avahi_address_cmp(a, &b) == 0;
872 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
874 assert(iface != AVAHI_IF_UNSPEC);
877 /* If it isn't the MDNS port it can't be generated by us */
878 if (port != AVAHI_MDNS_PORT)
881 return avahi_interface_has_address(s->monitor, iface, a);
884 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
886 int from_local_iface = 0;
893 assert(src_address->proto == dst_address->proto);
895 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
897 avahi_log_warn("Received packet from invalid interface.");
902 /* This fixes RHBZ #475394 */
903 avahi_log_warn("Received packet from invalid source port %u.", (unsigned) port);
907 if (avahi_address_is_ipv4_in_ipv6(src_address))
908 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
911 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
912 /* This originates from our local reflector, so let's ignore it */
915 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
916 if (s->config.enable_reflector)
917 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
919 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
920 avahi_log_warn("Received invalid packet.");
924 if (avahi_dns_packet_is_query(p)) {
925 int legacy_unicast = 0;
927 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
928 avahi_log_warn("Invalid query packet.");
932 if (port != AVAHI_MDNS_PORT) {
935 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
936 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
937 avahi_log_warn("Invalid legacy unicast query packet.");
945 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
947 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
950 char t[AVAHI_ADDRESS_STR_MAX];
952 if (port != AVAHI_MDNS_PORT) {
953 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);
957 if (ttl != 255 && s->config.check_response_ttl) {
958 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);
962 if (!is_mdns_mcast_address(dst_address) &&
963 !avahi_interface_address_on_link(i, src_address)) {
965 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);
969 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
970 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
971 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
973 avahi_log_warn("Invalid response packet from host %s.", avahi_address_snprint(t, sizeof(t), src_address));
977 handle_response_packet(s, p, i, src_address, from_local_iface);
981 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
983 AvahiLegacyUnicastReflectSlot *slot;
988 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
989 avahi_log_warn("Received invalid packet.");
993 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
994 avahi_log_warn("Received legacy unicast response with unknown id");
998 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1002 /* Patch the original ID into this response */
1003 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1005 /* Forward the response to the correct client */
1006 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1008 /* Undo changes to packet */
1009 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1012 static void cleanup_dead(AvahiServer *s) {
1015 avahi_cleanup_dead_entries(s);
1016 avahi_browser_cleanup(s);
1019 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1020 AvahiServer *s = userdata;
1021 AvahiAddress dest, src;
1022 AvahiDnsPacket *p = NULL;
1029 assert(events & AVAHI_WATCH_IN);
1031 if (fd == s->fd_ipv4) {
1032 dest.proto = src.proto = AVAHI_PROTO_INET;
1033 p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1035 assert(fd == s->fd_ipv6);
1036 dest.proto = src.proto = AVAHI_PROTO_INET6;
1037 p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1041 if (iface == AVAHI_IF_UNSPEC)
1042 iface = avahi_find_interface_for_address(s->monitor, &dest);
1044 if (iface != AVAHI_IF_UNSPEC)
1045 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1047 avahi_log_error("Incoming packet recieved on address that isn't local.");
1049 avahi_dns_packet_free(p);
1055 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1056 AvahiServer *s = userdata;
1057 AvahiDnsPacket *p = NULL;
1061 assert(events & AVAHI_WATCH_IN);
1063 if (fd == s->fd_legacy_unicast_ipv4)
1064 p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1066 assert(fd == s->fd_legacy_unicast_ipv6);
1067 p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1071 dispatch_legacy_unicast_packet(s, p);
1072 avahi_dns_packet_free(p);
1078 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1081 if (s->state == state)
1086 avahi_interface_monitor_update_rrs(s->monitor, 0);
1089 s->callback(s, state, s->userdata);
1092 static void withdraw_host_rrs(AvahiServer *s) {
1095 if (s->hinfo_entry_group)
1096 avahi_s_entry_group_reset(s->hinfo_entry_group);
1098 if (s->browse_domain_entry_group)
1099 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1101 avahi_interface_monitor_update_rrs(s->monitor, 1);
1102 s->n_host_rr_pending = 0;
1105 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1108 assert(s->n_host_rr_pending > 0);
1110 if (--s->n_host_rr_pending == 0)
1111 server_set_state(s, AVAHI_SERVER_RUNNING);
1114 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1118 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1119 s->state == AVAHI_SERVER_REGISTERING)
1120 s->n_host_rr_pending ++;
1122 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1123 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1124 withdraw_host_rrs(s);
1125 server_set_state(s, AVAHI_SERVER_COLLISION);
1127 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1128 s->state == AVAHI_SERVER_REGISTERING)
1129 avahi_server_decrease_host_rr_pending(s);
1132 static void register_hinfo(AvahiServer *s) {
1133 struct utsname utsname;
1138 if (!s->config.publish_hinfo)
1141 if (s->hinfo_entry_group)
1142 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1144 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1146 if (!s->hinfo_entry_group) {
1147 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1151 /* Fill in HINFO rr */
1152 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1154 if (uname(&utsname) < 0)
1155 avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1158 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1159 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1161 avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1163 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1164 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1169 avahi_record_unref(r);
1172 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1173 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1177 static void register_localhost(AvahiServer *s) {
1181 /* Add localhost entries */
1182 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1183 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1185 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1186 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1189 static void register_browse_domain(AvahiServer *s) {
1192 if (!s->config.publish_domain)
1195 if (avahi_domain_equal(s->domain_name, "local"))
1198 if (s->browse_domain_entry_group)
1199 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1201 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1203 if (!s->browse_domain_entry_group) {
1204 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1208 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) {
1209 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1213 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1214 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1217 static void register_stuff(AvahiServer *s) {
1220 server_set_state(s, AVAHI_SERVER_REGISTERING);
1221 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1224 register_browse_domain(s);
1225 avahi_interface_monitor_update_rrs(s->monitor, 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();
1255 hn[strcspn(hn, ".")] = 0;
1259 if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
1261 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1264 withdraw_host_rrs(s);
1266 avahi_free(s->host_name);
1267 s->host_name = hn ? hn : avahi_strdup(host_name);
1275 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1279 AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1282 dn = avahi_strdup("local");
1286 if (avahi_domain_equal(s->domain_name, domain_name)) {
1288 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1291 withdraw_host_rrs(s);
1293 avahi_free(s->domain_name);
1294 s->domain_name = avahi_normalize_name_strdup(domain_name);
1303 static int valid_server_config(const AvahiServerConfig *sc) {
1308 if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1309 return AVAHI_ERR_INVALID_CONFIG;
1311 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1312 return AVAHI_ERR_INVALID_HOST_NAME;
1314 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1315 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1317 for (l = sc->browse_domains; l; l = l->next)
1318 if (!avahi_is_valid_domain_name((char*) l->text))
1319 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1324 static int setup_sockets(AvahiServer *s) {
1327 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1328 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1330 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1331 return AVAHI_ERR_NO_NETWORK;
1333 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1334 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1335 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1336 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1338 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1339 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1343 s->watch_legacy_unicast_ipv4 =
1344 s->watch_legacy_unicast_ipv6 = NULL;
1346 if (s->fd_ipv4 >= 0)
1347 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1348 if (s->fd_ipv6 >= 0)
1349 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1351 if (s->fd_legacy_unicast_ipv4 >= 0)
1352 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);
1353 if (s->fd_legacy_unicast_ipv6 >= 0)
1354 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);
1359 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1363 if (sc && (e = valid_server_config(sc)) < 0) {
1369 if (!(s = avahi_new(AvahiServer, 1))) {
1371 *error = AVAHI_ERR_NO_MEMORY;
1376 s->poll_api = poll_api;
1379 avahi_server_config_copy(&s->config, sc);
1381 avahi_server_config_init(&s->config);
1383 if ((e = setup_sockets(s)) < 0) {
1387 avahi_server_config_free(&s->config);
1393 s->n_host_rr_pending = 0;
1394 s->need_entry_cleanup = 0;
1395 s->need_group_cleanup = 0;
1396 s->need_browser_cleanup = 0;
1397 s->hinfo_entry_group = NULL;
1398 s->browse_domain_entry_group = NULL;
1399 s->error = AVAHI_OK;
1400 s->state = AVAHI_SERVER_INVALID;
1402 s->callback = callback;
1403 s->userdata = userdata;
1405 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1407 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1408 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1409 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1411 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1412 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1413 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1414 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1415 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1416 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1417 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1418 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1419 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1421 s->legacy_unicast_reflect_slots = NULL;
1422 s->legacy_unicast_reflect_id = 0;
1424 s->record_list = avahi_record_list_new();
1427 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1428 s->host_name[strcspn(s->host_name, ".")] = 0;
1429 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1430 s->host_name_fqdn = NULL;
1434 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1435 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1437 if (s->config.enable_wide_area) {
1438 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1439 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1441 s->wide_area_lookup_engine = NULL;
1443 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1445 s->monitor = avahi_interface_monitor_new(s);
1446 avahi_interface_monitor_sync(s->monitor);
1448 register_localhost(s);
1454 void avahi_server_free(AvahiServer* s) {
1457 /* Remove all browsers */
1459 while (s->dns_server_browsers)
1460 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1461 while (s->host_name_resolvers)
1462 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1463 while (s->address_resolvers)
1464 avahi_s_address_resolver_free(s->address_resolvers);
1465 while (s->domain_browsers)
1466 avahi_s_domain_browser_free(s->domain_browsers);
1467 while (s->service_type_browsers)
1468 avahi_s_service_type_browser_free(s->service_type_browsers);
1469 while (s->service_browsers)
1470 avahi_s_service_browser_free(s->service_browsers);
1471 while (s->service_resolvers)
1472 avahi_s_service_resolver_free(s->service_resolvers);
1473 while (s->record_browsers)
1474 avahi_s_record_browser_destroy(s->record_browsers);
1476 /* Remove all locally rgeistered stuff */
1479 avahi_entry_free(s, s->entries);
1481 avahi_interface_monitor_free(s->monitor);
1484 avahi_entry_group_free(s, s->groups);
1488 avahi_hashmap_free(s->entries_by_key);
1489 avahi_record_list_free(s->record_list);
1490 avahi_hashmap_free(s->record_browser_hashmap);
1492 if (s->wide_area_lookup_engine)
1493 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1494 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1496 avahi_time_event_queue_free(s->time_event_queue);
1501 s->poll_api->watch_free(s->watch_ipv4);
1503 s->poll_api->watch_free(s->watch_ipv6);
1505 if (s->watch_legacy_unicast_ipv4)
1506 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1507 if (s->watch_legacy_unicast_ipv6)
1508 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1512 if (s->fd_ipv4 >= 0)
1514 if (s->fd_ipv6 >= 0)
1517 if (s->fd_legacy_unicast_ipv4 >= 0)
1518 close(s->fd_legacy_unicast_ipv4);
1519 if (s->fd_legacy_unicast_ipv6 >= 0)
1520 close(s->fd_legacy_unicast_ipv6);
1522 /* Free other stuff */
1524 avahi_free(s->host_name);
1525 avahi_free(s->domain_name);
1526 avahi_free(s->host_name_fqdn);
1528 avahi_server_config_free(&s->config);
1533 const char* avahi_server_get_domain_name(AvahiServer *s) {
1536 return s->domain_name;
1539 const char* avahi_server_get_host_name(AvahiServer *s) {
1542 return s->host_name;
1545 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1548 return s->host_name_fqdn;
1551 void* avahi_server_get_data(AvahiServer *s) {
1557 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1560 s->userdata = userdata;
1563 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1569 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1572 memset(c, 0, sizeof(AvahiServerConfig));
1575 c->allow_interfaces = NULL;
1576 c->deny_interfaces = NULL;
1577 c->host_name = NULL;
1578 c->domain_name = NULL;
1579 c->check_response_ttl = 0;
1580 c->publish_hinfo = 1;
1581 c->publish_addresses = 1;
1582 c->publish_workstation = 1;
1583 c->publish_domain = 1;
1584 c->use_iff_running = 0;
1585 c->enable_reflector = 0;
1587 c->add_service_cookie = 0;
1588 c->enable_wide_area = 0;
1589 c->n_wide_area_servers = 0;
1590 c->disallow_other_stacks = 0;
1591 c->browse_domains = NULL;
1592 c->disable_publishing = 0;
1593 c->allow_point_to_point = 0;
1594 c->publish_aaaa_on_ipv4 = 1;
1595 c->publish_a_on_ipv6 = 0;
1600 void avahi_server_config_free(AvahiServerConfig *c) {
1603 avahi_free(c->host_name);
1604 avahi_free(c->domain_name);
1605 avahi_string_list_free(c->browse_domains);
1606 avahi_string_list_free(c->allow_interfaces);
1607 avahi_string_list_free(c->deny_interfaces);
1610 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1611 char *d = NULL, *h = NULL;
1612 AvahiStringList *browse = NULL, *allow = NULL, *deny = NULL;
1617 if (!(h = avahi_strdup(c->host_name)))
1621 if (!(d = avahi_strdup(c->domain_name))) {
1626 if (!(browse = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1632 if (!(allow = avahi_string_list_copy(c->allow_interfaces)) && c->allow_interfaces) {
1633 avahi_string_list_free(browse);
1639 if (!(deny = avahi_string_list_copy(c->deny_interfaces)) && c->deny_interfaces) {
1640 avahi_string_list_free(allow);
1641 avahi_string_list_free(browse);
1649 ret->domain_name = d;
1650 ret->browse_domains = browse;
1651 ret->allow_interfaces = allow;
1652 ret->deny_interfaces = deny;
1657 int avahi_server_errno(AvahiServer *s) {
1663 /* Just for internal use */
1664 int avahi_server_set_errno(AvahiServer *s, int error) {
1667 return s->error = error;
1670 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1673 return s->local_service_cookie;
1676 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1682 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1684 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1685 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1686 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1693 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) {
1694 AvahiKey *key = NULL;
1697 char n[AVAHI_DOMAIN_NAME_MAX];
1704 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1705 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1706 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1707 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1708 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1710 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1711 return avahi_server_set_errno(s, ret);
1713 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1714 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1716 e = find_entry(s, interface, protocol, key);
1717 avahi_key_unref(key);
1720 *ret_group = e->group;
1724 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1727 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1728 AvahiKey *key = NULL;
1734 if (!s->host_name_fqdn)
1737 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1740 e = find_entry(s, interface, protocol, key);
1741 avahi_key_unref(key);
1746 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1749 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1755 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1757 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1758 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1759 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1760 avahi_record_equal_no_ttl(record, e->record))
1766 /** Set the wide area DNS servers */
1767 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1770 if (!s->wide_area_lookup_engine)
1771 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1773 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1777 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1783 /** Set the browsing domains */
1784 int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1789 for (l = s->config.browse_domains; l; l = l->next)
1790 if (!avahi_is_valid_domain_name((char*) l->text))
1791 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1793 avahi_string_list_free(s->config.browse_domains);
1794 s->config.browse_domains = avahi_string_list_copy(domains);