4 This file is part of avahi.
6 avahi is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as
8 published by the Free Software Foundation; either version 2.1 of the
9 License, or (at your option) any later version.
11 avahi is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14 Public License for more details.
16 You should have received a copy of the GNU Lesser General Public
17 License along with avahi; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <sys/socket.h>
27 #include <arpa/inet.h>
29 #include <sys/utsname.h>
41 #define AVAHI_RR_HOLDOFF_MSEC 1000
42 #define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 60000
43 #define AVAHI_RR_RATE_LIMIT_COUNT 15
45 static void free_entry(AvahiServer*s, AvahiEntry *e) {
51 avahi_goodbye_entry(s, e, TRUE);
53 /* Remove from linked list */
54 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
56 /* Remove from hash table indexed by name */
57 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
58 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
60 g_hash_table_replace(s->entries_by_key, t->record->key, t);
62 g_hash_table_remove(s->entries_by_key, e->record->key);
64 /* Remove from associated group */
66 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
68 avahi_record_unref(e->record);
72 static void free_group(AvahiServer *s, AvahiEntryGroup *g) {
77 free_entry(s, g->entries);
79 if (g->register_time_event)
80 avahi_time_event_queue_remove(s->time_event_queue, g->register_time_event);
82 AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
86 static void cleanup_dead(AvahiServer *s) {
87 AvahiEntryGroup *g, *ng;
92 if (s->need_group_cleanup) {
93 for (g = s->groups; g; g = ng) {
100 s->need_group_cleanup = FALSE;
103 if (s->need_entry_cleanup) {
104 for (e = s->entries; e; e = ne) {
105 ne = e->entries_next;
111 s->need_entry_cleanup = FALSE;
114 if (s->need_browser_cleanup)
115 avahi_browser_cleanup(s);
118 static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const gchar *name, guint16 type, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
127 g_assert(type != AVAHI_DNS_TYPE_ANY);
129 k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
131 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
132 if (!e->dead && avahi_entry_registered(s, e, i))
133 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
138 void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
144 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
145 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
146 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
147 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
148 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
149 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
150 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
155 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary) {
160 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
163 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
171 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
174 if (avahi_key_is_pattern(k)) {
176 /* Handle ANY query */
178 for (e = s->entries; e; e = e->entries_next)
179 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
180 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
184 /* Handle all other queries */
186 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
187 if (!e->dead && avahi_entry_registered(s, e, i))
188 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
192 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
199 for (k = e->group->entries; k; k = k->by_group_next) {
201 avahi_goodbye_entry(s, k, FALSE);
206 e->group->n_probing = 0;
208 avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
210 avahi_goodbye_entry(s, e, FALSE);
214 s->need_entry_cleanup = TRUE;
217 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
223 for (e = g_hash_table_lookup(s->entries_by_key, key); e; e = e->by_key_next)
225 withdraw_entry(s, e);
228 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
231 gboolean ours = FALSE, won = FALSE, lost = FALSE;
237 t = avahi_record_to_string(record);
239 /* avahi_log_debug("incoming_probe()"); */
241 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
248 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
253 if (avahi_entry_probing(s, e, i)) {
265 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
267 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
268 withdraw_rrset(s, record->key);
270 /* avahi_log_debug("Not conflicting probe"); */
276 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
277 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
278 AvahiEntry *e, *n, *conflicting_entry = NULL;
285 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
287 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
290 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
293 /* Either our entry or the other is intended to be unique, so let's check */
295 if (avahi_record_equal_no_ttl(e->record, record)) {
296 ours = TRUE; /* We have an identical record, so this is no conflict */
298 /* Check wheter there is a TTL conflict */
299 if (record->ttl <= e->record->ttl/2 &&
300 avahi_entry_registered(s, e, i)) {
303 t = avahi_record_to_string(record);
305 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
306 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
312 /* There's no need to check the other entries of this RRset */
317 if (avahi_entry_registered(s, e, i)) {
319 /* A conflict => we have to return to probe mode */
321 conflicting_entry = e;
323 } else if (avahi_entry_probing(s, e, i)) {
325 /* We are currently registering a matching record, but
326 * someone else already claimed it, so let's
329 withdraw_immediately = TRUE;
334 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
336 if (!ours && conflict) {
341 t = avahi_record_to_string(record);
343 if (withdraw_immediately) {
344 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
345 withdraw_rrset(s, record->key);
347 g_assert(conflicting_entry);
348 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
349 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
351 /* Local unique records are returned to probing
352 * state. Local shared records are reannounced. */
361 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
362 gboolean *unicast_response = userdata;
366 g_assert(unicast_response);
368 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
371 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
375 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
378 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean immediately) {
382 g_assert(!legacy_unicast || (a && port > 0 && p));
384 if (legacy_unicast) {
385 AvahiDnsPacket *reply;
388 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
390 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
392 append_aux_records_to_list(s, i, r, FALSE);
394 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
395 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
397 gchar *t = avahi_record_to_string(r);
398 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
402 avahi_record_unref(r);
405 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
406 avahi_interface_send_packet_unicast(i, reply, a, port);
408 avahi_dns_packet_free(reply);
411 gboolean unicast_response, flush_cache, auxiliary;
412 AvahiDnsPacket *reply = NULL;
415 /* In case the query packet was truncated never respond
416 immediately, because known answer suppression records might be
417 contained in later packets */
418 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
420 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
422 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
424 append_aux_records_to_list(s, i, r, unicast_response);
426 /* Due to some reasons the record has not been scheduled.
427 * The client requested an unicast response in that
428 * case. Therefore we prepare such a response */
434 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
437 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
439 /* Appending this record succeeded, so incremeant
440 * the specific header field, and return to the caller */
442 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
447 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
450 /* The record is too large for one packet, so create a larger packet */
452 avahi_dns_packet_free(reply);
453 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
454 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
455 size = AVAHI_DNS_PACKET_MAX_SIZE;
456 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
458 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
459 avahi_dns_packet_free(reply);
461 gchar *t = avahi_record_to_string(r);
462 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
466 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
469 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
470 avahi_interface_send_packet_unicast(i, reply, a, port);
471 avahi_dns_packet_free(reply);
476 avahi_record_unref(r);
480 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
481 avahi_interface_send_packet_unicast(i, reply, a, port);
482 avahi_dns_packet_free(reply);
486 avahi_record_list_flush(s->record_list);
490 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
497 if (!s->config.enable_reflector)
500 for (j = s->monitor->interfaces; j; j = j->interface_next)
501 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
502 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
505 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
506 AvahiServer *s = userdata;
513 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
517 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
524 if (!s->config.enable_reflector)
527 for (j = s->monitor->interfaces; j; j = j->interface_next)
528 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
529 /* Post the query to other networks */
530 avahi_interface_post_query(j, k, TRUE);
532 /* Reply from caches of other network. This is needed to
533 * "work around" known answer suppression. */
535 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
539 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
546 if (!s->config.enable_reflector)
549 for (j = s->monitor->interfaces; j; j = j->interface_next)
550 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
551 avahi_interface_post_probe(j, r, TRUE);
554 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
563 /* avahi_log_debug("query"); */
565 g_assert(avahi_record_list_empty(s->record_list));
567 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
569 /* Handle the questions */
570 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
572 gboolean unicast_response = FALSE;
574 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
575 avahi_log_warn("Packet too short (1)");
580 reflect_query(s, i, key);
582 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
583 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
584 /* Allow our own queries to be suppressed by incoming
585 * queries only when they do not include known answers */
586 avahi_query_scheduler_incoming(i->query_scheduler, key);
588 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
589 avahi_key_unref(key);
592 /* Known Answer Suppression */
593 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
595 gboolean unique = FALSE;
597 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
598 avahi_log_warn("Packet too short (2)");
602 if (handle_conflict(s, i, record, unique, a)) {
603 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
604 avahi_record_list_drop(s->record_list, record);
607 avahi_record_unref(record);
611 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
613 gboolean unique = FALSE;
615 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
616 avahi_log_warn("Packet too short (3)");
620 if (!avahi_key_is_pattern(record->key)) {
621 reflect_probe(s, i, record);
622 incoming_probe(s, record, i);
625 avahi_record_unref(record);
628 if (!avahi_record_list_empty(s->record_list))
629 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
634 avahi_record_list_flush(s->record_list);
637 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
645 /* avahi_log_debug("response"); */
647 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
648 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
650 gboolean cache_flush = FALSE;
653 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
654 avahi_log_warn("Packet too short (4)");
658 if (!avahi_key_is_pattern(record->key)) {
660 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
663 if (handle_conflict(s, i, record, cache_flush, a)) {
664 reflect_response(s, i, record, cache_flush);
665 avahi_cache_update(i->cache, record, cache_flush, a);
666 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
670 avahi_record_unref(record);
673 /* If the incoming response contained a conflicting record, some
674 records have been scheduling for sending. We need to flush them
676 if (!avahi_record_list_empty(s->record_list))
677 avahi_server_generate_response(s, i, NULL, NULL, 0, FALSE, TRUE);
680 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
681 guint n, idx = (guint) -1;
682 AvahiLegacyUnicastReflectSlot *slot;
686 if (!s->legacy_unicast_reflect_slots)
687 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
689 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
690 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
692 if (!s->legacy_unicast_reflect_slots[idx])
696 if (idx == (guint) -1 || s->legacy_unicast_reflect_slots[idx])
699 slot = s->legacy_unicast_reflect_slots[idx] = g_new(AvahiLegacyUnicastReflectSlot, 1);
700 slot->id = s->legacy_unicast_reflect_id++;
705 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
711 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
713 g_assert(s->legacy_unicast_reflect_slots[idx] == slot);
715 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
718 s->legacy_unicast_reflect_slots[idx] = NULL;
721 static void free_slots(AvahiServer *s) {
725 if (!s->legacy_unicast_reflect_slots)
728 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
729 if (s->legacy_unicast_reflect_slots[idx])
730 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
732 g_free(s->legacy_unicast_reflect_slots);
733 s->legacy_unicast_reflect_slots = NULL;
736 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
741 if (!s->legacy_unicast_reflect_slots)
744 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
746 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
749 return s->legacy_unicast_reflect_slots[idx];
752 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
753 AvahiLegacyUnicastReflectSlot *slot = userdata;
757 g_assert(slot->time_event == e);
759 deallocate_slot(slot->server, slot);
762 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
763 AvahiLegacyUnicastReflectSlot *slot;
771 g_assert(i->protocol == a->family);
773 if (!s->config.enable_reflector)
776 /* avahi_log_debug("legacy unicast reflector"); */
778 /* Reflecting legacy unicast queries is a little more complicated
779 than reflecting normal queries, since we must route the
780 responses back to the right client. Therefore we must store
781 some information for finding the right client contact data for
782 response packets. In contrast to normal queries legacy
783 unicast query and response packets are reflected untouched and
784 are not reassembled into larger packets */
786 if (!(slot = allocate_slot(s))) {
787 /* No slot available, we drop this legacy unicast query */
788 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
792 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
795 slot->interface = i->hardware->index;
797 avahi_elapse_time(&slot->elapse_time, 2000, 0);
798 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
800 /* Patch the packet with our new locally generatedt id */
801 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
803 for (j = s->monitor->interfaces; j; j = j->interface_next)
804 if (avahi_interface_relevant(j) &&
806 (s->config.reflect_ipv || j->protocol == i->protocol)) {
808 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
809 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
810 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
811 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
815 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
818 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
823 if (!s->config.enable_reflector)
826 avahi_address_from_sockaddr(sa, &a);
828 if (!avahi_address_is_local(s->monitor, &a))
831 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
832 struct sockaddr_in lsa;
833 socklen_t l = sizeof(lsa);
835 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
836 avahi_log_warn("getsockname(): %s", strerror(errno));
838 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
842 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
843 struct sockaddr_in6 lsa;
844 socklen_t l = sizeof(lsa);
846 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
847 avahi_log_warn("getsockname(): %s", strerror(errno));
849 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
855 static gboolean is_mdns_mcast_address(const AvahiAddress *a) {
859 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
860 return avahi_address_cmp(a, &b) == 0;
863 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, gint ttl) {
874 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
875 !avahi_interface_relevant(i)) {
876 avahi_log_warn("Recieved packet from invalid interface.");
880 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
882 port = avahi_port_from_sockaddr(sa);
883 avahi_address_from_sockaddr(sa, &a);
885 if (avahi_address_is_ipv4_in_ipv6(&a))
886 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
889 if (originates_from_local_legacy_unicast_socket(s, sa))
890 /* This originates from our local reflector, so let's ignore it */
893 if (avahi_dns_packet_check_valid(p) < 0) {
894 avahi_log_warn("Recieved invalid packet.");
898 if (avahi_dns_packet_is_query(p)) {
899 gboolean legacy_unicast = FALSE;
901 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
902 avahi_log_warn("Invalid query packet.");
906 if (port != AVAHI_MDNS_PORT) {
909 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
910 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
911 avahi_log_warn("Invalid legacy unicast query packet.");
915 legacy_unicast = TRUE;
919 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
921 handle_query_packet(s, p, i, &a, port, legacy_unicast);
923 /* avahi_log_debug("Handled query"); */
925 if (port != AVAHI_MDNS_PORT) {
926 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
930 if (ttl != 255 && s->config.check_response_ttl) {
931 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
935 if (!is_mdns_mcast_address(dest) &&
936 !avahi_interface_address_on_link(i, &a)) {
937 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
941 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
942 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
943 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
944 avahi_log_warn("Invalid response packet.");
948 handle_response_packet(s, p, i, &a);
949 /* avahi_log_debug("Handled response"); */
953 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, gint ttl) {
954 AvahiInterface *i, *j;
957 AvahiLegacyUnicastReflectSlot *slot;
964 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
965 !avahi_interface_relevant(i)) {
966 avahi_log_warn("Recieved packet from invalid interface.");
970 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
972 port = avahi_port_from_sockaddr(sa);
973 avahi_address_from_sockaddr(sa, &a);
975 if (avahi_address_is_ipv4_in_ipv6(&a))
976 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
979 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
980 avahi_log_warn("Recieved invalid packet.");
984 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
985 avahi_log_warn("Recieved legacy unicast response with unknown id");
989 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
990 !avahi_interface_relevant(j))
993 /* Patch the original ID into this response */
994 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
996 /* Forward the response to the correct client */
997 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
999 /* Undo changes to packet */
1000 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
1003 static void work(AvahiServer *s) {
1004 struct sockaddr_in6 sa6;
1005 struct sockaddr_in sa;
1013 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
1014 dest.family = AVAHI_PROTO_INET;
1015 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1016 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1017 avahi_dns_packet_free(p);
1021 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
1022 dest.family = AVAHI_PROTO_INET6;
1023 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1024 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1025 avahi_dns_packet_free(p);
1029 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
1030 dest.family = AVAHI_PROTO_INET;
1031 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1032 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1033 avahi_dns_packet_free(p);
1037 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
1038 dest.family = AVAHI_PROTO_INET6;
1039 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1040 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1041 avahi_dns_packet_free(p);
1046 static gboolean prepare_func(GSource *source, gint *timeout) {
1054 static gboolean check_func(GSource *source) {
1056 gushort revents = 0;
1060 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1063 if (s->fd_ipv4 >= 0)
1064 revents |= s->pollfd_ipv4.revents;
1065 if (s->fd_ipv6 >= 0)
1066 revents |= s->pollfd_ipv6.revents;
1067 if (s->fd_legacy_unicast_ipv4 >= 0)
1068 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1069 if (s->fd_legacy_unicast_ipv6 >= 0)
1070 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1072 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1075 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1079 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1088 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1091 if (s->state == state)
1097 s->callback(s, state, s->userdata);
1100 static void withdraw_host_rrs(AvahiServer *s) {
1103 if (s->hinfo_entry_group)
1104 avahi_entry_group_reset(s->hinfo_entry_group);
1106 if (s->browse_domain_entry_group)
1107 avahi_entry_group_reset(s->browse_domain_entry_group);
1109 avahi_update_host_rrs(s->monitor, TRUE);
1110 s->n_host_rr_pending = 0;
1113 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1116 g_assert(s->n_host_rr_pending > 0);
1118 if (--s->n_host_rr_pending == 0)
1119 server_set_state(s, AVAHI_SERVER_RUNNING);
1122 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1125 s->n_host_rr_pending ++;
1128 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1132 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1133 s->state == AVAHI_SERVER_REGISTERING)
1134 avahi_server_increase_host_rr_pending(s);
1136 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1137 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1138 withdraw_host_rrs(s);
1139 server_set_state(s, AVAHI_SERVER_COLLISION);
1141 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1142 s->state == AVAHI_SERVER_REGISTERING)
1143 avahi_server_decrease_host_rr_pending(s);
1146 static void register_hinfo(AvahiServer *s) {
1147 struct utsname utsname;
1152 if (!s->config.publish_hinfo)
1155 if (s->hinfo_entry_group)
1156 g_assert(avahi_entry_group_is_empty(s->hinfo_entry_group));
1158 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1160 /* Fill in HINFO rr */
1161 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME);
1163 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1164 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1165 avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1166 avahi_record_unref(r);
1168 avahi_entry_group_commit(s->hinfo_entry_group);
1171 static void register_localhost(AvahiServer *s) {
1175 /* Add localhost entries */
1176 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1177 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1179 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1180 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1183 static void register_browse_domain(AvahiServer *s) {
1186 if (!s->config.publish_domain)
1189 if (s->browse_domain_entry_group)
1190 g_assert(avahi_entry_group_is_empty(s->browse_domain_entry_group));
1192 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1194 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);
1195 avahi_entry_group_commit(s->browse_domain_entry_group);
1198 static void register_stuff(AvahiServer *s) {
1201 server_set_state(s, AVAHI_SERVER_REGISTERING);
1203 register_browse_domain(s);
1204 avahi_update_host_rrs(s->monitor, FALSE);
1206 if (s->n_host_rr_pending == 0)
1207 server_set_state(s, AVAHI_SERVER_RUNNING);
1210 static void update_fqdn(AvahiServer *s) {
1213 g_assert(s->host_name);
1214 g_assert(s->domain_name);
1216 g_free(s->host_name_fqdn);
1217 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1220 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1222 g_assert(host_name);
1224 withdraw_host_rrs(s);
1226 g_free(s->host_name);
1227 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1228 s->host_name[strcspn(s->host_name, ".")] = 0;
1235 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1237 g_assert(domain_name);
1239 withdraw_host_rrs(s);
1241 g_free(s->domain_name);
1242 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local");
1249 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1254 memset(pollfd, 0, sizeof(GPollFD));
1256 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1257 g_source_add_poll(s->source, pollfd);
1260 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1263 static GSourceFuncs source_funcs = {
1272 s = g_new(AvahiServer, 1);
1273 s->n_host_rr_pending = 0;
1274 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1277 avahi_server_config_copy(&s->config, sc);
1279 avahi_server_config_init(&s->config);
1281 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1282 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1284 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1285 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1286 avahi_server_config_free(&s->config);
1291 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1292 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1293 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1294 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1296 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1297 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1299 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1301 /* Prepare IO source registration */
1302 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1303 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1305 if (s->fd_ipv4 >= 0)
1306 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1307 if (s->fd_ipv6 >= 0)
1308 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1309 if (s->fd_legacy_unicast_ipv4 >= 0)
1310 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1311 if (s->fd_legacy_unicast_ipv6 >= 0)
1312 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1314 g_source_attach(s->source, s->context);
1316 s->callback = callback;
1317 s->userdata = userdata;
1319 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1320 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1321 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1323 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1324 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1325 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1326 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1327 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1328 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1329 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1330 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1331 AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1333 s->legacy_unicast_reflect_slots = NULL;
1334 s->legacy_unicast_reflect_id = 0;
1337 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1338 s->host_name[strcspn(s->host_name, ".")] = 0;
1339 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local");
1340 s->host_name_fqdn = NULL;
1343 s->record_list = avahi_record_list_new();
1345 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1347 s->state = AVAHI_SERVER_INVALID;
1349 s->monitor = avahi_interface_monitor_new(s);
1350 avahi_interface_monitor_sync(s->monitor);
1352 register_localhost(s);
1354 s->hinfo_entry_group = NULL;
1355 s->browse_domain_entry_group = NULL;
1361 void avahi_server_free(AvahiServer* s) {
1365 free_entry(s, s->entries);
1367 avahi_interface_monitor_free(s->monitor);
1370 free_group(s, s->groups);
1374 while (s->dns_server_browsers)
1375 avahi_dns_server_browser_free(s->dns_server_browsers);
1376 while (s->host_name_resolvers)
1377 avahi_host_name_resolver_free(s->host_name_resolvers);
1378 while (s->address_resolvers)
1379 avahi_address_resolver_free(s->address_resolvers);
1380 while (s->domain_browsers)
1381 avahi_domain_browser_free(s->domain_browsers);
1382 while (s->service_type_browsers)
1383 avahi_service_type_browser_free(s->service_type_browsers);
1384 while (s->service_browsers)
1385 avahi_service_browser_free(s->service_browsers);
1386 while (s->service_resolvers)
1387 avahi_service_resolver_free(s->service_resolvers);
1388 while (s->record_browsers)
1389 avahi_record_browser_destroy(s->record_browsers);
1390 g_hash_table_destroy(s->record_browser_hashtable);
1392 g_hash_table_destroy(s->entries_by_key);
1394 avahi_time_event_queue_free(s->time_event_queue);
1396 avahi_record_list_free(s->record_list);
1398 if (s->fd_ipv4 >= 0)
1400 if (s->fd_ipv6 >= 0)
1402 if (s->fd_legacy_unicast_ipv4 >= 0)
1403 close(s->fd_legacy_unicast_ipv4);
1404 if (s->fd_legacy_unicast_ipv6 >= 0)
1405 close(s->fd_legacy_unicast_ipv6);
1407 g_free(s->host_name);
1408 g_free(s->domain_name);
1409 g_free(s->host_name_fqdn);
1411 g_source_destroy(s->source);
1412 g_source_unref(s->source);
1413 g_main_context_unref(s->context);
1415 avahi_server_config_free(&s->config);
1420 static gint check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1426 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1430 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1433 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1436 if (interface <= 0 ||
1437 e->interface <= 0 ||
1438 e->interface == interface ||
1439 protocol == AVAHI_PROTO_UNSPEC ||
1440 e->protocol == AVAHI_PROTO_UNSPEC ||
1441 e->protocol == protocol)
1450 gint avahi_server_add(
1453 AvahiIfIndex interface,
1454 AvahiProtocol protocol,
1455 AvahiEntryFlags flags,
1466 if (avahi_key_is_pattern(r->key))
1469 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1472 e = g_new(AvahiEntry, 1);
1474 e->record = avahi_record_ref(r);
1476 e->interface = interface;
1477 e->protocol = protocol;
1481 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1483 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1485 /* Insert into hash table indexed by name */
1486 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1487 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1488 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1490 /* Insert into group list */
1492 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1494 avahi_announce_entry(s, e);
1499 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1500 AvahiEntry **e = (AvahiEntry**) state;
1505 *e = g ? g->entries : s->entries;
1507 while (*e && (*e)->dead)
1508 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1513 return avahi_record_ref((*e)->record);
1516 void avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, gpointer userdata) {
1522 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1524 for (e = s->entries; e; e = e->entries_next) {
1531 t = avahi_record_to_string(e->record);
1532 g_snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1535 callback(ln, userdata);
1538 avahi_dump_caches(s->monitor, callback, userdata);
1541 gint avahi_server_add_ptr(
1544 AvahiIfIndex interface,
1545 AvahiProtocol protocol,
1546 AvahiEntryFlags flags,
1549 const gchar *dest) {
1556 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl);
1557 r->data.ptr.name = avahi_normalize_name(dest);
1558 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1559 avahi_record_unref(r);
1563 gint avahi_server_add_address(
1566 AvahiIfIndex interface,
1567 AvahiProtocol protocol,
1568 AvahiEntryFlags flags,
1577 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1579 if (a->family == AVAHI_PROTO_INET) {
1583 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1584 r->data.a.address = a->data.ipv4;
1585 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1586 avahi_record_unref(r);
1588 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1589 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1596 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1597 r->data.aaaa.address = a->data.ipv6;
1598 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1599 avahi_record_unref(r);
1601 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1602 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1605 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1606 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1615 static gint server_add_txt_strlst_nocopy(
1618 AvahiIfIndex interface,
1619 AvahiProtocol protocol,
1620 AvahiEntryFlags flags,
1623 AvahiStringList *strlst) {
1630 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl);
1631 r->data.txt.string_list = strlst;
1632 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1633 avahi_record_unref(r);
1638 gint avahi_server_add_txt_strlst(
1641 AvahiIfIndex interface,
1642 AvahiProtocol protocol,
1643 AvahiEntryFlags flags,
1646 AvahiStringList *strlst) {
1648 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1651 gint avahi_server_add_txt_va(
1654 AvahiIfIndex interface,
1655 AvahiProtocol protocol,
1656 AvahiEntryFlags flags,
1663 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1666 gint avahi_server_add_txt(
1669 AvahiIfIndex interface,
1670 AvahiProtocol protocol,
1671 AvahiEntryFlags flags,
1682 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1688 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1693 while (*s && size >= 2) {
1694 if (*s == '.' || *s == '\\') {
1710 static gint server_add_service_strlst_nocopy(
1713 AvahiIfIndex interface,
1714 AvahiProtocol protocol,
1717 const gchar *domain,
1720 AvahiStringList *strlst) {
1722 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1731 escape_service_name(ename, sizeof(ename), name);
1734 while (domain[0] == '.')
1737 domain = s->domain_name;
1740 host = s->host_name_fqdn;
1742 d = avahi_normalize_name(domain);
1743 t = avahi_normalize_name(type);
1745 g_snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1746 g_snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1748 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name);
1750 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1751 r->data.srv.priority = 0;
1752 r->data.srv.weight = 0;
1753 r->data.srv.port = port;
1754 r->data.srv.name = avahi_normalize_name(host);
1755 ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1756 avahi_record_unref(r);
1758 ret |= server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1760 g_snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1761 ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1769 gint avahi_server_add_service_strlst(
1772 AvahiIfIndex interface,
1773 AvahiProtocol protocol,
1776 const gchar *domain,
1779 AvahiStringList *strlst) {
1781 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1784 gint avahi_server_add_service_va(
1787 AvahiIfIndex interface,
1788 AvahiProtocol protocol,
1791 const gchar *domain,
1800 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1803 gint avahi_server_add_service(
1806 AvahiIfIndex interface,
1807 AvahiProtocol protocol,
1810 const gchar *domain,
1823 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1828 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1829 static const gchar hex[] = "0123456789abcdef";
1831 const guint8 *k = p;
1833 while (sl > 1 && pl > 0) {
1834 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1850 gint avahi_server_add_dns_server_address(
1853 AvahiIfIndex interface,
1854 AvahiProtocol protocol,
1855 const gchar *domain,
1856 AvahiDNSServerType type,
1857 const AvahiAddress *address,
1858 guint16 port /** should be 53 */) {
1862 gchar n[64] = "ip-";
1866 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1867 g_assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1869 if (address->family == AVAHI_PROTO_INET) {
1870 hexstring(n+3, sizeof(n)-3, &address->data, 4);
1871 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1872 r->data.a.address = address->data.ipv4;
1874 hexstring(n+3, sizeof(n)-3, &address->data, 6);
1875 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1876 r->data.aaaa.address = address->data.ipv6;
1879 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1880 avahi_record_unref(r);
1882 ret |= avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1887 gint avahi_server_add_dns_server_name(
1890 AvahiIfIndex interface,
1891 AvahiProtocol protocol,
1892 const gchar *domain,
1893 AvahiDNSServerType type,
1895 guint16 port /** should be 53 */) {
1903 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1906 while (domain[0] == '.')
1909 domain = s->domain_name;
1911 d = avahi_normalize_name(domain);
1912 g_snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
1915 r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1916 r->data.srv.priority = 0;
1917 r->data.srv.weight = 0;
1918 r->data.srv.port = port;
1919 r->data.srv.name = avahi_normalize_name(name);
1920 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
1921 avahi_record_unref(r);
1926 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1927 AvahiKey *k = userdata;
1933 avahi_interface_post_query(i, k, FALSE);
1936 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1940 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1943 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1946 if (g->state == state)
1952 g->callback(g->server, g, state, g->userdata);
1955 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1960 g = g_new(AvahiEntryGroup, 1);
1962 g->callback = callback;
1963 g->userdata = userdata;
1965 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1967 g->n_register_try = 0;
1968 g->register_time_event = NULL;
1969 g->register_time.tv_sec = 0;
1970 g->register_time.tv_usec = 0;
1971 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1973 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1977 void avahi_entry_group_free(AvahiEntryGroup *g) {
1981 g_assert(g->server);
1983 for (e = g->entries; e; e = e->by_group_next) {
1985 avahi_goodbye_entry(g->server, e, TRUE);
1990 if (g->register_time_event) {
1991 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
1992 g->register_time_event = NULL;
1997 g->server->need_group_cleanup = TRUE;
1998 g->server->need_entry_cleanup = TRUE;
2001 static void entry_group_commit_real(AvahiEntryGroup *g) {
2004 g_get_current_time(&g->register_time);
2006 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2009 avahi_announce_group(g->server, g);
2010 avahi_entry_group_check_probed(g, FALSE);
2014 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
2015 AvahiEntryGroup *g = userdata;
2018 /* avahi_log_debug("Holdoff passed, waking up and going on."); */
2020 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2021 g->register_time_event = NULL;
2023 /* Holdoff time passed, so let's start probing */
2024 entry_group_commit_real(g);
2027 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
2033 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2034 return AVAHI_ERR_BAD_STATE;
2036 g->n_register_try++;
2038 avahi_timeval_add(&g->register_time,
2039 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2040 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2041 AVAHI_RR_HOLDOFF_MSEC));
2043 g_get_current_time(&now);
2045 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2046 /* Holdoff time passed, so let's start probing */
2047 /* avahi_log_debug("Holdoff passed, directly going on."); */
2049 entry_group_commit_real(g);
2051 /* avahi_log_debug("Holdoff not passed, sleeping."); */
2053 /* Holdoff time has not yet passed, so let's wait */
2054 g_assert(!g->register_time_event);
2055 g->register_time_event = avahi_time_event_queue_add(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2057 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2063 void avahi_entry_group_reset(AvahiEntryGroup *g) {
2067 if (g->register_time_event) {
2068 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2069 g->register_time_event = NULL;
2072 for (e = g->entries; e; e = e->by_group_next) {
2074 avahi_goodbye_entry(g->server, e, TRUE);
2079 if (g->register_time_event) {
2080 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2081 g->register_time_event = NULL;
2084 g->server->need_entry_cleanup = TRUE;
2087 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2090 gboolean avahi_entry_commited(AvahiEntry *e) {
2095 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2096 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2099 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
2106 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
2109 g->userdata = userdata;
2112 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
2118 gboolean avahi_entry_group_is_empty(AvahiEntryGroup *g) {
2122 /* Look for an entry that is not dead */
2123 for (e = g->entries; e; e = e->by_group_next)
2130 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
2133 return s->domain_name;
2136 const gchar* avahi_server_get_host_name(AvahiServer *s) {
2139 return s->host_name;
2142 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2145 return s->host_name_fqdn;
2148 gpointer avahi_server_get_data(AvahiServer *s) {
2154 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2157 s->userdata = userdata;
2160 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2166 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2169 memset(c, 0, sizeof(AvahiServerConfig));
2172 c->host_name = NULL;
2173 c->domain_name = NULL;
2174 c->check_response_ttl = FALSE;
2175 c->publish_hinfo = TRUE;
2176 c->publish_addresses = TRUE;
2177 c->publish_workstation = TRUE;
2178 c->publish_domain = TRUE;
2179 c->use_iff_running = FALSE;
2180 c->enable_reflector = FALSE;
2181 c->reflect_ipv = FALSE;
2186 void avahi_server_config_free(AvahiServerConfig *c) {
2189 g_free(c->host_name);
2190 g_free(c->domain_name);
2193 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2199 ret->host_name = g_strdup(c->host_name);
2200 ret->domain_name = g_strdup(c->domain_name);