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_HOST_RR_HOLDOFF_MSEC 2000
43 static void free_entry(AvahiServer*s, AvahiEntry *e) {
49 avahi_goodbye_entry(s, e, TRUE);
51 /* Remove from linked list */
52 AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
54 /* Remove from hash table indexed by name */
55 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
56 AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
58 g_hash_table_replace(s->entries_by_key, t->record->key, t);
60 g_hash_table_remove(s->entries_by_key, e->record->key);
62 /* Remove from associated group */
64 AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
66 avahi_record_unref(e->record);
70 static void free_group(AvahiServer *s, AvahiEntryGroup *g) {
75 free_entry(s, g->entries);
77 AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
81 static void cleanup_dead(AvahiServer *s) {
82 AvahiEntryGroup *g, *ng;
87 if (s->need_group_cleanup) {
88 for (g = s->groups; g; g = ng) {
95 s->need_group_cleanup = FALSE;
98 if (s->need_entry_cleanup) {
99 for (e = s->entries; e; e = ne) {
100 ne = e->entries_next;
106 s->need_entry_cleanup = FALSE;
109 if (s->need_browser_cleanup)
110 avahi_browser_cleanup(s);
113 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) {
122 g_assert(type != AVAHI_DNS_TYPE_ANY);
124 k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
126 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
127 if (!e->dead && avahi_entry_registered(s, e, i))
128 callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
133 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) {
139 if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
140 if (r->key->type == AVAHI_DNS_TYPE_PTR) {
141 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
142 enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
143 } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
144 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
145 enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
150 void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary) {
155 avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
158 void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
166 /* avahi_log_debug("Posting responses matching [%s]", txt = avahi_key_to_string(k)); */
169 if (avahi_key_is_pattern(k)) {
171 /* Handle ANY query */
173 for (e = s->entries; e; e = e->entries_next)
174 if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
175 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
179 /* Handle all other queries */
181 for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
182 if (!e->dead && avahi_entry_registered(s, e, i))
183 avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
187 static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
194 for (k = e->group->entries; k; k = k->by_group_next) {
195 avahi_goodbye_entry(s, k, FALSE);
199 avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
201 avahi_goodbye_entry(s, e, FALSE);
205 s->need_entry_cleanup = TRUE;
208 static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
214 for (e = g_hash_table_lookup(s->entries_by_key, key); e; e = e->by_key_next)
215 withdraw_entry(s, e);
218 static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
221 gboolean ours = FALSE, won = FALSE, lost = FALSE;
227 t = avahi_record_to_string(record);
229 /* avahi_log_debug("incoming_probe()"); */
231 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
238 if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
243 if (avahi_entry_probing(s, e, i)) {
255 avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
257 avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
258 withdraw_rrset(s, record->key);
260 /* avahi_log_debug("Not conflicting probe"); */
266 static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
267 gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE;
268 AvahiEntry *e, *n, *conflicting_entry = NULL;
275 /* avahi_log_debug("CHECKING FOR CONFLICT: [%s]", t); */
277 for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
280 if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique))
283 /* Either our entry or the other is intended to be unique, so let's check */
285 if (avahi_record_equal_no_ttl(e->record, record)) {
286 ours = TRUE; /* We have an identical record, so this is no conflict */
288 /* Check wheter there is a TTL conflict */
289 if (record->ttl <= e->record->ttl/2 &&
290 avahi_entry_registered(s, e, i)) {
293 t = avahi_record_to_string(record);
295 avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
296 avahi_server_prepare_matching_responses(s, i, e->record->key, FALSE);
302 /* There's no need to check the other entries of this RRset */
307 if (avahi_entry_registered(s, e, i)) {
309 /* A conflict => we have to return to probe mode */
311 conflicting_entry = e;
313 } else if (avahi_entry_probing(s, e, i)) {
315 /* We are currently registering a matching record, but
316 * someone else already claimed it, so let's
319 withdraw_immediately = TRUE;
324 /* avahi_log_debug("ours=%i conflict=%i", ours, conflict); */
326 if (!ours && conflict) {
331 t = avahi_record_to_string(record);
333 if (withdraw_immediately) {
334 avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
335 withdraw_rrset(s, record->key);
337 g_assert(conflicting_entry);
338 avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
339 avahi_entry_return_to_initial_state(s, conflicting_entry, i);
341 /* Local unique records are returned to probing
342 * state. Local shared records are reannounced. */
351 static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
352 gboolean *unicast_response = userdata;
356 g_assert(unicast_response);
358 avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
361 static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
365 avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
368 void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean immediately) {
372 g_assert(!legacy_unicast || (a && port > 0 && p));
374 if (legacy_unicast) {
375 AvahiDnsPacket *reply;
378 reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
380 while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
382 append_aux_records_to_list(s, i, r, FALSE);
384 if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
385 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
387 gchar *t = avahi_record_to_string(r);
388 avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
392 avahi_record_unref(r);
395 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
396 avahi_interface_send_packet_unicast(i, reply, a, port);
398 avahi_dns_packet_free(reply);
401 gboolean unicast_response, flush_cache, auxiliary;
402 AvahiDnsPacket *reply = NULL;
405 /* In case the query packet was truncated never respond
406 immediately, because known answer suppression records might be
407 contained in later packets */
408 gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
410 while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
412 if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
414 append_aux_records_to_list(s, i, r, unicast_response);
416 /* Due to some reasons the record has not been scheduled.
417 * The client requested an unicast response in that
418 * case. Therefore we prepare such a response */
424 reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
427 if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
429 /* Appending this record succeeded, so incremeant
430 * the specific header field, and return to the caller */
432 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
437 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
440 /* The record is too large for one packet, so create a larger packet */
442 avahi_dns_packet_free(reply);
443 size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
444 if (size > AVAHI_DNS_PACKET_MAX_SIZE)
445 size = AVAHI_DNS_PACKET_MAX_SIZE;
446 reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
448 if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
449 avahi_dns_packet_free(reply);
451 gchar *t = avahi_record_to_string(r);
452 avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
456 avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
459 /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
460 avahi_interface_send_packet_unicast(i, reply, a, port);
461 avahi_dns_packet_free(reply);
466 avahi_record_unref(r);
470 if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
471 avahi_interface_send_packet_unicast(i, reply, a, port);
472 avahi_dns_packet_free(reply);
476 avahi_record_list_flush(s->record_list);
480 static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
487 if (!s->config.enable_reflector)
490 for (j = s->monitor->interfaces; j; j = j->interface_next)
491 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
492 avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
495 static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
496 AvahiServer *s = userdata;
503 avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
507 static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
514 if (!s->config.enable_reflector)
517 for (j = s->monitor->interfaces; j; j = j->interface_next)
518 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
519 /* Post the query to other networks */
520 avahi_interface_post_query(j, k, TRUE);
522 /* Reply from caches of other network. This is needed to
523 * "work around" known answer suppression. */
525 avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
529 static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
536 if (!s->config.enable_reflector)
539 for (j = s->monitor->interfaces; j; j = j->interface_next)
540 if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
541 avahi_interface_post_probe(j, r, TRUE);
544 static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
553 /* avahi_log_debug("query"); */
555 g_assert(avahi_record_list_empty(s->record_list));
557 is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
559 /* Handle the questions */
560 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
562 gboolean unicast_response = FALSE;
564 if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
565 avahi_log_warn("Packet too short (1)");
570 reflect_query(s, i, key);
572 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
573 !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
574 /* Allow our own queries to be suppressed by incoming
575 * queries only when they do not include known answers */
576 avahi_query_scheduler_incoming(i->query_scheduler, key);
578 avahi_server_prepare_matching_responses(s, i, key, unicast_response);
579 avahi_key_unref(key);
582 /* Known Answer Suppression */
583 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
585 gboolean unique = FALSE;
587 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
588 avahi_log_warn("Packet too short (2)");
592 if (handle_conflict(s, i, record, unique, a)) {
593 avahi_response_scheduler_suppress(i->response_scheduler, record, a);
594 avahi_record_list_drop(s->record_list, record);
597 avahi_record_unref(record);
601 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
603 gboolean unique = FALSE;
605 if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
606 avahi_log_warn("Packet too short (3)");
610 if (!avahi_key_is_pattern(record->key)) {
611 reflect_probe(s, i, record);
612 incoming_probe(s, record, i);
615 avahi_record_unref(record);
618 if (!avahi_record_list_empty(s->record_list))
619 avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
624 avahi_record_list_flush(s->record_list);
627 static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
635 /* avahi_log_debug("response"); */
637 for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
638 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
640 gboolean cache_flush = FALSE;
643 if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
644 avahi_log_warn("Packet too short (4)");
648 if (!avahi_key_is_pattern(record->key)) {
650 /* avahi_log_debug("Handling response: %s", txt = avahi_record_to_string(record)); */
653 if (handle_conflict(s, i, record, cache_flush, a)) {
654 reflect_response(s, i, record, cache_flush);
655 avahi_cache_update(i->cache, record, cache_flush, a);
656 avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
660 avahi_record_unref(record);
663 /* If the incoming response contained a conflicting record, some
664 records have been scheduling for sending. We need to flush them
666 if (!avahi_record_list_empty(s->record_list))
667 avahi_server_generate_response(s, i, NULL, NULL, 0, FALSE, TRUE);
670 static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
671 guint n, idx = (guint) -1;
672 AvahiLegacyUnicastReflectSlot *slot;
676 if (!s->legacy_unicast_reflect_slots)
677 s->legacy_unicast_reflect_slots = g_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS);
679 for (n = 0; n < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; n++, s->legacy_unicast_reflect_id++) {
680 idx = s->legacy_unicast_reflect_id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
682 if (!s->legacy_unicast_reflect_slots[idx])
686 if (idx == (guint) -1 || s->legacy_unicast_reflect_slots[idx])
689 slot = s->legacy_unicast_reflect_slots[idx] = g_new(AvahiLegacyUnicastReflectSlot, 1);
690 slot->id = s->legacy_unicast_reflect_id++;
695 static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
701 idx = slot->id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
703 g_assert(s->legacy_unicast_reflect_slots[idx] == slot);
705 avahi_time_event_queue_remove(s->time_event_queue, slot->time_event);
708 s->legacy_unicast_reflect_slots[idx] = NULL;
711 static void free_slots(AvahiServer *s) {
715 if (!s->legacy_unicast_reflect_slots)
718 for (idx = 0; idx < AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS; idx ++)
719 if (s->legacy_unicast_reflect_slots[idx])
720 deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
722 g_free(s->legacy_unicast_reflect_slots);
723 s->legacy_unicast_reflect_slots = NULL;
726 static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, guint16 id) {
731 if (!s->legacy_unicast_reflect_slots)
734 idx = id % AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS;
736 if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
739 return s->legacy_unicast_reflect_slots[idx];
742 static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
743 AvahiLegacyUnicastReflectSlot *slot = userdata;
747 g_assert(slot->time_event == e);
749 deallocate_slot(slot->server, slot);
752 static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
753 AvahiLegacyUnicastReflectSlot *slot;
761 g_assert(i->protocol == a->family);
763 if (!s->config.enable_reflector)
766 /* avahi_log_debug("legacy unicast reflectr"); */
768 /* Reflecting legacy unicast queries is a little more complicated
769 than reflecting normal queries, since we must route the
770 responses back to the right client. Therefore we must store
771 some information for finding the right client contact data for
772 response packets. In contrast to normal queries legacy
773 unicast query and response packets are reflected untouched and
774 are not reassembled into larger packets */
776 if (!(slot = allocate_slot(s))) {
777 /* No slot available, we drop this legacy unicast query */
778 avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
782 slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
785 slot->interface = i->hardware->index;
787 avahi_elapse_time(&slot->elapse_time, 2000, 0);
788 slot->time_event = avahi_time_event_queue_add(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
790 /* Patch the packet with our new locally generatedt id */
791 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
793 for (j = s->monitor->interfaces; j; j = j->interface_next)
794 if (avahi_interface_relevant(j) &&
796 (s->config.reflect_ipv || j->protocol == i->protocol)) {
798 if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
799 avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, 0);
800 } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
801 avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, 0);
805 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
808 static gboolean originates_from_local_legacy_unicast_socket(AvahiServer *s, const struct sockaddr *sa) {
813 if (!s->config.enable_reflector)
816 avahi_address_from_sockaddr(sa, &a);
818 if (!avahi_address_is_local(s->monitor, &a))
821 if (sa->sa_family == AF_INET && s->fd_legacy_unicast_ipv4 >= 0) {
822 struct sockaddr_in lsa;
823 socklen_t l = sizeof(lsa);
825 if (getsockname(s->fd_legacy_unicast_ipv4, &lsa, &l) != 0)
826 avahi_log_warn("getsockname(): %s", strerror(errno));
828 return lsa.sin_port == ((const struct sockaddr_in*) sa)->sin_port;
832 if (sa->sa_family == AF_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
833 struct sockaddr_in6 lsa;
834 socklen_t l = sizeof(lsa);
836 if (getsockname(s->fd_legacy_unicast_ipv6, &lsa, &l) != 0)
837 avahi_log_warn("getsockname(): %s", strerror(errno));
839 return lsa.sin6_port == ((const struct sockaddr_in6*) sa)->sin6_port;
845 static gboolean is_mdns_mcast_address(const AvahiAddress *a) {
849 avahi_address_parse(a->family == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->family, &b);
850 return avahi_address_cmp(a, &b) == 0;
853 static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiAddress *dest, AvahiIfIndex iface, gint ttl) {
864 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
865 !avahi_interface_relevant(i)) {
866 avahi_log_warn("Recieved packet from invalid interface.");
870 /* avahi_log_debug("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
872 port = avahi_port_from_sockaddr(sa);
873 avahi_address_from_sockaddr(sa, &a);
875 if (avahi_address_is_ipv4_in_ipv6(&a))
876 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
879 if (originates_from_local_legacy_unicast_socket(s, sa))
880 /* This originates from our local reflector, so let's ignore it */
883 if (avahi_dns_packet_check_valid(p) < 0) {
884 avahi_log_warn("Recieved invalid packet.");
888 if (avahi_dns_packet_is_query(p)) {
889 gboolean legacy_unicast = FALSE;
891 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
892 avahi_log_warn("Invalid query packet.");
896 if (port != AVAHI_MDNS_PORT) {
899 if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
900 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
901 avahi_log_warn("Invalid legacy unicast query packet.");
905 legacy_unicast = TRUE;
909 reflect_legacy_unicast_query_packet(s, p, i, &a, port);
911 handle_query_packet(s, p, i, &a, port, legacy_unicast);
913 /* avahi_log_debug("Handled query"); */
915 if (port != AVAHI_MDNS_PORT) {
916 avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
920 if (ttl != 255 && s->config.check_response_ttl) {
921 avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
925 if (!is_mdns_mcast_address(dest) &&
926 !avahi_interface_address_on_link(i, &a)) {
927 avahi_log_warn("Recivied non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
931 if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
932 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
933 avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
934 avahi_log_warn("Invalid response packet.");
938 handle_response_packet(s, p, i, &a);
939 /* avahi_log_debug("Handled response"); */
943 static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, gint ttl) {
944 AvahiInterface *i, *j;
947 AvahiLegacyUnicastReflectSlot *slot;
954 if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) ||
955 !avahi_interface_relevant(i)) {
956 avahi_log_warn("Recieved packet from invalid interface.");
960 /* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
962 port = avahi_port_from_sockaddr(sa);
963 avahi_address_from_sockaddr(sa, &a);
965 if (avahi_address_is_ipv4_in_ipv6(&a))
966 /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
969 if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
970 avahi_log_warn("Recieved invalid packet.");
974 if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
975 avahi_log_warn("Recieved legacy unicast response with unknown id");
979 if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.family)) ||
980 !avahi_interface_relevant(j))
983 /* Patch the original ID into this response */
984 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
986 /* Forward the response to the correct client */
987 avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
989 /* Undo changes to packet */
990 avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
993 static void work(AvahiServer *s) {
994 struct sockaddr_in6 sa6;
995 struct sockaddr_in sa;
1003 if (s->fd_ipv4 >= 0 && (s->pollfd_ipv4.revents & G_IO_IN)) {
1004 dest.family = AVAHI_PROTO_INET;
1005 if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1006 dispatch_packet(s, p, (struct sockaddr*) &sa, &dest, iface, ttl);
1007 avahi_dns_packet_free(p);
1011 if (s->fd_ipv6 >= 0 && (s->pollfd_ipv6.revents & G_IO_IN)) {
1012 dest.family = AVAHI_PROTO_INET6;
1013 if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1014 dispatch_packet(s, p, (struct sockaddr*) &sa6, &dest, iface, ttl);
1015 avahi_dns_packet_free(p);
1019 if (s->fd_legacy_unicast_ipv4 >= 0 && (s->pollfd_legacy_unicast_ipv4.revents & G_IO_IN)) {
1020 dest.family = AVAHI_PROTO_INET;
1021 if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
1022 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
1023 avahi_dns_packet_free(p);
1027 if (s->fd_legacy_unicast_ipv6 >= 0 && (s->pollfd_legacy_unicast_ipv6.revents & G_IO_IN)) {
1028 dest.family = AVAHI_PROTO_INET6;
1029 if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
1030 dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
1031 avahi_dns_packet_free(p);
1036 static gboolean prepare_func(GSource *source, gint *timeout) {
1044 static gboolean check_func(GSource *source) {
1046 gushort revents = 0;
1050 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1053 if (s->fd_ipv4 >= 0)
1054 revents |= s->pollfd_ipv4.revents;
1055 if (s->fd_ipv6 >= 0)
1056 revents |= s->pollfd_ipv6.revents;
1057 if (s->fd_legacy_unicast_ipv4 >= 0)
1058 revents |= s->pollfd_legacy_unicast_ipv4.revents;
1059 if (s->fd_legacy_unicast_ipv6 >= 0)
1060 revents |= s->pollfd_legacy_unicast_ipv6.revents;
1062 return !!(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR));
1065 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
1069 s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
1078 static void server_set_state(AvahiServer *s, AvahiServerState state) {
1081 if (s->state == state)
1087 s->callback(s, state, s->userdata);
1090 static void withdraw_host_rrs(AvahiServer *s) {
1093 if (s->hinfo_entry_group) {
1094 avahi_entry_group_free(s->hinfo_entry_group);
1095 s->hinfo_entry_group = NULL;
1098 if (s->browse_domain_entry_group) {
1099 avahi_entry_group_free(s->browse_domain_entry_group);
1100 s->browse_domain_entry_group = NULL;
1103 avahi_update_host_rrs(s->monitor, TRUE);
1104 s->n_host_rr_pending = 0;
1107 void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
1110 g_assert(s->n_host_rr_pending > 0);
1112 if (--s->n_host_rr_pending == 0)
1113 server_set_state(s, AVAHI_SERVER_RUNNING);
1116 void avahi_server_increase_host_rr_pending(AvahiServer *s) {
1119 s->n_host_rr_pending ++;
1122 void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) {
1126 if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
1127 s->state == AVAHI_SERVER_REGISTERING)
1128 avahi_server_increase_host_rr_pending(s);
1129 else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
1130 (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
1131 withdraw_host_rrs(s);
1132 server_set_state(s, AVAHI_SERVER_COLLISION);
1133 } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
1134 s->state == AVAHI_SERVER_REGISTERING)
1135 avahi_server_decrease_host_rr_pending(s);
1138 static void register_hinfo(AvahiServer *s) {
1139 struct utsname utsname;
1144 if (!s->config.publish_hinfo || s->hinfo_entry_group)
1147 s->hinfo_entry_group = avahi_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
1149 /* Fill in HINFO rr */
1150 r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME);
1152 r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
1153 r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
1154 avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
1155 avahi_record_unref(r);
1157 avahi_entry_group_commit(s->hinfo_entry_group);
1160 static void register_localhost(AvahiServer *s) {
1164 /* Add localhost entries */
1165 avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
1166 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
1168 avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
1169 avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
1172 static void register_browse_domain(AvahiServer *s) {
1175 if (!s->config.publish_domain || s->browse_domain_entry_group)
1178 s->browse_domain_entry_group = avahi_entry_group_new(s, NULL, NULL);
1179 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);
1180 avahi_entry_group_commit(s->browse_domain_entry_group);
1183 static void register_stuff(AvahiServer *s) {
1186 server_set_state(s, AVAHI_SERVER_REGISTERING);
1188 register_browse_domain(s);
1189 avahi_update_host_rrs(s->monitor, FALSE);
1191 if (s->n_host_rr_pending == 0)
1192 server_set_state(s, AVAHI_SERVER_RUNNING);
1195 static void update_fqdn(AvahiServer *s) {
1198 g_assert(s->host_name);
1199 g_assert(s->domain_name);
1201 g_free(s->host_name_fqdn);
1202 s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain_name);
1205 static void register_time_event_callback(AvahiTimeEvent *e, gpointer userdata) {
1206 AvahiServer *s = userdata;
1211 g_assert(e == s->register_time_event);
1212 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1213 s->register_time_event = NULL;
1215 if (s->state == AVAHI_SERVER_SLEEPING)
1219 static void delayed_register_stuff(AvahiServer *s) {
1224 avahi_elapse_time(&tv, AVAHI_HOST_RR_HOLDOFF_MSEC, 0);
1226 if (s->register_time_event)
1227 avahi_time_event_queue_update(s->time_event_queue, s->register_time_event, &tv);
1229 s->register_time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, register_time_event_callback, s);
1232 gint avahi_server_set_host_name(AvahiServer *s, const gchar *host_name) {
1234 g_assert(host_name);
1236 server_set_state(s, AVAHI_SERVER_SLEEPING);
1237 withdraw_host_rrs(s);
1239 g_free(s->host_name);
1240 s->host_name = host_name ? avahi_normalize_name(host_name) : avahi_get_host_name();
1241 s->host_name[strcspn(s->host_name, ".")] = 0;
1244 delayed_register_stuff(s);
1248 gint avahi_server_set_domain_name(AvahiServer *s, const gchar *domain_name) {
1250 g_assert(domain_name);
1252 server_set_state(s, AVAHI_SERVER_SLEEPING);
1253 withdraw_host_rrs(s);
1255 g_free(s->domain_name);
1256 s->domain_name = domain_name ? avahi_normalize_name(domain_name) : g_strdup("local");
1259 delayed_register_stuff(s);
1264 static void prepare_pollfd(AvahiServer *s, GPollFD *pollfd, gint fd) {
1269 memset(pollfd, 0, sizeof(GPollFD));
1271 pollfd->events = G_IO_IN|G_IO_ERR|G_IO_HUP;
1272 g_source_add_poll(s->source, pollfd);
1275 AvahiServer *avahi_server_new(GMainContext *c, const AvahiServerConfig *sc, AvahiServerCallback callback, gpointer userdata) {
1278 static GSourceFuncs source_funcs = {
1287 s = g_new(AvahiServer, 1);
1288 s->n_host_rr_pending = 0;
1289 s->need_entry_cleanup = s->need_group_cleanup = s->need_browser_cleanup = FALSE;
1292 avahi_server_config_copy(&s->config, sc);
1294 avahi_server_config_init(&s->config);
1296 s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4() : -1;
1297 s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6() : -1;
1299 if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
1300 g_critical("Selected neither IPv6 nor IPv4 support, aborting.\n");
1301 avahi_server_config_free(&s->config);
1306 if (s->fd_ipv4 < 0 && s->config.use_ipv4)
1307 avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
1308 else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
1309 avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
1311 s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
1312 s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
1314 g_main_context_ref(s->context = (c ? c : g_main_context_default()));
1316 /* Prepare IO source registration */
1317 s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
1318 *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
1320 if (s->fd_ipv4 >= 0)
1321 prepare_pollfd(s, &s->pollfd_ipv4, s->fd_ipv4);
1322 if (s->fd_ipv6 >= 0)
1323 prepare_pollfd(s, &s->pollfd_ipv6, s->fd_ipv6);
1324 if (s->fd_legacy_unicast_ipv4 >= 0)
1325 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv4, s->fd_legacy_unicast_ipv4);
1326 if (s->fd_legacy_unicast_ipv6 >= 0)
1327 prepare_pollfd(s, &s->pollfd_legacy_unicast_ipv6, s->fd_legacy_unicast_ipv6);
1329 g_source_attach(s->source, s->context);
1331 s->callback = callback;
1332 s->userdata = userdata;
1334 AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
1335 s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1336 AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
1338 AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, s->record_browsers);
1339 s->record_browser_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
1340 AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, s->host_name_resolvers);
1341 AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, s->address_resolvers);
1342 AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, s->domain_browsers);
1343 AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, s->service_type_browsers);
1344 AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, s->service_browsers);
1345 AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, s->service_resolvers);
1346 AVAHI_LLIST_HEAD_INIT(AvahiDNSServerBrowser, s->dns_server_browsers);
1348 s->legacy_unicast_reflect_slots = NULL;
1349 s->legacy_unicast_reflect_id = 0;
1352 s->host_name = s->config.host_name ? avahi_normalize_name(s->config.host_name) : avahi_get_host_name();
1353 s->host_name[strcspn(s->host_name, ".")] = 0;
1354 s->domain_name = s->config.domain_name ? avahi_normalize_name(s->config.domain_name) : g_strdup("local");
1355 s->host_name_fqdn = NULL;
1358 s->record_list = avahi_record_list_new();
1360 s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
1361 s->register_time_event = NULL;
1363 s->state = AVAHI_SERVER_INVALID;
1365 s->monitor = avahi_interface_monitor_new(s);
1366 avahi_interface_monitor_sync(s->monitor);
1368 register_localhost(s);
1370 s->hinfo_entry_group = NULL;
1371 s->browse_domain_entry_group = NULL;
1377 void avahi_server_free(AvahiServer* s) {
1381 free_entry(s, s->entries);
1383 avahi_interface_monitor_free(s->monitor);
1386 free_group(s, s->groups);
1390 while (s->dns_server_browsers)
1391 avahi_dns_server_browser_free(s->dns_server_browsers);
1392 while (s->host_name_resolvers)
1393 avahi_host_name_resolver_free(s->host_name_resolvers);
1394 while (s->address_resolvers)
1395 avahi_address_resolver_free(s->address_resolvers);
1396 while (s->domain_browsers)
1397 avahi_domain_browser_free(s->domain_browsers);
1398 while (s->service_type_browsers)
1399 avahi_service_type_browser_free(s->service_type_browsers);
1400 while (s->service_browsers)
1401 avahi_service_browser_free(s->service_browsers);
1402 while (s->service_resolvers)
1403 avahi_service_resolver_free(s->service_resolvers);
1404 while (s->record_browsers)
1405 avahi_record_browser_destroy(s->record_browsers);
1406 g_hash_table_destroy(s->record_browser_hashtable);
1408 g_hash_table_destroy(s->entries_by_key);
1410 if (s->register_time_event)
1411 avahi_time_event_queue_remove(s->time_event_queue, s->register_time_event);
1412 avahi_time_event_queue_free(s->time_event_queue);
1414 avahi_record_list_free(s->record_list);
1416 if (s->fd_ipv4 >= 0)
1418 if (s->fd_ipv6 >= 0)
1420 if (s->fd_legacy_unicast_ipv4 >= 0)
1421 close(s->fd_legacy_unicast_ipv4);
1422 if (s->fd_legacy_unicast_ipv6 >= 0)
1423 close(s->fd_legacy_unicast_ipv6);
1425 g_free(s->host_name);
1426 g_free(s->domain_name);
1427 g_free(s->host_name_fqdn);
1429 g_source_destroy(s->source);
1430 g_source_unref(s->source);
1431 g_main_context_unref(s->context);
1433 avahi_server_config_free(&s->config);
1438 static gint check_record_conflict(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *r, AvahiEntryFlags flags) {
1444 for (e = g_hash_table_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
1448 if (!(flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_UNIQUE))
1451 if ((flags & AVAHI_ENTRY_ALLOWMUTIPLE) && (e->flags & AVAHI_ENTRY_ALLOWMUTIPLE) )
1454 if (interface <= 0 ||
1455 e->interface <= 0 ||
1456 e->interface == interface ||
1457 protocol == AVAHI_PROTO_UNSPEC ||
1458 e->protocol == AVAHI_PROTO_UNSPEC ||
1459 e->protocol == protocol)
1468 gint avahi_server_add(
1473 AvahiEntryFlags flags,
1484 if (avahi_key_is_pattern(r->key))
1487 if (check_record_conflict(s, interface, protocol, r, flags) < 0)
1490 e = g_new(AvahiEntry, 1);
1492 e->record = avahi_record_ref(r);
1494 e->interface = interface;
1495 e->protocol = protocol;
1499 AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
1501 AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
1503 /* Insert into hash table indexed by name */
1504 t = g_hash_table_lookup(s->entries_by_key, e->record->key);
1505 AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
1506 g_hash_table_replace(s->entries_by_key, e->record->key, t);
1508 /* Insert into group list */
1510 AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
1512 avahi_announce_entry(s, e);
1517 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
1518 AvahiEntry **e = (AvahiEntry**) state;
1523 *e = g ? g->entries : s->entries;
1525 while (*e && (*e)->dead)
1526 *e = g ? (*e)->by_group_next : (*e)->entries_next;
1531 return avahi_record_ref((*e)->record);
1534 void avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, gpointer userdata) {
1540 callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
1542 for (e = s->entries; e; e = e->entries_next) {
1549 t = avahi_record_to_string(e->record);
1550 g_snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
1553 callback(ln, userdata);
1556 avahi_dump_caches(s->monitor, callback, userdata);
1559 gint avahi_server_add_ptr(
1564 AvahiEntryFlags flags,
1567 const gchar *dest) {
1574 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl);
1575 r->data.ptr.name = avahi_normalize_name(dest);
1576 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1577 avahi_record_unref(r);
1581 gint avahi_server_add_address(
1586 AvahiEntryFlags flags,
1595 name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn;
1597 if (a->family == AVAHI_PROTO_INET) {
1601 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1602 r->data.a.address = a->data.ipv4;
1603 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1604 avahi_record_unref(r);
1606 reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
1607 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1614 r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1615 r->data.aaaa.address = a->data.ipv6;
1616 ret = avahi_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1617 avahi_record_unref(r);
1619 reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
1620 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1623 reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
1624 ret |= avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse, name);
1633 gint avahi_server_add_txt_strlst(
1638 AvahiEntryFlags flags,
1641 AvahiStringList *strlst) {
1648 r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl);
1649 r->data.txt.string_list = strlst;
1650 ret = avahi_server_add(s, g, interface, protocol, flags, r);
1651 avahi_record_unref(r);
1656 gint avahi_server_add_txt_va(
1661 AvahiEntryFlags flags,
1668 return avahi_server_add_txt_strlst(s, g, interface, protocol, flags, ttl, name, avahi_string_list_new_va(va));
1671 gint avahi_server_add_txt(
1676 AvahiEntryFlags flags,
1687 ret = avahi_server_add_txt_va(s, g, interface, protocol, flags, ttl, name, va);
1693 static void escape_service_name(gchar *d, guint size, const gchar *s) {
1698 while (*s && size >= 2) {
1699 if (*s == '.' || *s == '\\') {
1715 gint avahi_server_add_service_strlst(
1722 const gchar *domain,
1725 AvahiStringList *strlst) {
1727 gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
1736 escape_service_name(ename, sizeof(ename), name);
1739 while (domain[0] == '.')
1742 domain = s->domain_name;
1745 host = s->host_name_fqdn;
1747 d = avahi_normalize_name(domain);
1748 t = avahi_normalize_name(type);
1750 g_snprintf(ptr_name, sizeof(ptr_name), "%s.%s", t, d);
1751 g_snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, t, d);
1753 ret = avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, ptr_name, svc_name);
1755 r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1756 r->data.srv.priority = 0;
1757 r->data.srv.weight = 0;
1758 r->data.srv.port = port;
1759 r->data.srv.name = avahi_normalize_name(host);
1760 ret |= avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
1761 avahi_record_unref(r);
1763 ret |= avahi_server_add_txt_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
1765 g_snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", d);
1766 ret |=avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name);
1774 gint avahi_server_add_service_va(
1781 const gchar *domain,
1790 return avahi_server_add_service_strlst(s, g, interface, protocol, name, type, domain, host, port, avahi_string_list_new_va(va));
1793 gint avahi_server_add_service(
1800 const gchar *domain,
1813 ret = avahi_server_add_service_va(s, g, interface, protocol, name, type, domain, host, port, va);
1818 static void hexstring(gchar *s, size_t sl, const void *p, size_t pl) {
1819 static const gchar hex[] = "0123456789abcdef";
1821 const guint8 *k = p;
1823 while (sl > 1 && pl > 0) {
1824 *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
1840 gint avahi_server_add_dns_server_address(
1845 const gchar *domain,
1846 AvahiDNSServerType type,
1847 const AvahiAddress *address,
1848 guint16 port /** should be 53 */) {
1852 gchar n[64] = "ip-";
1856 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1857 g_assert(address->family == AVAHI_PROTO_INET || address->family == AVAHI_PROTO_INET6);
1859 if (address->family == AVAHI_PROTO_INET) {
1860 hexstring(n+3, sizeof(n)-3, &address->data, 4);
1861 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
1862 r->data.a.address = address->data.ipv4;
1864 hexstring(n+3, sizeof(n)-3, &address->data, 6);
1865 r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
1866 r->data.aaaa.address = address->data.ipv6;
1869 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE | AVAHI_ENTRY_ALLOWMUTIPLE, r);
1870 avahi_record_unref(r);
1872 ret |= avahi_server_add_dns_server_name(s, g, interface, protocol, domain, type, n, port);
1877 gint avahi_server_add_dns_server_name(
1882 const gchar *domain,
1883 AvahiDNSServerType type,
1885 guint16 port /** should be 53 */) {
1893 g_assert(type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE);
1896 while (domain[0] == '.')
1899 domain = s->domain_name;
1901 d = avahi_normalize_name(domain);
1902 g_snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", d);
1905 r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME);
1906 r->data.srv.priority = 0;
1907 r->data.srv.weight = 0;
1908 r->data.srv.port = port;
1909 r->data.srv.name = avahi_normalize_name(name);
1910 ret = avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_NULL, r);
1911 avahi_record_unref(r);
1916 static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
1917 AvahiKey *k = userdata;
1923 avahi_interface_post_query(i, k, FALSE);
1926 void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
1930 avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
1933 void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
1936 if (g->state == state)
1942 g->callback(g->server, g, state, g->userdata);
1947 AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
1952 g = g_new(AvahiEntryGroup, 1);
1954 g->callback = callback;
1955 g->userdata = userdata;
1957 g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1959 AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1961 AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
1965 void avahi_entry_group_free(AvahiEntryGroup *g) {
1969 g_assert(g->server);
1971 for (e = g->entries; e; e = e->by_group_next) {
1972 avahi_goodbye_entry(g->server, e, TRUE);
1978 g->server->need_group_cleanup = TRUE;
1979 g->server->need_entry_cleanup = TRUE;
1982 gint avahi_entry_group_commit(AvahiEntryGroup *g) {
1986 if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
1989 avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1990 avahi_announce_group(g->server, g);
1991 avahi_entry_group_check_probed(g, FALSE);
1996 gboolean avahi_entry_commited(AvahiEntry *e) {
2001 e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
2002 e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
2005 AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
2012 void avahi_entry_group_set_data(AvahiEntryGroup *g, gpointer userdata) {
2015 g->userdata = userdata;
2018 gpointer avahi_entry_group_get_data(AvahiEntryGroup *g) {
2024 const gchar* avahi_server_get_domain_name(AvahiServer *s) {
2027 return s->domain_name;
2030 const gchar* avahi_server_get_host_name(AvahiServer *s) {
2033 return s->host_name;
2036 const gchar* avahi_server_get_host_name_fqdn(AvahiServer *s) {
2039 return s->host_name_fqdn;
2042 gpointer avahi_server_get_data(AvahiServer *s) {
2048 void avahi_server_set_data(AvahiServer *s, gpointer userdata) {
2051 s->userdata = userdata;
2054 AvahiServerState avahi_server_get_state(AvahiServer *s) {
2060 AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
2063 memset(c, 0, sizeof(AvahiServerConfig));
2066 c->host_name = NULL;
2067 c->domain_name = NULL;
2068 c->check_response_ttl = FALSE;
2069 c->publish_hinfo = TRUE;
2070 c->publish_addresses = TRUE;
2071 c->publish_workstation = TRUE;
2072 c->publish_domain = TRUE;
2073 c->use_iff_running = FALSE;
2074 c->enable_reflector = FALSE;
2075 c->reflect_ipv = FALSE;
2080 void avahi_server_config_free(AvahiServerConfig *c) {
2083 g_free(c->host_name);
2084 g_free(c->domain_name);
2087 AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
2093 ret->host_name = g_strdup(c->host_name);
2094 ret->domain_name = g_strdup(c->domain_name);