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)
224 withdraw_entry(s, e);
227 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
230 gboolean ours = FALSE, won = FALSE, lost = FALSE;
236 t = avahi_record_to_string(record);
238 /* avahi_log_debug("incoming_probe()"); */
240 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
247 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
252 if (avahi_entry_probing(s, e, i)) {
264 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
266 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
267 withdraw_rrset(s, record->key);
269 /* avahi_log_debug("Not conflicting probe"); */
275 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
276 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
277 AvahiEntry *e, *n, *conflicting_entry = NULL;
284 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
286 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
289 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
292 /* Either our entry or the other is intended to be unique, so let's check */
294 if (avahi_record_equal_no_ttl(e->record, record)) {
295 ours = TRUE; /* We have an identical record, so this is no conflict */
297 /* Check wheter there is a TTL conflict */
298 if (record->ttl <= e->record->ttl/2 &&
299 avahi_entry_registered(s, e, i)) {
302 t = avahi_record_to_string(record);
304 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
305 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
311 /* There's no need to check the other entries of this RRset */
316 if (avahi_entry_registered(s, e, i)) {
318 /* A conflict => we have to return to probe mode */
320 conflicting_entry = e;
322 } else if (avahi_entry_probing(s, e, i)) {
324 /* We are currently registering a matching record, but
325 * someone else already claimed it, so let's
328 withdraw_immediately = TRUE;
333 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
335 if (!ours && conflict) {
340 t = avahi_record_to_string(record);
342 if (withdraw_immediately) {
343 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
344 withdraw_rrset(s, record->key);
346 g_assert(conflicting_entry);
347 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
348 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
350 /* Local unique records are returned to probing
351 * state. Local shared records are reannounced. */
360 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
361 gboolean *unicast_response = userdata;
365 g_assert(unicast_response);
367 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
370 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
374 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
377 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean immediately) {
381 g_assert(!legacy_unicast || (a && port > 0 && p));
383 if (legacy_unicast) {
384 AvahiDnsPacket *reply;
387 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
389 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
391 append_aux_records_to_list(s, i, r, FALSE);
393 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
394 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
396 gchar *t = avahi_record_to_string(r);
397 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
401 avahi_record_unref(r);
404 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
405 avahi_interface_send_packet_unicast(i, reply, a, port);
407 avahi_dns_packet_free(reply);
410 gboolean unicast_response, flush_cache, auxiliary;
411 AvahiDnsPacket *reply = NULL;
414 /* In case the query packet was truncated never respond
415 immediately, because known answer suppression records might be
416 contained in later packets */
417 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
419 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
421 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
423 append_aux_records_to_list(s, i, r, unicast_response);
425 /* Due to some reasons the record has not been scheduled.
426 * The client requested an unicast response in that
427 * case. Therefore we prepare such a response */
433 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
436 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
438 /* Appending this record succeeded, so incremeant
439 * the specific header field, and return to the caller */
441 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
446 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
449 /* The record is too large for one packet, so create a larger packet */
451 avahi_dns_packet_free(reply);
452 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
453 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
454 size = AVAHI_DNS_PACKET_MAX_SIZE;
455 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
457 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
458 avahi_dns_packet_free(reply);
460 gchar *t = avahi_record_to_string(r);
461 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
465 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
468 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
469 avahi_interface_send_packet_unicast(i, reply, a, port);
470 avahi_dns_packet_free(reply);
475 avahi_record_unref(r);
479 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
480 avahi_interface_send_packet_unicast(i, reply, a, port);
481 avahi_dns_packet_free(reply);
485 avahi_record_list_flush(s->record_list);
489 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
496 if (!s->config.enable_reflector)
499 for (j = s->monitor->interfaces; j; j = j->interface_next)
500 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
501 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
504 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
505 AvahiServer *s = userdata;
512 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
516 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
523 if (!s->config.enable_reflector)
526 for (j = s->monitor->interfaces; j; j = j->interface_next)
527 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
528 /* Post the query to other networks */
529 avahi_interface_post_query(j, k, TRUE);
531 /* Reply from caches of other network. This is needed to
532 * "work around" known answer suppression. */
534 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
538 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
545 if (!s->config.enable_reflector)
548 for (j = s->monitor->interfaces; j; j = j->interface_next)
549 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
550 avahi_interface_post_probe(j, r, TRUE);
553 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
562 /* avahi_log_debug("query"); */
564 g_assert(avahi_record_list_empty(s->record_list));
566 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
568 /* Handle the questions */
569 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
571 gboolean unicast_response = FALSE;
573 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
574 avahi_log_warn("Packet too short (1)");
579 reflect_query(s, i, key);
581 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
582 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
583 /* Allow our own queries to be suppressed by incoming
584 * queries only when they do not include known answers */
585 avahi_query_scheduler_incoming(i->query_scheduler, key);
587 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
588 avahi_key_unref(key);
591 /* Known Answer Suppression */
592 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
594 gboolean unique = FALSE;
596 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
597 avahi_log_warn("Packet too short (2)");
601 if (handle_conflict(s, i, record, unique, a)) {
602 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
603 avahi_record_list_drop(s->record_list, record);
606 avahi_record_unref(record);
610 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
612 gboolean unique = FALSE;
614 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
615 avahi_log_warn("Packet too short (3)");
619 if (!avahi_key_is_pattern(record->key)) {
620 reflect_probe(s, i, record);
621 incoming_probe(s, record, i);
624 avahi_record_unref(record);
627 if (!avahi_record_list_empty(s->record_list))
628 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
633 avahi_record_list_flush(s->record_list);
636 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
644 /* avahi_log_debug("response"); */
646 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
647 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
649 gboolean cache_flush = FALSE;
652 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
653 avahi_log_warn("Packet too short (4)");
657 if (!avahi_key_is_pattern(record->key)) {
659 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
662 if (handle_conflict(s, i, record, cache_flush, a)) {
663 reflect_response(s, i, record, cache_flush);
664 avahi_cache_update(i->cache, record, cache_flush, a);
665 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
669 avahi_record_unref(record);
672 /* If the incoming response contained a conflicting record, some
673 records have been scheduling for sending. We need to flush them
675 if (!avahi_record_list_empty(s->record_list))
676 avahi_server_generate_response(s, i, NULL, NULL, 0, FALSE, TRUE);
679 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
680 guint n, idx = (guint) -1;
681 AvahiLegacyUnicastReflectSlot *slot;
685 if (!s->legacy_unicast_reflect_slots)
686 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
688 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
689 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
691 if (!s->legacy_unicast_reflect_slots[idx])
695 if (idx == (guint) -1 || s->legacy_unicast_reflect_slots[idx])
698 slot = s->legacy_unicast_reflect_slots[idx] = g_new(AvahiLegacyUnicastReflectSlot, 1);
699 slot->id = s->legacy_unicast_reflect_id++;
704 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
710 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
712 g_assert(s->legacy_unicast_reflect_slots[idx] == slot);
714 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
717 s->legacy_unicast_reflect_slots[idx] = NULL;
720 static void free_slots(AvahiServer *s) {
724 if (!s->legacy_unicast_reflect_slots)
727 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
728 if (s->legacy_unicast_reflect_slots[idx])
729 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
731 g_free(s->legacy_unicast_reflect_slots);
732 s->legacy_unicast_reflect_slots = NULL;
735 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
740 if (!s->legacy_unicast_reflect_slots)
743 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
745 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
748 return s->legacy_unicast_reflect_slots[idx];
751 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
752 AvahiLegacyUnicastReflectSlot *slot = userdata;
756 g_assert(slot->time_event == e);
758 deallocate_slot(slot->server, slot);
761 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
762 AvahiLegacyUnicastReflectSlot *slot;
770 g_assert(i->protocol == a->family);
772 if (!s->config.enable_reflector)
775 /* avahi_log_debug("legacy unicast reflector"); */
777 /* Reflecting legacy unicast queries is a little more complicated
778 than reflecting normal queries, since we must route the
779 responses back to the right client. Therefore we must store
780 some information for finding the right client contact data for
781 response packets. In contrast to normal queries legacy
782 unicast query and response packets are reflected untouched and
783 are not reassembled into larger packets */
785 if (!(slot = allocate_slot(s))) {
786 /* No slot available, we drop this legacy unicast query */
787 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
791 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
794 slot->interface = i->hardware->index;
796 avahi_elapse_time(&slot->elapse_time, 2000, 0);
797 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
799 /* Patch the packet with our new locally generatedt id */
800 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
802 for (j = s->monitor->interfaces; j; j = j->interface_next)
803 if (avahi_interface_relevant(j) &&
805 (s->config.reflect_ipv || j->protocol == i->protocol)) {
807 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
808 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
809 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
810 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
814 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
817 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
822 if (!s->config.enable_reflector)
825 avahi_address_from_sockaddr(sa, &a);
827 if (!avahi_address_is_local(s->monitor, &a))
830 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
831 struct sockaddr_in lsa;
832 socklen_t l = sizeof(lsa);
834 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
835 avahi_log_warn("getsockname(): %s", strerror(errno));
837 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
841 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
842 struct sockaddr_in6 lsa;
843 socklen_t l = sizeof(lsa);
845 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
846 avahi_log_warn("getsockname(): %s", strerror(errno));
848 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
854 static gboolean is_mdns_mcast_address(const AvahiAddress *a) {
858 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
859 return avahi_address_cmp(a, &b) == 0;
862 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, gint ttl) {
873 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
874 !avahi_interface_relevant(i)) {
875 avahi_log_warn("Recieved packet from invalid interface.");
879 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
881 port = avahi_port_from_sockaddr(sa);
882 avahi_address_from_sockaddr(sa, &a);
884 if (avahi_address_is_ipv4_in_ipv6(&a))
885 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
888 if (originates_from_local_legacy_unicast_socket(s, sa))
889 /* This originates from our local reflector, so let's ignore it */
892 if (avahi_dns_packet_check_valid(p) < 0) {
893 avahi_log_warn("Recieved invalid packet.");
897 if (avahi_dns_packet_is_query(p)) {
898 gboolean legacy_unicast = FALSE;
900 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
901 avahi_log_warn("Invalid query packet.");
905 if (port != AVAHI_MDNS_PORT) {
908 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
909 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
910 avahi_log_warn("Invalid legacy unicast query packet.");
914 legacy_unicast = TRUE;
918 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
920 handle_query_packet(s, p, i, &a, port, legacy_unicast);
922 /* avahi_log_debug("Handled query"); */
924 if (port != AVAHI_MDNS_PORT) {
925 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
929 if (ttl != 255 && s->config.check_response_ttl) {
930 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
934 if (!is_mdns_mcast_address(dest) &&
935 !avahi_interface_address_on_link(i, &a)) {
936 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
940 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
941 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
942 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
943 avahi_log_warn("Invalid response packet.");
947 handle_response_packet(s, p, i, &a);
948 /* avahi_log_debug("Handled response"); */
952 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, gint ttl) {
953 AvahiInterface *i, *j;
956 AvahiLegacyUnicastReflectSlot *slot;
963 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
964 !avahi_interface_relevant(i)) {
965 avahi_log_warn("Recieved packet from invalid interface.");
969 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
971 port = avahi_port_from_sockaddr(sa);
972 avahi_address_from_sockaddr(sa, &a);
974 if (avahi_address_is_ipv4_in_ipv6(&a))
975 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
978 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
979 avahi_log_warn("Recieved invalid packet.");
983 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
984 avahi_log_warn("Recieved legacy unicast response with unknown id");
988 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
989 !avahi_interface_relevant(j))
992 /* Patch the original ID into this response */
993 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
995 /* Forward the response to the correct client */
996 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
998 /* Undo changes to packet */
999 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1002 static void work(AvahiServer *s) {
1003 struct sockaddr_in6 sa6;
1004 struct sockaddr_in sa;
1012 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
1013 dest.family = AVAHI_PROTO_INET;
1014 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1015 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1016 avahi_dns_packet_free(p);
1020 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
1021 dest.family = AVAHI_PROTO_INET6;
1022 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1023 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1024 avahi_dns_packet_free(p);
1028 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
1029 dest.family = AVAHI_PROTO_INET;
1030 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1031 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1032 avahi_dns_packet_free(p);
1036 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
1037 dest.family = AVAHI_PROTO_INET6;
1038 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1039 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1040 avahi_dns_packet_free(p);
1045 static gboolean prepare_func(GSource *source, gint *timeout) {
1053 static gboolean check_func(GSource *source) {
1055 gushort revents = 0;
1059 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1062 if (s->fd_ipv4 >= 0)
1063 revents |= s->pollfd_ipv4.revents;
1064 if (s->fd_ipv6 >= 0)
1065 revents |= s->pollfd_ipv6.revents;
1066 if (s->fd_legacy_unicast_ipv4 >= 0)
1067 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1068 if (s->fd_legacy_unicast_ipv6 >= 0)
1069 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1071 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1074 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1078 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1087 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1090 if (s->state == state)
1096 s->callback(s, state, s->userdata);
1099 static void withdraw_host_rrs(AvahiServer *s) {
1102 if (s->hinfo_entry_group)
1103 avahi_entry_group_reset(s->hinfo_entry_group);
1105 if (s->browse_domain_entry_group)
1106 avahi_entry_group_reset(s->browse_domain_entry_group);
1108 avahi_update_host_rrs(s->monitor, TRUE);
1109 s->n_host_rr_pending = 0;
1112 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1115 g_assert(s->n_host_rr_pending > 0);
1117 if (--s->n_host_rr_pending == 0)
1118 server_set_state(s, AVAHI_SERVER_RUNNING);
1121 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1124 s->n_host_rr_pending ++;
1127 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1131 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1132 s->state == AVAHI_SERVER_REGISTERING)
1133 avahi_server_increase_host_rr_pending(s);
1134 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1135 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1136 withdraw_host_rrs(s);
1137 server_set_state(s, AVAHI_SERVER_COLLISION);
1138 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1139 s->state == AVAHI_SERVER_REGISTERING)
1140 avahi_server_decrease_host_rr_pending(s);
1143 static void register_hinfo(AvahiServer *s) {
1144 struct utsname utsname;
1149 if (!s->config.publish_hinfo)
1152 if (s->hinfo_entry_group)
1153 g_assert(avahi_entry_group_is_empty(s->hinfo_entry_group));
1155 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1157 /* Fill in HINFO rr */
1158 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME);
1160 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1161 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1162 avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1163 avahi_record_unref(r);
1165 avahi_entry_group_commit(s->hinfo_entry_group);
1168 static void register_localhost(AvahiServer *s) {
1172 /* Add localhost entries */
1173 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1174 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1176 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1177 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1180 static void register_browse_domain(AvahiServer *s) {
1183 if (!s->config.publish_domain)
1186 if (s->browse_domain_entry_group)
1187 g_assert(avahi_entry_group_is_empty(s->browse_domain_entry_group));
1189 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1191 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);
1192 avahi_entry_group_commit(s->browse_domain_entry_group);
1195 static void register_stuff(AvahiServer *s) {
1198 server_set_state(s, AVAHI_SERVER_REGISTERING);
1200 register_browse_domain(s);
1201 avahi_update_host_rrs(s->monitor, FALSE);
1203 if (s->n_host_rr_pending == 0)
1204 server_set_state(s, AVAHI_SERVER_RUNNING);
1207 static void update_fqdn(AvahiServer *s) {
1210 g_assert(s->host_name);
1211 g_assert(s->domain_name);
1213 g_free(s->host_name_fqdn);
1214 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1217 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1219 g_assert(host_name);
1221 withdraw_host_rrs(s);
1223 g_free(s->host_name);
1224 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1225 s->host_name[strcspn(s->host_name, ".")] = 0;
1232 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1234 g_assert(domain_name);
1236 withdraw_host_rrs(s);
1238 g_free(s->domain_name);
1239 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local");
1246 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1251 memset(pollfd, 0, sizeof(GPollFD));
1253 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1254 g_source_add_poll(s->source, pollfd);
1257 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1260 static GSourceFuncs source_funcs = {
1269 s = g_new(AvahiServer, 1);
1270 s->n_host_rr_pending = 0;
1271 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1274 avahi_server_config_copy(&s->config, sc);
1276 avahi_server_config_init(&s->config);
1278 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1279 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1281 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1282 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1283 avahi_server_config_free(&s->config);
1288 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1289 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1290 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1291 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1293 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1294 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1296 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1298 /* Prepare IO source registration */
1299 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1300 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1302 if (s->fd_ipv4 >= 0)
1303 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1304 if (s->fd_ipv6 >= 0)
1305 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1306 if (s->fd_legacy_unicast_ipv4 >= 0)
1307 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1308 if (s->fd_legacy_unicast_ipv6 >= 0)
1309 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1311 g_source_attach(s->source, s->context);
1313 s->callback = callback;
1314 s->userdata = userdata;
1316 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1317 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1318 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1320 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1321 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1322 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1323 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1324 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1325 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1326 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1327 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1328 AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1330 s->legacy_unicast_reflect_slots = NULL;
1331 s->legacy_unicast_reflect_id = 0;
1334 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1335 s->host_name[strcspn(s->host_name, ".")] = 0;
1336 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local");
1337 s->host_name_fqdn = NULL;
1340 s->record_list = avahi_record_list_new();
1342 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1344 s->state = AVAHI_SERVER_INVALID;
1346 s->monitor = avahi_interface_monitor_new(s);
1347 avahi_interface_monitor_sync(s->monitor);
1349 register_localhost(s);
1351 s->hinfo_entry_group = NULL;
1352 s->browse_domain_entry_group = NULL;
1358 void avahi_server_free(AvahiServer* s) {
1362 free_entry(s, s->entries);
1364 avahi_interface_monitor_free(s->monitor);
1367 free_group(s, s->groups);
1371 while (s->dns_server_browsers)
1372 avahi_dns_server_browser_free(s->dns_server_browsers);
1373 while (s->host_name_resolvers)
1374 avahi_host_name_resolver_free(s->host_name_resolvers);
1375 while (s->address_resolvers)
1376 avahi_address_resolver_free(s->address_resolvers);
1377 while (s->domain_browsers)
1378 avahi_domain_browser_free(s->domain_browsers);
1379 while (s->service_type_browsers)
1380 avahi_service_type_browser_free(s->service_type_browsers);
1381 while (s->service_browsers)
1382 avahi_service_browser_free(s->service_browsers);
1383 while (s->service_resolvers)
1384 avahi_service_resolver_free(s->service_resolvers);
1385 while (s->record_browsers)
1386 avahi_record_browser_destroy(s->record_browsers);
1387 g_hash_table_destroy(s->record_browser_hashtable);
1389 g_hash_table_destroy(s->entries_by_key);
1391 avahi_time_event_queue_free(s->time_event_queue);
1393 avahi_record_list_free(s->record_list);
1395 if (s->fd_ipv4 >= 0)
1397 if (s->fd_ipv6 >= 0)
1399 if (s->fd_legacy_unicast_ipv4 >= 0)
1400 close(s->fd_legacy_unicast_ipv4);
1401 if (s->fd_legacy_unicast_ipv6 >= 0)
1402 close(s->fd_legacy_unicast_ipv6);
1404 g_free(s->host_name);
1405 g_free(s->domain_name);
1406 g_free(s->host_name_fqdn);
1408 g_source_destroy(s->source);
1409 g_source_unref(s->source);
1410 g_main_context_unref(s->context);
1412 avahi_server_config_free(&s->config);
1417 static gint check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1423 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1427 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1430 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1433 if (interface <= 0 ||
1434 e->interface <= 0 ||
1435 e->interface == interface ||
1436 protocol == AVAHI_PROTO_UNSPEC ||
1437 e->protocol == AVAHI_PROTO_UNSPEC ||
1438 e->protocol == protocol)
1447 gint avahi_server_add(
1450 AvahiIfIndex interface,
1451 AvahiProtocol protocol,
1452 AvahiEntryFlags flags,
1463 if (avahi_key_is_pattern(r->key))
1466 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1469 e = g_new(AvahiEntry, 1);
1471 e->record = avahi_record_ref(r);
1473 e->interface = interface;
1474 e->protocol = protocol;
1478 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1480 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1482 /* Insert into hash table indexed by name */
1483 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1484 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1485 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1487 /* Insert into group list */
1489 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1491 avahi_announce_entry(s, e);
1496 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1497 AvahiEntry **e = (AvahiEntry**) state;
1502 *e = g ? g->entries : s->entries;
1504 while (*e && (*e)->dead)
1505 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1510 return avahi_record_ref((*e)->record);
1513 void avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, gpointer userdata) {
1519 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1521 for (e = s->entries; e; e = e->entries_next) {
1528 t = avahi_record_to_string(e->record);
1529 g_snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1532 callback(ln, userdata);
1535 avahi_dump_caches(s->monitor, callback, userdata);
1538 gint avahi_server_add_ptr(
1541 AvahiIfIndex interface,
1542 AvahiProtocol protocol,
1543 AvahiEntryFlags flags,
1546 const gchar *dest) {
1553 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl);
1554 r->data.ptr.name = avahi_normalize_name(dest);
1555 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1556 avahi_record_unref(r);
1560 gint avahi_server_add_address(
1563 AvahiIfIndex interface,
1564 AvahiProtocol protocol,
1565 AvahiEntryFlags flags,
1574 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1576 if (a->family == AVAHI_PROTO_INET) {
1580 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1581 r->data.a.address = a->data.ipv4;
1582 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1583 avahi_record_unref(r);
1585 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1586 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1593 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1594 r->data.aaaa.address = a->data.ipv6;
1595 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1596 avahi_record_unref(r);
1598 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1599 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1602 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1603 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1612 static gint server_add_txt_strlst_nocopy(
1615 AvahiIfIndex interface,
1616 AvahiProtocol protocol,
1617 AvahiEntryFlags flags,
1620 AvahiStringList *strlst) {
1627 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl);
1628 r->data.txt.string_list = strlst;
1629 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1630 avahi_record_unref(r);
1635 gint avahi_server_add_txt_strlst(
1638 AvahiIfIndex interface,
1639 AvahiProtocol protocol,
1640 AvahiEntryFlags flags,
1643 AvahiStringList *strlst) {
1645 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1648 gint avahi_server_add_txt_va(
1651 AvahiIfIndex interface,
1652 AvahiProtocol protocol,
1653 AvahiEntryFlags flags,
1660 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1663 gint avahi_server_add_txt(
1666 AvahiIfIndex interface,
1667 AvahiProtocol protocol,
1668 AvahiEntryFlags flags,
1679 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1685 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1690 while (*s && size >= 2) {
1691 if (*s == '.' || *s == '\\') {
1707 static gint server_add_service_strlst_nocopy(
1710 AvahiIfIndex interface,
1711 AvahiProtocol protocol,
1714 const gchar *domain,
1717 AvahiStringList *strlst) {
1719 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1728 escape_service_name(ename, sizeof(ename), name);
1731 while (domain[0] == '.')
1734 domain = s->domain_name;
1737 host = s->host_name_fqdn;
1739 d = avahi_normalize_name(domain);
1740 t = avahi_normalize_name(type);
1742 g_snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1743 g_snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1745 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name);
1747 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1748 r->data.srv.priority = 0;
1749 r->data.srv.weight = 0;
1750 r->data.srv.port = port;
1751 r->data.srv.name = avahi_normalize_name(host);
1752 ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1753 avahi_record_unref(r);
1755 ret |= server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1757 g_snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1758 ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1766 gint avahi_server_add_service_strlst(
1769 AvahiIfIndex interface,
1770 AvahiProtocol protocol,
1773 const gchar *domain,
1776 AvahiStringList *strlst) {
1778 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1781 gint avahi_server_add_service_va(
1784 AvahiIfIndex interface,
1785 AvahiProtocol protocol,
1788 const gchar *domain,
1797 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1800 gint avahi_server_add_service(
1803 AvahiIfIndex interface,
1804 AvahiProtocol protocol,
1807 const gchar *domain,
1820 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1825 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1826 static const gchar hex[] = "0123456789abcdef";
1828 const guint8 *k = p;
1830 while (sl > 1 && pl > 0) {
1831 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1847 gint avahi_server_add_dns_server_address(
1850 AvahiIfIndex interface,
1851 AvahiProtocol protocol,
1852 const gchar *domain,
1853 AvahiDNSServerType type,
1854 const AvahiAddress *address,
1855 guint16 port /** should be 53 */) {
1859 gchar n[64] = "ip-";
1863 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1864 g_assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1866 if (address->family == AVAHI_PROTO_INET) {
1867 hexstring(n+3, sizeof(n)-3, &address->data, 4);
1868 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1869 r->data.a.address = address->data.ipv4;
1871 hexstring(n+3, sizeof(n)-3, &address->data, 6);
1872 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1873 r->data.aaaa.address = address->data.ipv6;
1876 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1877 avahi_record_unref(r);
1879 ret |= avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1884 gint avahi_server_add_dns_server_name(
1887 AvahiIfIndex interface,
1888 AvahiProtocol protocol,
1889 const gchar *domain,
1890 AvahiDNSServerType type,
1892 guint16 port /** should be 53 */) {
1900 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1903 while (domain[0] == '.')
1906 domain = s->domain_name;
1908 d = avahi_normalize_name(domain);
1909 g_snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
1912 r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1913 r->data.srv.priority = 0;
1914 r->data.srv.weight = 0;
1915 r->data.srv.port = port;
1916 r->data.srv.name = avahi_normalize_name(name);
1917 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
1918 avahi_record_unref(r);
1923 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1924 AvahiKey *k = userdata;
1930 avahi_interface_post_query(i, k, FALSE);
1933 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1937 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1940 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1943 if (g->state == state)
1949 g->callback(g->server, g, state, g->userdata);
1954 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1959 g = g_new(AvahiEntryGroup, 1);
1961 g->callback = callback;
1962 g->userdata = userdata;
1964 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1966 g->n_register_try = 0;
1967 g->register_time_event = NULL;
1968 g->register_time.tv_sec = 0;
1969 g->register_time.tv_usec = 0;
1970 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1972 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1976 void avahi_entry_group_free(AvahiEntryGroup *g) {
1980 g_assert(g->server);
1982 for (e = g->entries; e; e = e->by_group_next) {
1984 avahi_goodbye_entry(g->server, e, TRUE);
1989 if (g->register_time_event) {
1990 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
1991 g->register_time_event = NULL;
1996 g->server->need_group_cleanup = TRUE;
1997 g->server->need_entry_cleanup = TRUE;
2000 static void entry_group_commit_real(AvahiEntryGroup *g) {
2003 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2004 avahi_announce_group(g->server, g);
2005 avahi_entry_group_check_probed(g, FALSE);
2007 g_get_current_time(&g->register_time);
2010 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
2011 AvahiEntryGroup *g = userdata;
2014 avahi_log_debug("Holdoff passed, waking up and going on.");
2016 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2017 g->register_time_event = NULL;
2019 /* Holdoff time passed, so let's start probing */
2020 entry_group_commit_real(g);
2023 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
2029 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2030 return AVAHI_ERR_BAD_STATE;
2032 g->n_register_try++;
2034 avahi_timeval_add(&g->register_time,
2035 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2036 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2037 AVAHI_RR_HOLDOFF_MSEC));
2039 g_get_current_time(&now);
2041 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2042 /* Holdoff time passed, so let's start probing */
2043 avahi_log_debug("Holdoff passed, directly going on.");
2045 entry_group_commit_real(g);
2047 avahi_log_debug("Holdoff not passed, sleeping.");
2049 /* Holdoff time has not yet passed, so let's wait */
2050 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_SLEEPING);
2052 g_assert(!g->register_time_event);
2053 g->register_time_event = avahi_time_event_queue_add(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2059 void avahi_entry_group_reset(AvahiEntryGroup *g) {
2063 if (g->register_time_event) {
2064 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2065 g->register_time_event = NULL;
2068 for (e = g->entries; e; e = e->by_group_next) {
2070 avahi_goodbye_entry(g->server, e, TRUE);
2075 g->server->need_entry_cleanup = TRUE;
2078 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2081 gboolean avahi_entry_commited(AvahiEntry *e) {
2086 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2087 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2090 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
2097 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
2100 g->userdata = userdata;
2103 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
2109 gboolean avahi_entry_group_is_empty(AvahiEntryGroup *g) {
2113 /* Look for an entry that is not dead */
2114 for (e = g->entries; e; e = e->by_group_next)
2121 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
2124 return s->domain_name;
2127 const gchar* avahi_server_get_host_name(AvahiServer *s) {
2130 return s->host_name;
2133 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2136 return s->host_name_fqdn;
2139 gpointer avahi_server_get_data(AvahiServer *s) {
2145 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2148 s->userdata = userdata;
2151 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2157 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2160 memset(c, 0, sizeof(AvahiServerConfig));
2163 c->host_name = NULL;
2164 c->domain_name = NULL;
2165 c->check_response_ttl = FALSE;
2166 c->publish_hinfo = TRUE;
2167 c->publish_addresses = TRUE;
2168 c->publish_workstation = TRUE;
2169 c->publish_domain = TRUE;
2170 c->use_iff_running = FALSE;
2171 c->enable_reflector = FALSE;
2172 c->reflect_ipv = FALSE;
2177 void avahi_server_config_free(AvahiServerConfig *c) {
2180 g_free(c->host_name);
2181 g_free(c->domain_name);
2184 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2190 ret->host_name = g_strdup(c->host_name);
2191 ret->domain_name = g_strdup(c->domain_name);