+static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
+ g_assert(s);
+ g_assert(r);
+
+ avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
+}
+
+void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean immediately) {
+
+ g_assert(s);
+ g_assert(i);
+ g_assert(!legacy_unicast || (a && port > 0 && p));
+
+ if (legacy_unicast) {
+ AvahiDnsPacket *reply;
+ AvahiRecord *r;
+
+ reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
+
+ while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
+
+ append_aux_records_to_list(s, i, r, FALSE);
+
+ if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
+ avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
+ else {
+ gchar *t = avahi_record_to_string(r);
+ avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
+ g_free(t);
+ }
+
+ avahi_record_unref(r);
+ }
+
+ if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
+ avahi_interface_send_packet_unicast(i, reply, a, port);
+
+ avahi_dns_packet_free(reply);
+
+ } else {
+ gboolean unicast_response, flush_cache, auxiliary;
+ AvahiDnsPacket *reply = NULL;
+ AvahiRecord *r;
+
+ /* In case the query packet was truncated never respond
+ immediately, because known answer suppression records might be
+ contained in later packets */
+ gboolean tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
+
+ while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
+
+ if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
+
+ append_aux_records_to_list(s, i, r, unicast_response);
+
+ /* Due to some reasons the record has not been scheduled.
+ * The client requested an unicast response in that
+ * case. Therefore we prepare such a response */
+
+ for (;;) {
+
+ if (!reply) {
+ g_assert(p);
+ reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
+ }
+
+ if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
+
+ /* Appending this record succeeded, so incremeant
+ * the specific header field, and return to the caller */
+
+ avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
+
+ break;
+ }
+
+ if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
+ guint size;
+
+ /* The record is too large for one packet, so create a larger packet */
+
+ avahi_dns_packet_free(reply);
+ size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
+ if (size > AVAHI_DNS_PACKET_MAX_SIZE)
+ size = AVAHI_DNS_PACKET_MAX_SIZE;
+ reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
+
+ if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
+ avahi_dns_packet_free(reply);
+
+ gchar *t = avahi_record_to_string(r);
+ avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
+ g_free(t);
+ break;
+ } else
+ avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
+ }
+
+ /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
+ avahi_interface_send_packet_unicast(i, reply, a, port);
+ avahi_dns_packet_free(reply);
+ reply = NULL;
+ }
+ }
+
+ avahi_record_unref(r);
+ }
+
+ if (reply) {
+ if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
+ avahi_interface_send_packet_unicast(i, reply, a, port);
+ avahi_dns_packet_free(reply);
+ }
+ }
+
+ avahi_record_list_flush(s->record_list);
+}
+
+
+static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean flush_cache) {
+ AvahiInterface *j;
+
+ g_assert(s);
+ g_assert(i);
+ g_assert(r);
+
+ if (!s->config.enable_reflector)
+ return;
+
+ for (j = s->monitor->interfaces; j; j = j->interface_next)
+ if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
+ avahi_interface_post_response(j, r, flush_cache, NULL, TRUE);
+}
+
+static gpointer reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
+ AvahiServer *s = userdata;
+
+ g_assert(c);
+ g_assert(pattern);
+ g_assert(e);
+ g_assert(s);
+
+ avahi_record_list_push(s->record_list, e->record, e->cache_flush, FALSE, FALSE);
+ return NULL;
+}
+
+static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
+ AvahiInterface *j;
+
+ g_assert(s);
+ g_assert(i);
+ g_assert(k);
+
+ if (!s->config.enable_reflector)
+ return;
+
+ for (j = s->monitor->interfaces; j; j = j->interface_next)
+ if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
+ /* Post the query to other networks */
+ avahi_interface_post_query(j, k, TRUE);
+
+ /* Reply from caches of other network. This is needed to
+ * "work around" known answer suppression. */
+
+ avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
+ }
+}
+
+static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
+ AvahiInterface *j;
+
+ g_assert(s);
+ g_assert(i);
+ g_assert(r);
+
+ if (!s->config.enable_reflector)
+ return;
+
+ for (j = s->monitor->interfaces; j; j = j->interface_next)
+ if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
+ avahi_interface_post_probe(j, r, TRUE);
+}
+
+static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {