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);
1135 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1136 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1137 withdraw_host_rrs(s);
1138 server_set_state(s, AVAHI_SERVER_COLLISION);
1140 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1141 s->state == AVAHI_SERVER_REGISTERING)
1142 avahi_server_decrease_host_rr_pending(s);
1145 static void register_hinfo(AvahiServer *s) {
1146 struct utsname utsname;
1151 if (!s->config.publish_hinfo)
1154 if (s->hinfo_entry_group)
1155 g_assert(avahi_entry_group_is_empty(s->hinfo_entry_group));
1157 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1159 /* Fill in HINFO rr */
1160 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME);
1162 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1163 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1164 avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1165 avahi_record_unref(r);
1167 avahi_entry_group_commit(s->hinfo_entry_group);
1170 static void register_localhost(AvahiServer *s) {
1174 /* Add localhost entries */
1175 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1176 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1178 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1179 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1182 static void register_browse_domain(AvahiServer *s) {
1185 if (!s->config.publish_domain)
1188 if (s->browse_domain_entry_group)
1189 g_assert(avahi_entry_group_is_empty(s->browse_domain_entry_group));
1191 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1193 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);
1194 avahi_entry_group_commit(s->browse_domain_entry_group);
1197 static void register_stuff(AvahiServer *s) {
1200 server_set_state(s, AVAHI_SERVER_REGISTERING);
1202 register_browse_domain(s);
1203 avahi_update_host_rrs(s->monitor, FALSE);
1205 if (s->n_host_rr_pending == 0)
1206 server_set_state(s, AVAHI_SERVER_RUNNING);
1209 static void update_fqdn(AvahiServer *s) {
1212 g_assert(s->host_name);
1213 g_assert(s->domain_name);
1215 g_free(s->host_name_fqdn);
1216 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1219 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1221 g_assert(host_name);
1223 withdraw_host_rrs(s);
1225 g_free(s->host_name);
1226 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1227 s->host_name[strcspn(s->host_name, ".")] = 0;
1234 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1236 g_assert(domain_name);
1238 withdraw_host_rrs(s);
1240 g_free(s->domain_name);
1241 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local");
1248 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1253 memset(pollfd, 0, sizeof(GPollFD));
1255 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1256 g_source_add_poll(s->source, pollfd);
1259 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1262 static GSourceFuncs source_funcs = {
1271 s = g_new(AvahiServer, 1);
1272 s->n_host_rr_pending = 0;
1273 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1276 avahi_server_config_copy(&s->config, sc);
1278 avahi_server_config_init(&s->config);
1280 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1281 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1283 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1284 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1285 avahi_server_config_free(&s->config);
1290 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1291 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1292 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1293 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1295 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1296 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1298 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1300 /* Prepare IO source registration */
1301 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1302 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1304 if (s->fd_ipv4 >= 0)
1305 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1306 if (s->fd_ipv6 >= 0)
1307 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1308 if (s->fd_legacy_unicast_ipv4 >= 0)
1309 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1310 if (s->fd_legacy_unicast_ipv6 >= 0)
1311 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1313 g_source_attach(s->source, s->context);
1315 s->callback = callback;
1316 s->userdata = userdata;
1318 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1319 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1320 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1322 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1323 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1324 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1325 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1326 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1327 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1328 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1329 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1330 AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1332 s->legacy_unicast_reflect_slots = NULL;
1333 s->legacy_unicast_reflect_id = 0;
1336 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1337 s->host_name[strcspn(s->host_name, ".")] = 0;
1338 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local");
1339 s->host_name_fqdn = NULL;
1342 s->record_list = avahi_record_list_new();
1344 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1346 s->state = AVAHI_SERVER_INVALID;
1348 s->monitor = avahi_interface_monitor_new(s);
1349 avahi_interface_monitor_sync(s->monitor);
1351 register_localhost(s);
1353 s->hinfo_entry_group = NULL;
1354 s->browse_domain_entry_group = NULL;
1360 void avahi_server_free(AvahiServer* s) {
1364 free_entry(s, s->entries);
1366 avahi_interface_monitor_free(s->monitor);
1369 free_group(s, s->groups);
1373 while (s->dns_server_browsers)
1374 avahi_dns_server_browser_free(s->dns_server_browsers);
1375 while (s->host_name_resolvers)
1376 avahi_host_name_resolver_free(s->host_name_resolvers);
1377 while (s->address_resolvers)
1378 avahi_address_resolver_free(s->address_resolvers);
1379 while (s->domain_browsers)
1380 avahi_domain_browser_free(s->domain_browsers);
1381 while (s->service_type_browsers)
1382 avahi_service_type_browser_free(s->service_type_browsers);
1383 while (s->service_browsers)
1384 avahi_service_browser_free(s->service_browsers);
1385 while (s->service_resolvers)
1386 avahi_service_resolver_free(s->service_resolvers);
1387 while (s->record_browsers)
1388 avahi_record_browser_destroy(s->record_browsers);
1389 g_hash_table_destroy(s->record_browser_hashtable);
1391 g_hash_table_destroy(s->entries_by_key);
1393 avahi_time_event_queue_free(s->time_event_queue);
1395 avahi_record_list_free(s->record_list);
1397 if (s->fd_ipv4 >= 0)
1399 if (s->fd_ipv6 >= 0)
1401 if (s->fd_legacy_unicast_ipv4 >= 0)
1402 close(s->fd_legacy_unicast_ipv4);
1403 if (s->fd_legacy_unicast_ipv6 >= 0)
1404 close(s->fd_legacy_unicast_ipv6);
1406 g_free(s->host_name);
1407 g_free(s->domain_name);
1408 g_free(s->host_name_fqdn);
1410 g_source_destroy(s->source);
1411 g_source_unref(s->source);
1412 g_main_context_unref(s->context);
1414 avahi_server_config_free(&s->config);
1419 static gint check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1425 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1429 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1432 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1435 if (interface <= 0 ||
1436 e->interface <= 0 ||
1437 e->interface == interface ||
1438 protocol == AVAHI_PROTO_UNSPEC ||
1439 e->protocol == AVAHI_PROTO_UNSPEC ||
1440 e->protocol == protocol)
1449 gint avahi_server_add(
1452 AvahiIfIndex interface,
1453 AvahiProtocol protocol,
1454 AvahiEntryFlags flags,
1465 if (avahi_key_is_pattern(r->key))
1468 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1471 e = g_new(AvahiEntry, 1);
1473 e->record = avahi_record_ref(r);
1475 e->interface = interface;
1476 e->protocol = protocol;
1480 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1482 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1484 /* Insert into hash table indexed by name */
1485 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1486 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1487 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1489 /* Insert into group list */
1491 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1493 avahi_announce_entry(s, e);
1498 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1499 AvahiEntry **e = (AvahiEntry**) state;
1504 *e = g ? g->entries : s->entries;
1506 while (*e && (*e)->dead)
1507 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1512 return avahi_record_ref((*e)->record);
1515 void avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, gpointer userdata) {
1521 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1523 for (e = s->entries; e; e = e->entries_next) {
1530 t = avahi_record_to_string(e->record);
1531 g_snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1534 callback(ln, userdata);
1537 avahi_dump_caches(s->monitor, callback, userdata);
1540 gint avahi_server_add_ptr(
1543 AvahiIfIndex interface,
1544 AvahiProtocol protocol,
1545 AvahiEntryFlags flags,
1548 const gchar *dest) {
1555 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl);
1556 r->data.ptr.name = avahi_normalize_name(dest);
1557 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1558 avahi_record_unref(r);
1562 gint avahi_server_add_address(
1565 AvahiIfIndex interface,
1566 AvahiProtocol protocol,
1567 AvahiEntryFlags flags,
1576 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1578 if (a->family == AVAHI_PROTO_INET) {
1582 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1583 r->data.a.address = a->data.ipv4;
1584 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1585 avahi_record_unref(r);
1587 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1588 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1595 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1596 r->data.aaaa.address = a->data.ipv6;
1597 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1598 avahi_record_unref(r);
1600 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1601 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1604 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1605 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1614 static gint server_add_txt_strlst_nocopy(
1617 AvahiIfIndex interface,
1618 AvahiProtocol protocol,
1619 AvahiEntryFlags flags,
1622 AvahiStringList *strlst) {
1629 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl);
1630 r->data.txt.string_list = strlst;
1631 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1632 avahi_record_unref(r);
1637 gint avahi_server_add_txt_strlst(
1640 AvahiIfIndex interface,
1641 AvahiProtocol protocol,
1642 AvahiEntryFlags flags,
1645 AvahiStringList *strlst) {
1647 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_copy(strlst));
1650 gint avahi_server_add_txt_va(
1653 AvahiIfIndex interface,
1654 AvahiProtocol protocol,
1655 AvahiEntryFlags flags,
1662 return server_add_txt_strlst_nocopy(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1665 gint avahi_server_add_txt(
1668 AvahiIfIndex interface,
1669 AvahiProtocol protocol,
1670 AvahiEntryFlags flags,
1681 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1687 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1692 while (*s && size >= 2) {
1693 if (*s == '.' || *s == '\\') {
1709 static gint server_add_service_strlst_nocopy(
1712 AvahiIfIndex interface,
1713 AvahiProtocol protocol,
1716 const gchar *domain,
1719 AvahiStringList *strlst) {
1721 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1730 escape_service_name(ename, sizeof(ename), name);
1733 while (domain[0] == '.')
1736 domain = s->domain_name;
1739 host = s->host_name_fqdn;
1741 d = avahi_normalize_name(domain);
1742 t = avahi_normalize_name(type);
1744 g_snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1745 g_snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1747 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name);
1749 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1750 r->data.srv.priority = 0;
1751 r->data.srv.weight = 0;
1752 r->data.srv.port = port;
1753 r->data.srv.name = avahi_normalize_name(host);
1754 ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1755 avahi_record_unref(r);
1757 ret |= server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1759 g_snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1760 ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1768 gint avahi_server_add_service_strlst(
1771 AvahiIfIndex interface,
1772 AvahiProtocol protocol,
1775 const gchar *domain,
1778 AvahiStringList *strlst) {
1780 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_copy(strlst));
1783 gint avahi_server_add_service_va(
1786 AvahiIfIndex interface,
1787 AvahiProtocol protocol,
1790 const gchar *domain,
1799 return server_add_service_strlst_nocopy(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1802 gint avahi_server_add_service(
1805 AvahiIfIndex interface,
1806 AvahiProtocol protocol,
1809 const gchar *domain,
1822 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1827 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1828 static const gchar hex[] = "0123456789abcdef";
1830 const guint8 *k = p;
1832 while (sl > 1 && pl > 0) {
1833 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1849 gint avahi_server_add_dns_server_address(
1852 AvahiIfIndex interface,
1853 AvahiProtocol protocol,
1854 const gchar *domain,
1855 AvahiDNSServerType type,
1856 const AvahiAddress *address,
1857 guint16 port /** should be 53 */) {
1861 gchar n[64] = "ip-";
1865 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1866 g_assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1868 if (address->family == AVAHI_PROTO_INET) {
1869 hexstring(n+3, sizeof(n)-3, &address->data, 4);
1870 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1871 r->data.a.address = address->data.ipv4;
1873 hexstring(n+3, sizeof(n)-3, &address->data, 6);
1874 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1875 r->data.aaaa.address = address->data.ipv6;
1878 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1879 avahi_record_unref(r);
1881 ret |= avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1886 gint avahi_server_add_dns_server_name(
1889 AvahiIfIndex interface,
1890 AvahiProtocol protocol,
1891 const gchar *domain,
1892 AvahiDNSServerType type,
1894 guint16 port /** should be 53 */) {
1902 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1905 while (domain[0] == '.')
1908 domain = s->domain_name;
1910 d = avahi_normalize_name(domain);
1911 g_snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
1914 r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1915 r->data.srv.priority = 0;
1916 r->data.srv.weight = 0;
1917 r->data.srv.port = port;
1918 r->data.srv.name = avahi_normalize_name(name);
1919 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
1920 avahi_record_unref(r);
1925 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1926 AvahiKey *k = userdata;
1932 avahi_interface_post_query(i, k, FALSE);
1935 void avahi_server_post_query(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
1939 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1942 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1945 if (g->state == state)
1951 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 g_get_current_time(&g->register_time);
2005 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2008 avahi_announce_group(g->server, g);
2009 avahi_entry_group_check_probed(g, FALSE);
2013 static void entry_group_register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
2014 AvahiEntryGroup *g = userdata;
2017 avahi_log_debug("Holdoff passed, waking up and going on.");
2019 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2020 g->register_time_event = NULL;
2022 /* Holdoff time passed, so let's start probing */
2023 entry_group_commit_real(g);
2026 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
2032 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
2033 return AVAHI_ERR_BAD_STATE;
2035 g->n_register_try++;
2037 avahi_timeval_add(&g->register_time,
2038 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
2039 AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
2040 AVAHI_RR_HOLDOFF_MSEC));
2042 g_get_current_time(&now);
2044 if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
2045 /* Holdoff time passed, so let's start probing */
2046 avahi_log_debug("Holdoff passed, directly going on.");
2048 entry_group_commit_real(g);
2050 avahi_log_debug("Holdoff not passed, sleeping.");
2052 /* Holdoff time has not yet passed, so let's wait */
2053 g_assert(!g->register_time_event);
2054 g->register_time_event = avahi_time_event_queue_add(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
2056 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
2062 void avahi_entry_group_reset(AvahiEntryGroup *g) {
2066 if (g->register_time_event) {
2067 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2068 g->register_time_event = NULL;
2071 for (e = g->entries; e; e = e->by_group_next) {
2073 avahi_goodbye_entry(g->server, e, TRUE);
2078 if (g->register_time_event) {
2079 avahi_time_event_queue_remove(g->server->time_event_queue, g->register_time_event);
2080 g->register_time_event = NULL;
2083 g->server->need_entry_cleanup = TRUE;
2086 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
2089 gboolean avahi_entry_commited(AvahiEntry *e) {
2094 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2095 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2098 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
2105 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
2108 g->userdata = userdata;
2111 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
2117 gboolean avahi_entry_group_is_empty(AvahiEntryGroup *g) {
2121 /* Look for an entry that is not dead */
2122 for (e = g->entries; e; e = e->by_group_next)
2129 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
2132 return s->domain_name;
2135 const gchar* avahi_server_get_host_name(AvahiServer *s) {
2138 return s->host_name;
2141 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2144 return s->host_name_fqdn;
2147 gpointer avahi_server_get_data(AvahiServer *s) {
2153 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2156 s->userdata = userdata;
2159 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2165 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2168 memset(c, 0, sizeof(AvahiServerConfig));
2171 c->host_name = NULL;
2172 c->domain_name = NULL;
2173 c->check_response_ttl = FALSE;
2174 c->publish_hinfo = TRUE;
2175 c->publish_addresses = TRUE;
2176 c->publish_workstation = TRUE;
2177 c->publish_domain = TRUE;
2178 c->use_iff_running = FALSE;
2179 c->enable_reflector = FALSE;
2180 c->reflect_ipv = FALSE;
2185 void avahi_server_config_free(AvahiServerConfig *c) {
2188 g_free(c->host_name);
2189 g_free(c->domain_name);
2192 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2198 ret->host_name = g_strdup(c->host_name);
2199 ret->domain_name = g_strdup(c->domain_name);