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 /* avahi_log_debug("query"); */
565 assert(avahi_record_list_is_empty(s->record_list));
567 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
569 /* Handle the questions */
570 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
572 int unicast_response = 0;
574 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
575 avahi_log_warn("Packet too short (1)");
579 if (!legacy_unicast && !from_local_iface) {
580 reflect_query(s, i, key);
581 avahi_cache_start_poof(i->cache, key, a);
584 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
585 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
586 /* Allow our own queries to be suppressed by incoming
587 * queries only when they do not include known answers */
588 avahi_query_scheduler_incoming(i->query_scheduler, key);
590 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
591 avahi_key_unref(key);
594 if (!legacy_unicast) {
596 /* Known Answer Suppression */
597 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
601 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
602 avahi_log_warn("Packet too short (2)");
606 if (handle_conflict(s, i, record, unique)) {
607 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
608 avahi_record_list_drop(s->record_list, record);
609 avahi_cache_stop_poof(i->cache, record, a);
612 avahi_record_unref(record);
616 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
620 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
621 avahi_log_warn("Packet too short (3)");
625 if (!avahi_key_is_pattern(record->key)) {
626 if (!from_local_iface)
627 reflect_probe(s, i, record);
628 incoming_probe(s, record, i);
631 avahi_record_unref(record);
635 if (!avahi_record_list_is_empty(s->record_list))
636 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
641 avahi_record_list_flush(s->record_list);
644 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
652 /* avahi_log_debug("response"); */
654 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
655 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
660 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
661 avahi_log_warn("Packet too short (4)");
665 if (!avahi_key_is_pattern(record->key)) {
667 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
668 /* avahi_free(txt); */
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 /* avahi_log_debug("legacy unicast reflector"); */
790 /* Reflecting legacy unicast queries is a little more complicated
791 than reflecting normal queries, since we must route the
792 responses back to the right client. Therefore we must store
793 some information for finding the right client contact data for
794 response packets. In contrast to normal queries legacy
795 unicast query and response packets are reflected untouched and
796 are not reassembled into larger packets */
798 if (!(slot = allocate_slot(s))) {
799 /* No slot available, we drop this legacy unicast query */
800 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
804 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
807 slot->interface = i->hardware->index;
809 avahi_elapse_time(&slot->elapse_time, 2000, 0);
810 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
812 /* Patch the packet with our new locally generatet id */
813 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
815 for (j = s->monitor->interfaces; j; j = j->interface_next)
816 if (avahi_interface_is_relevant(j) &&
818 (s->config.reflect_ipv || j->protocol == i->protocol)) {
820 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
821 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
822 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
823 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
827 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
830 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
835 if (!s->config.enable_reflector)
838 if (!avahi_address_is_local(s->monitor, address))
841 if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
842 struct sockaddr_in lsa;
843 socklen_t l = sizeof(lsa);
845 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
846 avahi_log_warn("getsockname(): %s", strerror(errno));
848 return lsa.sin_port == port;
852 if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
853 struct sockaddr_in6 lsa;
854 socklen_t l = sizeof(lsa);
856 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
857 avahi_log_warn("getsockname(): %s", strerror(errno));
859 return lsa.sin6_port == port;
865 static int is_mdns_mcast_address(const AvahiAddress *a) {
869 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
870 return avahi_address_cmp(a, &b) == 0;
873 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
875 assert(iface != AVAHI_IF_UNSPEC);
878 /* If it isn't the MDNS port it can't be generated by us */
879 if (port != AVAHI_MDNS_PORT)
882 return avahi_interface_has_address(s->monitor, iface, a);
885 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
887 int from_local_iface = 0;
894 assert(src_address->proto == dst_address->proto);
896 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
897 !avahi_interface_is_relevant(i)) {
898 avahi_log_warn("Recieved packet from invalid interface.");
902 /* avahi_log_debug("new packet received on interface '%s.%i'.", i->hardware->name, i->protocol); */
904 if (avahi_address_is_ipv4_in_ipv6(src_address))
905 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
908 if (originates_from_local_legacy_unicast_socket(s, src_address, port))
909 /* This originates from our local reflector, so let's ignore it */
912 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
913 if (s->config.enable_reflector)
914 from_local_iface = originates_from_local_iface(s, iface, src_address, port);
916 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
917 avahi_log_warn("Recieved invalid packet.");
921 if (avahi_dns_packet_is_query(p)) {
922 int legacy_unicast = 0;
924 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
925 avahi_log_warn("Invalid query packet.");
929 if (port != AVAHI_MDNS_PORT) {
932 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
933 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
934 avahi_log_warn("Invalid legacy unicast query packet.");
942 reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
944 handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
946 /* avahi_log_debug("Handled query"); */
948 if (port != AVAHI_MDNS_PORT) {
949 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
953 if (ttl != 255 && s->config.check_response_ttl) {
954 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
958 if (!is_mdns_mcast_address(dst_address) &&
959 !avahi_interface_address_on_link(i, src_address)) {
960 avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
964 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
965 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
966 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
967 avahi_log_warn("Invalid response packet.");
971 handle_response_packet(s, p, i, src_address, from_local_iface);
972 /* avahi_log_debug("Handled response"); */
976 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
978 AvahiLegacyUnicastReflectSlot *slot;
983 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
984 avahi_log_warn("Recieved invalid packet.");
988 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
989 avahi_log_warn("Recieved legacy unicast response with unknown id");
993 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
994 !avahi_interface_is_relevant(j))
997 /* Patch the original ID into this response */
998 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1000 /* Forward the response to the correct client */
1001 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1003 /* Undo changes to packet */
1004 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1007 static void cleanup_dead(AvahiServer *s) {
1010 avahi_cleanup_dead_entries(s);
1011 avahi_browser_cleanup(s);
1014 static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1015 AvahiServer *s = userdata;
1016 AvahiAddress dest, src;
1017 AvahiDnsPacket *p = NULL;
1024 assert(events & AVAHI_WATCH_IN);
1026 if (fd == s->fd_ipv4) {
1027 dest.proto = src.proto = AVAHI_PROTO_INET;
1028 p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
1030 assert(fd == s->fd_ipv6);
1031 dest.proto = src.proto = AVAHI_PROTO_INET6;
1032 p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
1036 if (iface == AVAHI_IF_UNSPEC)
1037 iface = avahi_find_interface_for_address(s->monitor, &dest);
1039 if (iface != AVAHI_IF_UNSPEC)
1040 dispatch_packet(s, p, &src, port, &dest, iface, ttl);
1042 avahi_log_error("Incoming packet recieved on address that isn't local.");
1044 avahi_dns_packet_free(p);
1050 static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1051 AvahiServer *s = userdata;
1052 AvahiDnsPacket *p = NULL;
1056 assert(events & AVAHI_WATCH_IN);
1058 if (fd == s->fd_legacy_unicast_ipv4)
1059 p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
1061 assert(fd == s->fd_legacy_unicast_ipv6);
1062 p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
1066 dispatch_legacy_unicast_packet(s, p);
1067 avahi_dns_packet_free(p);
1073 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1076 if (s->state == state)
1082 s->callback(s, state, s->userdata);
1085 static void withdraw_host_rrs(AvahiServer *s) {
1088 if (s->hinfo_entry_group)
1089 avahi_s_entry_group_reset(s->hinfo_entry_group);
1091 if (s->browse_domain_entry_group)
1092 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1094 avahi_interface_monitor_update_rrs(s->monitor, 1);
1095 s->n_host_rr_pending = 0;
1098 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1101 assert(s->n_host_rr_pending > 0);
1103 if (--s->n_host_rr_pending == 0)
1104 server_set_state(s, AVAHI_SERVER_RUNNING);
1107 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1111 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1112 s->state == AVAHI_SERVER_REGISTERING)
1113 s->n_host_rr_pending ++;
1115 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1116 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1117 withdraw_host_rrs(s);
1118 server_set_state(s, AVAHI_SERVER_COLLISION);
1120 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1121 s->state == AVAHI_SERVER_REGISTERING)
1122 avahi_server_decrease_host_rr_pending(s);
1125 static void register_hinfo(AvahiServer *s) {
1126 struct utsname utsname;
1131 if (!s->config.publish_hinfo)
1134 if (s->hinfo_entry_group)
1135 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1137 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1139 if (!s->hinfo_entry_group) {
1140 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1144 /* Fill in HINFO rr */
1145 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1147 if (uname(&utsname) < 0)
1148 avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
1151 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1152 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1154 avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
1156 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1157 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1162 avahi_record_unref(r);
1165 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1166 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1170 static void register_localhost(AvahiServer *s) {
1174 /* Add localhost entries */
1175 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1176 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1178 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1179 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1182 static void register_browse_domain(AvahiServer *s) {
1185 if (!s->config.publish_domain)
1188 if (s->browse_domain_entry_group)
1189 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1191 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1193 if (!s->browse_domain_entry_group) {
1194 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1198 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) {
1199 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1203 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1204 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1207 static void register_stuff(AvahiServer *s) {
1210 server_set_state(s, AVAHI_SERVER_REGISTERING);
1211 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1214 register_browse_domain(s);
1215 avahi_interface_monitor_update_rrs(s->monitor, 0);
1217 s->n_host_rr_pending --;
1219 if (s->n_host_rr_pending == 0)
1220 server_set_state(s, AVAHI_SERVER_RUNNING);
1223 static void update_fqdn(AvahiServer *s) {
1227 assert(s->host_name);
1228 assert(s->domain_name);
1230 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1233 avahi_free(s->host_name_fqdn);
1234 s->host_name_fqdn = n;
1237 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1241 if (host_name && !avahi_is_valid_host_name(host_name))
1242 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1244 withdraw_host_rrs(s);
1246 avahi_free(s->host_name);
1247 s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
1248 s->host_name[strcspn(s->host_name, ".")] = 0;
1255 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1257 assert(domain_name);
1259 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1260 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1262 withdraw_host_rrs(s);
1264 avahi_free(s->domain_name);
1265 s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
1272 static int valid_server_config(const AvahiServerConfig *sc) {
1274 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1275 return AVAHI_ERR_INVALID_HOST_NAME;
1277 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1278 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1283 static int setup_sockets(AvahiServer *s) {
1286 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1287 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1289 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1290 return AVAHI_ERR_NO_NETWORK;
1292 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1293 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1294 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1295 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1297 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1298 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1302 s->watch_legacy_unicast_ipv4 =
1303 s->watch_legacy_unicast_ipv6 = NULL;
1305 if (s->fd_ipv4 >= 0)
1306 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, mcast_socket_event, s);
1307 if (s->fd_ipv6 >= 0)
1308 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, mcast_socket_event, s);
1310 if (s->fd_legacy_unicast_ipv4 >= 0)
1311 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);
1312 if (s->fd_legacy_unicast_ipv6 >= 0)
1313 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);
1318 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1322 if (sc && (e = valid_server_config(sc)) < 0) {
1328 if (!(s = avahi_new(AvahiServer, 1))) {
1330 *error = AVAHI_ERR_NO_MEMORY;
1335 s->poll_api = poll_api;
1338 avahi_server_config_copy(&s->config, sc);
1340 avahi_server_config_init(&s->config);
1342 if ((e = setup_sockets(s)) < 0) {
1346 avahi_server_config_free(&s->config);
1352 s->n_host_rr_pending = 0;
1353 s->need_entry_cleanup = 0;
1354 s->need_group_cleanup = 0;
1355 s->need_browser_cleanup = 0;
1356 s->hinfo_entry_group = NULL;
1357 s->browse_domain_entry_group = NULL;
1358 s->error = AVAHI_OK;
1359 s->state = AVAHI_SERVER_INVALID;
1361 s->callback = callback;
1362 s->userdata = userdata;
1364 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1366 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1367 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1368 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1370 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1371 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1372 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1373 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1374 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1375 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1376 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1377 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1378 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1380 s->legacy_unicast_reflect_slots = NULL;
1381 s->legacy_unicast_reflect_id = 0;
1383 s->record_list = avahi_record_list_new();
1386 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1387 s->host_name[strcspn(s->host_name, ".")] = 0;
1388 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1389 s->host_name_fqdn = NULL;
1393 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1394 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1396 if (s->config.enable_wide_area) {
1397 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1398 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1400 s->wide_area_lookup_engine = NULL;
1402 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1404 s->monitor = avahi_interface_monitor_new(s);
1405 avahi_interface_monitor_sync(s->monitor);
1407 register_localhost(s);
1413 void avahi_server_free(AvahiServer* s) {
1416 /* Remove all browsers */
1418 while (s->dns_server_browsers)
1419 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1420 while (s->host_name_resolvers)
1421 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1422 while (s->address_resolvers)
1423 avahi_s_address_resolver_free(s->address_resolvers);
1424 while (s->domain_browsers)
1425 avahi_s_domain_browser_free(s->domain_browsers);
1426 while (s->service_type_browsers)
1427 avahi_s_service_type_browser_free(s->service_type_browsers);
1428 while (s->service_browsers)
1429 avahi_s_service_browser_free(s->service_browsers);
1430 while (s->service_resolvers)
1431 avahi_s_service_resolver_free(s->service_resolvers);
1432 while (s->record_browsers)
1433 avahi_s_record_browser_destroy(s->record_browsers);
1435 /* Remove all locally rgeistered stuff */
1438 avahi_entry_free(s, s->entries);
1440 avahi_interface_monitor_free(s->monitor);
1443 avahi_entry_group_free(s, s->groups);
1447 avahi_hashmap_free(s->entries_by_key);
1448 avahi_record_list_free(s->record_list);
1449 avahi_hashmap_free(s->record_browser_hashmap);
1451 if (s->wide_area_lookup_engine)
1452 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1453 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1455 avahi_time_event_queue_free(s->time_event_queue);
1460 s->poll_api->watch_free(s->watch_ipv4);
1462 s->poll_api->watch_free(s->watch_ipv6);
1464 if (s->watch_legacy_unicast_ipv4)
1465 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1466 if (s->watch_legacy_unicast_ipv6)
1467 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1471 if (s->fd_ipv4 >= 0)
1473 if (s->fd_ipv6 >= 0)
1476 if (s->fd_legacy_unicast_ipv4 >= 0)
1477 close(s->fd_legacy_unicast_ipv4);
1478 if (s->fd_legacy_unicast_ipv6 >= 0)
1479 close(s->fd_legacy_unicast_ipv6);
1481 /* Free other stuff */
1483 avahi_free(s->host_name);
1484 avahi_free(s->domain_name);
1485 avahi_free(s->host_name_fqdn);
1487 avahi_server_config_free(&s->config);
1492 const char* avahi_server_get_domain_name(AvahiServer *s) {
1495 return s->domain_name;
1498 const char* avahi_server_get_host_name(AvahiServer *s) {
1501 return s->host_name;
1504 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1507 return s->host_name_fqdn;
1510 void* avahi_server_get_data(AvahiServer *s) {
1516 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1519 s->userdata = userdata;
1522 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1528 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1531 memset(c, 0, sizeof(AvahiServerConfig));
1534 c->host_name = NULL;
1535 c->domain_name = NULL;
1536 c->check_response_ttl = 0;
1537 c->publish_hinfo = 1;
1538 c->publish_addresses = 1;
1539 c->publish_workstation = 1;
1540 c->publish_domain = 1;
1541 c->use_iff_running = 0;
1542 c->enable_reflector = 0;
1544 c->add_service_cookie = 1;
1545 c->enable_wide_area = 0;
1546 c->n_wide_area_servers = 0;
1547 c->disallow_other_stacks = 0;
1548 c->browse_domains = NULL;
1549 c->disable_publishing = 0;
1554 void avahi_server_config_free(AvahiServerConfig *c) {
1557 avahi_free(c->host_name);
1558 avahi_free(c->domain_name);
1559 avahi_string_list_free(c->browse_domains);
1562 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1563 char *d = NULL, *h = NULL;
1564 AvahiStringList *l = NULL;
1569 if (!(h = avahi_strdup(c->host_name)))
1573 if (!(d = avahi_strdup(c->domain_name))) {
1578 if (!(l = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
1586 ret->domain_name = d;
1587 ret->browse_domains = l;
1592 int avahi_server_errno(AvahiServer *s) {
1598 /* Just for internal use */
1599 int avahi_server_set_errno(AvahiServer *s, int error) {
1602 return s->error = error;
1605 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1608 return s->local_service_cookie;
1611 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1617 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1619 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1620 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1621 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1628 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) {
1629 AvahiKey *key = NULL;
1632 char n[AVAHI_DOMAIN_NAME_MAX];
1639 if (!AVAHI_IF_VALID(interface))
1640 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
1642 if (!AVAHI_IF_VALID(protocol))
1643 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
1645 if (!avahi_is_valid_service_name(name))
1646 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1648 if (!avahi_is_valid_service_type_strict(type))
1649 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1651 if (domain && !avahi_is_valid_domain_name(domain))
1652 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1654 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1655 return avahi_server_set_errno(s, ret);
1657 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1658 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1660 e = find_entry(s, interface, protocol, key);
1661 avahi_key_unref(key);
1664 *ret_group = e->group;
1668 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1671 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1672 AvahiKey *key = NULL;
1678 if (!s->host_name_fqdn)
1681 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1684 e = find_entry(s, interface, protocol, key);
1685 avahi_key_unref(key);
1690 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1693 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1699 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1701 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1702 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1703 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1704 avahi_record_equal_no_ttl(record, e->record))
1710 /** Set the wide area DNS servers */
1711 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1714 if (!s->wide_area_lookup_engine)
1715 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1717 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);