--- /dev/null
+/* $Id$ */
+
+/***
+ This file is part of avahi.
+
+ avahi is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ avahi is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+ Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with avahi; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include "rrlist.h"
+#include "llist.h"
+
+typedef struct AvahiRecordListItem AvahiRecordListItem;
+
+struct AvahiRecordListItem {
+ AvahiRecord *record;
+ gboolean unicast_response;
+ gboolean flush_cache;
+ AVAHI_LLIST_FIELDS(AvahiRecordListItem, items);
+};
+
+
+struct AvahiRecordList {
+ AVAHI_LLIST_HEAD(AvahiRecordListItem, items);
+};
+
+AvahiRecordList *avahi_record_list_new(void) {
+ AvahiRecordList *l = g_new(AvahiRecordList, 1);
+ AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->items);
+ return l;
+}
+
+void avahi_record_list_free(AvahiRecordList *l) {
+ g_assert(l);
+
+ avahi_record_list_flush(l);
+ g_free(l);
+}
+
+static void item_free(AvahiRecordList *l, AvahiRecordListItem *i) {
+ g_assert(i);
+
+ AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->items, i);
+ avahi_record_unref(i->record);
+ g_free(i);
+}
+
+void avahi_record_list_flush(AvahiRecordList *l) {
+ g_assert(l);
+
+ while (l->items)
+ item_free(l, l->items);
+}
+
+AvahiRecord* avahi_record_list_pop(AvahiRecordList *l, gboolean *flush_cache, gboolean *unicast_response) {
+ AvahiRecord *r;
+
+ if (!l->items)
+ return NULL;
+
+ r = avahi_record_ref(l->items->record);
+ if (unicast_response) *unicast_response = l->items->unicast_response;
+ if (flush_cache) *flush_cache = l->items->flush_cache;
+
+ item_free(l, l->items);
+
+ return r;
+}
+
+void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, gboolean flush_cache, gboolean unicast_response) {
+ AvahiRecordListItem *i;
+
+ g_assert(l);
+ g_assert(r);
+
+ for (i = l->items; i; i = i->items_next)
+ if (avahi_record_equal_no_ttl(i->record, r))
+ return;
+
+ i = g_new(AvahiRecordListItem, 1);
+ i->unicast_response = unicast_response;
+ i->flush_cache = flush_cache;
+ i->record = avahi_record_ref(r);
+ AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->items, i);
+}
+
+void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r) {
+ AvahiRecordListItem *i;
+
+ g_assert(l);
+ g_assert(r);
+
+ for (i = l->items; i; i = i->items_next)
+ if (avahi_record_equal_no_ttl(i->record, r)) {
+ item_free(l, i);
+ break;
+ }
+}
+
+
+gboolean avahi_record_list_empty(AvahiRecordList *l) {
+ g_assert(l);
+
+ return !l->items;
+}
}
}
-static void send_unicast_response_packet(AvahiServer *s, AvahiInterface *i, const AvahiAddress *a, guint16 port) {
+static void post_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response);
+
+static void add_aux_records(AvahiServer *s, AvahiInterface *i, const gchar *name, guint16 type, gboolean unicast_response) {
+ AvahiKey *k;
+ AvahiEntry *e;
+
g_assert(s);
- g_assert(a);
- g_assert(port > 0);
- g_assert(s->unicast_packet);
+ g_assert(i);
+ g_assert(name);
- if (avahi_dns_packet_get_field(s->unicast_packet, AVAHI_DNS_FIELD_ANCOUNT) != 0)
- avahi_interface_send_packet_unicast(i, s->unicast_packet, a, port);
+ k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
+
+ for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
+ if (!e->dead && avahi_entry_registered(s, e, i))
+ post_response(s, i, e, unicast_response);
- avahi_dns_packet_free(s->unicast_packet);
- s->unicast_packet = NULL;
+ avahi_key_unref(k);
}
-static void post_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, AvahiRecord *r, gboolean flush_cache, gboolean legacy_unicast, gboolean unicast_response) {
+static void post_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response) {
g_assert(s);
- g_assert(a);
- g_assert(port > 0);
- g_assert(r);
-
- if (legacy_unicast) {
-
- /* Respond with a legacy unicast packet */
-
- if (!(s->unicast_packet))
- s->unicast_packet = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
-
- if (avahi_dns_packet_append_record(s->unicast_packet, r, FALSE, 10))
-
- /* Increment the ANCOUNT field */
-
- avahi_dns_packet_set_field(s->unicast_packet, AVAHI_DNS_FIELD_ANCOUNT,
- avahi_dns_packet_get_field(s->unicast_packet, AVAHI_DNS_FIELD_ANCOUNT)+1);
-
- /* If there's no space left for this response we simply don't send it */
-
- } else {
-
- if (!avahi_interface_post_response(i, a, r, flush_cache, FALSE) && 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 (!(s->unicast_packet))
- s->unicast_packet = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE);
-
- if (avahi_dns_packet_append_record(s->unicast_packet, r, flush_cache, 0)) {
-
- /* Appending this record succeeded, so incremeant
- * the specific header field, and return to the caller */
-
- avahi_dns_packet_set_field(s->unicast_packet, AVAHI_DNS_FIELD_ANCOUNT,
- avahi_dns_packet_get_field(s->unicast_packet, AVAHI_DNS_FIELD_ANCOUNT)+1);
-
- break;
- }
-
- if (avahi_dns_packet_get_field(s->unicast_packet, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
- g_warning("Record too large, doesn't fit in any packet!");
- return;
- }
+ g_assert(i);
+ g_assert(e);
- /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
+ /* Append a record to a packet and all the records referred by it */
- send_unicast_response_packet(s, i, a, port);
-
- avahi_dns_packet_free(s->unicast_packet);
- s->unicast_packet = NULL;
- }
-
+ avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response);
+
+ if (e->record->key->class == AVAHI_DNS_CLASS_IN) {
+ if (e->record->key->type == AVAHI_DNS_TYPE_PTR) {
+ add_aux_records(s, i, e->record->data.ptr.name, AVAHI_DNS_TYPE_SRV, unicast_response);
+ add_aux_records(s, i, e->record->data.ptr.name, AVAHI_DNS_TYPE_TXT, unicast_response);
+ } else if (e->record->key->type == AVAHI_DNS_TYPE_SRV) {
+ add_aux_records(s, i, e->record->data.srv.name, AVAHI_DNS_TYPE_A, unicast_response);
+ add_aux_records(s, i, e->record->data.srv.name, AVAHI_DNS_TYPE_AAAA, unicast_response);
}
}
}
-
-static void handle_query_key(AvahiServer *s, AvahiDnsPacket *p, AvahiKey *k, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean unicast_response) {
+static void handle_query_key(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
AvahiEntry *e;
gchar *txt;
g_assert(s);
- g_assert(k);
g_assert(i);
- g_assert(a);
+ g_assert(k);
g_message("Handling query: %s", txt = avahi_key_to_string(k));
g_free(txt);
for (e = s->entries; e; e = e->entries_next)
if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
- post_response(s, p, i, a, port, e->record, e->flags & AVAHI_ENTRY_UNIQUE, legacy_unicast, unicast_response);
+ post_response(s, i, e, unicast_response);
} else {
for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
if (!e->dead && avahi_entry_registered(s, e, i))
- post_response(s, p, i, a, port, e->record, e->flags & AVAHI_ENTRY_UNIQUE, legacy_unicast, unicast_response);
+ post_response(s, i, e, unicast_response);
}
}
g_message("Recieved conflicting record [%s]. Local host won. Refreshing.", t);
valid = FALSE;
- avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE);
+ avahi_interface_post_response(i, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE);
}
/* Check wheter there is a TTL conflict */
} else if (equal && record->ttl <= e->record->ttl/2) {
/* Correct the TTL */
valid = FALSE;
- avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE);
+ avahi_interface_post_response(i, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE);
g_message("Recieved record with bad TTL [%s]. Refreshing.", t);
}
return valid;
}
-static void incoming_known_answer(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean legacy_unicast, gboolean unique, const AvahiAddress *a) {
+static void generate_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
+
g_assert(s);
+ g_assert(p);
g_assert(i);
- g_assert(r);
-
- if (legacy_unicast)
- return;
+ g_assert(a);
+
+ 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_pop(s->record_list, NULL, NULL))) {
+
+ 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);
+ g_warning("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;
+ AvahiDnsPacket *reply = NULL;
+ AvahiRecord *r;
+
+ while ((r = avahi_record_list_pop(s->record_list, &flush_cache, &unicast_response))) {
+
+ if (!avahi_interface_post_response(i, r, flush_cache, FALSE) && 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)
+ 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) {
+ gchar *t = avahi_record_to_string(r);
+ g_warning("Record [%s] too large, doesn't fit in any packet!", t);
+ g_free(t);
+ break;
+ }
+
+ /* 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 (handle_conflict(s, i, r, unique, a))
- avahi_packet_scheduler_incoming_known_answer(i->scheduler, r, a);
+ 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);
+ }
+ }
}
static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
g_assert(i);
g_assert(a);
- g_assert(!s->unicast_packet);
-
+ g_assert(avahi_record_list_empty(s->record_list));
+
/* Handle the questions */
for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
AvahiKey *key;
if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
g_warning("Packet too short (1)");
- return;
+ goto fail;
}
- handle_query_key(s, p, key, i, a, port, legacy_unicast, unicast_response);
+ handle_query_key(s, i, key, unicast_response);
avahi_key_unref(key);
}
if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
g_warning("Packet too short (2)");
- return;
+ goto fail;
}
- incoming_known_answer(s, i, record, legacy_unicast, unique, a);
+ if (handle_conflict(s, i, record, unique, a))
+ avahi_record_list_drop(s->record_list, record);
+
avahi_record_unref(record);
}
if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
g_warning("Packet too short (3)");
- return;
+ goto fail;
}
if (record->key->type != AVAHI_DNS_TYPE_ANY)
avahi_record_unref(record);
}
- if (s->unicast_packet)
- send_unicast_response_packet(s, i, a, port);
+ if (!avahi_record_list_empty(s->record_list))
+ generate_response(s, p, i, a, port, legacy_unicast);
+
+ return;
+
+fail:
+ avahi_record_list_flush(s->record_list);
}
static void handle_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
g_warning("Packet too short (4)");
- return;
+ break;
}
if (record->key->type != AVAHI_DNS_TYPE_ANY) {
s->hostname = g_strdup_printf("%s.local.", hn);
g_free(hn);
- s->unicast_packet = NULL;
+ s->record_list = avahi_record_list_new();
s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
s->monitor = avahi_interface_monitor_new(s);
avahi_time_event_queue_free(s->time_event_queue);
+ avahi_record_list_free(s->record_list);
+
if (s->fd_ipv4 >= 0)
close(s->fd_ipv4);
if (s->fd_ipv6 >= 0)
close(s->fd_ipv6);
-
+
g_free(s->hostname);
g_source_destroy(s->source);
g_source_unref(s->source);
g_main_context_unref(s->context);
- if (s->unicast_packet)
- avahi_dns_packet_free(s->unicast_packet);
-
g_free(s);
}
g_assert(type);
g_assert(name);
- avahi_server_add_service(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
+ avahi_server_add_service_strlst(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
}
void avahi_server_add_service(
g_assert(i);
g_assert(tmpdata);
- avahi_interface_post_response(i, NULL, tmpdata->record, tmpdata->flush_cache, FALSE);
+ avahi_interface_post_response(i, tmpdata->record, tmpdata->flush_cache, FALSE);
}
void avahi_server_post_response(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *record, gboolean flush_cache) {