+static void post_response(flxServer *s, flxRecord *r, gint iface, const flxAddress *a) {
+}
+
+static void handle_query_key(flxServer *s, flxKey *k, gint iface, const flxAddress *a) {
+ flxEntry *e;
+
+ g_assert(s);
+ g_assert(k);
+ g_assert(a);
+
+ for (e = g_hash_table_lookup(s->rrset_by_name, k); e; e = e->next_by_name) {
+
+ if ((e->interface <= 0 || e->interface == iface) &&
+ (e->protocol == AF_UNSPEC || e->protocol == a->family))
+ post_response(s, e->record, iface, a);
+ }
+}
+
+static void handle_query(flxServer *s, flxDnsPacket *p, gint iface, const flxAddress *a) {
+ guint n;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(a);
+
+ for (n = flx_dns_packet_get_field(p, DNS_FIELD_QDCOUNT); n > 0; n --) {
+
+ flxKey *key;
+
+ if (!(key = flx_dns_packet_consume_query(p))) {
+ g_warning("Packet too short");
+ return;
+ }
+
+ handle_query_key(s, key, iface, a);
+ flx_key_unref(key);
+ }
+}
+
+static void add_response_to_cache(flxCache *c, flxDnsPacket *p, const flxAddress *a) {
+ guint n;
+
+ g_assert(c);
+ g_assert(p);
+ g_assert(a);
+ for (n = flx_dns_packet_get_field(p, DNS_FIELD_ANCOUNT); n > 0; n--) {
+
+ flxRecord *rr;
+ gboolean cache_flush = FALSE;
+
+ if (!(rr = flx_dns_packet_consume_rr(p, &cache_flush))) {
+ g_warning("Packet too short");
+ return;
+ }
+
+ flx_cache_update(c, rr, cache_flush, a);
+ flx_record_unref(rr);
+ }
+}
+
+static void dispatch_packet(flxServer *s, flxDnsPacket *p, struct sockaddr *sa, gint iface, gint ttl) {
+ flxInterface *i;
+ flxAddress a;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(sa);
+ g_assert(iface > 0);
+
+ if (!(i = flx_interface_monitor_get_interface(s->monitor, iface))) {
+ g_warning("Recieved packet from invalid interface.");
+ return;
+ }
+
+ if (ttl != 255) {
+ g_warning("Recieved packet with invalid TTL on interface '%s'.", i->name);
+ return;
+ }
+
+ if (sa->sa_family == AF_INET6) {
+ static const unsigned char ipv4_in_ipv6[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF };
+
+ if (memcmp(((struct sockaddr_in6*) sa)->sin6_addr.s6_addr, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0) {
+
+ /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
+ return;
+ }
+ }
+
+ if (flx_dns_packet_check_valid(p) < 0) {
+ g_warning("Recieved invalid packet.");
+ return;
+ }
+
+
+ flx_address_from_sockaddr(sa, &a);
+
+ if (flx_dns_packet_is_query(p)) {
+
+ if (flx_dns_packet_get_field(p, DNS_FIELD_QDCOUNT) == 0 ||
+ flx_dns_packet_get_field(p, DNS_FIELD_ARCOUNT) != 0
+ flx_dns_packet_get_field(p, DNS_FIELD_NSCOUNT) != 0) {
+ g_warning("Invalid query packet.");
+ return;
+ }
+
+ handle_query(s, p, iface, &a);
+ g_message("Handled query");
+ } else {
+ flxCache *c;
+
+ if (flx_dns_packet_get_field(p, DNS_FIELD_QDCOUNT) != 0 ||
+ flx_dns_packet_get_field(p, DNS_FIELD_ANCOUNT) == 0 ||
+ flx_dns_packet_get_field(p, DNS_FIELD_NSCOUNT) != 0 ||
+ flx_dns_packet_get_field(p, DNS_FIELD_ARCOUNT) != 0) {
+ g_warning("Invalid response packet.");
+ return;
+ }
+
+ c = a.family == AF_INET ? i->ipv4_cache : i->ipv6_cache;
+ add_response_to_cache(c, p, &a);
+
+ g_message("Handled responnse");
+ }
+}
+
+static gboolean work(flxServer *s) {
+ struct sockaddr_in6 sa6;
+ struct sockaddr_in sa;
+ flxDnsPacket *p;
+ gint iface = -1;
+ guint8 ttl;
+
+ g_assert(s);
+
+ if (s->pollfd_ipv4.revents & G_IO_IN) {
+ if ((p = flx_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl)))
+ dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
+ }
+
+ if (s->pollfd_ipv6.revents & G_IO_IN) {
+ if ((p = flx_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl)))
+ dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
+ }
+
+ return TRUE;
+}
+
+static gboolean prepare_func(GSource *source, gint *timeout) {
+ g_assert(source);
+ g_assert(timeout);
+
+ *timeout = -1;
+ return FALSE;
+}
+
+static gboolean check_func(GSource *source) {
+ flxServer* s;
+ g_assert(source);
+
+ s = *((flxServer**) (((guint8*) source) + sizeof(GSource)));
+ g_assert(s);
+
+ return (s->pollfd_ipv4.revents | s->pollfd_ipv6.revents) & (G_IO_IN | G_IO_HUP | G_IO_ERR);
+}
+
+static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
+ flxServer* s;
+ g_assert(source);
+
+ s = *((flxServer**) (((guint8*) source) + sizeof(GSource)));
+ g_assert(s);
+
+ return work(s);
+}
+