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 <arpa/inet.h>
30 #include <sys/utsname.h>
37 #include <avahi-common/domain.h>
38 #include <avahi-common/timeval.h>
39 #include <avahi-common/malloc.h>
40 #include <avahi-common/error.h>
48 #include "dns-srv-rr.h"
49 #include "addr-util.h"
50 #include "domain-util.h"
53 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) {
59 if (type == AVAHI_DNS_TYPE_ANY) {
62 for (e = s->entries; e; e = e->entries_next)
64 avahi_entry_is_registered(s, e, i) &&
65 e->record->key->clazz == AVAHI_DNS_CLASS_IN &&
66 avahi_domain_equal(name, e->record->key->name))
67 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
73 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
76 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
77 if (!e->dead && avahi_entry_is_registered(s, e, i))
78 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
84 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) {
90 /* Call the specified callback far all records referenced by the one specified in *r */
92 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
93 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
94 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
95 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
96 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
97 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
98 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
99 } else if (r->key->type == AVAHI_DNS_TYPE_CNAME)
100 enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata);
104 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
109 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
112 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
117 /* Push all records that match the specified key to the record list */
119 if (avahi_key_is_pattern(k)) {
122 /* Handle ANY query */
124 for (e = s->entries; e; e = e->entries_next)
125 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
126 avahi_server_prepare_response(s, i, e, unicast_response, 0);
131 /* Handle all other queries */
133 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
134 if (!e->dead && avahi_entry_is_registered(s, e, i))
135 avahi_server_prepare_response(s, i, e, unicast_response, 0);
138 /* Look for CNAME records */
140 if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY)
141 && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) {
145 if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME)))
148 avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response);
149 avahi_key_unref(cname_key);
153 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
157 /* Withdraw the specified entry, and if is part of an entry group,
158 * put that into COLLISION state */
166 for (k = e->group->entries; k; k = k->by_group_next)
168 avahi_goodbye_entry(s, k, 0, 1);
172 e->group->n_probing = 0;
174 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
176 avahi_goodbye_entry(s, e, 0, 1);
180 s->need_entry_cleanup = 1;
183 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
189 /* Withdraw an entry RRSset */
191 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
192 withdraw_entry(s, e);
195 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
197 int ours = 0, won = 0, lost = 0;
203 /* Handle incoming probes and check if they conflict our own probes */
205 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
212 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
217 if (avahi_entry_is_probing(s, e, i)) {
227 char *t = avahi_record_to_string(record);
230 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
232 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
233 withdraw_rrset(s, record->key);
240 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) {
241 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
242 AvahiEntry *e, *n, *conflicting_entry = NULL;
248 /* Check whether an incoming record conflicts with one of our own */
250 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
256 /* Check if the incoming is a goodbye record */
257 if (avahi_record_is_goodbye(record)) {
259 if (avahi_record_equal_no_ttl(e->record, record)) {
263 t = avahi_record_to_string(record);
264 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
265 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
272 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
276 if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
279 /* Either our entry or the other is intended to be unique, so let's check */
281 if (avahi_record_equal_no_ttl(e->record, record)) {
282 ours = 1; /* We have an identical record, so this is no conflict */
284 /* Check wheter there is a TTL conflict */
285 if (record->ttl <= e->record->ttl/2 &&
286 avahi_entry_is_registered(s, e, i)) {
289 t = avahi_record_to_string(record);
291 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
292 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
298 /* There's no need to check the other entries of this RRset */
303 if (avahi_entry_is_registered(s, e, i)) {
305 /* A conflict => we have to return to probe mode */
307 conflicting_entry = e;
309 } else if (avahi_entry_is_probing(s, e, i)) {
311 /* We are currently registering a matching record, but
312 * someone else already claimed it, so let's
315 withdraw_immediately = 1;
320 if (!ours && conflict) {
325 t = avahi_record_to_string(record);
327 if (withdraw_immediately) {
328 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
329 withdraw_rrset(s, record->key);
331 assert(conflicting_entry);
332 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
333 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
335 /* Local unique records are returned to probing
336 * state. Local shared records are reannounced. */
345 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
346 int *unicast_response = userdata;
350 assert(unicast_response);
352 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
355 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
359 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
362 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
366 assert(!legacy_unicast || (a && port > 0 && p));
368 if (legacy_unicast) {
369 AvahiDnsPacket *reply;
372 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
375 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
377 append_aux_records_to_list(s, i, r, 0);
379 if (avahi_dns_packet_append_record(reply, r, 0, 10))
380 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
382 char *t = avahi_record_to_string(r);
383 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
387 avahi_record_unref(r);
390 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
391 avahi_interface_send_packet_unicast(i, reply, a, port);
393 avahi_dns_packet_free(reply);
396 int unicast_response, flush_cache, auxiliary;
397 AvahiDnsPacket *reply = NULL;
400 /* In case the query packet was truncated never respond
401 immediately, because known answer suppression records might be
402 contained in later packets */
403 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
405 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
407 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
409 append_aux_records_to_list(s, i, r, unicast_response);
411 /* Due to some reasons the record has not been scheduled.
412 * The client requested an unicast response in that
413 * case. Therefore we prepare such a response */
420 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
424 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
426 /* Appending this record succeeded, so incremeant
427 * the specific header field, and return to the caller */
429 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
434 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
437 /* The record is too large for one packet, so create a larger packet */
439 avahi_dns_packet_free(reply);
440 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
441 if (size > AVAHI_DNS_PACKET_SIZE_MAX)
442 size = AVAHI_DNS_PACKET_SIZE_MAX;
444 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
447 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
449 avahi_dns_packet_free(reply);
450 t = avahi_record_to_string(r);
451 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
455 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
458 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
459 avahi_interface_send_packet_unicast(i, reply, a, port);
460 avahi_dns_packet_free(reply);
465 avahi_record_unref(r);
469 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
470 avahi_interface_send_packet_unicast(i, reply, a, port);
471 avahi_dns_packet_free(reply);
475 avahi_record_list_flush(s->record_list);
479 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
486 if (!s->config.enable_reflector)
489 for (j = s->monitor->interfaces; j; j = j->interface_next)
490 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
491 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
494 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
495 AvahiServer *s = userdata;
502 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
506 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
513 if (!s->config.enable_reflector)
516 for (j = s->monitor->interfaces; j; j = j->interface_next)
517 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
518 /* Post the query to other networks */
519 avahi_interface_post_query(j, k, 1);
521 /* Reply from caches of other network. This is needed to
522 * "work around" known answer suppression. */
524 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
528 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
535 if (!s->config.enable_reflector)
538 for (j = s->monitor->interfaces; j; j = j->interface_next)
539 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
540 avahi_interface_post_probe(j, r, 1);
543 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
552 /* avahi_log_debug("query"); */
554 assert(avahi_record_list_is_empty(s->record_list));
556 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
558 /* Handle the questions */
559 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
561 int unicast_response = 0;
563 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
564 avahi_log_warn("Packet too short (1)");
568 if (!legacy_unicast && !from_local_iface) {
569 reflect_query(s, i, key);
570 avahi_cache_start_poof(i->cache, key, a);
573 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
574 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
575 /* Allow our own queries to be suppressed by incoming
576 * queries only when they do not include known answers */
577 avahi_query_scheduler_incoming(i->query_scheduler, key);
579 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
580 avahi_key_unref(key);
583 if (!legacy_unicast) {
585 /* Known Answer Suppression */
586 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
590 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
591 avahi_log_warn("Packet too short (2)");
595 if (handle_conflict(s, i, record, unique)) {
596 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
597 avahi_record_list_drop(s->record_list, record);
598 avahi_cache_stop_poof(i->cache, record, a);
601 avahi_record_unref(record);
605 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
609 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
610 avahi_log_warn("Packet too short (3)");
614 if (!avahi_key_is_pattern(record->key)) {
615 if (!from_local_iface)
616 reflect_probe(s, i, record);
617 incoming_probe(s, record, i);
620 avahi_record_unref(record);
624 if (!avahi_record_list_is_empty(s->record_list))
625 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
630 avahi_record_list_flush(s->record_list);
633 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
641 /* avahi_log_debug("response"); */
643 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
644 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
649 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
650 avahi_log_warn("Packet too short (4)");
654 if (!avahi_key_is_pattern(record->key)) {
656 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
657 /* avahi_free(txt); */
659 if (handle_conflict(s, i, record, cache_flush)) {
660 if (!from_local_iface)
661 reflect_response(s, i, record, cache_flush);
662 avahi_cache_update(i->cache, record, cache_flush, a);
663 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
667 avahi_record_unref(record);
670 /* If the incoming response contained a conflicting record, some
671 records have been scheduling for sending. We need to flush them
673 if (!avahi_record_list_is_empty(s->record_list))
674 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
677 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
678 unsigned n, idx = (unsigned) -1;
679 AvahiLegacyUnicastReflectSlot *slot;
683 if (!s->legacy_unicast_reflect_slots)
684 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
686 for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
687 idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
689 if (!s->legacy_unicast_reflect_slots[idx])
693 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
696 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
697 return NULL; /* OOM */
699 s->legacy_unicast_reflect_slots[idx] = slot;
700 slot->id = s->legacy_unicast_reflect_id++;
706 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
712 idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
714 assert(s->legacy_unicast_reflect_slots[idx] == slot);
716 avahi_time_event_free(slot->time_event);
719 s->legacy_unicast_reflect_slots[idx] = NULL;
722 static void free_slots(AvahiServer *s) {
726 if (!s->legacy_unicast_reflect_slots)
729 for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
730 if (s->legacy_unicast_reflect_slots[idx])
731 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
733 avahi_free(s->legacy_unicast_reflect_slots);
734 s->legacy_unicast_reflect_slots = NULL;
737 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
742 if (!s->legacy_unicast_reflect_slots)
745 idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
747 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
750 return s->legacy_unicast_reflect_slots[idx];
753 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
754 AvahiLegacyUnicastReflectSlot *slot = userdata;
758 assert(slot->time_event == e);
760 deallocate_slot(slot->server, slot);
763 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
764 AvahiLegacyUnicastReflectSlot *slot;
772 assert(i->protocol == a->proto);
774 if (!s->config.enable_reflector)
777 /* avahi_log_debug("legacy unicast reflector"); */
779 /* Reflecting legacy unicast queries is a little more complicated
780 than reflecting normal queries, since we must route the
781 responses back to the right client. Therefore we must store
782 some information for finding the right client contact data for
783 response packets. In contrast to normal queries legacy
784 unicast query and response packets are reflected untouched and
785 are not reassembled into larger packets */
787 if (!(slot = allocate_slot(s))) {
788 /* No slot available, we drop this legacy unicast query */
789 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
793 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
796 slot->interface = i->hardware->index;
798 avahi_elapse_time(&slot->elapse_time, 2000, 0);
799 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
801 /* Patch the packet with our new locally generatedt id */
802 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
804 for (j = s->monitor->interfaces; j; j = j->interface_next)
805 if (avahi_interface_is_relevant(j) &&
807 (s->config.reflect_ipv || j->protocol == i->protocol)) {
809 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
810 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
811 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
812 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
816 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
819 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
824 if (!s->config.enable_reflector)
827 avahi_address_from_sockaddr(sa, &a);
829 if (!avahi_address_is_local(s->monitor, &a))
832 if (sa->sa_family == AF_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 == ((const struct sockaddr_in*) sa)->sin_port;
843 if (sa->sa_family == AF_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 == ((const struct sockaddr_in6*) sa)->sin6_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 struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
880 int from_local_iface = 0;
888 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
889 !avahi_interface_is_relevant(i)) {
890 avahi_log_warn("Recieved packet from invalid interface.");
894 /* avahi_log_debug("new packet received on interface '%s.%i'.", i->hardware->name, i->protocol); */
896 port = avahi_port_from_sockaddr(sa);
897 avahi_address_from_sockaddr(sa, &a);
899 if (avahi_address_is_ipv4_in_ipv6(&a))
900 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
903 if (originates_from_local_legacy_unicast_socket(s, sa))
904 /* This originates from our local reflector, so let's ignore it */
907 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
908 if (s->config.enable_reflector)
909 from_local_iface = originates_from_local_iface(s, iface, &a, port);
911 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
912 avahi_log_warn("Recieved invalid packet.");
916 if (avahi_dns_packet_is_query(p)) {
917 int legacy_unicast = 0;
919 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
920 avahi_log_warn("Invalid query packet.");
924 if (port != AVAHI_MDNS_PORT) {
927 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
928 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
929 avahi_log_warn("Invalid legacy unicast query packet.");
937 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
939 handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
941 /* avahi_log_debug("Handled query"); */
943 if (port != AVAHI_MDNS_PORT) {
944 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
948 if (ttl != 255 && s->config.check_response_ttl) {
949 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
953 if (!is_mdns_mcast_address(dest) &&
954 !avahi_interface_address_on_link(i, &a)) {
955 avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
959 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
960 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
961 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
962 avahi_log_warn("Invalid response packet.");
966 handle_response_packet(s, p, i, &a, from_local_iface);
967 /* avahi_log_debug("Handled response"); */
971 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface) {
972 AvahiInterface *i, *j;
974 AvahiLegacyUnicastReflectSlot *slot;
981 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
982 !avahi_interface_is_relevant(i)) {
983 avahi_log_warn("Recieved packet from invalid interface.");
987 /* avahi_log_debug("new legacy unicast packet received on interface '%s.%i'.", i->hardware->name, i->protocol); */
989 avahi_address_from_sockaddr(sa, &a);
991 if (avahi_address_is_ipv4_in_ipv6(&a))
992 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
995 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
996 avahi_log_warn("Recieved invalid packet.");
1000 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1001 avahi_log_warn("Recieved legacy unicast response with unknown id");
1005 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1006 !avahi_interface_is_relevant(j))
1009 /* Patch the original ID into this response */
1010 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1012 /* Forward the response to the correct client */
1013 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1015 /* Undo changes to packet */
1016 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1019 static void cleanup_dead(AvahiServer *s) {
1022 avahi_cleanup_dead_entries(s);
1023 avahi_browser_cleanup(s);
1026 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1027 AvahiServer *s = userdata;
1032 struct sockaddr_in sa;
1033 struct sockaddr_in6 sa6;
1038 if (events & AVAHI_WATCH_IN) {
1040 if (fd == s->fd_ipv4) {
1041 dest.proto = AVAHI_PROTO_INET;
1042 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1043 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1044 avahi_dns_packet_free(p);
1046 } else if (fd == s->fd_ipv6) {
1047 dest.proto = AVAHI_PROTO_INET6;
1049 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1050 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1051 avahi_dns_packet_free(p);
1053 } else if (fd == s->fd_legacy_unicast_ipv4) {
1054 dest.proto = AVAHI_PROTO_INET;
1056 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1057 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface);
1058 avahi_dns_packet_free(p);
1060 } else if (fd == s->fd_legacy_unicast_ipv6) {
1061 dest.proto = AVAHI_PROTO_INET6;
1063 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1064 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface);
1065 avahi_dns_packet_free(p);
1074 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1077 if (s->state == state)
1083 s->callback(s, state, s->userdata);
1086 static void withdraw_host_rrs(AvahiServer *s) {
1089 if (s->hinfo_entry_group)
1090 avahi_s_entry_group_reset(s->hinfo_entry_group);
1092 if (s->browse_domain_entry_group)
1093 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1095 avahi_interface_monitor_update_rrs(s->monitor, 1);
1096 s->n_host_rr_pending = 0;
1099 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1102 assert(s->n_host_rr_pending > 0);
1104 if (--s->n_host_rr_pending == 0)
1105 server_set_state(s, AVAHI_SERVER_RUNNING);
1108 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
1112 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1113 s->state == AVAHI_SERVER_REGISTERING)
1114 s->n_host_rr_pending ++;
1116 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1117 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1118 withdraw_host_rrs(s);
1119 server_set_state(s, AVAHI_SERVER_COLLISION);
1121 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1122 s->state == AVAHI_SERVER_REGISTERING)
1123 avahi_server_decrease_host_rr_pending(s);
1126 static void register_hinfo(AvahiServer *s) {
1127 struct utsname utsname;
1132 if (!s->config.publish_hinfo)
1135 if (s->hinfo_entry_group)
1136 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1138 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1140 if (!s->hinfo_entry_group) {
1141 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1145 /* Fill in HINFO rr */
1146 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1148 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1149 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1151 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1152 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1156 avahi_record_unref(r);
1159 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1160 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1164 static void register_localhost(AvahiServer *s) {
1168 /* Add localhost entries */
1169 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1170 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1172 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1173 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1176 static void register_browse_domain(AvahiServer *s) {
1179 if (!s->config.publish_domain)
1182 if (s->browse_domain_entry_group)
1183 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1185 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1187 if (!s->browse_domain_entry_group) {
1188 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1192 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) {
1193 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1197 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1198 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1201 static void register_stuff(AvahiServer *s) {
1204 server_set_state(s, AVAHI_SERVER_REGISTERING);
1205 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1208 register_browse_domain(s);
1209 avahi_interface_monitor_update_rrs(s->monitor, 0);
1211 s->n_host_rr_pending --;
1213 if (s->n_host_rr_pending == 0)
1214 server_set_state(s, AVAHI_SERVER_RUNNING);
1217 static void update_fqdn(AvahiServer *s) {
1221 assert(s->host_name);
1222 assert(s->domain_name);
1224 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1227 avahi_free(s->host_name_fqdn);
1228 s->host_name_fqdn = n;
1231 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1235 if (host_name && !avahi_is_valid_host_name(host_name))
1236 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1238 withdraw_host_rrs(s);
1240 avahi_free(s->host_name);
1241 s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
1242 s->host_name[strcspn(s->host_name, ".")] = 0;
1249 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1251 assert(domain_name);
1253 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1254 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1256 withdraw_host_rrs(s);
1258 avahi_free(s->domain_name);
1259 s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
1266 static int valid_server_config(const AvahiServerConfig *sc) {
1268 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1269 return AVAHI_ERR_INVALID_HOST_NAME;
1271 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1272 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1277 static int setup_sockets(AvahiServer *s) {
1280 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1281 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1283 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1284 return AVAHI_ERR_NO_NETWORK;
1286 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1287 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1288 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1289 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1291 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1292 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1296 s->watch_legacy_unicast_ipv4 =
1297 s->watch_legacy_unicast_ipv6 = NULL;
1299 if (s->fd_ipv4 >= 0)
1300 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1301 if (s->fd_ipv6 >= 0)
1302 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1304 if (s->fd_legacy_unicast_ipv4 >= 0)
1305 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1306 if (s->fd_legacy_unicast_ipv6 >= 0)
1307 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1312 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1316 if (sc && (e = valid_server_config(sc)) < 0) {
1322 if (!(s = avahi_new(AvahiServer, 1))) {
1324 *error = AVAHI_ERR_NO_MEMORY;
1329 s->poll_api = poll_api;
1332 avahi_server_config_copy(&s->config, sc);
1334 avahi_server_config_init(&s->config);
1336 if ((e = setup_sockets(s)) < 0) {
1340 avahi_server_config_free(&s->config);
1346 s->n_host_rr_pending = 0;
1347 s->need_entry_cleanup = 0;
1348 s->need_group_cleanup = 0;
1349 s->need_browser_cleanup = 0;
1350 s->hinfo_entry_group = NULL;
1351 s->browse_domain_entry_group = NULL;
1352 s->error = AVAHI_OK;
1353 s->state = AVAHI_SERVER_INVALID;
1355 s->callback = callback;
1356 s->userdata = userdata;
1358 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1360 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1361 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1362 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1364 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1365 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1366 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1367 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1368 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1369 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1370 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1371 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1372 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1374 s->legacy_unicast_reflect_slots = NULL;
1375 s->legacy_unicast_reflect_id = 0;
1377 s->record_list = avahi_record_list_new();
1380 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1381 s->host_name[strcspn(s->host_name, ".")] = 0;
1382 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1383 s->host_name_fqdn = NULL;
1387 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1388 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1390 if (s->config.enable_wide_area) {
1391 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1392 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1394 s->wide_area_lookup_engine = NULL;
1396 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1398 s->monitor = avahi_interface_monitor_new(s);
1399 avahi_interface_monitor_sync(s->monitor);
1401 register_localhost(s);
1407 void avahi_server_free(AvahiServer* s) {
1410 /* Remove all browsers */
1412 while (s->dns_server_browsers)
1413 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1414 while (s->host_name_resolvers)
1415 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1416 while (s->address_resolvers)
1417 avahi_s_address_resolver_free(s->address_resolvers);
1418 while (s->domain_browsers)
1419 avahi_s_domain_browser_free(s->domain_browsers);
1420 while (s->service_type_browsers)
1421 avahi_s_service_type_browser_free(s->service_type_browsers);
1422 while (s->service_browsers)
1423 avahi_s_service_browser_free(s->service_browsers);
1424 while (s->service_resolvers)
1425 avahi_s_service_resolver_free(s->service_resolvers);
1426 while (s->record_browsers)
1427 avahi_s_record_browser_destroy(s->record_browsers);
1429 /* Remove all locally rgeistered stuff */
1432 avahi_entry_free(s, s->entries);
1434 avahi_interface_monitor_free(s->monitor);
1437 avahi_entry_group_free(s, s->groups);
1441 avahi_hashmap_free(s->entries_by_key);
1442 avahi_record_list_free(s->record_list);
1443 avahi_hashmap_free(s->record_browser_hashmap);
1445 if (s->wide_area_lookup_engine)
1446 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1447 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1449 avahi_time_event_queue_free(s->time_event_queue);
1454 s->poll_api->watch_free(s->watch_ipv4);
1456 s->poll_api->watch_free(s->watch_ipv6);
1458 if (s->watch_legacy_unicast_ipv4)
1459 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1460 if (s->watch_legacy_unicast_ipv6)
1461 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1465 if (s->fd_ipv4 >= 0)
1467 if (s->fd_ipv6 >= 0)
1470 if (s->fd_legacy_unicast_ipv4 >= 0)
1471 close(s->fd_legacy_unicast_ipv4);
1472 if (s->fd_legacy_unicast_ipv6 >= 0)
1473 close(s->fd_legacy_unicast_ipv6);
1475 /* Free other stuff */
1477 avahi_free(s->host_name);
1478 avahi_free(s->domain_name);
1479 avahi_free(s->host_name_fqdn);
1481 avahi_server_config_free(&s->config);
1486 const char* avahi_server_get_domain_name(AvahiServer *s) {
1489 return s->domain_name;
1492 const char* avahi_server_get_host_name(AvahiServer *s) {
1495 return s->host_name;
1498 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1501 return s->host_name_fqdn;
1504 void* avahi_server_get_data(AvahiServer *s) {
1510 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1513 s->userdata = userdata;
1516 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1522 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1525 memset(c, 0, sizeof(AvahiServerConfig));
1528 c->host_name = NULL;
1529 c->domain_name = NULL;
1530 c->check_response_ttl = 0;
1531 c->publish_hinfo = 1;
1532 c->publish_addresses = 1;
1533 c->publish_workstation = 1;
1534 c->publish_domain = 1;
1535 c->use_iff_running = 0;
1536 c->enable_reflector = 0;
1538 c->add_service_cookie = 1;
1539 c->enable_wide_area = 0;
1540 c->n_wide_area_servers = 0;
1541 c->disallow_other_stacks = 0;
1546 void avahi_server_config_free(AvahiServerConfig *c) {
1549 avahi_free(c->host_name);
1550 avahi_free(c->domain_name);
1553 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1554 char *d = NULL, *h = NULL;
1559 if (!(h = avahi_strdup(c->host_name)))
1563 if (!(d = avahi_strdup(c->domain_name))) {
1570 ret->domain_name = d;
1575 int avahi_server_errno(AvahiServer *s) {
1581 /* Just for internal use */
1582 int avahi_server_set_errno(AvahiServer *s, int error) {
1585 return s->error = error;
1588 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1591 return s->local_service_cookie;
1594 static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1600 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
1602 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1603 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1604 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
1611 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) {
1612 AvahiKey *key = NULL;
1615 char n[AVAHI_DOMAIN_NAME_MAX];
1622 if (!AVAHI_IF_VALID(interface))
1623 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_INTERFACE);
1625 if (!AVAHI_IF_VALID(protocol))
1626 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PROTOCOL);
1628 if (!avahi_is_valid_service_name(name))
1629 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1631 if (!avahi_is_valid_service_type_strict(type))
1632 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1634 if (domain && !avahi_is_valid_domain_name(domain))
1635 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1637 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1638 return avahi_server_set_errno(s, ret);
1640 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1641 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1643 e = find_entry(s, interface, protocol, key);
1644 avahi_key_unref(key);
1647 *ret_group = e->group;
1651 return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
1654 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
1655 AvahiKey *key = NULL;
1661 if (!s->host_name_fqdn)
1664 if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1667 e = find_entry(s, interface, protocol, key);
1668 avahi_key_unref(key);
1673 return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
1676 int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
1682 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
1684 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1685 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1686 (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
1687 avahi_record_equal_no_ttl(record, e->record))
1693 /** Set the wide area DNS servers */
1694 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1697 if (!s->wide_area_lookup_engine)
1698 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1700 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);