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/socket.h>
27 #include <arpa/inet.h>
29 #include <sys/utsname.h>
36 #include <avahi-common/domain.h>
37 #include <avahi-common/timeval.h>
38 #include <avahi-common/malloc.h>
39 #include <avahi-common/error.h>
47 #include "dns-srv-rr.h"
49 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) {
58 assert(type != AVAHI_DNS_TYPE_ANY);
60 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
63 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
64 if (!e->dead && avahi_entry_is_registered(s, e, i))
65 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
70 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) {
76 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
77 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
78 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
79 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
80 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
81 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
82 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
87 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
92 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
95 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
103 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
104 /* avahi_free(txt); */
106 if (avahi_key_is_pattern(k)) {
108 /* Handle ANY query */
110 for (e = s->entries; e; e = e->entries_next)
111 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
112 avahi_server_prepare_response(s, i, e, unicast_response, 0);
116 /* Handle all other queries */
118 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
119 if (!e->dead && avahi_entry_is_registered(s, e, i))
120 avahi_server_prepare_response(s, i, e, unicast_response, 0);
124 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
131 for (k = e->group->entries; k; k = k->by_group_next) {
133 avahi_goodbye_entry(s, k, 0, 1);
138 e->group->n_probing = 0;
140 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
142 avahi_goodbye_entry(s, e, 0, 1);
146 s->need_entry_cleanup = 1;
149 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
155 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
157 withdraw_entry(s, e);
160 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
163 int ours = 0, won = 0, lost = 0;
169 t = avahi_record_to_string(record);
171 /* avahi_log_debug("incoming_probe()"); */
173 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
180 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
185 if (avahi_entry_is_probing(s, e, i)) {
197 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
199 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
200 withdraw_rrset(s, record->key);
202 /* avahi_log_debug("Not conflicting probe"); */
208 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique, const AvahiAddress *a) {
209 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
210 AvahiEntry *e, *n, *conflicting_entry = NULL;
217 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
219 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
225 /* Check if the incoming is a goodbye record */
226 if (avahi_record_is_goodbye(record)) {
228 if (avahi_record_equal_no_ttl(e->record, record)) {
232 t = avahi_record_to_string(record);
233 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
234 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
241 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
245 if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
248 /* Either our entry or the other is intended to be unique, so let's check */
250 if (avahi_record_equal_no_ttl(e->record, record)) {
251 ours = 1; /* We have an identical record, so this is no conflict */
253 /* Check wheter there is a TTL conflict */
254 if (record->ttl <= e->record->ttl/2 &&
255 avahi_entry_is_registered(s, e, i)) {
258 t = avahi_record_to_string(record);
260 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
261 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
267 /* There's no need to check the other entries of this RRset */
272 if (avahi_entry_is_registered(s, e, i)) {
274 /* A conflict => we have to return to probe mode */
276 conflicting_entry = e;
278 } else if (avahi_entry_is_probing(s, e, i)) {
280 /* We are currently registering a matching record, but
281 * someone else already claimed it, so let's
284 withdraw_immediately = 1;
289 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
291 if (!ours && conflict) {
296 t = avahi_record_to_string(record);
298 if (withdraw_immediately) {
299 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
300 withdraw_rrset(s, record->key);
302 assert(conflicting_entry);
303 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
304 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
306 /* Local unique records are returned to probing
307 * state. Local shared records are reannounced. */
316 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
317 int *unicast_response = userdata;
321 assert(unicast_response);
323 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
326 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
330 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
333 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
337 assert(!legacy_unicast || (a && port > 0 && p));
339 if (legacy_unicast) {
340 AvahiDnsPacket *reply;
343 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
346 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
348 append_aux_records_to_list(s, i, r, 0);
350 if (avahi_dns_packet_append_record(reply, r, 0, 10))
351 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
353 char *t = avahi_record_to_string(r);
354 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
358 avahi_record_unref(r);
361 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
362 avahi_interface_send_packet_unicast(i, reply, a, port);
364 avahi_dns_packet_free(reply);
367 int unicast_response, flush_cache, auxiliary;
368 AvahiDnsPacket *reply = NULL;
371 /* In case the query packet was truncated never respond
372 immediately, because known answer suppression records might be
373 contained in later packets */
374 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
376 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
378 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
380 append_aux_records_to_list(s, i, r, unicast_response);
382 /* Due to some reasons the record has not been scheduled.
383 * The client requested an unicast response in that
384 * case. Therefore we prepare such a response */
391 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
395 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
397 /* Appending this record succeeded, so incremeant
398 * the specific header field, and return to the caller */
400 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
405 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
408 /* The record is too large for one packet, so create a larger packet */
410 avahi_dns_packet_free(reply);
411 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
412 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
413 size = AVAHI_DNS_PACKET_MAX_SIZE;
415 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
418 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
420 avahi_dns_packet_free(reply);
421 t = avahi_record_to_string(r);
422 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
426 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
429 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
430 avahi_interface_send_packet_unicast(i, reply, a, port);
431 avahi_dns_packet_free(reply);
436 avahi_record_unref(r);
440 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
441 avahi_interface_send_packet_unicast(i, reply, a, port);
442 avahi_dns_packet_free(reply);
446 avahi_record_list_flush(s->record_list);
450 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
457 if (!s->config.enable_reflector)
460 for (j = s->monitor->interfaces; j; j = j->interface_next)
461 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
462 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
465 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
466 AvahiServer *s = userdata;
473 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
477 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
484 if (!s->config.enable_reflector)
487 for (j = s->monitor->interfaces; j; j = j->interface_next)
488 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
489 /* Post the query to other networks */
490 avahi_interface_post_query(j, k, 1);
492 /* Reply from caches of other network. This is needed to
493 * "work around" known answer suppression. */
495 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
499 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
506 if (!s->config.enable_reflector)
509 for (j = s->monitor->interfaces; j; j = j->interface_next)
510 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
511 avahi_interface_post_probe(j, r, 1);
514 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
523 /* avahi_log_debug("query"); */
525 assert(avahi_record_list_is_empty(s->record_list));
527 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
529 /* Handle the questions */
530 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
532 int unicast_response = 0;
534 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
535 avahi_log_warn("Packet too short (1)");
539 if (!legacy_unicast && !from_local_iface) {
540 reflect_query(s, i, key);
541 avahi_cache_start_poof(i->cache, key, a);
544 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
545 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
546 /* Allow our own queries to be suppressed by incoming
547 * queries only when they do not include known answers */
548 avahi_query_scheduler_incoming(i->query_scheduler, key);
550 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
551 avahi_key_unref(key);
554 if (!legacy_unicast) {
556 /* Known Answer Suppression */
557 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
561 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
562 avahi_log_warn("Packet too short (2)");
566 if (handle_conflict(s, i, record, unique, a)) {
567 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
568 avahi_record_list_drop(s->record_list, record);
569 avahi_cache_stop_poof(i->cache, record, a);
572 avahi_record_unref(record);
576 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
580 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
581 avahi_log_warn("Packet too short (3)");
585 if (!avahi_key_is_pattern(record->key)) {
586 if (!from_local_iface)
587 reflect_probe(s, i, record);
588 incoming_probe(s, record, i);
591 avahi_record_unref(record);
595 if (!avahi_record_list_is_empty(s->record_list))
596 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
601 avahi_record_list_flush(s->record_list);
604 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
612 /* avahi_log_debug("response"); */
614 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
615 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
620 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
621 avahi_log_warn("Packet too short (4)");
625 if (!avahi_key_is_pattern(record->key)) {
627 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
628 /* avahi_free(txt); */
630 if (handle_conflict(s, i, record, cache_flush, a)) {
631 if (!from_local_iface)
632 reflect_response(s, i, record, cache_flush);
633 avahi_cache_update(i->cache, record, cache_flush, a);
634 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
638 avahi_record_unref(record);
641 /* If the incoming response contained a conflicting record, some
642 records have been scheduling for sending. We need to flush them
644 if (!avahi_record_list_is_empty(s->record_list))
645 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
648 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
649 unsigned n, idx = (unsigned) -1;
650 AvahiLegacyUnicastReflectSlot *slot;
654 if (!s->legacy_unicast_reflect_slots)
655 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
657 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
658 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
660 if (!s->legacy_unicast_reflect_slots[idx])
664 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
667 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
668 return NULL; /* OOM */
670 s->legacy_unicast_reflect_slots[idx] = slot;
671 slot->id = s->legacy_unicast_reflect_id++;
677 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
683 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
685 assert(s->legacy_unicast_reflect_slots[idx] == slot);
687 avahi_time_event_free(slot->time_event);
690 s->legacy_unicast_reflect_slots[idx] = NULL;
693 static void free_slots(AvahiServer *s) {
697 if (!s->legacy_unicast_reflect_slots)
700 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
701 if (s->legacy_unicast_reflect_slots[idx])
702 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
704 avahi_free(s->legacy_unicast_reflect_slots);
705 s->legacy_unicast_reflect_slots = NULL;
708 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
713 if (!s->legacy_unicast_reflect_slots)
716 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
718 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
721 return s->legacy_unicast_reflect_slots[idx];
724 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
725 AvahiLegacyUnicastReflectSlot *slot = userdata;
729 assert(slot->time_event == e);
731 deallocate_slot(slot->server, slot);
734 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
735 AvahiLegacyUnicastReflectSlot *slot;
743 assert(i->protocol == a->proto);
745 if (!s->config.enable_reflector)
748 /* avahi_log_debug("legacy unicast reflector"); */
750 /* Reflecting legacy unicast queries is a little more complicated
751 than reflecting normal queries, since we must route the
752 responses back to the right client. Therefore we must store
753 some information for finding the right client contact data for
754 response packets. In contrast to normal queries legacy
755 unicast query and response packets are reflected untouched and
756 are not reassembled into larger packets */
758 if (!(slot = allocate_slot(s))) {
759 /* No slot available, we drop this legacy unicast query */
760 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
764 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
767 slot->interface = i->hardware->index;
769 avahi_elapse_time(&slot->elapse_time, 2000, 0);
770 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
772 /* Patch the packet with our new locally generatedt id */
773 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
775 for (j = s->monitor->interfaces; j; j = j->interface_next)
776 if (avahi_interface_is_relevant(j) &&
778 (s->config.reflect_ipv || j->protocol == i->protocol)) {
780 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
781 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
782 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
783 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
787 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
790 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
795 if (!s->config.enable_reflector)
798 avahi_address_from_sockaddr(sa, &a);
800 if (!avahi_address_is_local(s->monitor, &a))
803 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
804 struct sockaddr_in lsa;
805 socklen_t l = sizeof(lsa);
807 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
808 avahi_log_warn("getsockname(): %s", strerror(errno));
810 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
814 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
815 struct sockaddr_in6 lsa;
816 socklen_t l = sizeof(lsa);
818 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
819 avahi_log_warn("getsockname(): %s", strerror(errno));
821 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
827 static int is_mdns_mcast_address(const AvahiAddress *a) {
831 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
832 return avahi_address_cmp(a, &b) == 0;
835 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
837 assert(iface != AVAHI_IF_UNSPEC);
840 /* If it isn't the MDNS port it can't be generated by us */
841 if (port != AVAHI_MDNS_PORT)
844 return avahi_interface_has_address(s->monitor, iface, a);
847 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
851 int from_local_iface = 0;
859 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
860 !avahi_interface_is_relevant(i)) {
861 avahi_log_warn("Recieved packet from invalid interface.");
865 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
867 port = avahi_port_from_sockaddr(sa);
868 avahi_address_from_sockaddr(sa, &a);
870 if (avahi_address_is_ipv4_in_ipv6(&a))
871 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
874 if (originates_from_local_legacy_unicast_socket(s, sa))
875 /* This originates from our local reflector, so let's ignore it */
878 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
879 if (s->config.enable_reflector)
880 from_local_iface = originates_from_local_iface(s, iface, &a, port);
882 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
883 avahi_log_warn("Recieved invalid packet.");
887 if (avahi_dns_packet_is_query(p)) {
888 int legacy_unicast = 0;
890 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
891 avahi_log_warn("Invalid query packet.");
895 if (port != AVAHI_MDNS_PORT) {
898 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
899 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
900 avahi_log_warn("Invalid legacy unicast query packet.");
908 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
910 handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
912 /* avahi_log_debug("Handled query"); */
914 if (port != AVAHI_MDNS_PORT) {
915 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
919 if (ttl != 255 && s->config.check_response_ttl) {
920 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
924 if (!is_mdns_mcast_address(dest) &&
925 !avahi_interface_address_on_link(i, &a)) {
926 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
930 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
931 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
932 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
933 avahi_log_warn("Invalid response packet.");
937 handle_response_packet(s, p, i, &a, from_local_iface);
938 /* avahi_log_debug("Handled response"); */
942 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface) {
943 AvahiInterface *i, *j;
945 AvahiLegacyUnicastReflectSlot *slot;
952 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
953 !avahi_interface_is_relevant(i)) {
954 avahi_log_warn("Recieved packet from invalid interface.");
958 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
960 avahi_address_from_sockaddr(sa, &a);
962 if (avahi_address_is_ipv4_in_ipv6(&a))
963 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
966 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
967 avahi_log_warn("Recieved invalid packet.");
971 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
972 avahi_log_warn("Recieved legacy unicast response with unknown id");
976 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
977 !avahi_interface_is_relevant(j))
980 /* Patch the original ID into this response */
981 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
983 /* Forward the response to the correct client */
984 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
986 /* Undo changes to packet */
987 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
990 static void cleanup_dead(AvahiServer *s) {
993 avahi_cleanup_dead_entries(s);
994 avahi_browser_cleanup(s);
997 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
998 AvahiServer *s = userdata;
1003 struct sockaddr_in sa;
1004 struct sockaddr_in6 sa6;
1009 if (events & AVAHI_WATCH_IN) {
1011 if (fd == s->fd_ipv4) {
1012 dest.proto = AVAHI_PROTO_INET;
1013 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1014 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1015 avahi_dns_packet_free(p);
1017 } else if (fd == s->fd_ipv6) {
1018 dest.proto = AVAHI_PROTO_INET6;
1020 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1021 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1022 avahi_dns_packet_free(p);
1024 } else if (fd == s->fd_legacy_unicast_ipv4) {
1025 dest.proto = AVAHI_PROTO_INET;
1027 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1028 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface);
1029 avahi_dns_packet_free(p);
1031 } else if (fd == s->fd_legacy_unicast_ipv6) {
1032 dest.proto = AVAHI_PROTO_INET6;
1034 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1035 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface);
1036 avahi_dns_packet_free(p);
1045 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1048 if (s->state == state)
1054 s->callback(s, state, s->userdata);
1057 static void withdraw_host_rrs(AvahiServer *s) {
1060 if (s->hinfo_entry_group)
1061 avahi_s_entry_group_reset(s->hinfo_entry_group);
1063 if (s->browse_domain_entry_group)
1064 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1066 avahi_interface_monitor_update_rrs(s->monitor, 1);
1067 s->n_host_rr_pending = 0;
1070 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1073 assert(s->n_host_rr_pending > 0);
1075 if (--s->n_host_rr_pending == 0)
1076 server_set_state(s, AVAHI_SERVER_RUNNING);
1079 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1083 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1084 s->state == AVAHI_SERVER_REGISTERING)
1085 s->n_host_rr_pending ++;
1087 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1088 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1089 withdraw_host_rrs(s);
1090 server_set_state(s, AVAHI_SERVER_COLLISION);
1092 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1093 s->state == AVAHI_SERVER_REGISTERING)
1094 avahi_server_decrease_host_rr_pending(s);
1097 static void register_hinfo(AvahiServer *s) {
1098 struct utsname utsname;
1103 if (!s->config.publish_hinfo)
1106 if (s->hinfo_entry_group)
1107 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1109 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1111 if (!s->hinfo_entry_group) {
1112 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1116 /* Fill in HINFO rr */
1117 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1119 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1120 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1122 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1123 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1127 avahi_record_unref(r);
1130 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1131 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1135 static void register_localhost(AvahiServer *s) {
1139 /* Add localhost entries */
1140 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1141 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1143 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1144 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1147 static void register_browse_domain(AvahiServer *s) {
1150 if (!s->config.publish_domain)
1153 if (s->browse_domain_entry_group)
1154 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1156 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1158 if (!s->browse_domain_entry_group) {
1159 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1163 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) {
1164 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1168 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1169 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1172 static void register_stuff(AvahiServer *s) {
1175 server_set_state(s, AVAHI_SERVER_REGISTERING);
1176 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1179 register_browse_domain(s);
1180 avahi_interface_monitor_update_rrs(s->monitor, 0);
1182 s->n_host_rr_pending --;
1184 if (s->n_host_rr_pending == 0)
1185 server_set_state(s, AVAHI_SERVER_RUNNING);
1188 static void update_fqdn(AvahiServer *s) {
1192 assert(s->host_name);
1193 assert(s->domain_name);
1195 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1198 avahi_free(s->host_name_fqdn);
1199 s->host_name_fqdn = n;
1202 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1206 if (host_name && !avahi_is_valid_host_name(host_name))
1207 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1209 withdraw_host_rrs(s);
1211 avahi_free(s->host_name);
1212 s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
1213 s->host_name[strcspn(s->host_name, ".")] = 0;
1220 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1222 assert(domain_name);
1224 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1225 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1227 withdraw_host_rrs(s);
1229 avahi_free(s->domain_name);
1230 s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
1237 static int valid_server_config(const AvahiServerConfig *sc) {
1239 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1240 return AVAHI_ERR_INVALID_HOST_NAME;
1242 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1243 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1248 static int setup_sockets(AvahiServer *s) {
1251 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1252 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1254 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1255 return AVAHI_ERR_NO_NETWORK;
1257 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1258 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1259 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1260 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1262 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1263 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1267 s->watch_legacy_unicast_ipv4 =
1268 s->watch_legacy_unicast_ipv6 = NULL;
1270 if (s->fd_ipv4 >= 0)
1271 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1272 if (s->fd_ipv6 >= 0)
1273 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1275 if (s->fd_legacy_unicast_ipv4 >= 0)
1276 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1277 if (s->fd_legacy_unicast_ipv6 >= 0)
1278 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1283 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1287 if (sc && (e = valid_server_config(sc)) < 0) {
1293 if (!(s = avahi_new(AvahiServer, 1))) {
1295 *error = AVAHI_ERR_NO_MEMORY;
1300 s->poll_api = poll_api;
1303 avahi_server_config_copy(&s->config, sc);
1305 avahi_server_config_init(&s->config);
1307 if ((e = setup_sockets(s)) < 0) {
1311 avahi_server_config_free(&s->config);
1318 s->n_host_rr_pending = 0;
1319 s->need_entry_cleanup = 0;
1320 s->need_group_cleanup = 0;
1321 s->need_browser_cleanup = 0;
1323 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1325 s->callback = callback;
1326 s->userdata = userdata;
1328 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1329 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1330 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1332 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1333 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1334 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1335 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1336 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1337 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1338 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1339 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1340 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1342 s->legacy_unicast_reflect_slots = NULL;
1343 s->legacy_unicast_reflect_id = 0;
1345 if (s->config.enable_wide_area) {
1346 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1347 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1349 s->wide_area_lookup_engine = NULL;
1351 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1354 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1355 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1358 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1359 s->host_name[strcspn(s->host_name, ".")] = 0;
1360 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1361 s->host_name_fqdn = NULL;
1364 s->record_list = avahi_record_list_new();
1366 s->state = AVAHI_SERVER_INVALID;
1368 s->monitor = avahi_interface_monitor_new(s);
1369 avahi_interface_monitor_sync(s->monitor);
1371 register_localhost(s);
1373 s->hinfo_entry_group = NULL;
1374 s->browse_domain_entry_group = NULL;
1377 s->error = AVAHI_OK;
1382 void avahi_server_free(AvahiServer* s) {
1385 /* Remove all browsers */
1387 while (s->dns_server_browsers)
1388 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1389 while (s->host_name_resolvers)
1390 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1391 while (s->address_resolvers)
1392 avahi_s_address_resolver_free(s->address_resolvers);
1393 while (s->domain_browsers)
1394 avahi_s_domain_browser_free(s->domain_browsers);
1395 while (s->service_type_browsers)
1396 avahi_s_service_type_browser_free(s->service_type_browsers);
1397 while (s->service_browsers)
1398 avahi_s_service_browser_free(s->service_browsers);
1399 while (s->service_resolvers)
1400 avahi_s_service_resolver_free(s->service_resolvers);
1401 while (s->record_browsers)
1402 avahi_s_record_browser_destroy(s->record_browsers);
1404 /* Remove all locally rgeistered stuff */
1407 avahi_entry_free(s, s->entries);
1409 avahi_interface_monitor_free(s->monitor);
1412 avahi_entry_group_free(s, s->groups);
1416 avahi_hashmap_free(s->entries_by_key);
1417 avahi_record_list_free(s->record_list);
1418 avahi_hashmap_free(s->record_browser_hashmap);
1420 if (s->wide_area_lookup_engine)
1421 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1422 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1424 avahi_time_event_queue_free(s->time_event_queue);
1429 s->poll_api->watch_free(s->watch_ipv4);
1431 s->poll_api->watch_free(s->watch_ipv6);
1433 if (s->watch_legacy_unicast_ipv4)
1434 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1435 if (s->watch_legacy_unicast_ipv6)
1436 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1440 if (s->fd_ipv4 >= 0)
1442 if (s->fd_ipv6 >= 0)
1445 if (s->fd_legacy_unicast_ipv4 >= 0)
1446 close(s->fd_legacy_unicast_ipv4);
1447 if (s->fd_legacy_unicast_ipv6 >= 0)
1448 close(s->fd_legacy_unicast_ipv6);
1450 /* Free other stuff */
1452 avahi_free(s->host_name);
1453 avahi_free(s->domain_name);
1454 avahi_free(s->host_name_fqdn);
1456 avahi_server_config_free(&s->config);
1461 const char* avahi_server_get_domain_name(AvahiServer *s) {
1464 return s->domain_name;
1467 const char* avahi_server_get_host_name(AvahiServer *s) {
1470 return s->host_name;
1473 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1476 return s->host_name_fqdn;
1479 void* avahi_server_get_data(AvahiServer *s) {
1485 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1488 s->userdata = userdata;
1491 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1497 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1500 memset(c, 0, sizeof(AvahiServerConfig));
1503 c->host_name = NULL;
1504 c->domain_name = NULL;
1505 c->check_response_ttl = 0;
1506 c->publish_hinfo = 1;
1507 c->publish_addresses = 1;
1508 c->publish_workstation = 1;
1509 c->publish_domain = 1;
1510 c->use_iff_running = 0;
1511 c->enable_reflector = 0;
1513 c->add_service_cookie = 1;
1514 c->enable_wide_area = 0;
1515 c->n_wide_area_servers = 0;
1516 c->disallow_other_stacks = 0;
1521 void avahi_server_config_free(AvahiServerConfig *c) {
1524 avahi_free(c->host_name);
1525 avahi_free(c->domain_name);
1528 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1529 char *d = NULL, *h = NULL;
1534 if (!(h = avahi_strdup(c->host_name)))
1538 if (!(d = avahi_strdup(c->domain_name))) {
1545 ret->domain_name = d;
1550 int avahi_server_errno(AvahiServer *s) {
1556 /* Just for internal use */
1557 int avahi_server_set_errno(AvahiServer *s, int error) {
1560 return s->error = error;
1563 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1566 return s->local_service_cookie;
1569 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char*domain) {
1570 AvahiKey *key = NULL;
1580 if (!avahi_is_valid_service_name(name))
1581 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1583 if (!avahi_is_valid_service_type_strict(type))
1584 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1586 if (domain && !avahi_is_valid_domain_name(domain))
1587 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1589 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1590 return avahi_server_set_errno(s, ret);
1592 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1593 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1597 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) {
1599 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1600 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1601 !(e->flags & AVAHI_PUBLISH_IS_PROXY)) {
1607 avahi_key_unref(key);
1612 /** Set the wide area DNS servers */
1613 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1616 if (!s->wide_area_lookup_engine)
1617 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1619 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);