4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
31 #include <sys/utsname.h>
38 #include <avahi-common/domain.h>
39 #include <avahi-common/timeval.h>
40 #include <avahi-common/malloc.h>
41 #include <avahi-common/error.h>
49 #include "dns-srv-rr.h"
50 #include "addr-util.h"
51 #include "domain-util.h"
54 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
60 if (type == AVAHI_DNS_TYPE_ANY) {
63 for (e = s->entries; e; e = e->entries_next)
65 avahi_entry_is_registered(s, e, i) &&
66 e->record->key->clazz == AVAHI_DNS_CLASS_IN &&
67 avahi_domain_equal(name, e->record->key->name))
68 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
74 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
77 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
78 if (!e->dead && avahi_entry_is_registered(s, e, i))
79 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
85 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
91 /* Call the specified callback far all records referenced by the one specified in *r */
93 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
94 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
95 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
96 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
97 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
98 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
99 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
100 } else if (r->key->type == AVAHI_DNS_TYPE_CNAME)
101 enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata);
105 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
110 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
113 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
118 /* Push all records that match the specified key to the record list */
120 if (avahi_key_is_pattern(k)) {
123 /* Handle ANY query */
125 for (e = s->entries; e; e = e->entries_next)
126 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
127 avahi_server_prepare_response(s, i, e, unicast_response, 0);
132 /* Handle all other queries */
134 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
135 if (!e->dead && avahi_entry_is_registered(s, e, i))
136 avahi_server_prepare_response(s, i, e, unicast_response, 0);
139 /* Look for CNAME records */
141 if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY)
142 && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) {
146 if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME)))
149 avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response);
150 avahi_key_unref(cname_key);
154 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
158 /* Withdraw the specified entry, and if is part of an entry group,
159 * put that into COLLISION state */
167 for (k = e->group->entries; k; k = k->by_group_next)
169 avahi_goodbye_entry(s, k, 0, 1);
173 e->group->n_probing = 0;
175 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
177 avahi_goodbye_entry(s, e, 0, 1);
181 s->need_entry_cleanup = 1;
184 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
190 /* Withdraw an entry RRSset */
192 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
193 withdraw_entry(s, e);
196 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
198 int ours = 0, won = 0, lost = 0;
204 /* Handle incoming probes and check if they conflict our own probes */
206 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
213 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
218 if (avahi_entry_is_probing(s, e, i)) {
228 char *t = avahi_record_to_string(record);
231 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
233 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
234 withdraw_rrset(s, record->key);
241 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) {
242 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
243 AvahiEntry *e, *n, *conflicting_entry = NULL;
249 /* Check whether an incoming record conflicts with one of our own */
251 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
257 /* Check if the incoming is a goodbye record */
258 if (avahi_record_is_goodbye(record)) {
260 if (avahi_record_equal_no_ttl(e->record, record)) {
264 t = avahi_record_to_string(record);
265 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
266 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
273 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
277 if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
280 /* Either our entry or the other is intended to be unique, so let's check */
282 if (avahi_record_equal_no_ttl(e->record, record)) {
283 ours = 1; /* We have an identical record, so this is no conflict */
285 /* Check wheter there is a TTL conflict */
286 if (record->ttl <= e->record->ttl/2 &&
287 avahi_entry_is_registered(s, e, i)) {
290 t = avahi_record_to_string(record);
292 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
293 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
299 /* There's no need to check the other entries of this RRset */
304 if (avahi_entry_is_registered(s, e, i)) {
306 /* A conflict => we have to return to probe mode */
308 conflicting_entry = e;
310 } else if (avahi_entry_is_probing(s, e, i)) {
312 /* We are currently registering a matching record, but
313 * someone else already claimed it, so let's
316 withdraw_immediately = 1;
321 if (!ours && conflict) {
326 t = avahi_record_to_string(record);
328 if (withdraw_immediately) {
329 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
330 withdraw_rrset(s, record->key);
332 assert(conflicting_entry);
333 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
334 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
336 /* Local unique records are returned to probing
337 * state. Local shared records are reannounced. */
346 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
347 int *unicast_response = userdata;
351 assert(unicast_response);
353 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
356 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
360 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
363 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
367 assert(!legacy_unicast || (a && port > 0 && p));
369 if (legacy_unicast) {
370 AvahiDnsPacket *reply;
373 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
376 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
378 append_aux_records_to_list(s, i, r, 0);
380 if (avahi_dns_packet_append_record(reply, r, 0, 10))
381 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
383 char *t = avahi_record_to_string(r);
384 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
388 avahi_record_unref(r);
391 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
392 avahi_interface_send_packet_unicast(i, reply, a, port);
394 avahi_dns_packet_free(reply);
397 int unicast_response, flush_cache, auxiliary;
398 AvahiDnsPacket *reply = NULL;
401 /* In case the query packet was truncated never respond
402 immediately, because known answer suppression records might be
403 contained in later packets */
404 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
406 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
408 int im = immediately;
410 /* Only send the response immediately if it contains a
411 * unique entry AND it is not in reply to a truncated
412 * packet AND it is not an auxiliary record AND all other
413 * responses for this record are unique too. */
415 if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list))
418 if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) {
420 /* Due to some reasons the record has not been scheduled.
421 * The client requested an unicast response in that
422 * case. Therefore we prepare such a response */
424 append_aux_records_to_list(s, i, r, unicast_response);
431 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
435 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
437 /* Appending this record succeeded, so incremeant
438 * the specific header field, and return to the caller */
440 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
445 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
448 /* The record is too large for one packet, so create a larger packet */
450 avahi_dns_packet_free(reply);
451 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
452 if (size > AVAHI_DNS_PACKET_SIZE_MAX)
453 size = AVAHI_DNS_PACKET_SIZE_MAX;
455 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
458 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
460 avahi_dns_packet_free(reply);
461 t = avahi_record_to_string(r);
462 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
466 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
469 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
470 avahi_interface_send_packet_unicast(i, reply, a, port);
471 avahi_dns_packet_free(reply);
476 avahi_record_unref(r);
480 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
481 avahi_interface_send_packet_unicast(i, reply, a, port);
482 avahi_dns_packet_free(reply);
486 avahi_record_list_flush(s->record_list);
490 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
497 if (!s->config.enable_reflector)
500 for (j = s->monitor->interfaces; j; j = j->interface_next)
501 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
502 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
505 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
506 AvahiServer *s = userdata;
513 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
517 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
524 if (!s->config.enable_reflector)
527 for (j = s->monitor->interfaces; j; j = j->interface_next)
528 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
529 /* Post the query to other networks */
530 avahi_interface_post_query(j, k, 1, NULL);
532 /* Reply from caches of other network. This is needed to
533 * "work around" known answer suppression. */
535 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
539 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
546 if (!s->config.enable_reflector)
549 for (j = s->monitor->interfaces; j; j = j->interface_next)
550 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
551 avahi_interface_post_probe(j, r, 1);
554 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
563 assert(avahi_record_list_is_empty(s->record_list));
565 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
567 /* Handle the questions */
568 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
570 int unicast_response = 0;
572 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
573 avahi_log_warn("Packet too short (1)");
577 if (!legacy_unicast && !from_local_iface) {
578 reflect_query(s, i, key);
579 avahi_cache_start_poof(i->cache, key, a);
582 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
583 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
584 /* Allow our own queries to be suppressed by incoming
585 * queries only when they do not include known answers */
586 avahi_query_scheduler_incoming(i->query_scheduler, key);
588 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
589 avahi_key_unref(key);
592 if (!legacy_unicast) {
594 /* Known Answer Suppression */
595 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
599 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
600 avahi_log_warn("Packet too short (2)");
604 if (handle_conflict(s, i, record, unique)) {
605 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
606 avahi_record_list_drop(s->record_list, record);
607 avahi_cache_stop_poof(i->cache, record, a);
610 avahi_record_unref(record);
614 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
618 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
619 avahi_log_warn("Packet too short (3)");
623 if (!avahi_key_is_pattern(record->key)) {
624 if (!from_local_iface)
625 reflect_probe(s, i, record);
626 incoming_probe(s, record, i);
629 avahi_record_unref(record);
633 if (!avahi_record_list_is_empty(s->record_list))
634 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
639 avahi_record_list_flush(s->record_list);
642 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
650 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
651 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
656 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
657 avahi_log_warn("Packet too short (4)");
661 if (!avahi_key_is_pattern(record->key)) {
663 if (handle_conflict(s, i, record, cache_flush)) {
664 if (!from_local_iface)
665 reflect_response(s, i, record, cache_flush);
666 avahi_cache_update(i->cache, record, cache_flush, a);
667 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
671 avahi_record_unref(record);
674 /* If the incoming response contained a conflicting record, some
675 records have been scheduling for sending. We need to flush them
677 if (!avahi_record_list_is_empty(s->record_list))
678 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
681 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
682 unsigned n, idx = (unsigned) -1;
683 AvahiLegacyUnicastReflectSlot *slot;
687 if (!s->legacy_unicast_reflect_slots)
688 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
690 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
691 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
693 if (!s->legacy_unicast_reflect_slots[idx])
697 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
700 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
701 return NULL; /* OOM */
703 s->legacy_unicast_reflect_slots[idx] = slot;
704 slot->id = s->legacy_unicast_reflect_id++;
710 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
716 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
718 assert(s->legacy_unicast_reflect_slots[idx] == slot);
720 avahi_time_event_free(slot->time_event);
723 s->legacy_unicast_reflect_slots[idx] = NULL;
726 static void free_slots(AvahiServer *s) {
730 if (!s->legacy_unicast_reflect_slots)
733 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
734 if (s->legacy_unicast_reflect_slots[idx])
735 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
737 avahi_free(s->legacy_unicast_reflect_slots);
738 s->legacy_unicast_reflect_slots = NULL;
741 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
746 if (!s->legacy_unicast_reflect_slots)
749 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
751 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
754 return s->legacy_unicast_reflect_slots[idx];
757 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
758 AvahiLegacyUnicastReflectSlot *slot = userdata;
762 assert(slot->time_event == e);
764 deallocate_slot(slot->server, slot);
767 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
768 AvahiLegacyUnicastReflectSlot *slot;
776 assert(i->protocol == a->proto);
778 if (!s->config.enable_reflector)
781 /* Reflecting legacy unicast queries is a little more complicated
782 than reflecting normal queries, since we must route the
783 responses back to the right client. Therefore we must store
784 some information for finding the right client contact data for
785 response packets. In contrast to normal queries legacy
786 unicast query and response packets are reflected untouched and
787 are not reassembled into larger packets */
789 if (!(slot = allocate_slot(s))) {
790 /* No slot available, we drop this legacy unicast query */
791 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
795 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
798 slot->interface = i->hardware->index;
800 avahi_elapse_time(&slot->elapse_time, 2000, 0);
801 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
803 /* Patch the packet with our new locally generatet id */
804 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
806 for (j = s->monitor->interfaces; j; j = j->interface_next)
807 if (avahi_interface_is_relevant(j) &&
809 (s->config.reflect_ipv || j->protocol == i->protocol)) {
811 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
812 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
813 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
814 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
818 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
821 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
826 if (!s->config.enable_reflector)
829 if (!avahi_address_is_local(s->monitor, address))
832 if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
833 struct sockaddr_in lsa;
834 socklen_t l = sizeof(lsa);
836 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
837 avahi_log_warn("getsockname(): %s", strerror(errno));
839 return lsa.sin_port == port;
843 if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
844 struct sockaddr_in6 lsa;
845 socklen_t l = sizeof(lsa);
847 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
848 avahi_log_warn("getsockname(): %s", strerror(errno));
850 return lsa.sin6_port == port;
856 static int is_mdns_mcast_address(const AvahiAddress *a) {
860 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
861 return avahi_address_cmp(a, &b) == 0;
864 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
866 assert(iface != AVAHI_IF_UNSPEC);
869 /* If it isn't the MDNS port it can't be generated by us */
870 if (port != AVAHI_MDNS_PORT)
873 return avahi_interface_has_address(s->monitor, iface, a);
876 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
878 int from_local_iface = 0;
885 assert(src_address->proto == dst_address->proto);
887 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
888 !avahi_interface_is_relevant(i)) {
889 avahi_log_warn("Recieved packet from invalid interface.");
893 if (avahi_address_is_ipv4_in_ipv6(src_address))
894 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
897 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
898 /* This originates from our local reflector, so let's ignore it */
901 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
902 if (s->config.enable_reflector)
903 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
905 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
906 avahi_log_warn("Recieved invalid packet.");
910 if (avahi_dns_packet_is_query(p)) {
911 int legacy_unicast = 0;
913 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
914 avahi_log_warn("Invalid query packet.");
918 if (port != AVAHI_MDNS_PORT) {
921 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
922 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
923 avahi_log_warn("Invalid legacy unicast query packet.");
931 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
933 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
936 if (port != AVAHI_MDNS_PORT) {
937 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
941 if (ttl != 255 && s->config.check_response_ttl) {
942 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
946 if (!is_mdns_mcast_address(dst_address) &&
947 !avahi_interface_address_on_link(i, src_address)) {
948 avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
952 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
953 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
954 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
955 avahi_log_warn("Invalid response packet.");
959 handle_response_packet(s, p, i, src_address, from_local_iface);
963 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
965 AvahiLegacyUnicastReflectSlot *slot;
970 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
971 avahi_log_warn("Recieved invalid packet.");
975 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
976 avahi_log_warn("Recieved legacy unicast response with unknown id");
980 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
981 !avahi_interface_is_relevant(j))
984 /* Patch the original ID into this response */
985 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
987 /* Forward the response to the correct client */
988 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
990 /* Undo changes to packet */
991 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
994 static void cleanup_dead(AvahiServer *s) {
997 avahi_cleanup_dead_entries(s);
998 avahi_browser_cleanup(s);
1001 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1002 AvahiServer *s = userdata;
1003 AvahiAddress dest, src;
1004 AvahiDnsPacket *p = NULL;
1011 assert(events & AVAHI_WATCH_IN);
1013 if (fd == s->fd_ipv4) {
1014 dest.proto = src.proto = AVAHI_PROTO_INET;
1015 p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1017 assert(fd == s->fd_ipv6);
1018 dest.proto = src.proto = AVAHI_PROTO_INET6;
1019 p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1023 if (iface == AVAHI_IF_UNSPEC)
1024 iface = avahi_find_interface_for_address(s->monitor, &dest);
1026 if (iface != AVAHI_IF_UNSPEC)
1027 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1029 avahi_log_error("Incoming packet recieved on address that isn't local.");
1031 avahi_dns_packet_free(p);
1037 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1038 AvahiServer *s = userdata;
1039 AvahiDnsPacket *p = NULL;
1043 assert(events & AVAHI_WATCH_IN);
1045 if (fd == s->fd_legacy_unicast_ipv4)
1046 p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1048 assert(fd == s->fd_legacy_unicast_ipv6);
1049 p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1053 dispatch_legacy_unicast_packet(s, p);
1054 avahi_dns_packet_free(p);
1060 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1063 if (s->state == state)
1068 avahi_interface_monitor_update_rrs(s->monitor, 0);
1071 s->callback(s, state, s->userdata);
1074 static void withdraw_host_rrs(AvahiServer *s) {
1077 if (s->hinfo_entry_group)
1078 avahi_s_entry_group_reset(s->hinfo_entry_group);
1080 if (s->browse_domain_entry_group)
1081 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1083 avahi_interface_monitor_update_rrs(s->monitor, 1);
1084 s->n_host_rr_pending = 0;
1087 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1090 assert(s->n_host_rr_pending > 0);
1092 if (--s->n_host_rr_pending == 0)
1093 server_set_state(s, AVAHI_SERVER_RUNNING);
1096 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1100 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1101 s->state == AVAHI_SERVER_REGISTERING)
1102 s->n_host_rr_pending ++;
1104 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1105 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1106 withdraw_host_rrs(s);
1107 server_set_state(s, AVAHI_SERVER_COLLISION);
1109 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1110 s->state == AVAHI_SERVER_REGISTERING)
1111 avahi_server_decrease_host_rr_pending(s);
1114 static void register_hinfo(AvahiServer *s) {
1115 struct utsname utsname;
1120 if (!s->config.publish_hinfo)
1123 if (s->hinfo_entry_group)
1124 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1126 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1128 if (!s->hinfo_entry_group) {
1129 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1133 /* Fill in HINFO rr */
1134 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1136 if (uname(&utsname) < 0)
1137 avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1140 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1141 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1143 avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1145 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1146 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1151 avahi_record_unref(r);
1154 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1155 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1159 static void register_localhost(AvahiServer *s) {
1163 /* Add localhost entries */
1164 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1165 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1167 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1168 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1171 static void register_browse_domain(AvahiServer *s) {
1174 if (!s->config.publish_domain)
1177 if (s->browse_domain_entry_group)
1178 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1180 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1182 if (!s->browse_domain_entry_group) {
1183 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1187 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) {
1188 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1192 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1193 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1196 static void register_stuff(AvahiServer *s) {
1199 server_set_state(s, AVAHI_SERVER_REGISTERING);
1200 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1203 register_browse_domain(s);
1204 avahi_interface_monitor_update_rrs(s->monitor, 0);
1206 s->n_host_rr_pending --;
1208 if (s->n_host_rr_pending == 0)
1209 server_set_state(s, AVAHI_SERVER_RUNNING);
1212 static void update_fqdn(AvahiServer *s) {
1216 assert(s->host_name);
1217 assert(s->domain_name);
1219 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1222 avahi_free(s->host_name_fqdn);
1223 s->host_name_fqdn = n;
1226 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1230 if (host_name && !avahi_is_valid_host_name(host_name))
1231 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1233 withdraw_host_rrs(s);
1235 avahi_free(s->host_name);
1236 s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
1237 s->host_name[strcspn(s->host_name, ".")] = 0;
1244 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1246 assert(domain_name);
1248 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1249 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1251 withdraw_host_rrs(s);
1253 avahi_free(s->domain_name);
1254 s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
1261 static int valid_server_config(const AvahiServerConfig *sc) {
1263 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1264 return AVAHI_ERR_INVALID_HOST_NAME;
1266 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1267 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1272 static int setup_sockets(AvahiServer *s) {
1275 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1276 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1278 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1279 return AVAHI_ERR_NO_NETWORK;
1281 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1282 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1283 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1284 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1286 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1287 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1291 s->watch_legacy_unicast_ipv4 =
1292 s->watch_legacy_unicast_ipv6 = NULL;
1294 if (s->fd_ipv4 >= 0)
1295 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1296 if (s->fd_ipv6 >= 0)
1297 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1299 if (s->fd_legacy_unicast_ipv4 >= 0)
1300 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);
1301 if (s->fd_legacy_unicast_ipv6 >= 0)
1302 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);
1307 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1311 if (sc && (e = valid_server_config(sc)) < 0) {
1317 if (!(s = avahi_new(AvahiServer, 1))) {
1319 *error = AVAHI_ERR_NO_MEMORY;
1324 s->poll_api = poll_api;
1327 avahi_server_config_copy(&s->config, sc);
1329 avahi_server_config_init(&s->config);
1331 if ((e = setup_sockets(s)) < 0) {
1335 avahi_server_config_free(&s->config);
1341 s->n_host_rr_pending = 0;
1342 s->need_entry_cleanup = 0;
1343 s->need_group_cleanup = 0;
1344 s->need_browser_cleanup = 0;
1345 s->hinfo_entry_group = NULL;
1346 s->browse_domain_entry_group = NULL;
1347 s->error = AVAHI_OK;
1348 s->state = AVAHI_SERVER_INVALID;
1350 s->callback = callback;
1351 s->userdata = userdata;
1353 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1355 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1356 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1357 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1359 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1360 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1361 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1362 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1363 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1364 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1365 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1366 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1367 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1369 s->legacy_unicast_reflect_slots = NULL;
1370 s->legacy_unicast_reflect_id = 0;
1372 s->record_list = avahi_record_list_new();
1375 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1376 s->host_name[strcspn(s->host_name, ".")] = 0;
1377 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1378 s->host_name_fqdn = NULL;
1382 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1383 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1385 if (s->config.enable_wide_area) {
1386 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1387 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1389 s->wide_area_lookup_engine = NULL;
1391 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1393 s->monitor = avahi_interface_monitor_new(s);
1394 avahi_interface_monitor_sync(s->monitor);
1396 register_localhost(s);
1402 void avahi_server_free(AvahiServer* s) {
1405 /* Remove all browsers */
1407 while (s->dns_server_browsers)
1408 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1409 while (s->host_name_resolvers)
1410 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1411 while (s->address_resolvers)
1412 avahi_s_address_resolver_free(s->address_resolvers);
1413 while (s->domain_browsers)
1414 avahi_s_domain_browser_free(s->domain_browsers);
1415 while (s->service_type_browsers)
1416 avahi_s_service_type_browser_free(s->service_type_browsers);
1417 while (s->service_browsers)
1418 avahi_s_service_browser_free(s->service_browsers);
1419 while (s->service_resolvers)
1420 avahi_s_service_resolver_free(s->service_resolvers);
1421 while (s->record_browsers)
1422 avahi_s_record_browser_destroy(s->record_browsers);
1424 /* Remove all locally rgeistered stuff */
1427 avahi_entry_free(s, s->entries);
1429 avahi_interface_monitor_free(s->monitor);
1432 avahi_entry_group_free(s, s->groups);
1436 avahi_hashmap_free(s->entries_by_key);
1437 avahi_record_list_free(s->record_list);
1438 avahi_hashmap_free(s->record_browser_hashmap);
1440 if (s->wide_area_lookup_engine)
1441 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1442 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1444 avahi_time_event_queue_free(s->time_event_queue);
1449 s->poll_api->watch_free(s->watch_ipv4);
1451 s->poll_api->watch_free(s->watch_ipv6);
1453 if (s->watch_legacy_unicast_ipv4)
1454 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1455 if (s->watch_legacy_unicast_ipv6)
1456 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1460 if (s->fd_ipv4 >= 0)
1462 if (s->fd_ipv6 >= 0)
1465 if (s->fd_legacy_unicast_ipv4 >= 0)
1466 close(s->fd_legacy_unicast_ipv4);
1467 if (s->fd_legacy_unicast_ipv6 >= 0)
1468 close(s->fd_legacy_unicast_ipv6);
1470 /* Free other stuff */
1472 avahi_free(s->host_name);
1473 avahi_free(s->domain_name);
1474 avahi_free(s->host_name_fqdn);
1476 avahi_server_config_free(&s->config);
1481 const char* avahi_server_get_domain_name(AvahiServer *s) {
1484 return s->domain_name;
1487 const char* avahi_server_get_host_name(AvahiServer *s) {
1490 return s->host_name;
1493 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1496 return s->host_name_fqdn;
1499 void* avahi_server_get_data(AvahiServer *s) {
1505 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1508 s->userdata = userdata;
1511 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1517 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1520 memset(c, 0, sizeof(AvahiServerConfig));
1523 c->host_name = NULL;
1524 c->domain_name = NULL;
1525 c->check_response_ttl = 0;
1526 c->publish_hinfo = 1;
1527 c->publish_addresses = 1;
1528 c->publish_workstation = 1;
1529 c->publish_domain = 1;
1530 c->use_iff_running = 0;
1531 c->enable_reflector = 0;
1533 c->add_service_cookie = 1;
1534 c->enable_wide_area = 0;
1535 c->n_wide_area_servers = 0;
1536 c->disallow_other_stacks = 0;
1537 c->browse_domains = NULL;
1538 c->disable_publishing = 0;
1543 void avahi_server_config_free(AvahiServerConfig *c) {
1546 avahi_free(c->host_name);
1547 avahi_free(c->domain_name);
1548 avahi_string_list_free(c->browse_domains);
1551 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1552 char *d = NULL, *h = NULL;
1553 AvahiStringList *l = NULL;
1558 if (!(h = avahi_strdup(c->host_name)))
1562 if (!(d = avahi_strdup(c->domain_name))) {
1567 if (!(l = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1575 ret->domain_name = d;
1576 ret->browse_domains = l;
1581 int avahi_server_errno(AvahiServer *s) {
1587 /* Just for internal use */
1588 int avahi_server_set_errno(AvahiServer *s, int error) {
1591 return s->error = error;
1594 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1597 return s->local_service_cookie;
1600 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1606 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1608 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1609 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1610 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1617 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) {
1618 AvahiKey *key = NULL;
1621 char n[AVAHI_DOMAIN_NAME_MAX];
1628 AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
1629 AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
1630 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
1631 AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
1632 AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
1634 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1635 return avahi_server_set_errno(s, ret);
1637 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1638 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1640 e = find_entry(s, interface, protocol, key);
1641 avahi_key_unref(key);
1644 *ret_group = e->group;
1648 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1651 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1652 AvahiKey *key = NULL;
1658 if (!s->host_name_fqdn)
1661 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1664 e = find_entry(s, interface, protocol, key);
1665 avahi_key_unref(key);
1670 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1673 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1679 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1681 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1682 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1683 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1684 avahi_record_equal_no_ttl(record, e->record))
1690 /** Set the wide area DNS servers */
1691 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1694 if (!s->wide_area_lookup_engine)
1695 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1697 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);