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>
48 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) {
57 assert(type != AVAHI_DNS_TYPE_ANY);
59 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
62 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
63 if (!e->dead && avahi_entry_is_registered(s, e, i))
64 callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
69 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) {
75 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
76 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
77 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
78 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
79 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
80 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
81 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
86 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
91 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
94 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
102 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
103 /* avahi_free(txt); */
105 if (avahi_key_is_pattern(k)) {
107 /* Handle ANY query */
109 for (e = s->entries; e; e = e->entries_next)
110 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
111 avahi_server_prepare_response(s, i, e, unicast_response, 0);
115 /* Handle all other queries */
117 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
118 if (!e->dead && avahi_entry_is_registered(s, e, i))
119 avahi_server_prepare_response(s, i, e, unicast_response, 0);
123 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
130 for (k = e->group->entries; k; k = k->by_group_next) {
132 avahi_goodbye_entry(s, k, 0, 1);
137 e->group->n_probing = 0;
139 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
141 avahi_goodbye_entry(s, e, 0, 1);
145 s->need_entry_cleanup = 1;
148 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
154 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
156 withdraw_entry(s, e);
159 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
162 int ours = 0, won = 0, lost = 0;
168 t = avahi_record_to_string(record);
170 /* avahi_log_debug("incoming_probe()"); */
172 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
179 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
184 if (avahi_entry_is_probing(s, e, i)) {
196 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
198 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
199 withdraw_rrset(s, record->key);
201 /* avahi_log_debug("Not conflicting probe"); */
207 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique, const AvahiAddress *a) {
208 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
209 AvahiEntry *e, *n, *conflicting_entry = NULL;
216 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
218 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
224 /* Check if the incoming is a goodbye record */
225 if (avahi_record_is_goodbye(record)) {
227 if (avahi_record_equal_no_ttl(e->record, record)) {
231 t = avahi_record_to_string(record);
232 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
233 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
240 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
244 if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
247 /* Either our entry or the other is intended to be unique, so let's check */
249 if (avahi_record_equal_no_ttl(e->record, record)) {
250 ours = 1; /* We have an identical record, so this is no conflict */
252 /* Check wheter there is a TTL conflict */
253 if (record->ttl <= e->record->ttl/2 &&
254 avahi_entry_is_registered(s, e, i)) {
257 t = avahi_record_to_string(record);
259 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
260 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
266 /* There's no need to check the other entries of this RRset */
271 if (avahi_entry_is_registered(s, e, i)) {
273 /* A conflict => we have to return to probe mode */
275 conflicting_entry = e;
277 } else if (avahi_entry_is_probing(s, e, i)) {
279 /* We are currently registering a matching record, but
280 * someone else already claimed it, so let's
283 withdraw_immediately = 1;
288 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
290 if (!ours && conflict) {
295 t = avahi_record_to_string(record);
297 if (withdraw_immediately) {
298 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
299 withdraw_rrset(s, record->key);
301 assert(conflicting_entry);
302 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
303 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
305 /* Local unique records are returned to probing
306 * state. Local shared records are reannounced. */
315 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
316 int *unicast_response = userdata;
320 assert(unicast_response);
322 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
325 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
329 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
332 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
336 assert(!legacy_unicast || (a && port > 0 && p));
338 if (legacy_unicast) {
339 AvahiDnsPacket *reply;
342 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
345 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
347 append_aux_records_to_list(s, i, r, 0);
349 if (avahi_dns_packet_append_record(reply, r, 0, 10))
350 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
352 char *t = avahi_record_to_string(r);
353 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
357 avahi_record_unref(r);
360 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
361 avahi_interface_send_packet_unicast(i, reply, a, port);
363 avahi_dns_packet_free(reply);
366 int unicast_response, flush_cache, auxiliary;
367 AvahiDnsPacket *reply = NULL;
370 /* In case the query packet was truncated never respond
371 immediately, because known answer suppression records might be
372 contained in later packets */
373 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
375 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
377 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
379 append_aux_records_to_list(s, i, r, unicast_response);
381 /* Due to some reasons the record has not been scheduled.
382 * The client requested an unicast response in that
383 * case. Therefore we prepare such a response */
390 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
394 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
396 /* Appending this record succeeded, so incremeant
397 * the specific header field, and return to the caller */
399 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
404 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
407 /* The record is too large for one packet, so create a larger packet */
409 avahi_dns_packet_free(reply);
410 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
411 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
412 size = AVAHI_DNS_PACKET_MAX_SIZE;
414 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
417 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
419 avahi_dns_packet_free(reply);
420 t = avahi_record_to_string(r);
421 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
425 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
428 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
429 avahi_interface_send_packet_unicast(i, reply, a, port);
430 avahi_dns_packet_free(reply);
435 avahi_record_unref(r);
439 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
440 avahi_interface_send_packet_unicast(i, reply, a, port);
441 avahi_dns_packet_free(reply);
445 avahi_record_list_flush(s->record_list);
449 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
456 if (!s->config.enable_reflector)
459 for (j = s->monitor->interfaces; j; j = j->interface_next)
460 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
461 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
464 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
465 AvahiServer *s = userdata;
472 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
476 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
483 if (!s->config.enable_reflector)
486 for (j = s->monitor->interfaces; j; j = j->interface_next)
487 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
488 /* Post the query to other networks */
489 avahi_interface_post_query(j, k, 1);
491 /* Reply from caches of other network. This is needed to
492 * "work around" known answer suppression. */
494 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
498 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
505 if (!s->config.enable_reflector)
508 for (j = s->monitor->interfaces; j; j = j->interface_next)
509 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
510 avahi_interface_post_probe(j, r, 1);
513 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
522 /* avahi_log_debug("query"); */
524 assert(avahi_record_list_is_empty(s->record_list));
526 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
528 /* Handle the questions */
529 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
531 int unicast_response = 0;
533 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
534 avahi_log_warn("Packet too short (1)");
538 if (!legacy_unicast && !from_local_iface) {
539 reflect_query(s, i, key);
540 avahi_cache_start_poof(i->cache, key, a);
543 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
544 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
545 /* Allow our own queries to be suppressed by incoming
546 * queries only when they do not include known answers */
547 avahi_query_scheduler_incoming(i->query_scheduler, key);
549 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
550 avahi_key_unref(key);
553 if (!legacy_unicast) {
555 /* Known Answer Suppression */
556 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
560 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
561 avahi_log_warn("Packet too short (2)");
565 if (handle_conflict(s, i, record, unique, a)) {
566 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
567 avahi_record_list_drop(s->record_list, record);
568 avahi_cache_stop_poof(i->cache, record, a);
571 avahi_record_unref(record);
575 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
579 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
580 avahi_log_warn("Packet too short (3)");
584 if (!avahi_key_is_pattern(record->key)) {
585 if (!from_local_iface)
586 reflect_probe(s, i, record);
587 incoming_probe(s, record, i);
590 avahi_record_unref(record);
594 if (!avahi_record_list_is_empty(s->record_list))
595 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
600 avahi_record_list_flush(s->record_list);
603 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
611 /* avahi_log_debug("response"); */
613 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
614 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
619 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
620 avahi_log_warn("Packet too short (4)");
624 if (!avahi_key_is_pattern(record->key)) {
626 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
627 /* avahi_free(txt); */
629 if (handle_conflict(s, i, record, cache_flush, a)) {
630 if (!from_local_iface)
631 reflect_response(s, i, record, cache_flush);
632 avahi_cache_update(i->cache, record, cache_flush, a);
633 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
637 avahi_record_unref(record);
640 /* If the incoming response contained a conflicting record, some
641 records have been scheduling for sending. We need to flush them
643 if (!avahi_record_list_is_empty(s->record_list))
644 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
647 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
648 unsigned n, idx = (unsigned) -1;
649 AvahiLegacyUnicastReflectSlot *slot;
653 if (!s->legacy_unicast_reflect_slots)
654 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
656 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
657 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
659 if (!s->legacy_unicast_reflect_slots[idx])
663 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
666 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
667 return NULL; /* OOM */
669 s->legacy_unicast_reflect_slots[idx] = slot;
670 slot->id = s->legacy_unicast_reflect_id++;
676 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
682 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
684 assert(s->legacy_unicast_reflect_slots[idx] == slot);
686 avahi_time_event_free(slot->time_event);
689 s->legacy_unicast_reflect_slots[idx] = NULL;
692 static void free_slots(AvahiServer *s) {
696 if (!s->legacy_unicast_reflect_slots)
699 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
700 if (s->legacy_unicast_reflect_slots[idx])
701 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
703 avahi_free(s->legacy_unicast_reflect_slots);
704 s->legacy_unicast_reflect_slots = NULL;
707 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
712 if (!s->legacy_unicast_reflect_slots)
715 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
717 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
720 return s->legacy_unicast_reflect_slots[idx];
723 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
724 AvahiLegacyUnicastReflectSlot *slot = userdata;
728 assert(slot->time_event == e);
730 deallocate_slot(slot->server, slot);
733 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
734 AvahiLegacyUnicastReflectSlot *slot;
742 assert(i->protocol == a->proto);
744 if (!s->config.enable_reflector)
747 /* avahi_log_debug("legacy unicast reflector"); */
749 /* Reflecting legacy unicast queries is a little more complicated
750 than reflecting normal queries, since we must route the
751 responses back to the right client. Therefore we must store
752 some information for finding the right client contact data for
753 response packets. In contrast to normal queries legacy
754 unicast query and response packets are reflected untouched and
755 are not reassembled into larger packets */
757 if (!(slot = allocate_slot(s))) {
758 /* No slot available, we drop this legacy unicast query */
759 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
763 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
766 slot->interface = i->hardware->index;
768 avahi_elapse_time(&slot->elapse_time, 2000, 0);
769 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
771 /* Patch the packet with our new locally generatedt id */
772 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
774 for (j = s->monitor->interfaces; j; j = j->interface_next)
775 if (avahi_interface_is_relevant(j) &&
777 (s->config.reflect_ipv || j->protocol == i->protocol)) {
779 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
780 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
781 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
782 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
786 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
789 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
794 if (!s->config.enable_reflector)
797 avahi_address_from_sockaddr(sa, &a);
799 if (!avahi_address_is_local(s->monitor, &a))
802 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
803 struct sockaddr_in lsa;
804 socklen_t l = sizeof(lsa);
806 if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
807 avahi_log_warn("getsockname(): %s", strerror(errno));
809 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
813 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
814 struct sockaddr_in6 lsa;
815 socklen_t l = sizeof(lsa);
817 if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
818 avahi_log_warn("getsockname(): %s", strerror(errno));
820 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
826 static int is_mdns_mcast_address(const AvahiAddress *a) {
830 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
831 return avahi_address_cmp(a, &b) == 0;
834 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
836 assert(iface != AVAHI_IF_UNSPEC);
839 /* If it isn't the MDNS port it can't be generated by us */
840 if (port != AVAHI_MDNS_PORT)
843 return avahi_interface_has_address(s->monitor, iface, a);
846 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
850 int from_local_iface = 0;
858 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
859 !avahi_interface_is_relevant(i)) {
860 avahi_log_warn("Recieved packet from invalid interface.");
864 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
866 port = avahi_port_from_sockaddr(sa);
867 avahi_address_from_sockaddr(sa, &a);
869 if (avahi_address_is_ipv4_in_ipv6(&a))
870 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
873 if (originates_from_local_legacy_unicast_socket(s, sa))
874 /* This originates from our local reflector, so let's ignore it */
877 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
878 if (s->config.enable_reflector)
879 from_local_iface = originates_from_local_iface(s, iface, &a, port);
881 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
882 avahi_log_warn("Recieved invalid packet.");
886 if (avahi_dns_packet_is_query(p)) {
887 int legacy_unicast = 0;
889 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
890 avahi_log_warn("Invalid query packet.");
894 if (port != AVAHI_MDNS_PORT) {
897 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
898 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
899 avahi_log_warn("Invalid legacy unicast query packet.");
907 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
909 handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
911 /* avahi_log_debug("Handled query"); */
913 if (port != AVAHI_MDNS_PORT) {
914 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
918 if (ttl != 255 && s->config.check_response_ttl) {
919 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
923 if (!is_mdns_mcast_address(dest) &&
924 !avahi_interface_address_on_link(i, &a)) {
925 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
929 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
930 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
931 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
932 avahi_log_warn("Invalid response packet.");
936 handle_response_packet(s, p, i, &a, from_local_iface);
937 /* avahi_log_debug("Handled response"); */
941 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface) {
942 AvahiInterface *i, *j;
944 AvahiLegacyUnicastReflectSlot *slot;
951 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
952 !avahi_interface_is_relevant(i)) {
953 avahi_log_warn("Recieved packet from invalid interface.");
957 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
959 avahi_address_from_sockaddr(sa, &a);
961 if (avahi_address_is_ipv4_in_ipv6(&a))
962 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
965 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
966 avahi_log_warn("Recieved invalid packet.");
970 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
971 avahi_log_warn("Recieved legacy unicast response with unknown id");
975 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
976 !avahi_interface_is_relevant(j))
979 /* Patch the original ID into this response */
980 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
982 /* Forward the response to the correct client */
983 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
985 /* Undo changes to packet */
986 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
989 static void cleanup_dead(AvahiServer *s) {
992 avahi_cleanup_dead_entries(s);
993 avahi_browser_cleanup(s);
996 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
997 AvahiServer *s = userdata;
1002 struct sockaddr_in sa;
1003 struct sockaddr_in6 sa6;
1008 if (events & AVAHI_WATCH_IN) {
1010 if (fd == s->fd_ipv4) {
1011 dest.proto = AVAHI_PROTO_INET;
1012 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1013 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1014 avahi_dns_packet_free(p);
1016 } else if (fd == s->fd_ipv6) {
1017 dest.proto = AVAHI_PROTO_INET6;
1019 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1020 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1021 avahi_dns_packet_free(p);
1023 } else if (fd == s->fd_legacy_unicast_ipv4) {
1024 dest.proto = AVAHI_PROTO_INET;
1026 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1027 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface);
1028 avahi_dns_packet_free(p);
1030 } else if (fd == s->fd_legacy_unicast_ipv6) {
1031 dest.proto = AVAHI_PROTO_INET6;
1033 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1034 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface);
1035 avahi_dns_packet_free(p);
1044 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1047 if (s->state == state)
1053 s->callback(s, state, s->userdata);
1056 static void withdraw_host_rrs(AvahiServer *s) {
1059 if (s->hinfo_entry_group)
1060 avahi_s_entry_group_reset(s->hinfo_entry_group);
1062 if (s->browse_domain_entry_group)
1063 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1065 avahi_interface_monitor_update_rrs(s->monitor, 1);
1066 s->n_host_rr_pending = 0;
1069 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1072 assert(s->n_host_rr_pending > 0);
1074 if (--s->n_host_rr_pending == 0)
1075 server_set_state(s, AVAHI_SERVER_RUNNING);
1078 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1081 s->n_host_rr_pending ++;
1084 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1088 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1089 s->state == AVAHI_SERVER_REGISTERING)
1090 avahi_server_increase_host_rr_pending(s);
1092 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1093 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1094 withdraw_host_rrs(s);
1095 server_set_state(s, AVAHI_SERVER_COLLISION);
1097 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1098 s->state == AVAHI_SERVER_REGISTERING)
1099 avahi_server_decrease_host_rr_pending(s);
1102 static void register_hinfo(AvahiServer *s) {
1103 struct utsname utsname;
1108 if (!s->config.publish_hinfo)
1111 if (s->hinfo_entry_group)
1112 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1114 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1116 if (!s->hinfo_entry_group) {
1117 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1121 /* Fill in HINFO rr */
1122 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1124 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1125 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1127 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
1128 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1132 avahi_record_unref(r);
1135 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1136 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1140 static void register_localhost(AvahiServer *s) {
1144 /* Add localhost entries */
1145 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1146 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
1148 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1149 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
1152 static void register_browse_domain(AvahiServer *s) {
1155 if (!s->config.publish_domain)
1158 if (s->browse_domain_entry_group)
1159 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1161 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1163 if (!s->browse_domain_entry_group) {
1164 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1168 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) {
1169 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1173 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1174 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1177 static void register_stuff(AvahiServer *s) {
1180 server_set_state(s, AVAHI_SERVER_REGISTERING);
1181 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1184 register_browse_domain(s);
1185 avahi_interface_monitor_update_rrs(s->monitor, 0);
1187 s->n_host_rr_pending --;
1189 if (s->n_host_rr_pending == 0)
1190 server_set_state(s, AVAHI_SERVER_RUNNING);
1193 static void update_fqdn(AvahiServer *s) {
1197 assert(s->host_name);
1198 assert(s->domain_name);
1200 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1203 avahi_free(s->host_name_fqdn);
1204 s->host_name_fqdn = n;
1207 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1211 if (host_name && !avahi_is_valid_host_name(host_name))
1212 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1214 withdraw_host_rrs(s);
1216 avahi_free(s->host_name);
1217 s->host_name = host_name ? avahi_normalize_name_strdup(host_name) : avahi_get_host_name_strdup();
1218 s->host_name[strcspn(s->host_name, ".")] = 0;
1225 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1227 assert(domain_name);
1229 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1230 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1232 withdraw_host_rrs(s);
1234 avahi_free(s->domain_name);
1235 s->domain_name = domain_name ? avahi_normalize_name_strdup(domain_name) : avahi_strdup("local");
1242 static int valid_server_config(const AvahiServerConfig *sc) {
1244 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1245 return AVAHI_ERR_INVALID_HOST_NAME;
1247 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1248 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1253 static int setup_sockets(AvahiServer *s) {
1256 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
1257 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
1259 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1260 return AVAHI_ERR_NO_NETWORK;
1262 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1263 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1264 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1265 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1267 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1268 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1272 s->watch_legacy_unicast_ipv4 =
1273 s->watch_legacy_unicast_ipv6 = NULL;
1275 if (s->fd_ipv4 >= 0)
1276 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1277 if (s->fd_ipv6 >= 0)
1278 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1280 if (s->fd_legacy_unicast_ipv4 >= 0)
1281 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1282 if (s->fd_legacy_unicast_ipv6 >= 0)
1283 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1288 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1292 if (sc && (e = valid_server_config(sc)) < 0) {
1298 if (!(s = avahi_new(AvahiServer, 1))) {
1300 *error = AVAHI_ERR_NO_MEMORY;
1305 s->poll_api = poll_api;
1308 avahi_server_config_copy(&s->config, sc);
1310 avahi_server_config_init(&s->config);
1312 if ((e = setup_sockets(s)) < 0) {
1316 avahi_server_config_free(&s->config);
1323 s->n_host_rr_pending = 0;
1324 s->need_entry_cleanup = 0;
1325 s->need_group_cleanup = 0;
1326 s->need_browser_cleanup = 0;
1328 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1330 s->callback = callback;
1331 s->userdata = userdata;
1333 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1334 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1335 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1337 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1338 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1339 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1340 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1341 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1342 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1343 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1344 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1345 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1347 s->legacy_unicast_reflect_slots = NULL;
1348 s->legacy_unicast_reflect_id = 0;
1350 if (s->config.enable_wide_area) {
1351 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1352 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1354 s->wide_area_lookup_engine = NULL;
1356 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1359 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1360 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1363 s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
1364 s->host_name[strcspn(s->host_name, ".")] = 0;
1365 s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
1366 s->host_name_fqdn = NULL;
1369 s->record_list = avahi_record_list_new();
1371 s->state = AVAHI_SERVER_INVALID;
1373 s->monitor = avahi_interface_monitor_new(s);
1374 avahi_interface_monitor_sync(s->monitor);
1376 register_localhost(s);
1378 s->hinfo_entry_group = NULL;
1379 s->browse_domain_entry_group = NULL;
1382 s->error = AVAHI_OK;
1387 void avahi_server_free(AvahiServer* s) {
1390 /* Remove all browsers */
1392 while (s->dns_server_browsers)
1393 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1394 while (s->host_name_resolvers)
1395 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1396 while (s->address_resolvers)
1397 avahi_s_address_resolver_free(s->address_resolvers);
1398 while (s->domain_browsers)
1399 avahi_s_domain_browser_free(s->domain_browsers);
1400 while (s->service_type_browsers)
1401 avahi_s_service_type_browser_free(s->service_type_browsers);
1402 while (s->service_browsers)
1403 avahi_s_service_browser_free(s->service_browsers);
1404 while (s->service_resolvers)
1405 avahi_s_service_resolver_free(s->service_resolvers);
1406 while (s->record_browsers)
1407 avahi_s_record_browser_destroy(s->record_browsers);
1409 /* Remove all locally rgeistered stuff */
1412 avahi_entry_free(s, s->entries);
1414 avahi_interface_monitor_free(s->monitor);
1417 avahi_entry_group_free(s, s->groups);
1421 avahi_hashmap_free(s->entries_by_key);
1422 avahi_record_list_free(s->record_list);
1423 avahi_hashmap_free(s->record_browser_hashmap);
1425 if (s->wide_area_lookup_engine)
1426 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1427 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1429 avahi_time_event_queue_free(s->time_event_queue);
1434 s->poll_api->watch_free(s->watch_ipv4);
1436 s->poll_api->watch_free(s->watch_ipv6);
1438 if (s->watch_legacy_unicast_ipv4)
1439 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1440 if (s->watch_legacy_unicast_ipv6)
1441 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1445 if (s->fd_ipv4 >= 0)
1447 if (s->fd_ipv6 >= 0)
1450 if (s->fd_legacy_unicast_ipv4 >= 0)
1451 close(s->fd_legacy_unicast_ipv4);
1452 if (s->fd_legacy_unicast_ipv6 >= 0)
1453 close(s->fd_legacy_unicast_ipv6);
1455 /* Free other stuff */
1457 avahi_free(s->host_name);
1458 avahi_free(s->domain_name);
1459 avahi_free(s->host_name_fqdn);
1461 avahi_server_config_free(&s->config);
1466 const char* avahi_server_get_domain_name(AvahiServer *s) {
1469 return s->domain_name;
1472 const char* avahi_server_get_host_name(AvahiServer *s) {
1475 return s->host_name;
1478 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
1481 return s->host_name_fqdn;
1484 void* avahi_server_get_data(AvahiServer *s) {
1490 void avahi_server_set_data(AvahiServer *s, void* userdata) {
1493 s->userdata = userdata;
1496 AvahiServerState avahi_server_get_state(AvahiServer *s) {
1502 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
1505 memset(c, 0, sizeof(AvahiServerConfig));
1508 c->host_name = NULL;
1509 c->domain_name = NULL;
1510 c->check_response_ttl = 0;
1511 c->publish_hinfo = 1;
1512 c->publish_addresses = 1;
1513 c->publish_workstation = 1;
1514 c->publish_domain = 1;
1515 c->use_iff_running = 0;
1516 c->enable_reflector = 0;
1518 c->add_service_cookie = 1;
1519 c->enable_wide_area = 0;
1520 c->n_wide_area_servers = 0;
1521 c->disallow_other_stacks = 0;
1526 void avahi_server_config_free(AvahiServerConfig *c) {
1529 avahi_free(c->host_name);
1530 avahi_free(c->domain_name);
1533 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
1534 char *d = NULL, *h = NULL;
1539 if (!(h = avahi_strdup(c->host_name)))
1543 if (!(d = avahi_strdup(c->domain_name))) {
1550 ret->domain_name = d;
1555 int avahi_server_errno(AvahiServer *s) {
1561 /* Just for internal use */
1562 int avahi_server_set_errno(AvahiServer *s, int error) {
1565 return s->error = error;
1568 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
1571 return s->local_service_cookie;
1574 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char*domain) {
1575 AvahiKey *key = NULL;
1585 if (!avahi_is_valid_service_name(name))
1586 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1588 if (!avahi_is_valid_service_type_strict(type))
1589 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1591 if (domain && !avahi_is_valid_domain_name(domain))
1592 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1594 if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
1595 return avahi_server_set_errno(s, ret);
1597 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
1598 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1602 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) {
1604 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
1605 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
1606 !(e->flags & AVAHI_PUBLISH_IS_PROXY)) {
1612 avahi_key_unref(key);
1617 /** Set the wide area DNS servers */
1618 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
1621 if (!s->wide_area_lookup_engine)
1622 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
1624 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);