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 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
410 append_aux_records_to_list(s, i, r, unicast_response);
412 /* Due to some reasons the record has not been scheduled.
413 * The client requested an unicast response in that
414 * case. Therefore we prepare such a response */
421 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
425 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
427 /* Appending this record succeeded, so incremeant
428 * the specific header field, and return to the caller */
430 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
435 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
438 /* The record is too large for one packet, so create a larger packet */
440 avahi_dns_packet_free(reply);
441 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
442 if (size > AVAHI_DNS_PACKET_SIZE_MAX)
443 size = AVAHI_DNS_PACKET_SIZE_MAX;
445 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
448 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
450 avahi_dns_packet_free(reply);
451 t = avahi_record_to_string(r);
452 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
456 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
459 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
460 avahi_interface_send_packet_unicast(i, reply, a, port);
461 avahi_dns_packet_free(reply);
466 avahi_record_unref(r);
470 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
471 avahi_interface_send_packet_unicast(i, reply, a, port);
472 avahi_dns_packet_free(reply);
476 avahi_record_list_flush(s->record_list);
480 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
487 if (!s->config.enable_reflector)
490 for (j = s->monitor->interfaces; j; j = j->interface_next)
491 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
492 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
495 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
496 AvahiServer *s = userdata;
503 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
507 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
514 if (!s->config.enable_reflector)
517 for (j = s->monitor->interfaces; j; j = j->interface_next)
518 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
519 /* Post the query to other networks */
520 avahi_interface_post_query(j, k, 1);
522 /* Reply from caches of other network. This is needed to
523 * "work around" known answer suppression. */
525 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
529 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
536 if (!s->config.enable_reflector)
539 for (j = s->monitor->interfaces; j; j = j->interface_next)
540 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
541 avahi_interface_post_probe(j, r, 1);
544 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
553 /* avahi_log_debug("query"); */
555 assert(avahi_record_list_is_empty(s->record_list));
557 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
559 /* Handle the questions */
560 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
562 int unicast_response = 0;
564 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
565 avahi_log_warn("Packet too short (1)");
569 if (!legacy_unicast && !from_local_iface) {
570 reflect_query(s, i, key);
571 avahi_cache_start_poof(i->cache, key, a);
574 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
575 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
576 /* Allow our own queries to be suppressed by incoming
577 * queries only when they do not include known answers */
578 avahi_query_scheduler_incoming(i->query_scheduler, key);
580 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
581 avahi_key_unref(key);
584 if (!legacy_unicast) {
586 /* Known Answer Suppression */
587 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
591 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
592 avahi_log_warn("Packet too short (2)");
596 if (handle_conflict(s, i, record, unique)) {
597 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
598 avahi_record_list_drop(s->record_list, record);
599 avahi_cache_stop_poof(i->cache, record, a);
602 avahi_record_unref(record);
606 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
610 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
611 avahi_log_warn("Packet too short (3)");
615 if (!avahi_key_is_pattern(record->key)) {
616 if (!from_local_iface)
617 reflect_probe(s, i, record);
618 incoming_probe(s, record, i);
621 avahi_record_unref(record);
625 if (!avahi_record_list_is_empty(s->record_list))
626 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
631 avahi_record_list_flush(s->record_list);
634 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
642 /* avahi_log_debug("response"); */
644 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
645 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
650 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
651 avahi_log_warn("Packet too short (4)");
655 if (!avahi_key_is_pattern(record->key)) {
657 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
658 /* avahi_free(txt); */
660 if (handle_conflict(s, i, record, cache_flush)) {
661 if (!from_local_iface)
662 reflect_response(s, i, record, cache_flush);
663 avahi_cache_update(i->cache, record, cache_flush, a);
664 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
668 avahi_record_unref(record);
671 /* If the incoming response contained a conflicting record, some
672 records have been scheduling for sending. We need to flush them
674 if (!avahi_record_list_is_empty(s->record_list))
675 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
678 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
679 unsigned n, idx = (unsigned) -1;
680 AvahiLegacyUnicastReflectSlot *slot;
684 if (!s->legacy_unicast_reflect_slots)
685 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
687 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
688 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
690 if (!s->legacy_unicast_reflect_slots[idx])
694 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
697 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
698 return NULL; /* OOM */
700 s->legacy_unicast_reflect_slots[idx] = slot;
701 slot->id = s->legacy_unicast_reflect_id++;
707 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
713 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
715 assert(s->legacy_unicast_reflect_slots[idx] == slot);
717 avahi_time_event_free(slot->time_event);
720 s->legacy_unicast_reflect_slots[idx] = NULL;
723 static void free_slots(AvahiServer *s) {
727 if (!s->legacy_unicast_reflect_slots)
730 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
731 if (s->legacy_unicast_reflect_slots[idx])
732 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
734 avahi_free(s->legacy_unicast_reflect_slots);
735 s->legacy_unicast_reflect_slots = NULL;
738 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
743 if (!s->legacy_unicast_reflect_slots)
746 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
748 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
751 return s->legacy_unicast_reflect_slots[idx];
754 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
755 AvahiLegacyUnicastReflectSlot *slot = userdata;
759 assert(slot->time_event == e);
761 deallocate_slot(slot->server, slot);
764 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
765 AvahiLegacyUnicastReflectSlot *slot;
773 assert(i->protocol == a->proto);
775 if (!s->config.enable_reflector)
778 /* avahi_log_debug("legacy unicast reflector"); */
780 /* Reflecting legacy unicast queries is a little more complicated
781 than reflecting normal queries, since we must route the
782 responses back to the right client. Therefore we must store
783 some information for finding the right client contact data for
784 response packets. In contrast to normal queries legacy
785 unicast query and response packets are reflected untouched and
786 are not reassembled into larger packets */
788 if (!(slot = allocate_slot(s))) {
789 /* No slot available, we drop this legacy unicast query */
790 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
794 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
797 slot->interface = i->hardware->index;
799 avahi_elapse_time(&slot->elapse_time, 2000, 0);
800 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
802 /* Patch the packet with our new locally generatet id */
803 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
805 for (j = s->monitor->interfaces; j; j = j->interface_next)
806 if (avahi_interface_is_relevant(j) &&
808 (s->config.reflect_ipv || j->protocol == i->protocol)) {
810 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
811 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
812 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
813 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
817 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
820 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
825 if (!s->config.enable_reflector)
828 if (!avahi_address_is_local(s->monitor, address))
831 if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
832 struct sockaddr_in lsa;
833 socklen_t l = sizeof(lsa);
835 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
836 avahi_log_warn("getsockname(): %s", strerror(errno));
838 return lsa.sin_port == port;
842 if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
843 struct sockaddr_in6 lsa;
844 socklen_t l = sizeof(lsa);
846 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
847 avahi_log_warn("getsockname(): %s", strerror(errno));
849 return lsa.sin6_port == port;
855 static int is_mdns_mcast_address(const AvahiAddress *a) {
859 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
860 return avahi_address_cmp(a, &b) == 0;
863 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
865 assert(iface != AVAHI_IF_UNSPEC);
868 /* If it isn't the MDNS port it can't be generated by us */
869 if (port != AVAHI_MDNS_PORT)
872 return avahi_interface_has_address(s->monitor, iface, a);
875 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
877 int from_local_iface = 0;
884 assert(src_address->proto == dst_address->proto);
886 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
887 !avahi_interface_is_relevant(i)) {
888 avahi_log_warn("Recieved packet from invalid interface.");
892 /* avahi_log_debug("new packet received on interface '%s.%i'.", i->hardware->name, i->protocol); */
894 if (avahi_address_is_ipv4_in_ipv6(src_address))
895 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
898 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
899 /* This originates from our local reflector, so let's ignore it */
902 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
903 if (s->config.enable_reflector)
904 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
906 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
907 avahi_log_warn("Recieved invalid packet.");
911 if (avahi_dns_packet_is_query(p)) {
912 int legacy_unicast = 0;
914 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
915 avahi_log_warn("Invalid query packet.");
919 if (port != AVAHI_MDNS_PORT) {
922 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
923 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
924 avahi_log_warn("Invalid legacy unicast query packet.");
932 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
934 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
936 /* avahi_log_debug("Handled query"); */
938 if (port != AVAHI_MDNS_PORT) {
939 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
943 if (ttl != 255 && s->config.check_response_ttl) {
944 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
948 if (!is_mdns_mcast_address(dst_address) &&
949 !avahi_interface_address_on_link(i, src_address)) {
950 avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
954 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
955 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
956 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
957 avahi_log_warn("Invalid response packet.");
961 handle_response_packet(s, p, i, src_address, from_local_iface);
962 /* avahi_log_debug("Handled response"); */
966 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
968 AvahiLegacyUnicastReflectSlot *slot;
973 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
974 avahi_log_warn("Recieved invalid packet.");
978 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
979 avahi_log_warn("Recieved legacy unicast response with unknown id");
983 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
984 !avahi_interface_is_relevant(j))
987 /* Patch the original ID into this response */
988 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
990 /* Forward the response to the correct client */
991 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
993 /* Undo changes to packet */
994 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
997 static void cleanup_dead(AvahiServer *s) {
1000 avahi_cleanup_dead_entries(s);
1001 avahi_browser_cleanup(s);
1004 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1005 AvahiServer *s = userdata;
1006 AvahiAddress dest, src;
1007 AvahiDnsPacket *p = NULL;
1014 assert(events & AVAHI_WATCH_IN);
1016 if (fd == s->fd_ipv4) {
1017 dest.proto = src.proto = AVAHI_PROTO_INET;
1018 p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1020 assert(fd == s->fd_ipv6);
1021 dest.proto = src.proto = AVAHI_PROTO_INET6;
1022 p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1026 if (iface == AVAHI_IF_UNSPEC)
1027 iface = avahi_find_interface_for_address(s->monitor, &dest);
1029 if (iface != AVAHI_IF_UNSPEC)
1030 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1032 avahi_log_error("Incoming packet recieved on address that isn't local.");
1034 avahi_dns_packet_free(p);
1040 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1041 AvahiServer *s = userdata;
1042 AvahiDnsPacket *p = NULL;
1046 assert(events & AVAHI_WATCH_IN);
1048 if (fd == s->fd_legacy_unicast_ipv4)
1049 p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1051 assert(fd == s->fd_legacy_unicast_ipv6);
1052 p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1056 dispatch_legacy_unicast_packet(s, p);
1057 avahi_dns_packet_free(p);
1063 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1066 if (s->state == state)
1072 s->callback(s, state, s->userdata);
1075 static void withdraw_host_rrs(AvahiServer *s) {
1078 if (s->hinfo_entry_group)
1079 avahi_s_entry_group_reset(s->hinfo_entry_group);
1081 if (s->browse_domain_entry_group)
1082 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1084 avahi_interface_monitor_update_rrs(s->monitor, 1);
1085 s->n_host_rr_pending = 0;
1088 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1091 assert(s->n_host_rr_pending > 0);
1093 if (--s->n_host_rr_pending == 0)
1094 server_set_state(s, AVAHI_SERVER_RUNNING);
1097 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1101 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1102 s->state == AVAHI_SERVER_REGISTERING)
1103 s->n_host_rr_pending ++;
1105 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1106 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1107 withdraw_host_rrs(s);
1108 server_set_state(s, AVAHI_SERVER_COLLISION);
1110 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1111 s->state == AVAHI_SERVER_REGISTERING)
1112 avahi_server_decrease_host_rr_pending(s);
1115 static void register_hinfo(AvahiServer *s) {
1116 struct utsname utsname;
1121 if (!s->config.publish_hinfo)
1124 if (s->hinfo_entry_group)
1125 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1127 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1129 if (!s->hinfo_entry_group) {
1130 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1134 /* Fill in HINFO rr */
1135 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1137 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1138 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1140 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1141 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1145 avahi_record_unref(r);
1148 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1149 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1153 static void register_localhost(AvahiServer *s) {
1157 /* Add localhost entries */
1158 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1159 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1161 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1162 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1165 static void register_browse_domain(AvahiServer *s) {
1168 if (!s->config.publish_domain)
1171 if (s->browse_domain_entry_group)
1172 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1174 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1176 if (!s->browse_domain_entry_group) {
1177 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1181 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) {
1182 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1186 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1187 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1190 static void register_stuff(AvahiServer *s) {
1193 server_set_state(s, AVAHI_SERVER_REGISTERING);
1194 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1197 register_browse_domain(s);
1198 avahi_interface_monitor_update_rrs(s->monitor, 0);
1200 s->n_host_rr_pending --;
1202 if (s->n_host_rr_pending == 0)
1203 server_set_state(s, AVAHI_SERVER_RUNNING);
1206 static void update_fqdn(AvahiServer *s) {
1210 assert(s->host_name);
1211 assert(s->domain_name);
1213 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1216 avahi_free(s->host_name_fqdn);
1217 s->host_name_fqdn = n;
1220 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1224 if (host_name && !avahi_is_valid_host_name(host_name))
1225 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1227 withdraw_host_rrs(s);
1229 avahi_free(s->host_name);
1230 s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
1231 s->host_name[strcspn(s->host_name, ".")] = 0;
1238 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1240 assert(domain_name);
1242 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1243 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1245 withdraw_host_rrs(s);
1247 avahi_free(s->domain_name);
1248 s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
1255 static int valid_server_config(const AvahiServerConfig *sc) {
1257 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1258 return AVAHI_ERR_INVALID_HOST_NAME;
1260 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1261 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1266 static int setup_sockets(AvahiServer *s) {
1269 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1270 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1272 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1273 return AVAHI_ERR_NO_NETWORK;
1275 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1276 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1277 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1278 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1280 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1281 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1285 s->watch_legacy_unicast_ipv4 =
1286 s->watch_legacy_unicast_ipv6 = NULL;
1288 if (s->fd_ipv4 >= 0)
1289 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1290 if (s->fd_ipv6 >= 0)
1291 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1293 if (s->fd_legacy_unicast_ipv4 >= 0)
1294 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);
1295 if (s->fd_legacy_unicast_ipv6 >= 0)
1296 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);
1301 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1305 if (sc && (e = valid_server_config(sc)) < 0) {
1311 if (!(s = avahi_new(AvahiServer, 1))) {
1313 *error = AVAHI_ERR_NO_MEMORY;
1318 s->poll_api = poll_api;
1321 avahi_server_config_copy(&s->config, sc);
1323 avahi_server_config_init(&s->config);
1325 if ((e = setup_sockets(s)) < 0) {
1329 avahi_server_config_free(&s->config);
1335 s->n_host_rr_pending = 0;
1336 s->need_entry_cleanup = 0;
1337 s->need_group_cleanup = 0;
1338 s->need_browser_cleanup = 0;
1339 s->hinfo_entry_group = NULL;
1340 s->browse_domain_entry_group = NULL;
1341 s->error = AVAHI_OK;
1342 s->state = AVAHI_SERVER_INVALID;
1344 s->callback = callback;
1345 s->userdata = userdata;
1347 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1349 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1350 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1351 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1353 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1354 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1355 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1356 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1357 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1358 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1359 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1360 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1361 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1363 s->legacy_unicast_reflect_slots = NULL;
1364 s->legacy_unicast_reflect_id = 0;
1366 s->record_list = avahi_record_list_new();
1369 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1370 s->host_name[strcspn(s->host_name, ".")] = 0;
1371 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1372 s->host_name_fqdn = NULL;
1376 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1377 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1379 if (s->config.enable_wide_area) {
1380 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1381 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1383 s->wide_area_lookup_engine = NULL;
1385 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1387 s->monitor = avahi_interface_monitor_new(s);
1388 avahi_interface_monitor_sync(s->monitor);
1390 register_localhost(s);
1396 void avahi_server_free(AvahiServer* s) {
1399 /* Remove all browsers */
1401 while (s->dns_server_browsers)
1402 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1403 while (s->host_name_resolvers)
1404 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1405 while (s->address_resolvers)
1406 avahi_s_address_resolver_free(s->address_resolvers);
1407 while (s->domain_browsers)
1408 avahi_s_domain_browser_free(s->domain_browsers);
1409 while (s->service_type_browsers)
1410 avahi_s_service_type_browser_free(s->service_type_browsers);
1411 while (s->service_browsers)
1412 avahi_s_service_browser_free(s->service_browsers);
1413 while (s->service_resolvers)
1414 avahi_s_service_resolver_free(s->service_resolvers);
1415 while (s->record_browsers)
1416 avahi_s_record_browser_destroy(s->record_browsers);
1418 /* Remove all locally rgeistered stuff */
1421 avahi_entry_free(s, s->entries);
1423 avahi_interface_monitor_free(s->monitor);
1426 avahi_entry_group_free(s, s->groups);
1430 avahi_hashmap_free(s->entries_by_key);
1431 avahi_record_list_free(s->record_list);
1432 avahi_hashmap_free(s->record_browser_hashmap);
1434 if (s->wide_area_lookup_engine)
1435 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1436 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1438 avahi_time_event_queue_free(s->time_event_queue);
1443 s->poll_api->watch_free(s->watch_ipv4);
1445 s->poll_api->watch_free(s->watch_ipv6);
1447 if (s->watch_legacy_unicast_ipv4)
1448 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1449 if (s->watch_legacy_unicast_ipv6)
1450 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1454 if (s->fd_ipv4 >= 0)
1456 if (s->fd_ipv6 >= 0)
1459 if (s->fd_legacy_unicast_ipv4 >= 0)
1460 close(s->fd_legacy_unicast_ipv4);
1461 if (s->fd_legacy_unicast_ipv6 >= 0)
1462 close(s->fd_legacy_unicast_ipv6);
1464 /* Free other stuff */
1466 avahi_free(s->host_name);
1467 avahi_free(s->domain_name);
1468 avahi_free(s->host_name_fqdn);
1470 avahi_server_config_free(&s->config);
1475 const char* avahi_server_get_domain_name(AvahiServer *s) {
1478 return s->domain_name;
1481 const char* avahi_server_get_host_name(AvahiServer *s) {
1484 return s->host_name;
1487 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1490 return s->host_name_fqdn;
1493 void* avahi_server_get_data(AvahiServer *s) {
1499 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1502 s->userdata = userdata;
1505 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1511 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1514 memset(c, 0, sizeof(AvahiServerConfig));
1517 c->host_name = NULL;
1518 c->domain_name = NULL;
1519 c->check_response_ttl = 0;
1520 c->publish_hinfo = 1;
1521 c->publish_addresses = 1;
1522 c->publish_workstation = 1;
1523 c->publish_domain = 1;
1524 c->use_iff_running = 0;
1525 c->enable_reflector = 0;
1527 c->add_service_cookie = 1;
1528 c->enable_wide_area = 0;
1529 c->n_wide_area_servers = 0;
1530 c->disallow_other_stacks = 0;
1531 c->browse_domains = NULL;
1536 void avahi_server_config_free(AvahiServerConfig *c) {
1539 avahi_free(c->host_name);
1540 avahi_free(c->domain_name);
1541 avahi_string_list_free(c->browse_domains);
1544 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1545 char *d = NULL, *h = NULL;
1546 AvahiStringList *l = NULL;
1551 if (!(h = avahi_strdup(c->host_name)))
1555 if (!(d = avahi_strdup(c->domain_name))) {
1560 if (!(l = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1568 ret->domain_name = d;
1569 ret->browse_domains = l;
1574 int avahi_server_errno(AvahiServer *s) {
1580 /* Just for internal use */
1581 int avahi_server_set_errno(AvahiServer *s, int error) {
1584 return s->error = error;
1587 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1590 return s->local_service_cookie;
1593 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1599 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1601 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1602 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1603 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1610 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) {
1611 AvahiKey *key = NULL;
1614 char n[AVAHI_DOMAIN_NAME_MAX];
1621 if (!AVAHI_IF_VALID(interface))
1622 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
1624 if (!AVAHI_IF_VALID(protocol))
1625 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
1627 if (!avahi_is_valid_service_name(name))
1628 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1630 if (!avahi_is_valid_service_type_strict(type))
1631 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1633 if (domain && !avahi_is_valid_domain_name(domain))
1634 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1636 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1637 return avahi_server_set_errno(s, ret);
1639 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1640 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1642 e = find_entry(s, interface, protocol, key);
1643 avahi_key_unref(key);
1646 *ret_group = e->group;
1650 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1653 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1654 AvahiKey *key = NULL;
1660 if (!s->host_name_fqdn)
1663 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1666 e = find_entry(s, interface, protocol, key);
1667 avahi_key_unref(key);
1672 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1675 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1681 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1683 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1684 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1685 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1686 avahi_record_equal_no_ttl(record, e->record))
1692 /** Set the wide area DNS servers */
1693 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1696 if (!s->wide_area_lookup_engine)
1697 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1699 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);