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 #define AVAHI_RR_HOLDOFF_MSEC 1000
49 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
50 #define AVAHI_RR_RATE_LIMIT_COUNT 15
52 static void free_entry(AvahiServer*s, AvahiEntry *e) {
58 avahi_goodbye_entry(s, e, 1);
60 /* Remove from linked list */
61 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
63 /* Remove from hash table indexed by name */
64 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
65 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
67 avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
69 avahi_hashmap_remove(s->entries_by_key, e->record->key);
71 /* Remove from associated group */
73 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
75 avahi_record_unref(e->record);
79 static void free_group(AvahiServer *s, AvahiSEntryGroup *g) {
84 free_entry(s, g->entries);
86 if (g->register_time_event)
87 avahi_time_event_free(g->register_time_event);
89 AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g);
93 static void cleanup_dead(AvahiServer *s) {
96 if (s->need_group_cleanup) {
97 AvahiSEntryGroup *g, *next;
99 for (g = s->groups; g; g = next) {
100 next = g->groups_next;
106 s->need_group_cleanup = 0;
109 if (s->need_entry_cleanup) {
110 AvahiEntry *e, *next;
112 for (e = s->entries; e; e = next) {
113 next = e->entries_next;
119 s->need_entry_cleanup = 0;
122 if (s->need_browser_cleanup)
123 avahi_browser_cleanup(s);
126 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) {
135 assert(type != AVAHI_DNS_TYPE_ANY);
137 if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
140 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
141 if (!e->dead && avahi_entry_is_registered(s, e, i))
142 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
147 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) {
153 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
154 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
155 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
156 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
157 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
158 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
159 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
164 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
169 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
172 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
180 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
181 /* avahi_free(txt); */
183 if (avahi_key_is_pattern(k)) {
185 /* Handle ANY query */
187 for (e = s->entries; e; e = e->entries_next)
188 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
189 avahi_server_prepare_response(s, i, e, unicast_response, 0);
193 /* Handle all other queries */
195 for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
196 if (!e->dead && avahi_entry_is_registered(s, e, i))
197 avahi_server_prepare_response(s, i, e, unicast_response, 0);
201 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
208 for (k = e->group->entries; k; k = k->by_group_next) {
210 avahi_goodbye_entry(s, k, 0);
215 e->group->n_probing = 0;
217 avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
219 avahi_goodbye_entry(s, e, 0);
223 s->need_entry_cleanup = 1;
226 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
232 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
234 withdraw_entry(s, e);
237 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
240 int ours = 0, won = 0, lost = 0;
246 t = avahi_record_to_string(record);
248 /* avahi_log_debug("incoming_probe()"); */
250 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
257 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
262 if (avahi_entry_is_probing(s, e, i)) {
274 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
276 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
277 withdraw_rrset(s, record->key);
279 /* avahi_log_debug("Not conflicting probe"); */
285 static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique, const AvahiAddress *a) {
286 int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
287 AvahiEntry *e, *n, *conflicting_entry = NULL;
294 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
296 for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
302 /* Check if the incoming is a goodbye record */
303 if (avahi_record_is_goodbye(record)) {
305 if (avahi_record_equal_no_ttl(e->record, record)) {
309 t = avahi_record_to_string(record);
310 avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
311 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
318 /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
322 if (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique)
325 /* Either our entry or the other is intended to be unique, so let's check */
327 if (avahi_record_equal_no_ttl(e->record, record)) {
328 ours = 1; /* We have an identical record, so this is no conflict */
330 /* Check wheter there is a TTL conflict */
331 if (record->ttl <= e->record->ttl/2 &&
332 avahi_entry_is_registered(s, e, i)) {
335 t = avahi_record_to_string(record);
337 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
338 avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
344 /* There's no need to check the other entries of this RRset */
349 if (avahi_entry_is_registered(s, e, i)) {
351 /* A conflict => we have to return to probe mode */
353 conflicting_entry = e;
355 } else if (avahi_entry_is_probing(s, e, i)) {
357 /* We are currently registering a matching record, but
358 * someone else already claimed it, so let's
361 withdraw_immediately = 1;
366 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
368 if (!ours && conflict) {
373 t = avahi_record_to_string(record);
375 if (withdraw_immediately) {
376 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
377 withdraw_rrset(s, record->key);
379 assert(conflicting_entry);
380 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
381 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
383 /* Local unique records are returned to probing
384 * state. Local shared records are reannounced. */
393 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
394 int *unicast_response = userdata;
398 assert(unicast_response);
400 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
403 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
407 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
410 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
414 assert(!legacy_unicast || (a && port > 0 && p));
416 if (legacy_unicast) {
417 AvahiDnsPacket *reply;
420 if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
423 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
425 append_aux_records_to_list(s, i, r, 0);
427 if (avahi_dns_packet_append_record(reply, r, 0, 10))
428 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
430 char *t = avahi_record_to_string(r);
431 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
435 avahi_record_unref(r);
438 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
439 avahi_interface_send_packet_unicast(i, reply, a, port);
441 avahi_dns_packet_free(reply);
444 int unicast_response, flush_cache, auxiliary;
445 AvahiDnsPacket *reply = NULL;
448 /* In case the query packet was truncated never respond
449 immediately, because known answer suppression records might be
450 contained in later packets */
451 int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
453 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
455 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
457 append_aux_records_to_list(s, i, r, unicast_response);
459 /* Due to some reasons the record has not been scheduled.
460 * The client requested an unicast response in that
461 * case. Therefore we prepare such a response */
468 if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
472 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
474 /* Appending this record succeeded, so incremeant
475 * the specific header field, and return to the caller */
477 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
482 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
485 /* The record is too large for one packet, so create a larger packet */
487 avahi_dns_packet_free(reply);
488 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
489 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
490 size = AVAHI_DNS_PACKET_MAX_SIZE;
492 if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
495 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
497 avahi_dns_packet_free(reply);
498 t = avahi_record_to_string(r);
499 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
503 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
506 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
507 avahi_interface_send_packet_unicast(i, reply, a, port);
508 avahi_dns_packet_free(reply);
513 avahi_record_unref(r);
517 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
518 avahi_interface_send_packet_unicast(i, reply, a, port);
519 avahi_dns_packet_free(reply);
523 avahi_record_list_flush(s->record_list);
527 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
534 if (!s->config.enable_reflector)
537 for (j = s->monitor->interfaces; j; j = j->interface_next)
538 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
539 avahi_interface_post_response(j, r, flush_cache, NULL, 1);
542 static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
543 AvahiServer *s = userdata;
550 avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
554 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
561 if (!s->config.enable_reflector)
564 for (j = s->monitor->interfaces; j; j = j->interface_next)
565 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
566 /* Post the query to other networks */
567 avahi_interface_post_query(j, k, 1);
569 /* Reply from caches of other network. This is needed to
570 * "work around" known answer suppression. */
572 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
576 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
583 if (!s->config.enable_reflector)
586 for (j = s->monitor->interfaces; j; j = j->interface_next)
587 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
588 avahi_interface_post_probe(j, r, 1);
591 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
600 /* avahi_log_debug("query"); */
602 assert(avahi_record_list_is_empty(s->record_list));
604 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
606 /* Handle the questions */
607 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
609 int unicast_response = 0;
611 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
612 avahi_log_warn("Packet too short (1)");
616 if (!legacy_unicast && !from_local_iface)
617 reflect_query(s, i, key);
619 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
620 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
621 /* Allow our own queries to be suppressed by incoming
622 * queries only when they do not include known answers */
623 avahi_query_scheduler_incoming(i->query_scheduler, key);
625 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
626 avahi_key_unref(key);
629 if (!legacy_unicast) {
631 /* Known Answer Suppression */
632 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
636 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
637 avahi_log_warn("Packet too short (2)");
641 if (handle_conflict(s, i, record, unique, a)) {
642 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
643 avahi_record_list_drop(s->record_list, record);
646 avahi_record_unref(record);
650 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
654 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
655 avahi_log_warn("Packet too short (3)");
659 if (!avahi_key_is_pattern(record->key)) {
660 if (!from_local_iface)
661 reflect_probe(s, i, record);
662 incoming_probe(s, record, i);
665 avahi_record_unref(record);
669 if (!avahi_record_list_is_empty(s->record_list))
670 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
675 avahi_record_list_flush(s->record_list);
678 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
686 /* avahi_log_debug("response"); */
688 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
689 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
694 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
695 avahi_log_warn("Packet too short (4)");
699 if (!avahi_key_is_pattern(record->key)) {
701 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
702 /* avahi_free(txt); */
704 if (handle_conflict(s, i, record, cache_flush, a)) {
705 if (!from_local_iface)
706 reflect_response(s, i, record, cache_flush);
707 avahi_cache_update(i->cache, record, cache_flush, a);
708 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
712 avahi_record_unref(record);
715 /* If the incoming response contained a conflicting record, some
716 records have been scheduling for sending. We need to flush them
718 if (!avahi_record_list_is_empty(s->record_list))
719 avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
722 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
723 unsigned n, idx = (unsigned) -1;
724 AvahiLegacyUnicastReflectSlot *slot;
728 if (!s->legacy_unicast_reflect_slots)
729 s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
731 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
732 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
734 if (!s->legacy_unicast_reflect_slots[idx])
738 if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
741 if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
742 return NULL; /* OOM */
744 s->legacy_unicast_reflect_slots[idx] = slot;
745 slot->id = s->legacy_unicast_reflect_id++;
751 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
757 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
759 assert(s->legacy_unicast_reflect_slots[idx] == slot);
761 avahi_time_event_free(slot->time_event);
764 s->legacy_unicast_reflect_slots[idx] = NULL;
767 static void free_slots(AvahiServer *s) {
771 if (!s->legacy_unicast_reflect_slots)
774 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
775 if (s->legacy_unicast_reflect_slots[idx])
776 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
778 avahi_free(s->legacy_unicast_reflect_slots);
779 s->legacy_unicast_reflect_slots = NULL;
782 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
787 if (!s->legacy_unicast_reflect_slots)
790 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
792 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
795 return s->legacy_unicast_reflect_slots[idx];
798 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
799 AvahiLegacyUnicastReflectSlot *slot = userdata;
803 assert(slot->time_event == e);
805 deallocate_slot(slot->server, slot);
808 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
809 AvahiLegacyUnicastReflectSlot *slot;
817 assert(i->protocol == a->proto);
819 if (!s->config.enable_reflector)
822 /* avahi_log_debug("legacy unicast reflector"); */
824 /* Reflecting legacy unicast queries is a little more complicated
825 than reflecting normal queries, since we must route the
826 responses back to the right client. Therefore we must store
827 some information for finding the right client contact data for
828 response packets. In contrast to normal queries legacy
829 unicast query and response packets are reflected untouched and
830 are not reassembled into larger packets */
832 if (!(slot = allocate_slot(s))) {
833 /* No slot available, we drop this legacy unicast query */
834 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
838 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
841 slot->interface = i->hardware->index;
843 avahi_elapse_time(&slot->elapse_time, 2000, 0);
844 slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
846 /* Patch the packet with our new locally generatedt id */
847 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
849 for (j = s->monitor->interfaces; j; j = j->interface_next)
850 if (avahi_interface_relevant(j) &&
852 (s->config.reflect_ipv || j->protocol == i->protocol)) {
854 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
855 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
856 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
857 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
861 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
864 static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
869 if (!s->config.enable_reflector)
872 avahi_address_from_sockaddr(sa, &a);
874 if (!avahi_address_is_local(s->monitor, &a))
877 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
878 struct sockaddr_in lsa;
879 socklen_t l = sizeof(lsa);
881 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
882 avahi_log_warn("getsockname(): %s", strerror(errno));
884 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
888 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
889 struct sockaddr_in6 lsa;
890 socklen_t l = sizeof(lsa);
892 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
893 avahi_log_warn("getsockname(): %s", strerror(errno));
895 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
901 static int is_mdns_mcast_address(const AvahiAddress *a) {
905 avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
906 return avahi_address_cmp(a, &b) == 0;
909 static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
911 assert(iface != AVAHI_IF_UNSPEC);
914 /* If it isn't the MDNS port it can't be generated by us */
915 if (port != AVAHI_MDNS_PORT)
918 return avahi_interface_has_address(s->monitor, iface, a);
921 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, int ttl) {
925 int from_local_iface = 0;
933 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
934 !avahi_interface_relevant(i)) {
935 avahi_log_warn("Recieved packet from invalid interface.");
939 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
941 port = avahi_port_from_sockaddr(sa);
942 avahi_address_from_sockaddr(sa, &a);
944 if (avahi_address_is_ipv4_in_ipv6(&a))
945 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
948 if (originates_from_local_legacy_unicast_socket(s, sa))
949 /* This originates from our local reflector, so let's ignore it */
952 /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
953 if (s->config.enable_reflector)
954 from_local_iface = originates_from_local_iface(s, iface, &a, port);
956 if (avahi_dns_packet_check_valid_multicast(p) < 0) {
957 avahi_log_warn("Recieved invalid packet.");
961 if (avahi_dns_packet_is_query(p)) {
962 int legacy_unicast = 0;
964 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
965 avahi_log_warn("Invalid query packet.");
969 if (port != AVAHI_MDNS_PORT) {
972 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
973 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
974 avahi_log_warn("Invalid legacy unicast query packet.");
982 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
984 handle_query_packet(s, p, i, &a, port, legacy_unicast, from_local_iface);
986 /* avahi_log_debug("Handled query"); */
988 if (port != AVAHI_MDNS_PORT) {
989 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
993 if (ttl != 255 && s->config.check_response_ttl) {
994 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
998 if (!is_mdns_mcast_address(dest) &&
999 !avahi_interface_address_on_link(i, &a)) {
1000 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
1004 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
1005 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
1006 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
1007 avahi_log_warn("Invalid response packet.");
1011 handle_response_packet(s, p, i, &a, from_local_iface);
1012 /* avahi_log_debug("Handled response"); */
1016 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface) {
1017 AvahiInterface *i, *j;
1019 AvahiLegacyUnicastReflectSlot *slot;
1026 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, avahi_af_to_proto(sa->sa_family))) ||
1027 !avahi_interface_relevant(i)) {
1028 avahi_log_warn("Recieved packet from invalid interface.");
1032 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
1034 avahi_address_from_sockaddr(sa, &a);
1036 if (avahi_address_is_ipv4_in_ipv6(&a))
1037 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
1040 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
1041 avahi_log_warn("Recieved invalid packet.");
1045 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
1046 avahi_log_warn("Recieved legacy unicast response with unknown id");
1050 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
1051 !avahi_interface_relevant(j))
1054 /* Patch the original ID into this response */
1055 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
1057 /* Forward the response to the correct client */
1058 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1060 /* Undo changes to packet */
1061 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1064 static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
1065 AvahiServer *s = userdata;
1070 struct sockaddr_in sa;
1071 struct sockaddr_in6 sa6;
1076 if (events & AVAHI_WATCH_IN) {
1078 if (fd == s->fd_ipv4) {
1079 dest.proto = AVAHI_PROTO_INET;
1080 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1081 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1082 avahi_dns_packet_free(p);
1084 } else if (fd == s->fd_ipv6) {
1085 dest.proto = AVAHI_PROTO_INET6;
1087 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1088 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1089 avahi_dns_packet_free(p);
1091 } else if (fd == s->fd_legacy_unicast_ipv4) {
1092 dest.proto = AVAHI_PROTO_INET;
1094 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1095 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface);
1096 avahi_dns_packet_free(p);
1098 } else if (fd == s->fd_legacy_unicast_ipv6) {
1099 dest.proto = AVAHI_PROTO_INET6;
1101 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1102 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface);
1103 avahi_dns_packet_free(p);
1112 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1115 if (s->state == state)
1121 s->callback(s, state, s->userdata);
1124 static void withdraw_host_rrs(AvahiServer *s) {
1127 if (s->hinfo_entry_group)
1128 avahi_s_entry_group_reset(s->hinfo_entry_group);
1130 if (s->browse_domain_entry_group)
1131 avahi_s_entry_group_reset(s->browse_domain_entry_group);
1133 avahi_update_host_rrs(s->monitor, 1);
1134 s->n_host_rr_pending = 0;
1137 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1140 assert(s->n_host_rr_pending > 0);
1142 if (--s->n_host_rr_pending == 0)
1143 server_set_state(s, AVAHI_SERVER_RUNNING);
1146 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1149 s->n_host_rr_pending ++;
1152 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1156 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1157 s->state == AVAHI_SERVER_REGISTERING)
1158 avahi_server_increase_host_rr_pending(s);
1160 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1161 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1162 withdraw_host_rrs(s);
1163 server_set_state(s, AVAHI_SERVER_COLLISION);
1165 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1166 s->state == AVAHI_SERVER_REGISTERING)
1167 avahi_server_decrease_host_rr_pending(s);
1170 static void register_hinfo(AvahiServer *s) {
1171 struct utsname utsname;
1176 if (!s->config.publish_hinfo)
1179 if (s->hinfo_entry_group)
1180 assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
1182 s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1184 if (!s->hinfo_entry_group) {
1185 avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
1189 /* Fill in HINFO rr */
1190 if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1192 r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
1193 r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
1195 if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r) < 0) {
1196 avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
1200 avahi_record_unref(r);
1203 if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
1204 avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
1208 static void register_localhost(AvahiServer *s) {
1212 /* Add localhost entries */
1213 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1214 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1216 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1217 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1220 static void register_browse_domain(AvahiServer *s) {
1223 if (!s->config.publish_domain)
1226 if (s->browse_domain_entry_group)
1227 assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
1229 s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
1231 if (!s->browse_domain_entry_group) {
1232 avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
1236 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) {
1237 avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
1241 if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
1242 avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
1245 static void register_stuff(AvahiServer *s) {
1248 server_set_state(s, AVAHI_SERVER_REGISTERING);
1249 s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
1252 register_browse_domain(s);
1253 avahi_update_host_rrs(s->monitor, 0);
1255 s->n_host_rr_pending --;
1257 if (s->n_host_rr_pending == 0)
1258 server_set_state(s, AVAHI_SERVER_RUNNING);
1261 static void update_fqdn(AvahiServer *s) {
1265 assert(s->host_name);
1266 assert(s->domain_name);
1268 if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
1271 avahi_free(s->host_name_fqdn);
1272 s->host_name_fqdn = n;
1275 int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
1279 if (host_name && !avahi_is_valid_host_name(host_name))
1280 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1282 withdraw_host_rrs(s);
1284 avahi_free(s->host_name);
1285 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1286 s->host_name[strcspn(s->host_name, ".")] = 0;
1293 int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
1295 assert(domain_name);
1297 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1298 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1300 withdraw_host_rrs(s);
1302 avahi_free(s->domain_name);
1303 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : avahi_strdup("local");
1310 static int valid_server_config(const AvahiServerConfig *sc) {
1312 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1313 return AVAHI_ERR_INVALID_HOST_NAME;
1315 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1316 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1321 static int setup_sockets(AvahiServer *s) {
1324 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1325 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1327 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
1328 return AVAHI_ERR_NO_NETWORK;
1330 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1331 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1332 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1333 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1335 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
1336 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
1340 s->watch_legacy_unicast_ipv4 =
1341 s->watch_legacy_unicast_ipv6 = NULL;
1343 if (s->fd_ipv4 >= 0)
1344 s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
1345 if (s->fd_ipv6 >= 0)
1346 s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
1348 if (s->fd_legacy_unicast_ipv4 >= 0)
1349 s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
1350 if (s->fd_legacy_unicast_ipv6 >= 0)
1351 s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
1356 AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
1360 if ((e = valid_server_config(sc)) < 0) {
1366 if (!(s = avahi_new(AvahiServer, 1))) {
1368 *error = AVAHI_ERR_NO_MEMORY;
1373 s->poll_api = poll_api;
1376 avahi_server_config_copy(&s->config, sc);
1378 avahi_server_config_init(&s->config);
1380 if ((e = setup_sockets(s)) < 0) {
1384 avahi_server_config_free(&s->config);
1391 s->n_host_rr_pending = 0;
1392 s->need_entry_cleanup = 0;
1393 s->need_group_cleanup = 0;
1394 s->need_browser_cleanup = 0;
1396 s->time_event_queue = avahi_time_event_queue_new(poll_api);
1398 s->callback = callback;
1399 s->userdata = userdata;
1401 s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1402 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1403 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1405 s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
1406 AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
1407 AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
1408 AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
1409 AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
1410 AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
1411 AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
1412 AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
1413 AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
1415 s->legacy_unicast_reflect_slots = NULL;
1416 s->legacy_unicast_reflect_id = 0;
1419 if (s->config.enable_wide_area) {
1420 s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
1421 avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
1423 s->wide_area_lookup_engine = NULL;
1425 s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
1428 s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
1429 } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
1432 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1433 s->host_name[strcspn(s->host_name, ".")] = 0;
1434 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : avahi_strdup("local");
1435 s->host_name_fqdn = NULL;
1438 s->record_list = avahi_record_list_new();
1440 s->state = AVAHI_SERVER_INVALID;
1442 s->monitor = avahi_interface_monitor_new(s);
1443 avahi_interface_monitor_sync(s->monitor);
1445 register_localhost(s);
1447 s->hinfo_entry_group = NULL;
1448 s->browse_domain_entry_group = NULL;
1451 s->error = AVAHI_OK;
1456 void avahi_server_free(AvahiServer* s) {
1459 /* Remove all browsers */
1461 while (s->dns_server_browsers)
1462 avahi_s_dns_server_browser_free(s->dns_server_browsers);
1463 while (s->host_name_resolvers)
1464 avahi_s_host_name_resolver_free(s->host_name_resolvers);
1465 while (s->address_resolvers)
1466 avahi_s_address_resolver_free(s->address_resolvers);
1467 while (s->domain_browsers)
1468 avahi_s_domain_browser_free(s->domain_browsers);
1469 while (s->service_type_browsers)
1470 avahi_s_service_type_browser_free(s->service_type_browsers);
1471 while (s->service_browsers)
1472 avahi_s_service_browser_free(s->service_browsers);
1473 while (s->service_resolvers)
1474 avahi_s_service_resolver_free(s->service_resolvers);
1475 while (s->record_browsers)
1476 avahi_s_record_browser_destroy(s->record_browsers);
1478 /* Remove all locally rgeistered stuff */
1481 free_entry(s, s->entries);
1483 avahi_interface_monitor_free(s->monitor);
1486 free_group(s, s->groups);
1490 avahi_hashmap_free(s->entries_by_key);
1491 avahi_record_list_free(s->record_list);
1492 avahi_hashmap_free(s->record_browser_hashmap);
1494 if (s->wide_area_lookup_engine)
1495 avahi_wide_area_engine_free(s->wide_area_lookup_engine);
1496 avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
1498 avahi_time_event_queue_free(s->time_event_queue);
1503 s->poll_api->watch_free(s->watch_ipv4);
1505 s->poll_api->watch_free(s->watch_ipv6);
1507 if (s->watch_legacy_unicast_ipv4)
1508 s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
1509 if (s->watch_legacy_unicast_ipv6)
1510 s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
1514 if (s->fd_ipv4 >= 0)
1516 if (s->fd_ipv6 >= 0)
1519 if (s->fd_legacy_unicast_ipv4 >= 0)
1520 close(s->fd_legacy_unicast_ipv4);
1521 if (s->fd_legacy_unicast_ipv6 >= 0)
1522 close(s->fd_legacy_unicast_ipv6);
1524 /* Free other stuff */
1526 avahi_free(s->host_name);
1527 avahi_free(s->domain_name);
1528 avahi_free(s->host_name_fqdn);
1530 avahi_server_config_free(&s->config);
1535 static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1541 for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1545 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1548 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1551 if ((interface <= 0 ||
1552 e->interface <= 0 ||
1553 e->interface == interface) &&
1554 (protocol == AVAHI_PROTO_UNSPEC ||
1555 e->protocol == AVAHI_PROTO_UNSPEC ||
1556 e->protocol == protocol))
1564 int avahi_server_add(
1566 AvahiSEntryGroup *g,
1567 AvahiIfIndex interface,
1568 AvahiProtocol protocol,
1569 AvahiEntryFlags flags,
1578 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1580 if (avahi_key_is_pattern(r->key))
1581 return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1583 if (!avahi_record_is_valid(r))
1584 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1586 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1587 return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1589 if (!(e = avahi_new(AvahiEntry, 1)))
1590 return avahi_server_set_errno(s, AVAHI_ERR_NO_NETWORK);
1593 e->record = avahi_record_ref(r);
1595 e->interface = interface;
1596 e->protocol = protocol;
1600 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1602 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1604 /* Insert into hash table indexed by name */
1605 t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
1606 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1607 avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
1609 /* Insert into group list */
1611 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1613 avahi_announce_entry(s, e);
1618 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
1619 AvahiEntry **e = (AvahiEntry**) state;
1624 *e = g ? g->entries : s->entries;
1626 while (*e && (*e)->dead)
1627 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1632 return avahi_record_ref((*e)->record);
1635 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
1641 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1643 for (e = s->entries; e; e = e->entries_next) {
1650 if (!(t = avahi_record_to_string(e->record)))
1651 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1653 snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1656 callback(ln, userdata);
1659 avahi_dump_caches(s->monitor, callback, userdata);
1661 if (s->wide_area_lookup_engine)
1662 avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata);
1666 int avahi_server_add_ptr(
1668 AvahiSEntryGroup *g,
1669 AvahiIfIndex interface,
1670 AvahiProtocol protocol,
1671 AvahiEntryFlags flags,
1682 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl)))
1683 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1685 r->data.ptr.name = avahi_normalize_name(dest);
1686 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1687 avahi_record_unref(r);
1691 int avahi_server_add_address(
1693 AvahiSEntryGroup *g,
1694 AvahiIfIndex interface,
1695 AvahiProtocol protocol,
1696 AvahiEntryFlags flags,
1706 if (!(n = avahi_normalize_name(name)))
1707 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1711 name = s->host_name_fqdn;
1713 if (!avahi_is_valid_domain_name(name)) {
1714 ret = avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1718 if (a->proto == AVAHI_PROTO_INET) {
1722 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1723 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1727 r->data.a.address = a->data.ipv4;
1728 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1729 avahi_record_unref(r);
1734 if (!(reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4))) {
1735 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1739 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1740 avahi_free(reverse);
1746 assert(a->proto == AVAHI_PROTO_INET6);
1748 if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1749 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1753 r->data.aaaa.address = a->data.ipv6;
1754 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1755 avahi_record_unref(r);
1760 if (!(reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6))) {
1761 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1765 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1766 avahi_free(reverse);
1771 if (!(reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6))) {
1772 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1776 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1777 avahi_free(reverse);
1787 static int server_add_txt_strlst_nocopy(
1789 AvahiSEntryGroup *g,
1790 AvahiIfIndex interface,
1791 AvahiProtocol protocol,
1792 AvahiEntryFlags flags,
1795 AvahiStringList *strlst) {
1802 if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl)))
1803 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1805 r->data.txt.string_list = strlst;
1806 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1807 avahi_record_unref(r);
1812 int avahi_server_add_txt_strlst(
1814 AvahiSEntryGroup *g,
1815 AvahiIfIndex interface,
1816 AvahiProtocol protocol,
1817 AvahiEntryFlags flags,
1820 AvahiStringList *strlst) {
1824 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1827 int avahi_server_add_txt_va(
1829 AvahiSEntryGroup *g,
1830 AvahiIfIndex interface,
1831 AvahiProtocol protocol,
1832 AvahiEntryFlags flags,
1839 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1842 int avahi_server_add_txt(
1844 AvahiSEntryGroup *g,
1845 AvahiIfIndex interface,
1846 AvahiProtocol protocol,
1847 AvahiEntryFlags flags,
1858 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1864 static void escape_service_name(char *d, size_t size, const char *s) {
1869 while (*s && size >= 2) {
1870 if (*s == '.' || *s == '\\') {
1886 static AvahiStringList *add_magic_cookie(
1888 AvahiStringList *strlst) {
1892 if (!s->config.add_service_cookie)
1895 if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE))
1896 /* This string list already contains a magic cookie */
1899 return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie);
1902 static int server_add_service_strlst_nocopy(
1904 AvahiSEntryGroup *g,
1905 AvahiIfIndex interface,
1906 AvahiProtocol protocol,
1912 AvahiStringList *strlst) {
1914 char ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1915 char *t = NULL, *d = NULL, *h = NULL;
1916 AvahiRecord *r = NULL;
1923 if (!avahi_is_valid_service_name(name))
1924 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1926 if (!avahi_is_valid_service_type(type))
1927 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1929 if (domain && !avahi_is_valid_domain_name(domain))
1930 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1932 if (host && !avahi_is_valid_domain_name(host))
1933 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1935 escape_service_name(ename, sizeof(ename), name);
1938 domain = s->domain_name;
1941 host = s->host_name_fqdn;
1943 if (!(d = avahi_normalize_name(domain)) ||
1944 !(t = avahi_normalize_name(type)) ||
1945 !(h = avahi_normalize_name(host))) {
1946 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1950 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1951 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1953 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1956 if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
1957 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1961 r->data.srv.priority = 0;
1962 r->data.srv.weight = 0;
1963 r->data.srv.port = port;
1964 r->data.srv.name = h;
1966 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1967 avahi_record_unref(r);
1972 strlst = add_magic_cookie(s, strlst);
1974 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1980 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1981 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1989 avahi_string_list_free(strlst);
1994 int avahi_server_add_service_strlst(
1996 AvahiSEntryGroup *g,
1997 AvahiIfIndex interface,
1998 AvahiProtocol protocol,
2004 AvahiStringList *strlst) {
2010 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
2013 int avahi_server_add_service_va(
2015 AvahiSEntryGroup *g,
2016 AvahiIfIndex interface,
2017 AvahiProtocol protocol,
2029 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
2032 int avahi_server_add_service(
2034 AvahiSEntryGroup *g,
2035 AvahiIfIndex interface,
2036 AvahiProtocol protocol,
2052 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
2057 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
2058 static const char hex[] = "0123456789abcdef";
2060 const uint8_t *k = p;
2062 while (sl > 1 && pl > 0) {
2063 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
2079 int avahi_server_add_dns_server_address(
2081 AvahiSEntryGroup *g,
2082 AvahiIfIndex interface,
2083 AvahiProtocol protocol,
2085 AvahiDNSServerType type,
2086 const AvahiAddress *address,
2087 uint16_t port /** should be 53 */) {
2095 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2096 assert(address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6);
2099 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2101 if (domain && !avahi_is_valid_domain_name(domain))
2102 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2104 if (address->proto == AVAHI_PROTO_INET) {
2105 hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address));
2106 snprintf(n, sizeof(n), "ip-%s.%s", h, s->domain_name);
2107 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
2108 r->data.a.address = address->data.ipv4;
2110 hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address));
2111 snprintf(n, sizeof(n), "ip6-%s.%s", h, s->domain_name);
2112 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
2113 r->data.aaaa.address = address->data.ipv6;
2117 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2119 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
2120 avahi_record_unref(r);
2125 return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
2128 int avahi_server_add_dns_server_name(
2130 AvahiSEntryGroup *g,
2131 AvahiIfIndex interface,
2132 AvahiProtocol protocol,
2134 AvahiDNSServerType type,
2136 uint16_t port /** should be 53 */) {
2139 char t[256], *d = NULL, *n = NULL;
2144 assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
2147 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2149 if (!avahi_is_valid_domain_name(name))
2150 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
2152 if (domain && !avahi_is_valid_domain_name(domain))
2153 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2157 domain = s->domain_name;
2159 if (!(n = avahi_normalize_name(name)) ||
2160 !(d = avahi_normalize_name(domain))) {
2163 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2166 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2169 if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
2171 return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2174 r->data.srv.priority = 0;
2175 r->data.srv.weight = 0;
2176 r->data.srv.port = port;
2177 r->data.srv.name = n;
2178 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2179 avahi_record_unref(r);
2184 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
2185 AvahiKey *k = userdata;
2191 avahi_interface_post_query(i, k, 0);
2194 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2198 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2201 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
2204 if (g->state == state)
2207 assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2212 g->callback(g->server, g, state, g->userdata);
2215 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
2216 AvahiSEntryGroup *g;
2220 if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
2221 avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2226 g->callback = callback;
2227 g->userdata = userdata;
2229 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2231 g->n_register_try = 0;
2232 g->register_time_event = NULL;
2233 g->register_time.tv_sec = 0;
2234 g->register_time.tv_usec = 0;
2235 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2237 AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
2241 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
2247 for (e = g->entries; e; e = e->by_group_next) {
2249 avahi_goodbye_entry(g->server, e, 1);
2254 if (g->register_time_event) {
2255 avahi_time_event_free(g->register_time_event);
2256 g->register_time_event = NULL;
2261 g->server->need_group_cleanup = 1;
2262 g->server->need_entry_cleanup = 1;
2265 static void entry_group_commit_real(AvahiSEntryGroup *g) {
2268 gettimeofday(&g->register_time, NULL);
2270 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2273 avahi_announce_group(g->server, g);
2274 avahi_s_entry_group_check_probed(g, 0);
2278 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, void* userdata) {
2279 AvahiSEntryGroup *g = userdata;
2282 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2284 avahi_time_event_free(g->register_time_event);
2285 g->register_time_event = NULL;
2287 /* Holdoff time passed, so let's start probing */
2288 entry_group_commit_real(g);
2291 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
2297 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2298 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2300 g->n_register_try++;
2302 avahi_timeval_add(&g->register_time,
2303 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2304 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2305 AVAHI_RR_HOLDOFF_MSEC));
2307 gettimeofday(&now, NULL);
2309 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2310 /* Holdoff time passed, so let's start probing */
2311 /* avahi_log_debug("Holdoff passed, directly going on."); */
2313 entry_group_commit_real(g);
2315 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2317 /* Holdoff time has not yet passed, so let's wait */
2318 assert(!g->register_time_event);
2319 g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2321 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2327 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
2331 if (g->register_time_event) {
2332 avahi_time_event_free(g->register_time_event);
2333 g->register_time_event = NULL;
2336 for (e = g->entries; e; e = e->by_group_next) {
2338 avahi_goodbye_entry(g->server, e, 1);
2343 if (g->register_time_event) {
2344 avahi_time_event_free(g->register_time_event);
2345 g->register_time_event = NULL;
2348 g->server->need_entry_cleanup = 1;
2351 avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2354 int avahi_entry_is_commited(AvahiEntry *e) {
2359 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2360 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2363 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
2370 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
2373 g->userdata = userdata;
2376 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
2382 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
2386 /* Look for an entry that is not dead */
2387 for (e = g->entries; e; e = e->by_group_next)
2394 const char* avahi_server_get_domain_name(AvahiServer *s) {
2397 return s->domain_name;
2400 const char* avahi_server_get_host_name(AvahiServer *s) {
2403 return s->host_name;
2406 const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2409 return s->host_name_fqdn;
2412 void* avahi_server_get_data(AvahiServer *s) {
2418 void avahi_server_set_data(AvahiServer *s, void* userdata) {
2421 s->userdata = userdata;
2424 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2430 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2433 memset(c, 0, sizeof(AvahiServerConfig));
2436 c->host_name = NULL;
2437 c->domain_name = NULL;
2438 c->check_response_ttl = 0;
2439 c->publish_hinfo = 1;
2440 c->publish_addresses = 1;
2441 c->publish_workstation = 1;
2442 c->publish_domain = 1;
2443 c->use_iff_running = 0;
2444 c->enable_reflector = 0;
2446 c->add_service_cookie = 1;
2447 c->enable_wide_area = 0;
2448 c->n_wide_area_servers = 0;
2453 void avahi_server_config_free(AvahiServerConfig *c) {
2456 avahi_free(c->host_name);
2457 avahi_free(c->domain_name);
2460 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2461 char *d = NULL, *h = NULL;
2466 if (!(h = avahi_strdup(c->host_name)))
2470 if (!(d = avahi_strdup(c->domain_name))) {
2477 ret->domain_name = d;
2482 int avahi_server_errno(AvahiServer *s) {
2488 /* Just for internal use */
2489 int avahi_server_set_errno(AvahiServer *s, int error) {
2492 return s->error = error;
2495 uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
2498 return s->local_service_cookie;
2501 int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char*domain) {
2502 AvahiKey *key = NULL;
2504 char ename[64], n[256];
2513 if (!avahi_is_valid_service_name(name))
2514 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
2516 if (!avahi_is_valid_service_type(type))
2517 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
2519 if (domain && !avahi_is_valid_domain_name(domain))
2520 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
2522 escape_service_name(ename, sizeof(ename), name);
2524 if (!(d = avahi_normalize_name(domain)) ||
2525 !(t = avahi_normalize_name(type))) {
2526 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2529 snprintf(n, sizeof(n), "%s.%s.%s", ename, t, d);
2531 if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) {
2532 ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
2538 for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next) {
2540 if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
2541 (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC)) {
2547 avahi_key_unref(key);
2562 avahi_key_unref(key);
2567 /** Set the wide area DNS servers */
2568 int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
2571 if (!s->wide_area_lookup_engine)
2572 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
2574 avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);