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 avahi_cache_start_poof(i->cache, key, a);
591 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
592 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
593 /* Allow our own queries to be suppressed by incoming
594 * queries only when they do not include known answers */
595 avahi_query_scheduler_incoming(i->query_scheduler, key);
597 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
598 avahi_key_unref(key);
601 if (!legacy_unicast) {
603 /* Known Answer Suppression */
604 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
608 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
609 avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe a UTF-8 problem?)");
613 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
614 avahi_record_list_drop(s->record_list, record);
615 avahi_cache_stop_poof(i->cache, record, a);
617 avahi_record_unref(record);
621 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
625 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
626 avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe a UTF-8 problem?)");
630 if (!avahi_key_is_pattern(record->key)) {
631 if (!from_local_iface)
632 reflect_probe(s, i, record);
633 incoming_probe(s, record, i);
636 avahi_record_unref(record);
640 if (!avahi_record_list_is_empty(s->record_list))
641 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
646 avahi_record_list_flush(s->record_list);
649 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
657 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
658 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
663 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
664 avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe a UTF-8 problem?)");
668 if (!avahi_key_is_pattern(record->key)) {
670 if (handle_conflict(s, i, record, cache_flush)) {
671 if (!from_local_iface)
672 reflect_response(s, i, record, cache_flush);
673 avahi_cache_update(i->cache, record, cache_flush, a);
674 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
678 avahi_record_unref(record);
681 /* If the incoming response contained a conflicting record, some
682 records have been scheduling for sending. We need to flush them
684 if (!avahi_record_list_is_empty(s->record_list))
685 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
688 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
689 unsigned n, idx = (unsigned) -1;
690 AvahiLegacyUnicastReflectSlot *slot;
694 if (!s->legacy_unicast_reflect_slots)
695 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
697 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
698 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
700 if (!s->legacy_unicast_reflect_slots[idx])
704 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
707 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
708 return NULL; /* OOM */
710 s->legacy_unicast_reflect_slots[idx] = slot;
711 slot->id = s->legacy_unicast_reflect_id++;
717 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
723 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
725 assert(s->legacy_unicast_reflect_slots[idx] == slot);
727 avahi_time_event_free(slot->time_event);
730 s->legacy_unicast_reflect_slots[idx] = NULL;
733 static void free_slots(AvahiServer *s) {
737 if (!s->legacy_unicast_reflect_slots)
740 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
741 if (s->legacy_unicast_reflect_slots[idx])
742 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
744 avahi_free(s->legacy_unicast_reflect_slots);
745 s->legacy_unicast_reflect_slots = NULL;
748 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
753 if (!s->legacy_unicast_reflect_slots)
756 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
758 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
761 return s->legacy_unicast_reflect_slots[idx];
764 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
765 AvahiLegacyUnicastReflectSlot *slot = userdata;
769 assert(slot->time_event == e);
771 deallocate_slot(slot->server, slot);
774 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
775 AvahiLegacyUnicastReflectSlot *slot;
783 assert(i->protocol == a->proto);
785 if (!s->config.enable_reflector)
788 /* Reflecting legacy unicast queries is a little more complicated
789 than reflecting normal queries, since we must route the
790 responses back to the right client. Therefore we must store
791 some information for finding the right client contact data for
792 response packets. In contrast to normal queries legacy
793 unicast query and response packets are reflected untouched and
794 are not reassembled into larger packets */
796 if (!(slot = allocate_slot(s))) {
797 /* No slot available, we drop this legacy unicast query */
798 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
802 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
805 slot->interface = i->hardware->index;
807 avahi_elapse_time(&slot->elapse_time, 2000, 0);
808 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
810 /* Patch the packet with our new locally generatet id */
811 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
813 for (j = s->monitor->interfaces; j; j = j->interface_next)
816 (s->config.reflect_ipv || j->protocol == i->protocol)) {
818 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
819 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
820 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
821 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
825 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
828 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
833 if (!s->config.enable_reflector)
836 if (!avahi_address_is_local(s->monitor, address))
839 if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
840 struct sockaddr_in lsa;
841 socklen_t l = sizeof(lsa);
843 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
844 avahi_log_warn("getsockname(): %s", strerror(errno));
846 return lsa.sin_port == port;
850 if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
851 struct sockaddr_in6 lsa;
852 socklen_t l = sizeof(lsa);
854 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
855 avahi_log_warn("getsockname(): %s", strerror(errno));
857 return lsa.sin6_port == port;
863 static int is_mdns_mcast_address(const AvahiAddress *a) {
867 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
868 return avahi_address_cmp(a, &b) == 0;
871 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
873 assert(iface != AVAHI_IF_UNSPEC);
876 /* If it isn't the MDNS port it can't be generated by us */
877 if (port != AVAHI_MDNS_PORT)
880 return avahi_interface_has_address(s->monitor, iface, a);
883 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
885 int from_local_iface = 0;
892 assert(src_address->proto == dst_address->proto);
894 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
896 avahi_log_warn("Received packet from invalid interface.");
900 if (avahi_address_is_ipv4_in_ipv6(src_address))
901 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
904 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
905 /* This originates from our local reflector, so let's ignore it */
908 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
909 if (s->config.enable_reflector)
910 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
912 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
913 avahi_log_warn("Received invalid packet.");
917 if (avahi_dns_packet_is_query(p)) {
918 int legacy_unicast = 0;
920 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
921 avahi_log_warn("Invalid query packet.");
925 if (port != AVAHI_MDNS_PORT) {
928 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
929 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
930 avahi_log_warn("Invalid legacy unicast query packet.");
938 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
940 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
943 if (port != AVAHI_MDNS_PORT) {
944 avahi_log_warn("Received response with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
948 if (ttl != 255 && s->config.check_response_ttl) {
949 avahi_log_warn("Received response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
953 if (!is_mdns_mcast_address(dst_address) &&
954 !avahi_interface_address_on_link(i, src_address)) {
955 avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
959 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
960 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
961 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
962 avahi_log_warn("Invalid response packet.");
966 handle_response_packet(s, p, i, src_address, from_local_iface);
970 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
972 AvahiLegacyUnicastReflectSlot *slot;
977 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
978 avahi_log_warn("Received invalid packet.");
982 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
983 avahi_log_warn("Received legacy unicast response with unknown id");
987 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
991 /* Patch the original ID into this response */
992 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
994 /* Forward the response to the correct client */
995 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
997 /* Undo changes to packet */
998 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1001 static void cleanup_dead(AvahiServer *s) {
1004 avahi_cleanup_dead_entries(s);
1005 avahi_browser_cleanup(s);
1008 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1009 AvahiServer *s = userdata;
1010 AvahiAddress dest, src;
1011 AvahiDnsPacket *p = NULL;
1018 assert(events & AVAHI_WATCH_IN);
1020 if (fd == s->fd_ipv4) {
1021 dest.proto = src.proto = AVAHI_PROTO_INET;
1022 p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1024 assert(fd == s->fd_ipv6);
1025 dest.proto = src.proto = AVAHI_PROTO_INET6;
1026 p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1030 if (iface == AVAHI_IF_UNSPEC)
1031 iface = avahi_find_interface_for_address(s->monitor, &dest);
1033 if (iface != AVAHI_IF_UNSPEC)
1034 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1036 avahi_log_error("Incoming packet recieved on address that isn't local.");
1038 avahi_dns_packet_free(p);
1044 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1045 AvahiServer *s = userdata;
1046 AvahiDnsPacket *p = NULL;
1050 assert(events & AVAHI_WATCH_IN);
1052 if (fd == s->fd_legacy_unicast_ipv4)
1053 p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1055 assert(fd == s->fd_legacy_unicast_ipv6);
1056 p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1060 dispatch_legacy_unicast_packet(s, p);
1061 avahi_dns_packet_free(p);
1067 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1070 if (s->state == state)
1075 avahi_interface_monitor_update_rrs(s->monitor, 0);
1078 s->callback(s, state, s->userdata);
1081 static void withdraw_host_rrs(AvahiServer *s) {
1084 if (s->hinfo_entry_group)
1085 avahi_s_entry_group_reset(s->hinfo_entry_group);
1087 if (s->browse_domain_entry_group)
1088 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1090 avahi_interface_monitor_update_rrs(s->monitor, 1);
1091 s->n_host_rr_pending = 0;
1094 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1097 assert(s->n_host_rr_pending > 0);
1099 if (--s->n_host_rr_pending == 0)
1100 server_set_state(s, AVAHI_SERVER_RUNNING);
1103 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1107 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1108 s->state == AVAHI_SERVER_REGISTERING)
1109 s->n_host_rr_pending ++;
1111 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1112 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1113 withdraw_host_rrs(s);
1114 server_set_state(s, AVAHI_SERVER_COLLISION);
1116 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1117 s->state == AVAHI_SERVER_REGISTERING)
1118 avahi_server_decrease_host_rr_pending(s);
1121 static void register_hinfo(AvahiServer *s) {
1122 struct utsname utsname;
1127 if (!s->config.publish_hinfo)
1130 if (s->hinfo_entry_group)
1131 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1133 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1135 if (!s->hinfo_entry_group) {
1136 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1140 /* Fill in HINFO rr */
1141 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1143 if (uname(&utsname) < 0)
1144 avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1147 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1148 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1150 avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1152 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1153 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1158 avahi_record_unref(r);
1161 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1162 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1166 static void register_localhost(AvahiServer *s) {
1170 /* Add localhost entries */
1171 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1172 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1174 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1175 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1178 static void register_browse_domain(AvahiServer *s) {
1181 if (!s->config.publish_domain)
1184 if (avahi_domain_equal(s->domain_name, "local"))
1187 if (s->browse_domain_entry_group)
1188 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1190 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1192 if (!s->browse_domain_entry_group) {
1193 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1197 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) {
1198 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1202 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1203 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1206 static void register_stuff(AvahiServer *s) {
1209 server_set_state(s, AVAHI_SERVER_REGISTERING);
1210 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1213 register_browse_domain(s);
1214 avahi_interface_monitor_update_rrs(s->monitor, 0);
1216 s->n_host_rr_pending --;
1218 if (s->n_host_rr_pending == 0)
1219 server_set_state(s, AVAHI_SERVER_RUNNING);
1222 static void update_fqdn(AvahiServer *s) {
1226 assert(s->host_name);
1227 assert(s->domain_name);
1229 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1232 avahi_free(s->host_name_fqdn);
1233 s->host_name_fqdn = n;
1236 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1240 AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
1243 hn = avahi_get_host_name_strdup();
1244 hn[strcspn(hn, ".")] = 0;
1248 if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
1250 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1253 withdraw_host_rrs(s);
1255 avahi_free(s->host_name);
1256 s->host_name = hn ? hn : avahi_strdup(host_name);
1264 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1268 AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
1271 dn = avahi_strdup("local");
1275 if (avahi_domain_equal(s->domain_name, domain_name)) {
1277 return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
1280 withdraw_host_rrs(s);
1282 avahi_free(s->domain_name);
1283 s->domain_name = avahi_normalize_name_strdup(domain_name);
1292 static int valid_server_config(const AvahiServerConfig *sc) {
1297 if (sc->n_wide_area_servers > AVAHI_WIDE_AREA_SERVERS_MAX)
1298 return AVAHI_ERR_INVALID_CONFIG;
1300 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1301 return AVAHI_ERR_INVALID_HOST_NAME;
1303 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1304 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1306 for (l = sc->browse_domains; l; l = l->next)
1307 if (!avahi_is_valid_domain_name((char*) l->text))
1308 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1313 static int setup_sockets(AvahiServer *s) {
1316 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1317 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1319 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1320 return AVAHI_ERR_NO_NETWORK;
1322 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1323 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1324 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1325 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1327 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1328 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1332 s->watch_legacy_unicast_ipv4 =
1333 s->watch_legacy_unicast_ipv6 = NULL;
1335 if (s->fd_ipv4 >= 0)
1336 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1337 if (s->fd_ipv6 >= 0)
1338 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1340 if (s->fd_legacy_unicast_ipv4 >= 0)
1341 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);
1342 if (s->fd_legacy_unicast_ipv6 >= 0)
1343 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);
1348 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1352 if (sc && (e = valid_server_config(sc)) < 0) {
1358 if (!(s = avahi_new(AvahiServer, 1))) {
1360 *error = AVAHI_ERR_NO_MEMORY;
1365 s->poll_api = poll_api;
1368 avahi_server_config_copy(&s->config, sc);
1370 avahi_server_config_init(&s->config);
1372 if ((e = setup_sockets(s)) < 0) {
1376 avahi_server_config_free(&s->config);
1382 s->n_host_rr_pending = 0;
1383 s->need_entry_cleanup = 0;
1384 s->need_group_cleanup = 0;
1385 s->need_browser_cleanup = 0;
1386 s->hinfo_entry_group = NULL;
1387 s->browse_domain_entry_group = NULL;
1388 s->error = AVAHI_OK;
1389 s->state = AVAHI_SERVER_INVALID;
1391 s->callback = callback;
1392 s->userdata = userdata;
1394 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1396 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1397 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1398 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1400 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1401 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1402 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1403 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1404 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1405 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1406 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1407 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1408 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1410 s->legacy_unicast_reflect_slots = NULL;
1411 s->legacy_unicast_reflect_id = 0;
1413 s->record_list = avahi_record_list_new();
1416 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1417 s->host_name[strcspn(s->host_name, ".")] = 0;
1418 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1419 s->host_name_fqdn = NULL;
1423 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1424 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1426 if (s->config.enable_wide_area) {
1427 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1428 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1430 s->wide_area_lookup_engine = NULL;
1432 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1434 s->monitor = avahi_interface_monitor_new(s);
1435 avahi_interface_monitor_sync(s->monitor);
1437 register_localhost(s);
1443 void avahi_server_free(AvahiServer* s) {
1446 /* Remove all browsers */
1448 while (s->dns_server_browsers)
1449 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1450 while (s->host_name_resolvers)
1451 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1452 while (s->address_resolvers)
1453 avahi_s_address_resolver_free(s->address_resolvers);
1454 while (s->domain_browsers)
1455 avahi_s_domain_browser_free(s->domain_browsers);
1456 while (s->service_type_browsers)
1457 avahi_s_service_type_browser_free(s->service_type_browsers);
1458 while (s->service_browsers)
1459 avahi_s_service_browser_free(s->service_browsers);
1460 while (s->service_resolvers)
1461 avahi_s_service_resolver_free(s->service_resolvers);
1462 while (s->record_browsers)
1463 avahi_s_record_browser_destroy(s->record_browsers);
1465 /* Remove all locally rgeistered stuff */
1468 avahi_entry_free(s, s->entries);
1470 avahi_interface_monitor_free(s->monitor);
1473 avahi_entry_group_free(s, s->groups);
1477 avahi_hashmap_free(s->entries_by_key);
1478 avahi_record_list_free(s->record_list);
1479 avahi_hashmap_free(s->record_browser_hashmap);
1481 if (s->wide_area_lookup_engine)
1482 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1483 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1485 avahi_time_event_queue_free(s->time_event_queue);
1490 s->poll_api->watch_free(s->watch_ipv4);
1492 s->poll_api->watch_free(s->watch_ipv6);
1494 if (s->watch_legacy_unicast_ipv4)
1495 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1496 if (s->watch_legacy_unicast_ipv6)
1497 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1501 if (s->fd_ipv4 >= 0)
1503 if (s->fd_ipv6 >= 0)
1506 if (s->fd_legacy_unicast_ipv4 >= 0)
1507 close(s->fd_legacy_unicast_ipv4);
1508 if (s->fd_legacy_unicast_ipv6 >= 0)
1509 close(s->fd_legacy_unicast_ipv6);
1511 /* Free other stuff */
1513 avahi_free(s->host_name);
1514 avahi_free(s->domain_name);
1515 avahi_free(s->host_name_fqdn);
1517 avahi_server_config_free(&s->config);
1522 const char* avahi_server_get_domain_name(AvahiServer *s) {
1525 return s->domain_name;
1528 const char* avahi_server_get_host_name(AvahiServer *s) {
1531 return s->host_name;
1534 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1537 return s->host_name_fqdn;
1540 void* avahi_server_get_data(AvahiServer *s) {
1546 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1549 s->userdata = userdata;
1552 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1558 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1561 memset(c, 0, sizeof(AvahiServerConfig));
1564 c->host_name = NULL;
1565 c->domain_name = NULL;
1566 c->check_response_ttl = 0;
1567 c->publish_hinfo = 1;
1568 c->publish_addresses = 1;
1569 c->publish_workstation = 1;
1570 c->publish_domain = 1;
1571 c->use_iff_running = 0;
1572 c->enable_reflector = 0;
1574 c->add_service_cookie = 0;
1575 c->enable_wide_area = 0;
1576 c->n_wide_area_servers = 0;
1577 c->disallow_other_stacks = 0;
1578 c->browse_domains = NULL;
1579 c->disable_publishing = 0;
1580 c->allow_point_to_point = 0;
1581 c->publish_aaaa_on_ipv4 = 1;
1582 c->publish_a_on_ipv6 = 0;
1587 void avahi_server_config_free(AvahiServerConfig *c) {
1590 avahi_free(c->host_name);
1591 avahi_free(c->domain_name);
1592 avahi_string_list_free(c->browse_domains);
1595 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1596 char *d = NULL, *h = NULL;
1597 AvahiStringList *l = NULL;
1602 if (!(h = avahi_strdup(c->host_name)))
1606 if (!(d = avahi_strdup(c->domain_name))) {
1611 if (!(l = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1619 ret->domain_name = d;
1620 ret->browse_domains = l;
1625 int avahi_server_errno(AvahiServer *s) {
1631 /* Just for internal use */
1632 int avahi_server_set_errno(AvahiServer *s, int error) {
1635 return s->error = error;
1638 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1641 return s->local_service_cookie;
1644 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1650 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1652 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1653 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1654 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1661 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) {
1662 AvahiKey *key = NULL;
1665 char n[AVAHI_DOMAIN_NAME_MAX];
1672 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1673 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1674 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1675 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1676 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1678 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1679 return avahi_server_set_errno(s, ret);
1681 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1682 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1684 e = find_entry(s, interface, protocol, key);
1685 avahi_key_unref(key);
1688 *ret_group = e->group;
1692 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1695 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1696 AvahiKey *key = NULL;
1702 if (!s->host_name_fqdn)
1705 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1708 e = find_entry(s, interface, protocol, key);
1709 avahi_key_unref(key);
1714 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1717 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1723 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1725 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1726 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1727 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1728 avahi_record_equal_no_ttl(record, e->record))
1734 /** Set the wide area DNS servers */
1735 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1738 if (!s->wide_area_lookup_engine)
1739 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1741 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
1745 const AvahiServerConfig* avahi_server_get_config(AvahiServer *s) {
1751 /** Set the browsing domains */
1752 int avahi_server_set_browse_domains(AvahiServer *s, AvahiStringList *domains) {
1757 for (l = s->config.browse_domains; l; l = l->next)
1758 if (!avahi_is_valid_domain_name((char*) l->text))
1759 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1761 avahi_string_list_free(s->config.browse_domains);
1762 s->config.browse_domains = avahi_string_list_copy(domains);