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>
34 #include <avahi-common/domain.h>
35 #include <avahi-common/timeval.h>
43 #define AVAHI_RR_HOLDOFF_MSEC 1000
44 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
45 #define AVAHI_RR_RATE_LIMIT_COUNT 15
47 static void free_entry(AvahiServer*s, AvahiEntry *e) {
53 avahi_goodbye_entry(s, e, TRUE);
55 /* Remove from linked list */
56 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
58 /* Remove from hash table indexed by name */
59 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
60 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
62 g_hash_table_replace(s->entries_by_key, t->record->key, t);
64 g_hash_table_remove(s->entries_by_key, e->record->key);
66 /* Remove from associated group */
68 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
70 avahi_record_unref(e->record);
74 static void free_group(AvahiServer *s, AvahiEntryGroup *g) {
79 free_entry(s, g->entries);
81 if (g->register_time_event)
82 avahi_time_event_queue_remove(s->time_event_queue, g->register_time_event);
84 AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
88 static void cleanup_dead(AvahiServer *s) {
89 AvahiEntryGroup *g, *ng;
94 if (s->need_group_cleanup) {
95 for (g = s->groups; g; g = ng) {
102 s->need_group_cleanup = FALSE;
105 if (s->need_entry_cleanup) {
106 for (e = s->entries; e; e = ne) {
107 ne = e->entries_next;
113 s->need_entry_cleanup = FALSE;
116 if (s->need_browser_cleanup)
117 avahi_browser_cleanup(s);
120 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) {
129 g_assert(type != AVAHI_DNS_TYPE_ANY);
131 k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
133 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
134 if (!e->dead && avahi_entry_registered(s, e, i))
135 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
140 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) {
146 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
147 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
148 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
149 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
150 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
151 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
152 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
157 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary) {
162 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
165 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
173 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
176 if (avahi_key_is_pattern(k)) {
178 /* Handle ANY query */
180 for (e = s->entries; e; e = e->entries_next)
181 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
182 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
186 /* Handle all other queries */
188 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
189 if (!e->dead && avahi_entry_registered(s, e, i))
190 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
194 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
201 for (k = e->group->entries; k; k = k->by_group_next) {
203 avahi_goodbye_entry(s, k, FALSE);
208 e->group->n_probing = 0;
210 avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
212 avahi_goodbye_entry(s, e, FALSE);
216 s->need_entry_cleanup = TRUE;
219 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
225 for (e = g_hash_table_lookup(s->entries_by_key, key); e; e = e->by_key_next)
227 withdraw_entry(s, e);
230 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
233 gboolean ours = FALSE, won = FALSE, lost = FALSE;
239 t = avahi_record_to_string(record);
241 /* avahi_log_debug("incoming_probe()"); */
243 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
250 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
255 if (avahi_entry_probing(s, e, i)) {
268 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
270 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
271 withdraw_rrset(s, record->key);
273 /* avahi_log_debug("Not conflicting probe"); */
279 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
280 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
281 AvahiEntry *e, *n, *conflicting_entry = NULL;
288 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
290 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
293 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
296 /* Either our entry or the other is intended to be unique, so let's check */
298 if (avahi_record_equal_no_ttl(e->record, record)) {
299 ours = TRUE; /* We have an identical record, so this is no conflict */
301 /* Check wheter there is a TTL conflict */
302 if (record->ttl <= e->record->ttl/2 &&
303 avahi_entry_registered(s, e, i)) {
306 t = avahi_record_to_string(record);
308 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
309 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
315 /* There's no need to check the other entries of this RRset */
320 if (avahi_entry_registered(s, e, i)) {
322 /* A conflict => we have to return to probe mode */
324 conflicting_entry = e;
326 } else if (avahi_entry_probing(s, e, i)) {
328 /* We are currently registering a matching record, but
329 * someone else already claimed it, so let's
332 withdraw_immediately = TRUE;
337 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
339 if (!ours && conflict) {
344 t = avahi_record_to_string(record);
346 if (withdraw_immediately) {
347 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
348 withdraw_rrset(s, record->key);
350 g_assert(conflicting_entry);
351 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
352 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
354 /* Local unique records are returned to probing
355 * state. Local shared records are reannounced. */
364 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
365 gboolean *unicast_response = userdata;
369 g_assert(unicast_response);
371 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
374 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
378 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
381 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean immediately) {
385 g_assert(!legacy_unicast || (a && port > 0 && p));
387 if (legacy_unicast) {
388 AvahiDnsPacket *reply;
391 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
393 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
395 append_aux_records_to_list(s, i, r, FALSE);
397 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
398 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
400 gchar *t = avahi_record_to_string(r);
401 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
405 avahi_record_unref(r);
408 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
409 avahi_interface_send_packet_unicast(i, reply, a, port);
411 avahi_dns_packet_free(reply);
414 gboolean unicast_response, flush_cache, auxiliary;
415 AvahiDnsPacket *reply = NULL;
418 /* In case the query packet was truncated never respond
419 immediately, because known answer suppression records might be
420 contained in later packets */
421 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
423 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
425 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
427 append_aux_records_to_list(s, i, r, unicast_response);
429 /* Due to some reasons the record has not been scheduled.
430 * The client requested an unicast response in that
431 * case. Therefore we prepare such a response */
437 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
440 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
442 /* Appending this record succeeded, so incremeant
443 * the specific header field, and return to the caller */
445 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
450 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
453 /* The record is too large for one packet, so create a larger packet */
455 avahi_dns_packet_free(reply);
456 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
457 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
458 size = AVAHI_DNS_PACKET_MAX_SIZE;
459 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
461 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
463 avahi_dns_packet_free(reply);
464 t = avahi_record_to_string(r);
465 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
469 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
472 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
473 avahi_interface_send_packet_unicast(i, reply, a, port);
474 avahi_dns_packet_free(reply);
479 avahi_record_unref(r);
483 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
484 avahi_interface_send_packet_unicast(i, reply, a, port);
485 avahi_dns_packet_free(reply);
489 avahi_record_list_flush(s->record_list);
493 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
500 if (!s->config.enable_reflector)
503 for (j = s->monitor->interfaces; j; j = j->interface_next)
504 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
505 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
508 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
509 AvahiServer *s = userdata;
516 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
520 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
527 if (!s->config.enable_reflector)
530 for (j = s->monitor->interfaces; j; j = j->interface_next)
531 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
532 /* Post the query to other networks */
533 avahi_interface_post_query(j, k, TRUE);
535 /* Reply from caches of other network. This is needed to
536 * "work around" known answer suppression. */
538 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
542 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
549 if (!s->config.enable_reflector)
552 for (j = s->monitor->interfaces; j; j = j->interface_next)
553 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
554 avahi_interface_post_probe(j, r, TRUE);
557 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
566 /* avahi_log_debug("query"); */
568 g_assert(avahi_record_list_is_empty(s->record_list));
570 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
572 /* Handle the questions */
573 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
575 gboolean unicast_response = FALSE;
577 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
578 avahi_log_warn("Packet too short (1)");
583 reflect_query(s, i, key);
585 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
586 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
587 /* Allow our own queries to be suppressed by incoming
588 * queries only when they do not include known answers */
589 avahi_query_scheduler_incoming(i->query_scheduler, key);
591 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
592 avahi_key_unref(key);
595 /* Known Answer Suppression */
596 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
598 gboolean unique = FALSE;
600 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
601 avahi_log_warn("Packet too short (2)");
605 if (handle_conflict(s, i, record, unique, a)) {
606 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
607 avahi_record_list_drop(s->record_list, record);
610 avahi_record_unref(record);
614 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
616 gboolean unique = FALSE;
618 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
619 avahi_log_warn("Packet too short (3)");
623 if (!avahi_key_is_pattern(record->key)) {
624 reflect_probe(s, i, record);
625 incoming_probe(s, record, i);
628 avahi_record_unref(record);
631 if (!avahi_record_list_is_empty(s->record_list))
632 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
637 avahi_record_list_flush(s->record_list);
640 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
648 /* avahi_log_debug("response"); */
650 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
651 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
653 gboolean cache_flush = FALSE;
656 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
657 avahi_log_warn("Packet too short (4)");
661 if (!avahi_key_is_pattern(record->key)) {
663 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
666 if (handle_conflict(s, i, record, cache_flush, a)) {
667 reflect_response(s, i, record, cache_flush);
668 avahi_cache_update(i->cache, record, cache_flush, a);
669 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
673 avahi_record_unref(record);
676 /* If the incoming response contained a conflicting record, some
677 records have been scheduling for sending. We need to flush them
679 if (!avahi_record_list_is_empty(s->record_list))
680 avahi_server_generate_response(s, i, NULL, NULL, 0, FALSE, TRUE);
683 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
684 guint n, idx = (guint) -1;
685 AvahiLegacyUnicastReflectSlot *slot;
689 if (!s->legacy_unicast_reflect_slots)
690 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
692 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
693 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
695 if (!s->legacy_unicast_reflect_slots[idx])
699 if (idx == (guint) -1 || s->legacy_unicast_reflect_slots[idx])
702 slot = s->legacy_unicast_reflect_slots[idx] = g_new(AvahiLegacyUnicastReflectSlot, 1);
703 slot->id = s->legacy_unicast_reflect_id++;
708 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
714 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
716 g_assert(s->legacy_unicast_reflect_slots[idx] == slot);
718 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
721 s->legacy_unicast_reflect_slots[idx] = NULL;
724 static void free_slots(AvahiServer *s) {
728 if (!s->legacy_unicast_reflect_slots)
731 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
732 if (s->legacy_unicast_reflect_slots[idx])
733 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
735 g_free(s->legacy_unicast_reflect_slots);
736 s->legacy_unicast_reflect_slots = NULL;
739 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
744 if (!s->legacy_unicast_reflect_slots)
747 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
749 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
752 return s->legacy_unicast_reflect_slots[idx];
755 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
756 AvahiLegacyUnicastReflectSlot *slot = userdata;
760 g_assert(slot->time_event == e);
762 deallocate_slot(slot->server, slot);
765 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
766 AvahiLegacyUnicastReflectSlot *slot;
774 g_assert(i->protocol == a->family);
776 if (!s->config.enable_reflector)
779 /* avahi_log_debug("legacy unicast reflector"); */
781 /* Reflecting legacy unicast queries is a little more complicated
782 than reflecting normal queries, since we must route the
783 responses back to the right client. Therefore we must store
784 some information for finding the right client contact data for
785 response packets. In contrast to normal queries legacy
786 unicast query and response packets are reflected untouched and
787 are not reassembled into larger packets */
789 if (!(slot = allocate_slot(s))) {
790 /* No slot available, we drop this legacy unicast query */
791 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
795 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
798 slot->interface = i->hardware->index;
800 avahi_elapse_time(&slot->elapse_time, 2000, 0);
801 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
803 /* Patch the packet with our new locally generatedt id */
804 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
806 for (j = s->monitor->interfaces; j; j = j->interface_next)
807 if (avahi_interface_relevant(j) &&
809 (s->config.reflect_ipv || j->protocol == i->protocol)) {
811 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
812 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
813 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
814 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
818 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
821 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
826 if (!s->config.enable_reflector)
829 avahi_address_from_sockaddr(sa, &a);
831 if (!avahi_address_is_local(s->monitor, &a))
834 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
835 struct sockaddr_in lsa;
836 socklen_t l = sizeof(lsa);
838 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
839 avahi_log_warn("getsockname(): %s", strerror(errno));
841 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
845 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
846 struct sockaddr_in6 lsa;
847 socklen_t l = sizeof(lsa);
849 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
850 avahi_log_warn("getsockname(): %s", strerror(errno));
852 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
858 static gboolean is_mdns_mcast_address(const AvahiAddress *a) {
862 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
863 return avahi_address_cmp(a, &b) == 0;
866 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, gint ttl) {
877 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
878 !avahi_interface_relevant(i)) {
879 avahi_log_warn("Recieved packet from invalid interface.");
883 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
885 port = avahi_port_from_sockaddr(sa);
886 avahi_address_from_sockaddr(sa, &a);
888 if (avahi_address_is_ipv4_in_ipv6(&a))
889 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
892 if (originates_from_local_legacy_unicast_socket(s, sa))
893 /* This originates from our local reflector, so let's ignore it */
896 if (avahi_dns_packet_check_valid(p) < 0) {
897 avahi_log_warn("Recieved invalid packet.");
901 if (avahi_dns_packet_is_query(p)) {
902 gboolean legacy_unicast = FALSE;
904 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
905 avahi_log_warn("Invalid query packet.");
909 if (port != AVAHI_MDNS_PORT) {
912 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
913 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
914 avahi_log_warn("Invalid legacy unicast query packet.");
918 legacy_unicast = TRUE;
922 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
924 handle_query_packet(s, p, i, &a, port, legacy_unicast);
926 /* avahi_log_debug("Handled query"); */
928 if (port != AVAHI_MDNS_PORT) {
929 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
933 if (ttl != 255 && s->config.check_response_ttl) {
934 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
938 if (!is_mdns_mcast_address(dest) &&
939 !avahi_interface_address_on_link(i, &a)) {
940 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
944 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
945 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
946 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
947 avahi_log_warn("Invalid response packet.");
951 handle_response_packet(s, p, i, &a);
952 /* avahi_log_debug("Handled response"); */
956 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, gint ttl) {
957 AvahiInterface *i, *j;
960 AvahiLegacyUnicastReflectSlot *slot;
967 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
968 !avahi_interface_relevant(i)) {
969 avahi_log_warn("Recieved packet from invalid interface.");
973 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
975 port = avahi_port_from_sockaddr(sa);
976 avahi_address_from_sockaddr(sa, &a);
978 if (avahi_address_is_ipv4_in_ipv6(&a))
979 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
982 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
983 avahi_log_warn("Recieved invalid packet.");
987 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
988 avahi_log_warn("Recieved legacy unicast response with unknown id");
992 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
993 !avahi_interface_relevant(j))
996 /* Patch the original ID into this response */
997 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
999 /* Forward the response to the correct client */
1000 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
1002 /* Undo changes to packet */
1003 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1006 static void work(AvahiServer *s) {
1007 struct sockaddr_in6 sa6;
1008 struct sockaddr_in sa;
1016 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
1017 dest.family = AVAHI_PROTO_INET;
1018 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1019 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1020 avahi_dns_packet_free(p);
1024 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
1025 dest.family = AVAHI_PROTO_INET6;
1026 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1027 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1028 avahi_dns_packet_free(p);
1032 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
1033 dest.family = AVAHI_PROTO_INET;
1034 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1035 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1036 avahi_dns_packet_free(p);
1040 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
1041 dest.family = AVAHI_PROTO_INET6;
1042 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1043 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1044 avahi_dns_packet_free(p);
1049 static gboolean prepare_func(GSource *source, gint *timeout) {
1057 static gboolean check_func(GSource *source) {
1059 gushort revents = 0;
1063 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1066 if (s->fd_ipv4 >= 0)
1067 revents |= s->pollfd_ipv4.revents;
1068 if (s->fd_ipv6 >= 0)
1069 revents |= s->pollfd_ipv6.revents;
1070 if (s->fd_legacy_unicast_ipv4 >= 0)
1071 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1072 if (s->fd_legacy_unicast_ipv6 >= 0)
1073 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1075 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1078 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1082 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1091 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1094 if (s->state == state)
1100 s->callback(s, state, s->userdata);
1103 static void withdraw_host_rrs(AvahiServer *s) {
1106 if (s->hinfo_entry_group)
1107 avahi_entry_group_reset(s->hinfo_entry_group);
1109 if (s->browse_domain_entry_group)
1110 avahi_entry_group_reset(s->browse_domain_entry_group);
1112 avahi_update_host_rrs(s->monitor, TRUE);
1113 s->n_host_rr_pending = 0;
1116 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1119 g_assert(s->n_host_rr_pending > 0);
1121 if (--s->n_host_rr_pending == 0)
1122 server_set_state(s, AVAHI_SERVER_RUNNING);
1125 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1128 s->n_host_rr_pending ++;
1131 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1135 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1136 s->state == AVAHI_SERVER_REGISTERING)
1137 avahi_server_increase_host_rr_pending(s);
1139 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1140 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1141 withdraw_host_rrs(s);
1142 server_set_state(s, AVAHI_SERVER_COLLISION);
1144 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1145 s->state == AVAHI_SERVER_REGISTERING)
1146 avahi_server_decrease_host_rr_pending(s);
1149 static void register_hinfo(AvahiServer *s) {
1150 struct utsname utsname;
1155 if (!s->config.publish_hinfo)
1158 if (s->hinfo_entry_group)
1159 g_assert(avahi_entry_group_is_empty(s->hinfo_entry_group));
1161 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1163 /* Fill in HINFO rr */
1164 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME);
1166 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1167 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1168 avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1169 avahi_record_unref(r);
1171 avahi_entry_group_commit(s->hinfo_entry_group);
1174 static void register_localhost(AvahiServer *s) {
1178 /* Add localhost entries */
1179 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1180 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1182 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1183 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1186 static void register_browse_domain(AvahiServer *s) {
1189 if (!s->config.publish_domain)
1192 if (s->browse_domain_entry_group)
1193 g_assert(avahi_entry_group_is_empty(s->browse_domain_entry_group));
1195 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1197 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);
1198 avahi_entry_group_commit(s->browse_domain_entry_group);
1201 static void register_stuff(AvahiServer *s) {
1204 server_set_state(s, AVAHI_SERVER_REGISTERING);
1206 register_browse_domain(s);
1207 avahi_update_host_rrs(s->monitor, FALSE);
1209 if (s->n_host_rr_pending == 0)
1210 server_set_state(s, AVAHI_SERVER_RUNNING);
1213 static void update_fqdn(AvahiServer *s) {
1216 g_assert(s->host_name);
1217 g_assert(s->domain_name);
1219 g_free(s->host_name_fqdn);
1220 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1223 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1225 g_assert(host_name);
1227 if (host_name && !avahi_is_valid_host_name(host_name))
1228 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1230 withdraw_host_rrs(s);
1232 g_free(s->host_name);
1233 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1234 s->host_name[strcspn(s->host_name, ".")] = 0;
1241 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1243 g_assert(domain_name);
1245 if (domain_name && !avahi_is_valid_domain_name(domain_name))
1246 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1248 withdraw_host_rrs(s);
1250 g_free(s->domain_name);
1251 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local");
1258 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1263 memset(pollfd, 0, sizeof(GPollFD));
1265 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1266 g_source_add_poll(s->source, pollfd);
1269 static gint valid_server_config(const AvahiServerConfig *sc) {
1271 if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
1272 return AVAHI_ERR_INVALID_HOST_NAME;
1274 if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
1275 return AVAHI_ERR_INVALID_DOMAIN_NAME;
1280 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata, gint *error) {
1284 static GSourceFuncs source_funcs = {
1293 if ((e = valid_server_config(sc)) < 0) {
1299 s = g_new(AvahiServer, 1);
1300 s->n_host_rr_pending = 0;
1301 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1304 avahi_server_config_copy(&s->config, sc);
1306 avahi_server_config_init(&s->config);
1308 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1309 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1311 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1312 avahi_server_config_free(&s->config);
1316 *error = AVAHI_ERR_NO_NETWORK;
1321 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1322 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1323 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1324 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1326 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1327 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1329 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1331 /* Prepare IO source registration */
1332 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1333 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1335 if (s->fd_ipv4 >= 0)
1336 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1337 if (s->fd_ipv6 >= 0)
1338 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1339 if (s->fd_legacy_unicast_ipv4 >= 0)
1340 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1341 if (s->fd_legacy_unicast_ipv6 >= 0)
1342 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1344 g_source_attach(s->source, s->context);
1346 s->callback = callback;
1347 s->userdata = userdata;
1349 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1350 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1351 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1353 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1354 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1355 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1356 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1357 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1358 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1359 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1360 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1361 AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1363 s->legacy_unicast_reflect_slots = NULL;
1364 s->legacy_unicast_reflect_id = 0;
1367 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1368 s->host_name[strcspn(s->host_name, ".")] = 0;
1369 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local");
1370 s->host_name_fqdn = NULL;
1373 s->record_list = avahi_record_list_new();
1375 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1377 s->state = AVAHI_SERVER_INVALID;
1379 s->monitor = avahi_interface_monitor_new(s);
1380 avahi_interface_monitor_sync(s->monitor);
1382 register_localhost(s);
1384 s->hinfo_entry_group = NULL;
1385 s->browse_domain_entry_group = NULL;
1388 s->error = AVAHI_OK;
1393 void avahi_server_free(AvahiServer* s) {
1397 free_entry(s, s->entries);
1399 avahi_interface_monitor_free(s->monitor);
1402 free_group(s, s->groups);
1406 while (s->dns_server_browsers)
1407 avahi_dns_server_browser_free(s->dns_server_browsers);
1408 while (s->host_name_resolvers)
1409 avahi_host_name_resolver_free(s->host_name_resolvers);
1410 while (s->address_resolvers)
1411 avahi_address_resolver_free(s->address_resolvers);
1412 while (s->domain_browsers)
1413 avahi_domain_browser_free(s->domain_browsers);
1414 while (s->service_type_browsers)
1415 avahi_service_type_browser_free(s->service_type_browsers);
1416 while (s->service_browsers)
1417 avahi_service_browser_free(s->service_browsers);
1418 while (s->service_resolvers)
1419 avahi_service_resolver_free(s->service_resolvers);
1420 while (s->record_browsers)
1421 avahi_record_browser_destroy(s->record_browsers);
1422 g_hash_table_destroy(s->record_browser_hashtable);
1424 g_hash_table_destroy(s->entries_by_key);
1426 avahi_time_event_queue_free(s->time_event_queue);
1428 avahi_record_list_free(s->record_list);
1430 if (s->fd_ipv4 >= 0)
1432 if (s->fd_ipv6 >= 0)
1434 if (s->fd_legacy_unicast_ipv4 >= 0)
1435 close(s->fd_legacy_unicast_ipv4);
1436 if (s->fd_legacy_unicast_ipv6 >= 0)
1437 close(s->fd_legacy_unicast_ipv6);
1439 g_free(s->host_name);
1440 g_free(s->domain_name);
1441 g_free(s->host_name_fqdn);
1443 g_source_destroy(s->source);
1444 g_source_unref(s->source);
1445 g_main_context_unref(s->context);
1447 avahi_server_config_free(&s->config);
1452 static gint check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1458 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1462 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1465 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1468 if (interface <= 0 ||
1469 e->interface <= 0 ||
1470 e->interface == interface ||
1471 protocol == AVAHI_PROTO_UNSPEC ||
1472 e->protocol == AVAHI_PROTO_UNSPEC ||
1473 e->protocol == protocol)
1482 gint avahi_server_add(
1485 AvahiIfIndex interface,
1486 AvahiProtocol protocol,
1487 AvahiEntryFlags flags,
1496 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_TTL);
1498 if (avahi_key_is_pattern(r->key))
1499 return avahi_server_set_errno(s, AVAHI_ERR_IS_PATTERN);
1501 if (!avahi_record_is_valid(r))
1502 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_RECORD);
1504 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1505 return avahi_server_set_errno(s, AVAHI_ERR_LOCAL_COLLISION);
1507 e = g_new(AvahiEntry, 1);
1509 e->record = avahi_record_ref(r);
1511 e->interface = interface;
1512 e->protocol = protocol;
1516 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1518 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1520 /* Insert into hash table indexed by name */
1521 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1522 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1523 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1525 /* Insert into group list */
1527 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1529 avahi_announce_entry(s, e);
1534 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1535 AvahiEntry **e = (AvahiEntry**) state;
1540 *e = g ? g->entries : s->entries;
1542 while (*e && (*e)->dead)
1543 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1548 return avahi_record_ref((*e)->record);
1551 void avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, gpointer userdata) {
1557 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1559 for (e = s->entries; e; e = e->entries_next) {
1566 t = avahi_record_to_string(e->record);
1567 g_snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1570 callback(ln, userdata);
1573 avahi_dump_caches(s->monitor, callback, userdata);
1576 gint avahi_server_add_ptr(
1579 AvahiIfIndex interface,
1580 AvahiProtocol protocol,
1581 AvahiEntryFlags flags,
1584 const gchar *dest) {
1592 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl);
1593 r->data.ptr.name = avahi_normalize_name(dest);
1594 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1595 avahi_record_unref(r);
1599 gint avahi_server_add_address(
1602 AvahiIfIndex interface,
1603 AvahiProtocol protocol,
1604 AvahiEntryFlags flags,
1609 gint ret = AVAHI_OK;
1613 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1615 if (!avahi_is_valid_domain_name(name)) {
1616 avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1620 if (a->family == AVAHI_PROTO_INET) {
1624 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1625 r->data.a.address = a->data.ipv4;
1626 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1627 avahi_record_unref(r);
1632 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1633 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1640 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1641 r->data.aaaa.address = a->data.ipv6;
1642 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1643 avahi_record_unref(r);
1648 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1649 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1655 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1656 ret = avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1667 static gint server_add_txt_strlst_nocopy(
1670 AvahiIfIndex interface,
1671 AvahiProtocol protocol,
1672 AvahiEntryFlags flags,
1675 AvahiStringList *strlst) {
1682 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl);
1683 r->data.txt.string_list = strlst;
1684 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1685 avahi_record_unref(r);
1690 gint avahi_server_add_txt_strlst(
1693 AvahiIfIndex interface,
1694 AvahiProtocol protocol,
1695 AvahiEntryFlags flags,
1698 AvahiStringList *strlst) {
1702 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1705 gint avahi_server_add_txt_va(
1708 AvahiIfIndex interface,
1709 AvahiProtocol protocol,
1710 AvahiEntryFlags flags,
1717 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1720 gint avahi_server_add_txt(
1723 AvahiIfIndex interface,
1724 AvahiProtocol protocol,
1725 AvahiEntryFlags flags,
1736 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1742 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1747 while (*s && size >= 2) {
1748 if (*s == '.' || *s == '\\') {
1764 static gint server_add_service_strlst_nocopy(
1767 AvahiIfIndex interface,
1768 AvahiProtocol protocol,
1771 const gchar *domain,
1774 AvahiStringList *strlst) {
1776 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1778 AvahiRecord *r = NULL;
1785 if (!avahi_is_valid_service_name(name))
1786 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_NAME);
1788 if (!avahi_is_valid_service_type(type))
1789 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_SERVICE_TYPE);
1791 if (domain && !avahi_is_valid_domain_name(domain))
1792 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1794 if (host && !avahi_is_valid_domain_name(host))
1795 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
1798 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
1800 escape_service_name(ename, sizeof(ename), name);
1803 domain = s->domain_name;
1806 host = s->host_name_fqdn;
1808 d = avahi_normalize_name(domain);
1809 t = avahi_normalize_name(type);
1811 g_snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1812 g_snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1814 if ((ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
1817 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1818 r->data.srv.priority = 0;
1819 r->data.srv.weight = 0;
1820 r->data.srv.port = port;
1821 r->data.srv.name = avahi_normalize_name(host);
1822 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1823 avahi_record_unref(r);
1828 ret = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1834 g_snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1835 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1842 avahi_string_list_free(strlst);
1847 gint avahi_server_add_service_strlst(
1850 AvahiIfIndex interface,
1851 AvahiProtocol protocol,
1854 const gchar *domain,
1857 AvahiStringList *strlst) {
1863 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1866 gint avahi_server_add_service_va(
1869 AvahiIfIndex interface,
1870 AvahiProtocol protocol,
1873 const gchar *domain,
1882 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1885 gint avahi_server_add_service(
1888 AvahiIfIndex interface,
1889 AvahiProtocol protocol,
1892 const gchar *domain,
1905 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1910 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1911 static const gchar hex[] = "0123456789abcdef";
1913 const guint8 *k = p;
1915 while (sl > 1 && pl > 0) {
1916 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1932 gint avahi_server_add_dns_server_address(
1935 AvahiIfIndex interface,
1936 AvahiProtocol protocol,
1937 const gchar *domain,
1938 AvahiDNSServerType type,
1939 const AvahiAddress *address,
1940 guint16 port /** should be 53 */) {
1944 gchar n[64] = "ip-";
1948 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1949 g_assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1951 if (domain && !avahi_is_valid_domain_name(domain))
1952 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1955 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
1957 if (address->family == AVAHI_PROTO_INET) {
1958 hexstring(n+3, sizeof(n)-3, &address->data, 4);
1959 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1960 r->data.a.address = address->data.ipv4;
1962 hexstring(n+3, sizeof(n)-3, &address->data, 6);
1963 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1964 r->data.aaaa.address = address->data.ipv6;
1967 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1968 avahi_record_unref(r);
1973 return avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1976 gint avahi_server_add_dns_server_name(
1979 AvahiIfIndex interface,
1980 AvahiProtocol protocol,
1981 const gchar *domain,
1982 AvahiDNSServerType type,
1984 guint16 port /** should be 53 */) {
1992 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1994 if (domain && !avahi_is_valid_domain_name(domain))
1995 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
1998 return avahi_server_set_errno(s, AVAHI_ERR_INVALID_PORT);
2001 domain = s->domain_name;
2003 d = avahi_normalize_name(domain);
2004 g_snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
2007 r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
2008 r->data.srv.priority = 0;
2009 r->data.srv.weight = 0;
2010 r->data.srv.port = port;
2011 r->data.srv.name = avahi_normalize_name(name);
2012 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
2013 avahi_record_unref(r);
2018 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
2019 AvahiKey *k = userdata;
2025 avahi_interface_post_query(i, k, FALSE);
2028 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
2032 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
2035 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
2038 if (g->state == state)
2041 g_assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
2046 g->callback(g->server, g, state, g->userdata);
2049 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
2054 g = g_new(AvahiEntryGroup, 1);
2056 g->callback = callback;
2057 g->userdata = userdata;
2059 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
2061 g->n_register_try = 0;
2062 g->register_time_event = NULL;
2063 g->register_time.tv_sec = 0;
2064 g->register_time.tv_usec = 0;
2065 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
2067 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
2071 void avahi_entry_group_free(AvahiEntryGroup *g) {
2075 g_assert(g->server);
2077 for (e = g->entries; e; e = e->by_group_next) {
2079 avahi_goodbye_entry(g->server, e, TRUE);
2084 if (g->register_time_event) {
2085 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2086 g->register_time_event = NULL;
2091 g->server->need_group_cleanup = TRUE;
2092 g->server->need_entry_cleanup = TRUE;
2095 static void entry_group_commit_real(AvahiEntryGroup *g) {
2098 gettimeofday(&g->register_time, NULL);
2100 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2103 avahi_announce_group(g->server, g);
2104 avahi_entry_group_check_probed(g, FALSE);
2108 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
2109 AvahiEntryGroup *g = userdata;
2112 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2114 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2115 g->register_time_event = NULL;
2117 /* Holdoff time passed, so let's start probing */
2118 entry_group_commit_real(g);
2121 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
2127 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2128 return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
2130 g->n_register_try++;
2132 avahi_timeval_add(&g->register_time,
2133 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2134 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2135 AVAHI_RR_HOLDOFF_MSEC));
2137 gettimeofday(&now, NULL);
2139 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2140 /* Holdoff time passed, so let's start probing */
2141 /* avahi_log_debug("Holdoff passed, directly going on."); */
2143 entry_group_commit_real(g);
2145 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2147 /* Holdoff time has not yet passed, so let's wait */
2148 g_assert(!g->register_time_event);
2149 g->register_time_event = avahi_time_event_queue_add(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2151 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2157 void avahi_entry_group_reset(AvahiEntryGroup *g) {
2161 if (g->register_time_event) {
2162 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2163 g->register_time_event = NULL;
2166 for (e = g->entries; e; e = e->by_group_next) {
2168 avahi_goodbye_entry(g->server, e, TRUE);
2173 if (g->register_time_event) {
2174 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2175 g->register_time_event = NULL;
2178 g->server->need_entry_cleanup = TRUE;
2181 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2184 gboolean avahi_entry_commited(AvahiEntry *e) {
2189 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2190 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2193 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
2200 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
2203 g->userdata = userdata;
2206 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
2212 gboolean avahi_entry_group_is_empty(AvahiEntryGroup *g) {
2216 /* Look for an entry that is not dead */
2217 for (e = g->entries; e; e = e->by_group_next)
2224 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
2227 return s->domain_name;
2230 const gchar* avahi_server_get_host_name(AvahiServer *s) {
2233 return s->host_name;
2236 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2239 return s->host_name_fqdn;
2242 gpointer avahi_server_get_data(AvahiServer *s) {
2248 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2251 s->userdata = userdata;
2254 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2260 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2263 memset(c, 0, sizeof(AvahiServerConfig));
2266 c->host_name = NULL;
2267 c->domain_name = NULL;
2268 c->check_response_ttl = FALSE;
2269 c->publish_hinfo = TRUE;
2270 c->publish_addresses = TRUE;
2271 c->publish_workstation = TRUE;
2272 c->publish_domain = TRUE;
2273 c->use_iff_running = FALSE;
2274 c->enable_reflector = FALSE;
2275 c->reflect_ipv = FALSE;
2280 void avahi_server_config_free(AvahiServerConfig *c) {
2283 g_free(c->host_name);
2284 g_free(c->domain_name);
2287 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2293 ret->host_name = g_strdup(c->host_name);
2294 ret->domain_name = g_strdup(c->domain_name);
2299 gint avahi_server_errno(AvahiServer *s) {
2305 /* Just for internal use */
2306 gint avahi_server_set_errno(AvahiServer *s, gint error) {
2309 return s->error = error;