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>
41 #define AVAHI_RR_HOLDOFF_MSEC 1000
42 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
43 #define AVAHI_RR_RATE_LIMIT_COUNT 15
45 static void free_entry(AvahiServer*s, AvahiEntry *e) {
51 avahi_goodbye_entry(s, e, TRUE);
53 /* Remove from linked list */
54 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
56 /* Remove from hash table indexed by name */
57 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
58 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
60 g_hash_table_replace(s->entries_by_key, t->record->key, t);
62 g_hash_table_remove(s->entries_by_key, e->record->key);
64 /* Remove from associated group */
66 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
68 avahi_record_unref(e->record);
72 static void free_group(AvahiServer *s, AvahiEntryGroup *g) {
77 free_entry(s, g->entries);
79 if (g->register_time_event)
80 avahi_time_event_queue_remove(s->time_event_queue, g->register_time_event);
82 AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
86 static void cleanup_dead(AvahiServer *s) {
87 AvahiEntryGroup *g, *ng;
92 if (s->need_group_cleanup) {
93 for (g = s->groups; g; g = ng) {
100 s->need_group_cleanup = FALSE;
103 if (s->need_entry_cleanup) {
104 for (e = s->entries; e; e = ne) {
105 ne = e->entries_next;
111 s->need_entry_cleanup = FALSE;
114 if (s->need_browser_cleanup)
115 avahi_browser_cleanup(s);
118 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const gchar *name, guint16 type, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
127 g_assert(type != AVAHI_DNS_TYPE_ANY);
129 k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
131 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
132 if (!e->dead && avahi_entry_registered(s, e, i))
133 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
138 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
144 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
145 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
146 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
147 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
148 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
149 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
150 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
155 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary) {
160 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
163 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
171 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
174 if (avahi_key_is_pattern(k)) {
176 /* Handle ANY query */
178 for (e = s->entries; e; e = e->entries_next)
179 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
180 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
184 /* Handle all other queries */
186 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
187 if (!e->dead && avahi_entry_registered(s, e, i))
188 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
192 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
199 for (k = e->group->entries; k; k = k->by_group_next) {
201 avahi_goodbye_entry(s, k, FALSE);
206 e->group->n_probing = 0;
208 avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
210 avahi_goodbye_entry(s, e, FALSE);
214 s->need_entry_cleanup = TRUE;
217 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
223 for (e = g_hash_table_lookup(s->entries_by_key, key); e; e = e->by_key_next)
225 withdraw_entry(s, e);
228 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
231 gboolean ours = FALSE, won = FALSE, lost = FALSE;
237 t = avahi_record_to_string(record);
239 /* avahi_log_debug("incoming_probe()"); */
241 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
248 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
253 if (avahi_entry_probing(s, e, i)) {
266 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
268 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
269 withdraw_rrset(s, record->key);
271 /* avahi_log_debug("Not conflicting probe"); */
277 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
278 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
279 AvahiEntry *e, *n, *conflicting_entry = NULL;
286 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
288 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
291 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
294 /* Either our entry or the other is intended to be unique, so let's check */
296 if (avahi_record_equal_no_ttl(e->record, record)) {
297 ours = TRUE; /* We have an identical record, so this is no conflict */
299 /* Check wheter there is a TTL conflict */
300 if (record->ttl <= e->record->ttl/2 &&
301 avahi_entry_registered(s, e, i)) {
304 t = avahi_record_to_string(record);
306 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
307 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
313 /* There's no need to check the other entries of this RRset */
318 if (avahi_entry_registered(s, e, i)) {
320 /* A conflict => we have to return to probe mode */
322 conflicting_entry = e;
324 } else if (avahi_entry_probing(s, e, i)) {
326 /* We are currently registering a matching record, but
327 * someone else already claimed it, so let's
330 withdraw_immediately = TRUE;
335 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
337 if (!ours && conflict) {
342 t = avahi_record_to_string(record);
344 if (withdraw_immediately) {
345 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
346 withdraw_rrset(s, record->key);
348 g_assert(conflicting_entry);
349 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
350 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
352 /* Local unique records are returned to probing
353 * state. Local shared records are reannounced. */
362 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
363 gboolean *unicast_response = userdata;
367 g_assert(unicast_response);
369 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
372 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
376 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
379 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean immediately) {
383 g_assert(!legacy_unicast || (a && port > 0 && p));
385 if (legacy_unicast) {
386 AvahiDnsPacket *reply;
389 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
391 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
393 append_aux_records_to_list(s, i, r, FALSE);
395 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
396 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
398 gchar *t = avahi_record_to_string(r);
399 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
403 avahi_record_unref(r);
406 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
407 avahi_interface_send_packet_unicast(i, reply, a, port);
409 avahi_dns_packet_free(reply);
412 gboolean unicast_response, flush_cache, auxiliary;
413 AvahiDnsPacket *reply = NULL;
416 /* In case the query packet was truncated never respond
417 immediately, because known answer suppression records might be
418 contained in later packets */
419 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
421 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
423 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
425 append_aux_records_to_list(s, i, r, unicast_response);
427 /* Due to some reasons the record has not been scheduled.
428 * The client requested an unicast response in that
429 * case. Therefore we prepare such a response */
435 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
438 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
440 /* Appending this record succeeded, so incremeant
441 * the specific header field, and return to the caller */
443 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
448 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
451 /* The record is too large for one packet, so create a larger packet */
453 avahi_dns_packet_free(reply);
454 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
455 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
456 size = AVAHI_DNS_PACKET_MAX_SIZE;
457 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
459 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
460 avahi_dns_packet_free(reply);
462 gchar *t = avahi_record_to_string(r);
463 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
467 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
470 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
471 avahi_interface_send_packet_unicast(i, reply, a, port);
472 avahi_dns_packet_free(reply);
477 avahi_record_unref(r);
481 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
482 avahi_interface_send_packet_unicast(i, reply, a, port);
483 avahi_dns_packet_free(reply);
487 avahi_record_list_flush(s->record_list);
491 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
498 if (!s->config.enable_reflector)
501 for (j = s->monitor->interfaces; j; j = j->interface_next)
502 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
503 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
506 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
507 AvahiServer *s = userdata;
514 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
518 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
525 if (!s->config.enable_reflector)
528 for (j = s->monitor->interfaces; j; j = j->interface_next)
529 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
530 /* Post the query to other networks */
531 avahi_interface_post_query(j, k, TRUE);
533 /* Reply from caches of other network. This is needed to
534 * "work around" known answer suppression. */
536 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
540 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
547 if (!s->config.enable_reflector)
550 for (j = s->monitor->interfaces; j; j = j->interface_next)
551 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
552 avahi_interface_post_probe(j, r, TRUE);
555 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
564 /* avahi_log_debug("query"); */
566 g_assert(avahi_record_list_empty(s->record_list));
568 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
570 /* Handle the questions */
571 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
573 gboolean unicast_response = FALSE;
575 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
576 avahi_log_warn("Packet too short (1)");
581 reflect_query(s, i, key);
583 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
584 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
585 /* Allow our own queries to be suppressed by incoming
586 * queries only when they do not include known answers */
587 avahi_query_scheduler_incoming(i->query_scheduler, key);
589 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
590 avahi_key_unref(key);
593 /* Known Answer Suppression */
594 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
596 gboolean unique = FALSE;
598 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
599 avahi_log_warn("Packet too short (2)");
603 if (handle_conflict(s, i, record, unique, a)) {
604 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
605 avahi_record_list_drop(s->record_list, record);
608 avahi_record_unref(record);
612 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
614 gboolean unique = FALSE;
616 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
617 avahi_log_warn("Packet too short (3)");
621 if (!avahi_key_is_pattern(record->key)) {
622 reflect_probe(s, i, record);
623 incoming_probe(s, record, i);
626 avahi_record_unref(record);
629 if (!avahi_record_list_empty(s->record_list))
630 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
635 avahi_record_list_flush(s->record_list);
638 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
646 /* avahi_log_debug("response"); */
648 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
649 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
651 gboolean cache_flush = FALSE;
654 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
655 avahi_log_warn("Packet too short (4)");
659 if (!avahi_key_is_pattern(record->key)) {
661 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
664 if (handle_conflict(s, i, record, cache_flush, a)) {
665 reflect_response(s, i, record, cache_flush);
666 avahi_cache_update(i->cache, record, cache_flush, a);
667 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
671 avahi_record_unref(record);
674 /* If the incoming response contained a conflicting record, some
675 records have been scheduling for sending. We need to flush them
677 if (!avahi_record_list_empty(s->record_list))
678 avahi_server_generate_response(s, i, NULL, NULL, 0, FALSE, TRUE);
681 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
682 guint n, idx = (guint) -1;
683 AvahiLegacyUnicastReflectSlot *slot;
687 if (!s->legacy_unicast_reflect_slots)
688 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
690 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
691 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
693 if (!s->legacy_unicast_reflect_slots[idx])
697 if (idx == (guint) -1 || s->legacy_unicast_reflect_slots[idx])
700 slot = s->legacy_unicast_reflect_slots[idx] = g_new(AvahiLegacyUnicastReflectSlot, 1);
701 slot->id = s->legacy_unicast_reflect_id++;
706 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
712 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
714 g_assert(s->legacy_unicast_reflect_slots[idx] == slot);
716 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
719 s->legacy_unicast_reflect_slots[idx] = NULL;
722 static void free_slots(AvahiServer *s) {
726 if (!s->legacy_unicast_reflect_slots)
729 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
730 if (s->legacy_unicast_reflect_slots[idx])
731 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
733 g_free(s->legacy_unicast_reflect_slots);
734 s->legacy_unicast_reflect_slots = NULL;
737 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
742 if (!s->legacy_unicast_reflect_slots)
745 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
747 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
750 return s->legacy_unicast_reflect_slots[idx];
753 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
754 AvahiLegacyUnicastReflectSlot *slot = userdata;
758 g_assert(slot->time_event == e);
760 deallocate_slot(slot->server, slot);
763 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
764 AvahiLegacyUnicastReflectSlot *slot;
772 g_assert(i->protocol == a->family);
774 if (!s->config.enable_reflector)
777 /* avahi_log_debug("legacy unicast reflector"); */
779 /* Reflecting legacy unicast queries is a little more complicated
780 than reflecting normal queries, since we must route the
781 responses back to the right client. Therefore we must store
782 some information for finding the right client contact data for
783 response packets. In contrast to normal queries legacy
784 unicast query and response packets are reflected untouched and
785 are not reassembled into larger packets */
787 if (!(slot = allocate_slot(s))) {
788 /* No slot available, we drop this legacy unicast query */
789 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
793 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
796 slot->interface = i->hardware->index;
798 avahi_elapse_time(&slot->elapse_time, 2000, 0);
799 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
801 /* Patch the packet with our new locally generatedt id */
802 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
804 for (j = s->monitor->interfaces; j; j = j->interface_next)
805 if (avahi_interface_relevant(j) &&
807 (s->config.reflect_ipv || j->protocol == i->protocol)) {
809 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
810 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
811 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
812 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
816 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
819 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
824 if (!s->config.enable_reflector)
827 avahi_address_from_sockaddr(sa, &a);
829 if (!avahi_address_is_local(s->monitor, &a))
832 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
833 struct sockaddr_in lsa;
834 socklen_t l = sizeof(lsa);
836 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
837 avahi_log_warn("getsockname(): %s", strerror(errno));
839 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
843 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
844 struct sockaddr_in6 lsa;
845 socklen_t l = sizeof(lsa);
847 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
848 avahi_log_warn("getsockname(): %s", strerror(errno));
850 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
856 static gboolean is_mdns_mcast_address(const AvahiAddress *a) {
860 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
861 return avahi_address_cmp(a, &b) == 0;
864 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, gint ttl) {
875 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
876 !avahi_interface_relevant(i)) {
877 avahi_log_warn("Recieved packet from invalid interface.");
881 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
883 port = avahi_port_from_sockaddr(sa);
884 avahi_address_from_sockaddr(sa, &a);
886 if (avahi_address_is_ipv4_in_ipv6(&a))
887 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
890 if (originates_from_local_legacy_unicast_socket(s, sa))
891 /* This originates from our local reflector, so let's ignore it */
894 if (avahi_dns_packet_check_valid(p) < 0) {
895 avahi_log_warn("Recieved invalid packet.");
899 if (avahi_dns_packet_is_query(p)) {
900 gboolean legacy_unicast = FALSE;
902 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
903 avahi_log_warn("Invalid query packet.");
907 if (port != AVAHI_MDNS_PORT) {
910 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
911 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
912 avahi_log_warn("Invalid legacy unicast query packet.");
916 legacy_unicast = TRUE;
920 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
922 handle_query_packet(s, p, i, &a, port, legacy_unicast);
924 /* avahi_log_debug("Handled query"); */
926 if (port != AVAHI_MDNS_PORT) {
927 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
931 if (ttl != 255 && s->config.check_response_ttl) {
932 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
936 if (!is_mdns_mcast_address(dest) &&
937 !avahi_interface_address_on_link(i, &a)) {
938 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
942 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
943 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
944 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
945 avahi_log_warn("Invalid response packet.");
949 handle_response_packet(s, p, i, &a);
950 /* avahi_log_debug("Handled response"); */
954 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, gint ttl) {
955 AvahiInterface *i, *j;
958 AvahiLegacyUnicastReflectSlot *slot;
965 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
966 !avahi_interface_relevant(i)) {
967 avahi_log_warn("Recieved packet from invalid interface.");
971 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
973 port = avahi_port_from_sockaddr(sa);
974 avahi_address_from_sockaddr(sa, &a);
976 if (avahi_address_is_ipv4_in_ipv6(&a))
977 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
980 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
981 avahi_log_warn("Recieved invalid packet.");
985 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
986 avahi_log_warn("Recieved legacy unicast response with unknown id");
990 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
991 !avahi_interface_relevant(j))
994 /* Patch the original ID into this response */
995 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
997 /* Forward the response to the correct client */
998 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1000 /* Undo changes to packet */
1001 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1004 static void work(AvahiServer *s) {
1005 struct sockaddr_in6 sa6;
1006 struct sockaddr_in sa;
1014 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
1015 dest.family = AVAHI_PROTO_INET;
1016 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1017 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1018 avahi_dns_packet_free(p);
1022 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
1023 dest.family = AVAHI_PROTO_INET6;
1024 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1025 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1026 avahi_dns_packet_free(p);
1030 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
1031 dest.family = AVAHI_PROTO_INET;
1032 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1033 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1034 avahi_dns_packet_free(p);
1038 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
1039 dest.family = AVAHI_PROTO_INET6;
1040 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1041 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1042 avahi_dns_packet_free(p);
1047 static gboolean prepare_func(GSource *source, gint *timeout) {
1055 static gboolean check_func(GSource *source) {
1057 gushort revents = 0;
1061 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1064 if (s->fd_ipv4 >= 0)
1065 revents |= s->pollfd_ipv4.revents;
1066 if (s->fd_ipv6 >= 0)
1067 revents |= s->pollfd_ipv6.revents;
1068 if (s->fd_legacy_unicast_ipv4 >= 0)
1069 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1070 if (s->fd_legacy_unicast_ipv6 >= 0)
1071 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1073 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1076 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1080 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1089 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1092 if (s->state == state)
1098 s->callback(s, state, s->userdata);
1101 static void withdraw_host_rrs(AvahiServer *s) {
1104 if (s->hinfo_entry_group)
1105 avahi_entry_group_reset(s->hinfo_entry_group);
1107 if (s->browse_domain_entry_group)
1108 avahi_entry_group_reset(s->browse_domain_entry_group);
1110 avahi_update_host_rrs(s->monitor, TRUE);
1111 s->n_host_rr_pending = 0;
1114 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1117 g_assert(s->n_host_rr_pending > 0);
1119 if (--s->n_host_rr_pending == 0)
1120 server_set_state(s, AVAHI_SERVER_RUNNING);
1123 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1126 s->n_host_rr_pending ++;
1129 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1133 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1134 s->state == AVAHI_SERVER_REGISTERING)
1135 avahi_server_increase_host_rr_pending(s);
1137 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1138 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1139 withdraw_host_rrs(s);
1140 server_set_state(s, AVAHI_SERVER_COLLISION);
1142 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1143 s->state == AVAHI_SERVER_REGISTERING)
1144 avahi_server_decrease_host_rr_pending(s);
1147 static void register_hinfo(AvahiServer *s) {
1148 struct utsname utsname;
1153 if (!s->config.publish_hinfo)
1156 if (s->hinfo_entry_group)
1157 g_assert(avahi_entry_group_is_empty(s->hinfo_entry_group));
1159 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1161 /* Fill in HINFO rr */
1162 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME);
1164 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1165 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1166 avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1167 avahi_record_unref(r);
1169 avahi_entry_group_commit(s->hinfo_entry_group);
1172 static void register_localhost(AvahiServer *s) {
1176 /* Add localhost entries */
1177 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1178 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1180 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1181 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1184 static void register_browse_domain(AvahiServer *s) {
1187 if (!s->config.publish_domain)
1190 if (s->browse_domain_entry_group)
1191 g_assert(avahi_entry_group_is_empty(s->browse_domain_entry_group));
1193 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1195 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);
1196 avahi_entry_group_commit(s->browse_domain_entry_group);
1199 static void register_stuff(AvahiServer *s) {
1202 server_set_state(s, AVAHI_SERVER_REGISTERING);
1204 register_browse_domain(s);
1205 avahi_update_host_rrs(s->monitor, FALSE);
1207 if (s->n_host_rr_pending == 0)
1208 server_set_state(s, AVAHI_SERVER_RUNNING);
1211 static void update_fqdn(AvahiServer *s) {
1214 g_assert(s->host_name);
1215 g_assert(s->domain_name);
1217 g_free(s->host_name_fqdn);
1218 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1221 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1223 g_assert(host_name);
1225 if (host_name && !avahi_valid_host_name(host_name))
1226 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1228 withdraw_host_rrs(s);
1230 g_free(s->host_name);
1231 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1232 s->host_name[strcspn(s->host_name, ".")] = 0;
1239 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1241 g_assert(domain_name);
1243 if (domain_name && !avahi_valid_domain_name(domain_name))
1244 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1246 withdraw_host_rrs(s);
1248 g_free(s->domain_name);
1249 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local");
1256 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1261 memset(pollfd, 0, sizeof(GPollFD));
1263 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1264 g_source_add_poll(s->source, pollfd);
1267 static gint valid_server_config(const AvahiServerConfig *sc) {
1269 if (sc->host_name && !avahi_valid_host_name(sc->host_name))
1270 return AVAHI_ERR_INVALID_HOST_NAME;
1272 if (sc->domain_name && !avahi_valid_domain_name(sc->domain_name))
1273 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1278 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata, gint *error) {
1282 static GSourceFuncs source_funcs = {
1291 if ((e = valid_server_config(sc)) < 0) {
1297 s = g_new(AvahiServer, 1);
1298 s->n_host_rr_pending = 0;
1299 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1302 avahi_server_config_copy(&s->config, sc);
1304 avahi_server_config_init(&s->config);
1306 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1307 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1309 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1310 avahi_server_config_free(&s->config);
1314 *error = AVAHI_ERR_NO_NETWORK;
1319 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1320 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1321 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1322 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1324 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1325 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1327 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1329 /* Prepare IO source registration */
1330 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1331 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1333 if (s->fd_ipv4 >= 0)
1334 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1335 if (s->fd_ipv6 >= 0)
1336 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1337 if (s->fd_legacy_unicast_ipv4 >= 0)
1338 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1339 if (s->fd_legacy_unicast_ipv6 >= 0)
1340 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1342 g_source_attach(s->source, s->context);
1344 s->callback = callback;
1345 s->userdata = userdata;
1347 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1348 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1349 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1351 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1352 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1353 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1354 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1355 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1356 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1357 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1358 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1359 AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1361 s->legacy_unicast_reflect_slots = NULL;
1362 s->legacy_unicast_reflect_id = 0;
1365 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1366 s->host_name[strcspn(s->host_name, ".")] = 0;
1367 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local");
1368 s->host_name_fqdn = NULL;
1371 s->record_list = avahi_record_list_new();
1373 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1375 s->state = AVAHI_SERVER_INVALID;
1377 s->monitor = avahi_interface_monitor_new(s);
1378 avahi_interface_monitor_sync(s->monitor);
1380 register_localhost(s);
1382 s->hinfo_entry_group = NULL;
1383 s->browse_domain_entry_group = NULL;
1386 s->error = AVAHI_OK;
1391 void avahi_server_free(AvahiServer* s) {
1395 free_entry(s, s->entries);
1397 avahi_interface_monitor_free(s->monitor);
1400 free_group(s, s->groups);
1404 while (s->dns_server_browsers)
1405 avahi_dns_server_browser_free(s->dns_server_browsers);
1406 while (s->host_name_resolvers)
1407 avahi_host_name_resolver_free(s->host_name_resolvers);
1408 while (s->address_resolvers)
1409 avahi_address_resolver_free(s->address_resolvers);
1410 while (s->domain_browsers)
1411 avahi_domain_browser_free(s->domain_browsers);
1412 while (s->service_type_browsers)
1413 avahi_service_type_browser_free(s->service_type_browsers);
1414 while (s->service_browsers)
1415 avahi_service_browser_free(s->service_browsers);
1416 while (s->service_resolvers)
1417 avahi_service_resolver_free(s->service_resolvers);
1418 while (s->record_browsers)
1419 avahi_record_browser_destroy(s->record_browsers);
1420 g_hash_table_destroy(s->record_browser_hashtable);
1422 g_hash_table_destroy(s->entries_by_key);
1424 avahi_time_event_queue_free(s->time_event_queue);
1426 avahi_record_list_free(s->record_list);
1428 if (s->fd_ipv4 >= 0)
1430 if (s->fd_ipv6 >= 0)
1432 if (s->fd_legacy_unicast_ipv4 >= 0)
1433 close(s->fd_legacy_unicast_ipv4);
1434 if (s->fd_legacy_unicast_ipv6 >= 0)
1435 close(s->fd_legacy_unicast_ipv6);
1437 g_free(s->host_name);
1438 g_free(s->domain_name);
1439 g_free(s->host_name_fqdn);
1441 g_source_destroy(s->source);
1442 g_source_unref(s->source);
1443 g_main_context_unref(s->context);
1445 avahi_server_config_free(&s->config);
1450 static gint check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1456 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1460 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1463 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1466 if (interface <= 0 ||
1467 e->interface <= 0 ||
1468 e->interface == interface ||
1469 protocol == AVAHI_PROTO_UNSPEC ||
1470 e->protocol == AVAHI_PROTO_UNSPEC ||
1471 e->protocol == protocol)
1480 gint avahi_server_add(
1483 AvahiIfIndex interface,
1484 AvahiProtocol protocol,
1485 AvahiEntryFlags flags,
1494 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1496 if (avahi_key_is_pattern(r->key))
1497 return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1499 if (!avahi_record_valid(r))
1500 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1502 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1503 return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1505 e = g_new(AvahiEntry, 1);
1507 e->record = avahi_record_ref(r);
1509 e->interface = interface;
1510 e->protocol = protocol;
1514 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1516 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1518 /* Insert into hash table indexed by name */
1519 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1520 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1521 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1523 /* Insert into group list */
1525 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1527 avahi_announce_entry(s, e);
1532 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1533 AvahiEntry **e = (AvahiEntry**) state;
1538 *e = g ? g->entries : s->entries;
1540 while (*e && (*e)->dead)
1541 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1546 return avahi_record_ref((*e)->record);
1549 void avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, gpointer userdata) {
1555 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1557 for (e = s->entries; e; e = e->entries_next) {
1564 t = avahi_record_to_string(e->record);
1565 g_snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1568 callback(ln, userdata);
1571 avahi_dump_caches(s->monitor, callback, userdata);
1574 gint avahi_server_add_ptr(
1577 AvahiIfIndex interface,
1578 AvahiProtocol protocol,
1579 AvahiEntryFlags flags,
1582 const gchar *dest) {
1590 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl);
1591 r->data.ptr.name = avahi_normalize_name(dest);
1592 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1593 avahi_record_unref(r);
1597 gint avahi_server_add_address(
1600 AvahiIfIndex interface,
1601 AvahiProtocol protocol,
1602 AvahiEntryFlags flags,
1607 gint ret = AVAHI_OK;
1611 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1613 if (!avahi_valid_domain_name(name)) {
1614 avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1618 if (a->family == AVAHI_PROTO_INET) {
1622 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1623 r->data.a.address = a->data.ipv4;
1624 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1625 avahi_record_unref(r);
1630 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1631 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1638 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1639 r->data.aaaa.address = a->data.ipv6;
1640 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1641 avahi_record_unref(r);
1646 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1647 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1653 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1654 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1665 static gint server_add_txt_strlst_nocopy(
1668 AvahiIfIndex interface,
1669 AvahiProtocol protocol,
1670 AvahiEntryFlags flags,
1673 AvahiStringList *strlst) {
1680 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl);
1681 r->data.txt.string_list = strlst;
1682 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1683 avahi_record_unref(r);
1688 gint avahi_server_add_txt_strlst(
1691 AvahiIfIndex interface,
1692 AvahiProtocol protocol,
1693 AvahiEntryFlags flags,
1696 AvahiStringList *strlst) {
1700 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1703 gint avahi_server_add_txt_va(
1706 AvahiIfIndex interface,
1707 AvahiProtocol protocol,
1708 AvahiEntryFlags flags,
1715 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1718 gint avahi_server_add_txt(
1721 AvahiIfIndex interface,
1722 AvahiProtocol protocol,
1723 AvahiEntryFlags flags,
1734 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1740 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1745 while (*s && size >= 2) {
1746 if (*s == '.' || *s == '\\') {
1762 static gint server_add_service_strlst_nocopy(
1765 AvahiIfIndex interface,
1766 AvahiProtocol protocol,
1769 const gchar *domain,
1772 AvahiStringList *strlst) {
1774 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1776 AvahiRecord *r = NULL;
1783 if (!avahi_valid_service_name(name))
1784 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1786 if (!avahi_valid_service_type(type))
1787 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1789 if (domain && !avahi_valid_domain_name(domain))
1790 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1792 if (host && !avahi_valid_domain_name(host))
1793 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1796 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
1798 escape_service_name(ename, sizeof(ename), name);
1801 domain = s->domain_name;
1804 host = s->host_name_fqdn;
1806 d = avahi_normalize_name(domain);
1807 t = avahi_normalize_name(type);
1809 g_snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1810 g_snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1812 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1815 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1816 r->data.srv.priority = 0;
1817 r->data.srv.weight = 0;
1818 r->data.srv.port = port;
1819 r->data.srv.name = avahi_normalize_name(host);
1820 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1821 avahi_record_unref(r);
1826 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1832 g_snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1833 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1840 avahi_string_list_free(strlst);
1845 gint avahi_server_add_service_strlst(
1848 AvahiIfIndex interface,
1849 AvahiProtocol protocol,
1852 const gchar *domain,
1855 AvahiStringList *strlst) {
1861 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1864 gint avahi_server_add_service_va(
1867 AvahiIfIndex interface,
1868 AvahiProtocol protocol,
1871 const gchar *domain,
1880 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1883 gint avahi_server_add_service(
1886 AvahiIfIndex interface,
1887 AvahiProtocol protocol,
1890 const gchar *domain,
1903 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1908 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1909 static const gchar hex[] = "0123456789abcdef";
1911 const guint8 *k = p;
1913 while (sl > 1 && pl > 0) {
1914 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1930 gint avahi_server_add_dns_server_address(
1933 AvahiIfIndex interface,
1934 AvahiProtocol protocol,
1935 const gchar *domain,
1936 AvahiDNSServerType type,
1937 const AvahiAddress *address,
1938 guint16 port /** should be 53 */) {
1942 gchar n[64] = "ip-";
1946 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1947 g_assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1949 if (domain && !avahi_valid_domain_name(domain))
1950 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1953 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
1955 if (address->family == AVAHI_PROTO_INET) {
1956 hexstring(n+3, sizeof(n)-3, &address->data, 4);
1957 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1958 r->data.a.address = address->data.ipv4;
1960 hexstring(n+3, sizeof(n)-3, &address->data, 6);
1961 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1962 r->data.aaaa.address = address->data.ipv6;
1965 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1966 avahi_record_unref(r);
1971 return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1974 gint avahi_server_add_dns_server_name(
1977 AvahiIfIndex interface,
1978 AvahiProtocol protocol,
1979 const gchar *domain,
1980 AvahiDNSServerType type,
1982 guint16 port /** should be 53 */) {
1990 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1992 if (domain && !avahi_valid_domain_name(domain))
1993 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1996 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
1999 domain = s->domain_name;
2001 d = avahi_normalize_name(domain);
2002 g_snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2005 r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
2006 r->data.srv.priority = 0;
2007 r->data.srv.weight = 0;
2008 r->data.srv.port = port;
2009 r->data.srv.name = avahi_normalize_name(name);
2010 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2011 avahi_record_unref(r);
2016 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
2017 AvahiKey *k = userdata;
2023 avahi_interface_post_query(i, k, FALSE);
2026 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2030 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2033 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
2036 if (g->state == state)
2039 g_assert(state >= AVAHI_ENTRY_GROUP_UNCOMMITED && state <= AVAHI_ENTRY_GROUP_COLLISION);
2044 g->callback(g->server, g, state, g->userdata);
2047 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
2052 g = g_new(AvahiEntryGroup, 1);
2054 g->callback = callback;
2055 g->userdata = userdata;
2057 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2059 g->n_register_try = 0;
2060 g->register_time_event = NULL;
2061 g->register_time.tv_sec = 0;
2062 g->register_time.tv_usec = 0;
2063 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2065 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
2069 void avahi_entry_group_free(AvahiEntryGroup *g) {
2073 g_assert(g->server);
2075 for (e = g->entries; e; e = e->by_group_next) {
2077 avahi_goodbye_entry(g->server, e, TRUE);
2082 if (g->register_time_event) {
2083 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2084 g->register_time_event = NULL;
2089 g->server->need_group_cleanup = TRUE;
2090 g->server->need_entry_cleanup = TRUE;
2093 static void entry_group_commit_real(AvahiEntryGroup *g) {
2096 g_get_current_time(&g->register_time);
2098 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2101 avahi_announce_group(g->server, g);
2102 avahi_entry_group_check_probed(g, FALSE);
2106 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
2107 AvahiEntryGroup *g = userdata;
2110 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2112 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2113 g->register_time_event = NULL;
2115 /* Holdoff time passed, so let's start probing */
2116 entry_group_commit_real(g);
2119 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
2125 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2126 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2128 g->n_register_try++;
2130 avahi_timeval_add(&g->register_time,
2131 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2132 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2133 AVAHI_RR_HOLDOFF_MSEC));
2135 g_get_current_time(&now);
2137 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2138 /* Holdoff time passed, so let's start probing */
2139 /* avahi_log_debug("Holdoff passed, directly going on."); */
2141 entry_group_commit_real(g);
2143 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2145 /* Holdoff time has not yet passed, so let's wait */
2146 g_assert(!g->register_time_event);
2147 g->register_time_event = avahi_time_event_queue_add(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2149 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2155 void avahi_entry_group_reset(AvahiEntryGroup *g) {
2159 if (g->register_time_event) {
2160 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2161 g->register_time_event = NULL;
2164 for (e = g->entries; e; e = e->by_group_next) {
2166 avahi_goodbye_entry(g->server, e, TRUE);
2171 if (g->register_time_event) {
2172 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2173 g->register_time_event = NULL;
2176 g->server->need_entry_cleanup = TRUE;
2179 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2182 gboolean avahi_entry_commited(AvahiEntry *e) {
2187 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2188 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2191 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
2198 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
2201 g->userdata = userdata;
2204 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
2210 gboolean avahi_entry_group_is_empty(AvahiEntryGroup *g) {
2214 /* Look for an entry that is not dead */
2215 for (e = g->entries; e; e = e->by_group_next)
2222 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
2225 return s->domain_name;
2228 const gchar* avahi_server_get_host_name(AvahiServer *s) {
2231 return s->host_name;
2234 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2237 return s->host_name_fqdn;
2240 gpointer avahi_server_get_data(AvahiServer *s) {
2246 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2249 s->userdata = userdata;
2252 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2258 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2261 memset(c, 0, sizeof(AvahiServerConfig));
2264 c->host_name = NULL;
2265 c->domain_name = NULL;
2266 c->check_response_ttl = FALSE;
2267 c->publish_hinfo = TRUE;
2268 c->publish_addresses = TRUE;
2269 c->publish_workstation = TRUE;
2270 c->publish_domain = TRUE;
2271 c->use_iff_running = FALSE;
2272 c->enable_reflector = FALSE;
2273 c->reflect_ipv = FALSE;
2278 void avahi_server_config_free(AvahiServerConfig *c) {
2281 g_free(c->host_name);
2282 g_free(c->domain_name);
2285 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2291 ret->host_name = g_strdup(c->host_name);
2292 ret->domain_name = g_strdup(c->domain_name);
2297 const gchar *avahi_strerror(gint error) {
2298 g_assert(-error >= 0 && -error < -AVAHI_ERR_MAX);
2300 const gchar * const msg[- AVAHI_ERR_MAX] = {
2304 "Invalid host name",
2305 "Invalid domain name",
2306 "No suitable network protocol available",
2308 "Resource record key is pattern",
2309 "Local name collision",
2311 "Invalid service name",
2312 "Invalid service type",
2313 "Invalid port number",
2314 "Invalid record key",
2328 gint avahi_server_errno(AvahiServer *s) {
2334 /* Just for internal use */
2335 gint avahi_server_set_errno(AvahiServer *s, gint error) {
2338 return s->error = error;