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>
40 #define AVAHI_HOST_RR_HOLDOFF_MSEC 2000
42 static void free_entry(AvahiServer*s, AvahiEntry *e) {
48 avahi_goodbye_entry(s, e, TRUE);
50 /* Remove from linked list */
51 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
53 /* Remove from hash table indexed by name */
54 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
55 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
57 g_hash_table_replace(s->entries_by_key, t->record->key, t);
59 g_hash_table_remove(s->entries_by_key, e->record->key);
61 /* Remove from associated group */
63 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
65 avahi_record_unref(e->record);
69 static void free_group(AvahiServer *s, AvahiEntryGroup *g) {
74 free_entry(s, g->entries);
76 AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
80 static void cleanup_dead(AvahiServer *s) {
81 AvahiEntryGroup *g, *ng;
86 if (s->need_group_cleanup) {
87 for (g = s->groups; g; g = ng) {
94 s->need_group_cleanup = FALSE;
97 if (s->need_entry_cleanup) {
98 for (e = s->entries; e; e = ne) {
105 s->need_entry_cleanup = FALSE;
108 if (s->need_browser_cleanup)
109 avahi_browser_cleanup(s);
112 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) {
121 g_assert(type != AVAHI_DNS_TYPE_ANY);
123 k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
125 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
126 if (!e->dead && avahi_entry_registered(s, e, i))
127 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
132 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) {
138 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
139 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
140 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
141 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
142 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
143 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
144 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
149 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary) {
154 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
157 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
165 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
168 if (avahi_key_is_pattern(k)) {
170 /* Handle ANY query */
172 for (e = s->entries; e; e = e->entries_next)
173 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
174 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
178 /* Handle all other queries */
180 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
181 if (!e->dead && avahi_entry_registered(s, e, i))
182 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
186 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
193 for (k = e->group->entries; k; k = k->by_group_next) {
194 avahi_goodbye_entry(s, k, FALSE);
198 avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
200 avahi_goodbye_entry(s, e, FALSE);
204 s->need_entry_cleanup = TRUE;
207 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
213 for (e = g_hash_table_lookup(s->entries_by_key, key); e; e = e->by_key_next)
214 withdraw_entry(s, e);
217 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
220 gboolean ours = FALSE, won = FALSE, lost = FALSE;
226 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
233 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
238 if (avahi_entry_probing(s, e, i)) {
247 t = avahi_record_to_string(record);
252 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
254 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
255 withdraw_rrset(s, record->key);
262 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
263 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
264 AvahiEntry *e, *n, *conflicting_entry = NULL;
271 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
273 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
276 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
279 /* Either our entry or the other is intended to be unique, so let's check */
281 if (avahi_record_equal_no_ttl(e->record, record)) {
282 ours = TRUE; /* We have an identical record, so this is no conflict */
284 /* Check wheter there is a TTL conflict */
285 if (record->ttl <= e->record->ttl/2 &&
286 avahi_entry_registered(s, e, i)) {
289 t = avahi_record_to_string(record);
291 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
292 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
298 /* There's no need to check the other entries of this RRset */
303 if (avahi_entry_registered(s, e, i)) {
305 /* A conflict => we have to return to probe mode */
307 conflicting_entry = e;
309 } else if (avahi_entry_probing(s, e, i)) {
311 /* We are currently registering a matching record, but
312 * someone else already claimed it, so let's
315 withdraw_immediately = TRUE;
320 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
322 if (!ours && conflict) {
327 t = avahi_record_to_string(record);
329 if (withdraw_immediately) {
330 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
331 withdraw_rrset(s, record->key);
333 g_assert(conflicting_entry);
334 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
335 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
337 /* Local unique records are returned to probin
338 * state. Local shared records are reannounced. */
347 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
348 gboolean *unicast_response = userdata;
352 g_assert(unicast_response);
354 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
357 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
361 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
364 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean immediately) {
368 g_assert(!legacy_unicast || (a && port > 0 && p));
370 if (legacy_unicast) {
371 AvahiDnsPacket *reply;
374 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
376 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
378 append_aux_records_to_list(s, i, r, FALSE);
380 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
381 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
383 gchar *t = avahi_record_to_string(r);
384 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
388 avahi_record_unref(r);
391 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
392 avahi_interface_send_packet_unicast(i, reply, a, port);
394 avahi_dns_packet_free(reply);
397 gboolean unicast_response, flush_cache, auxiliary;
398 AvahiDnsPacket *reply = NULL;
401 /* In case the query packet was truncated never respond
402 immediately, because known answer suppression records might be
403 contained in later packets */
404 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
406 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
408 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
410 append_aux_records_to_list(s, i, r, unicast_response);
412 /* Due to some reasons the record has not been scheduled.
413 * The client requested an unicast response in that
414 * case. Therefore we prepare such a response */
420 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
423 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
425 /* Appending this record succeeded, so incremeant
426 * the specific header field, and return to the caller */
428 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
433 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
436 /* The record is too large for one packet, so create a larger packet */
438 avahi_dns_packet_free(reply);
439 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
440 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
441 size = AVAHI_DNS_PACKET_MAX_SIZE;
442 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
444 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
445 avahi_dns_packet_free(reply);
447 gchar *t = avahi_record_to_string(r);
448 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
452 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
455 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
456 avahi_interface_send_packet_unicast(i, reply, a, port);
457 avahi_dns_packet_free(reply);
462 avahi_record_unref(r);
466 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
467 avahi_interface_send_packet_unicast(i, reply, a, port);
468 avahi_dns_packet_free(reply);
472 avahi_record_list_flush(s->record_list);
476 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
483 if (!s->config.enable_reflector)
486 for (j = s->monitor->interfaces; j; j = j->interface_next)
487 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
488 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
491 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
492 AvahiServer *s = userdata;
499 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
503 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
510 if (!s->config.enable_reflector)
513 for (j = s->monitor->interfaces; j; j = j->interface_next)
514 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
515 /* Post the query to other networks */
516 avahi_interface_post_query(j, k, TRUE);
518 /* Reply from caches of other network. This is needed to
519 * "work around" known answer suppression. */
521 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
525 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
532 if (!s->config.enable_reflector)
535 for (j = s->monitor->interfaces; j; j = j->interface_next)
536 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
537 avahi_interface_post_probe(j, r, TRUE);
540 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
549 /* avahi_log_debug("query"); */
551 g_assert(avahi_record_list_empty(s->record_list));
553 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
555 /* Handle the questions */
556 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
558 gboolean unicast_response = FALSE;
560 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
561 avahi_log_warn("Packet too short (1)");
566 reflect_query(s, i, key);
568 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
569 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
570 /* Allow our own queries to be suppressed by incoming
571 * queries only when they do not include known answers */
572 avahi_query_scheduler_incoming(i->query_scheduler, key);
574 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
575 avahi_key_unref(key);
578 /* Known Answer Suppression */
579 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
581 gboolean unique = FALSE;
583 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
584 avahi_log_warn("Packet too short (2)");
588 if (handle_conflict(s, i, record, unique, a)) {
589 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
590 avahi_record_list_drop(s->record_list, record);
593 avahi_record_unref(record);
597 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
599 gboolean unique = FALSE;
601 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
602 avahi_log_warn("Packet too short (3)");
606 if (!avahi_key_is_pattern(record->key)) {
607 reflect_probe(s, i, record);
608 incoming_probe(s, record, i);
611 avahi_record_unref(record);
614 if (!avahi_record_list_empty(s->record_list))
615 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
620 avahi_record_list_flush(s->record_list);
623 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
631 /* avahi_log_debug("response"); */
633 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
634 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
636 gboolean cache_flush = FALSE;
639 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
640 avahi_log_warn("Packet too short (4)");
644 if (!avahi_key_is_pattern(record->key)) {
646 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
649 if (handle_conflict(s, i, record, cache_flush, a)) {
650 reflect_response(s, i, record, cache_flush);
651 avahi_cache_update(i->cache, record, cache_flush, a);
652 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
656 avahi_record_unref(record);
659 /* If the incoming response contained a conflicting record, some
660 records have been scheduling for sending. We need to flush them
662 if (!avahi_record_list_empty(s->record_list))
663 avahi_server_generate_response(s, i, NULL, NULL, 0, FALSE, TRUE);
666 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
667 guint n, index = (guint) -1;
668 AvahiLegacyUnicastReflectSlot *slot;
672 if (!s->legacy_unicast_reflect_slots)
673 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
675 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
676 index = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
678 if (!s->legacy_unicast_reflect_slots[index])
682 if (index == (guint) -1 || s->legacy_unicast_reflect_slots[index])
685 slot = s->legacy_unicast_reflect_slots[index] = g_new(AvahiLegacyUnicastReflectSlot, 1);
686 slot->id = s->legacy_unicast_reflect_id++;
691 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
697 index = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
699 g_assert(s->legacy_unicast_reflect_slots[index] == slot);
701 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
704 s->legacy_unicast_reflect_slots[index] = NULL;
707 static void free_slots(AvahiServer *s) {
711 if (!s->legacy_unicast_reflect_slots)
714 for (index = 0; index < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; index ++)
715 if (s->legacy_unicast_reflect_slots[index])
716 deallocate_slot(s, s->legacy_unicast_reflect_slots[index]);
718 g_free(s->legacy_unicast_reflect_slots);
719 s->legacy_unicast_reflect_slots = NULL;
722 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
727 if (!s->legacy_unicast_reflect_slots)
730 index = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
732 if (!s->legacy_unicast_reflect_slots[index] || s->legacy_unicast_reflect_slots[index]->id != id)
735 return s->legacy_unicast_reflect_slots[index];
738 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
739 AvahiLegacyUnicastReflectSlot *slot = userdata;
743 g_assert(slot->time_event == e);
745 deallocate_slot(slot->server, slot);
748 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
749 AvahiLegacyUnicastReflectSlot *slot;
757 g_assert(i->protocol == a->family);
759 if (!s->config.enable_reflector)
762 /* avahi_log_debug("legacy unicast reflectr"); */
764 /* Reflecting legacy unicast queries is a little more complicated
765 than reflecting normal queries, since we must route the
766 responses back to the right client. Therefore we must store
767 some information for finding the right client contact data for
768 response packets. In contrast to normal queries legacy
769 unicast query and response packets are reflected untouched and
770 are not reassembled into larger packets */
772 if (!(slot = allocate_slot(s))) {
773 /* No slot available, we drop this legacy unicast query */
774 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
778 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
781 slot->interface = i->hardware->index;
783 avahi_elapse_time(&slot->elapse_time, 2000, 0);
784 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
786 /* Patch the packet with our new locally generatedt id */
787 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
789 for (j = s->monitor->interfaces; j; j = j->interface_next)
790 if (avahi_interface_relevant(j) &&
792 (s->config.reflect_ipv || j->protocol == i->protocol)) {
794 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
795 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
796 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
797 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
801 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
804 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
809 if (!s->config.enable_reflector)
812 avahi_address_from_sockaddr(sa, &a);
814 if (!avahi_address_is_local(s->monitor, &a))
817 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
818 struct sockaddr_in lsa;
819 socklen_t l = sizeof(lsa);
821 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
822 avahi_log_warn("getsockname(): %s", strerror(errno));
824 return lsa.sin_port == ((struct sockaddr_in*) sa)->sin_port;
828 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
829 struct sockaddr_in6 lsa;
830 socklen_t l = sizeof(lsa);
832 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
833 avahi_log_warn("getsockname(): %s", strerror(errno));
835 return lsa.sin6_port == ((struct sockaddr_in6*) sa)->sin6_port;
841 static gboolean is_mdns_mcast_address(const AvahiAddress *a) {
845 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
846 return avahi_address_cmp(a, &b) == 0;
849 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, gint ttl) {
860 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
861 !avahi_interface_relevant(i)) {
862 avahi_log_warn("Recieved packet from invalid interface.");
866 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
868 port = avahi_port_from_sockaddr(sa);
869 avahi_address_from_sockaddr(sa, &a);
871 if (avahi_address_is_ipv4_in_ipv6(&a))
872 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
875 if (originates_from_local_legacy_unicast_socket(s, sa))
876 /* This originates from our local reflector, so let's ignore it */
879 if (avahi_dns_packet_check_valid(p) < 0) {
880 avahi_log_warn("Recieved invalid packet.");
884 if (avahi_dns_packet_is_query(p)) {
885 gboolean legacy_unicast = FALSE;
887 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
888 avahi_log_warn("Invalid query packet.");
892 if (port != AVAHI_MDNS_PORT) {
895 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
896 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
897 avahi_log_warn("Invalid legacy unicast query packet.");
901 legacy_unicast = TRUE;
905 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
907 handle_query_packet(s, p, i, &a, port, legacy_unicast);
909 /* avahi_log_debug("Handled query"); */
911 if (port != AVAHI_MDNS_PORT) {
912 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
916 if (ttl != 255 && s->config.check_response_ttl) {
917 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
921 if (!is_mdns_mcast_address(dest) &&
922 !avahi_interface_address_on_link(i, &a)) {
923 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
927 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
928 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
929 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
930 avahi_log_warn("Invalid response packet.");
934 handle_response_packet(s, p, i, &a);
935 /* avahi_log_debug("Handled response"); */
939 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, gint ttl) {
940 AvahiInterface *i, *j;
943 AvahiLegacyUnicastReflectSlot *slot;
950 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
951 !avahi_interface_relevant(i)) {
952 avahi_log_warn("Recieved packet from invalid interface.");
956 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
958 port = avahi_port_from_sockaddr(sa);
959 avahi_address_from_sockaddr(sa, &a);
961 if (avahi_address_is_ipv4_in_ipv6(&a))
962 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
965 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
966 avahi_log_warn("Recieved invalid packet.");
970 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
971 avahi_log_warn("Recieved legacy unicast response with unknown id");
975 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
976 !avahi_interface_relevant(j))
979 /* Patch the original ID into this response */
980 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
982 /* Forward the response to the correct client */
983 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
985 /* Undo changes to packet */
986 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
989 static void work(AvahiServer *s) {
990 struct sockaddr_in6 sa6;
991 struct sockaddr_in sa;
999 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
1000 dest.family = AVAHI_PROTO_INET;
1001 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1002 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1003 avahi_dns_packet_free(p);
1007 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
1008 dest.family = AVAHI_PROTO_INET6;
1009 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1010 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1011 avahi_dns_packet_free(p);
1015 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
1016 dest.family = AVAHI_PROTO_INET;
1017 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1018 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1019 avahi_dns_packet_free(p);
1023 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
1024 dest.family = AVAHI_PROTO_INET6;
1025 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1026 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1027 avahi_dns_packet_free(p);
1032 static gboolean prepare_func(GSource *source, gint *timeout) {
1040 static gboolean check_func(GSource *source) {
1042 gushort revents = 0;
1046 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1049 if (s->fd_ipv4 >= 0)
1050 revents |= s->pollfd_ipv4.revents;
1051 if (s->fd_ipv6 >= 0)
1052 revents |= s->pollfd_ipv6.revents;
1053 if (s->fd_legacy_unicast_ipv4 >= 0)
1054 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1055 if (s->fd_legacy_unicast_ipv6 >= 0)
1056 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1058 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1061 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1065 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1074 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1077 if (s->state == state)
1083 s->callback(s, state, s->userdata);
1086 static void withdraw_host_rrs(AvahiServer *s) {
1089 if (s->hinfo_entry_group) {
1090 avahi_entry_group_free(s->hinfo_entry_group);
1091 s->hinfo_entry_group = NULL;
1094 if (s->browse_domain_entry_group) {
1095 avahi_entry_group_free(s->browse_domain_entry_group);
1096 s->browse_domain_entry_group = NULL;
1099 avahi_update_host_rrs(s->monitor, TRUE);
1100 s->n_host_rr_pending = 0;
1103 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1106 g_assert(s->n_host_rr_pending > 0);
1108 if (--s->n_host_rr_pending == 0)
1109 server_set_state(s, AVAHI_SERVER_RUNNING);
1112 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1115 s->n_host_rr_pending ++;
1118 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1122 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1123 s->state == AVAHI_SERVER_REGISTERING)
1124 avahi_server_increase_host_rr_pending(s);
1125 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1126 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1127 withdraw_host_rrs(s);
1128 server_set_state(s, AVAHI_SERVER_COLLISION);
1129 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1130 s->state == AVAHI_SERVER_REGISTERING)
1131 avahi_server_decrease_host_rr_pending(s);
1134 static void register_hinfo(AvahiServer *s) {
1135 struct utsname utsname;
1140 if (!s->config.publish_hinfo || s->hinfo_entry_group)
1143 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1145 /* Fill in HINFO rr */
1146 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME);
1148 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1149 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1150 avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1151 avahi_record_unref(r);
1153 avahi_entry_group_commit(s->hinfo_entry_group);
1156 static void register_localhost(AvahiServer *s) {
1160 /* Add localhost entries */
1161 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1162 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1164 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1165 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1168 static void register_browse_domain(AvahiServer *s) {
1171 if (!s->config.publish_domain || s->browse_domain_entry_group)
1174 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1175 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);
1176 avahi_entry_group_commit(s->browse_domain_entry_group);
1179 static void register_stuff(AvahiServer *s) {
1182 server_set_state(s, AVAHI_SERVER_REGISTERING);
1184 register_browse_domain(s);
1185 avahi_update_host_rrs(s->monitor, FALSE);
1187 if (s->n_host_rr_pending == 0)
1188 server_set_state(s, AVAHI_SERVER_RUNNING);
1191 static void update_fqdn(AvahiServer *s) {
1194 g_assert(s->host_name);
1195 g_assert(s->domain_name);
1197 g_free(s->host_name_fqdn);
1198 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1201 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1202 AvahiServer *s = userdata;
1207 g_assert(e == s->register_time_event);
1208 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1209 s->register_time_event = NULL;
1211 if (s->state == AVAHI_SERVER_SLEEPING)
1215 static void delayed_register_stuff(AvahiServer *s) {
1220 avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1222 if (s->register_time_event)
1223 avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1225 s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1228 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1230 g_assert(host_name);
1232 server_set_state(s, AVAHI_SERVER_SLEEPING);
1233 withdraw_host_rrs(s);
1235 g_free(s->host_name);
1236 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1237 s->host_name[strcspn(s->host_name, ".")] = 0;
1240 delayed_register_stuff(s);
1244 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1246 g_assert(domain_name);
1248 server_set_state(s, AVAHI_SERVER_SLEEPING);
1249 withdraw_host_rrs(s);
1251 g_free(s->domain_name);
1252 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local.");
1255 delayed_register_stuff(s);
1260 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1265 memset(pollfd, 0, sizeof(GPollFD));
1267 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1268 g_source_add_poll(s->source, pollfd);
1271 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1274 static GSourceFuncs source_funcs = {
1283 s = g_new(AvahiServer, 1);
1284 s->n_host_rr_pending = 0;
1285 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1288 avahi_server_config_copy(&s->config, sc);
1290 avahi_server_config_init(&s->config);
1292 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1293 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1295 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1296 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1297 avahi_server_config_free(&s->config);
1302 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1303 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1304 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1305 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1307 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1308 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1310 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1312 /* Prepare IO source registration */
1313 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1314 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1316 if (s->fd_ipv4 >= 0)
1317 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1318 if (s->fd_ipv6 >= 0)
1319 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1320 if (s->fd_legacy_unicast_ipv4 >= 0)
1321 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1322 if (s->fd_legacy_unicast_ipv6 >= 0)
1323 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1325 g_source_attach(s->source, s->context);
1327 s->callback = callback;
1328 s->userdata = userdata;
1330 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1331 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1332 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1334 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1335 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1336 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1337 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1338 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1339 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1340 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1341 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1342 AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1344 s->legacy_unicast_reflect_slots = NULL;
1345 s->legacy_unicast_reflect_id = 0;
1348 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1349 s->host_name[strcspn(s->host_name, ".")] = 0;
1350 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local.");
1351 s->host_name_fqdn = NULL;
1354 s->record_list = avahi_record_list_new();
1356 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1357 s->register_time_event = NULL;
1359 s->state = AVAHI_SERVER_INVALID;
1361 s->monitor = avahi_interface_monitor_new(s);
1362 avahi_interface_monitor_sync(s->monitor);
1364 register_localhost(s);
1366 s->hinfo_entry_group = NULL;
1367 s->browse_domain_entry_group = NULL;
1373 void avahi_server_free(AvahiServer* s) {
1377 free_entry(s, s->entries);
1379 avahi_interface_monitor_free(s->monitor);
1382 free_group(s, s->groups);
1386 while (s->dns_server_browsers)
1387 avahi_dns_server_browser_free(s->dns_server_browsers);
1388 while (s->host_name_resolvers)
1389 avahi_host_name_resolver_free(s->host_name_resolvers);
1390 while (s->address_resolvers)
1391 avahi_address_resolver_free(s->address_resolvers);
1392 while (s->domain_browsers)
1393 avahi_domain_browser_free(s->domain_browsers);
1394 while (s->service_type_browsers)
1395 avahi_service_type_browser_free(s->service_type_browsers);
1396 while (s->service_browsers)
1397 avahi_service_browser_free(s->service_browsers);
1398 while (s->service_resolvers)
1399 avahi_service_resolver_free(s->service_resolvers);
1400 while (s->record_browsers)
1401 avahi_record_browser_destroy(s->record_browsers);
1402 g_hash_table_destroy(s->record_browser_hashtable);
1404 g_hash_table_destroy(s->entries_by_key);
1406 if (s->register_time_event)
1407 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1408 avahi_time_event_queue_free(s->time_event_queue);
1410 avahi_record_list_free(s->record_list);
1412 if (s->fd_ipv4 >= 0)
1414 if (s->fd_ipv6 >= 0)
1416 if (s->fd_legacy_unicast_ipv4 >= 0)
1417 close(s->fd_legacy_unicast_ipv4);
1418 if (s->fd_legacy_unicast_ipv6 >= 0)
1419 close(s->fd_legacy_unicast_ipv6);
1421 g_free(s->host_name);
1422 g_free(s->domain_name);
1423 g_free(s->host_name_fqdn);
1425 g_source_destroy(s->source);
1426 g_source_unref(s->source);
1427 g_main_context_unref(s->context);
1429 avahi_server_config_free(&s->config);
1434 static gint check_record_conflict(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1440 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1444 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1447 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1450 if (interface <= 0 ||
1451 e->interface <= 0 ||
1452 e->interface == interface ||
1453 protocol == AVAHI_PROTO_UNSPEC ||
1454 e->protocol == AVAHI_PROTO_UNSPEC ||
1455 e->protocol == protocol)
1464 gint avahi_server_add(
1469 AvahiEntryFlags flags,
1480 if (avahi_key_is_pattern(r->key))
1483 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1486 e = g_new(AvahiEntry, 1);
1488 e->record = avahi_record_ref(r);
1490 e->interface = interface;
1491 e->protocol = protocol;
1495 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1497 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1499 /* Insert into hash table indexed by name */
1500 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1501 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1502 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1504 /* Insert into group list */
1506 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1508 avahi_announce_entry(s, e);
1513 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1514 AvahiEntry **e = (AvahiEntry**) state;
1519 *e = g ? g->entries : s->entries;
1521 while (*e && (*e)->dead)
1522 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1527 return avahi_record_ref((*e)->record);
1530 void avahi_server_dump(AvahiServer *s, FILE *f) {
1535 fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
1537 for (e = s->entries; e; e = e->entries_next) {
1543 t = avahi_record_to_string(e->record);
1544 fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
1548 avahi_dump_caches(s->monitor, f);
1551 gint avahi_server_add_ptr(
1556 AvahiEntryFlags flags,
1559 const gchar *dest) {
1566 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl);
1567 r->data.ptr.name = avahi_normalize_name(dest);
1568 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1569 avahi_record_unref(r);
1573 gint avahi_server_add_address(
1578 AvahiEntryFlags flags,
1587 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1589 if (a->family == AVAHI_PROTO_INET) {
1593 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1594 r->data.a.address = a->data.ipv4;
1595 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1596 avahi_record_unref(r);
1598 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1599 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1606 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1607 r->data.aaaa.address = a->data.ipv6;
1608 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1609 avahi_record_unref(r);
1611 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1612 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1615 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1616 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1625 gint avahi_server_add_txt_strlst(
1630 AvahiEntryFlags flags,
1633 AvahiStringList *strlst) {
1640 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl);
1641 r->data.txt.string_list = strlst;
1642 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1643 avahi_record_unref(r);
1648 gint avahi_server_add_txt_va(
1653 AvahiEntryFlags flags,
1660 return avahi_server_add_txt_strlst(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1663 gint avahi_server_add_txt(
1668 AvahiEntryFlags flags,
1679 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1685 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1690 while (*s && size >= 2) {
1691 if (*s == '.' || *s == '\\') {
1707 gint avahi_server_add_service_strlst(
1714 const gchar *domain,
1717 AvahiStringList *strlst) {
1719 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1727 escape_service_name(ename, sizeof(ename), name);
1730 while (domain[0] == '.')
1733 domain = s->domain_name;
1736 host = s->host_name_fqdn;
1738 snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
1739 snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
1741 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name);
1743 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1744 r->data.srv.priority = 0;
1745 r->data.srv.weight = 0;
1746 r->data.srv.port = port;
1747 r->data.srv.name = avahi_normalize_name(host);
1748 ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1749 avahi_record_unref(r);
1751 ret |= avahi_server_add_txt_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1753 snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
1754 ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1759 gint avahi_server_add_service_va(
1766 const gchar *domain,
1775 return avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
1778 gint avahi_server_add_service(
1785 const gchar *domain,
1798 ret = avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
1803 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1804 static const gchar hex[] = "0123456789abcdef";
1806 const guint8 *k = p;
1808 while (sl > 1 && pl > 0) {
1809 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1825 gint avahi_server_add_dns_server_address(
1830 const gchar *domain,
1831 AvahiDNSServerType type,
1832 const AvahiAddress *address,
1833 guint16 port /** should be 53 */) {
1837 gchar n[64] = "ip-";
1841 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1842 g_assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1844 if (address->family == AVAHI_PROTO_INET) {
1845 hexstring(n+3, sizeof(n)-3, &address->data, 4);
1846 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1847 r->data.a.address = address->data.ipv4;
1849 hexstring(n+3, sizeof(n)-3, &address->data, 6);
1850 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1851 r->data.aaaa.address = address->data.ipv6;
1854 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1855 avahi_record_unref(r);
1857 ret |= avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1862 gint avahi_server_add_dns_server_name(
1867 const gchar *domain,
1868 AvahiDNSServerType type,
1870 guint16 port /** should be 53 */) {
1878 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1881 while (domain[0] == '.')
1884 domain = s->domain_name;
1886 snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", domain);
1888 r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1889 r->data.srv.priority = 0;
1890 r->data.srv.weight = 0;
1891 r->data.srv.port = port;
1892 r->data.srv.name = avahi_normalize_name(name);
1893 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
1894 avahi_record_unref(r);
1900 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1901 AvahiKey *k = userdata;
1907 avahi_interface_post_query(i, k, FALSE);
1910 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1914 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1917 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1920 if (g->state == state)
1926 g->callback(g->server, g, state, g->userdata);
1931 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1936 g = g_new(AvahiEntryGroup, 1);
1938 g->callback = callback;
1939 g->userdata = userdata;
1941 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1943 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1945 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1949 void avahi_entry_group_free(AvahiEntryGroup *g) {
1953 g_assert(g->server);
1955 for (e = g->entries; e; e = e->by_group_next) {
1956 avahi_goodbye_entry(g->server, e, TRUE);
1962 g->server->need_group_cleanup = TRUE;
1963 g->server->need_entry_cleanup = TRUE;
1966 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1970 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1973 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1974 avahi_announce_group(g->server, g);
1975 avahi_entry_group_check_probed(g, FALSE);
1980 gboolean avahi_entry_commited(AvahiEntry *e) {
1985 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1986 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1989 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
1996 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
1999 g->userdata = userdata;
2002 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
2008 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
2011 return s->domain_name;
2014 const gchar* avahi_server_get_host_name(AvahiServer *s) {
2017 return s->host_name;
2020 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2023 return s->host_name_fqdn;
2026 gpointer avahi_server_get_data(AvahiServer *s) {
2032 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2035 s->userdata = userdata;
2038 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2044 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2047 memset(c, 0, sizeof(AvahiServerConfig));
2050 c->host_name = NULL;
2051 c->domain_name = NULL;
2052 c->check_response_ttl = FALSE;
2053 c->publish_hinfo = TRUE;
2054 c->publish_addresses = TRUE;
2055 c->publish_workstation = TRUE;
2056 c->publish_domain = TRUE;
2057 c->use_iff_running = FALSE;
2058 c->enable_reflector = FALSE;
2059 c->reflect_ipv = FALSE;
2064 void avahi_server_config_free(AvahiServerConfig *c) {
2067 g_free(c->host_name);
2068 g_free(c->domain_name);
2071 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2077 ret->host_name = g_strdup(c->host_name);
2078 ret->domain_name = g_strdup(c->domain_name);