+/**
+ * Array of all open DNS requests from tunnels.
+ */
+static struct TunnelState *tunnels[UINT16_MAX + 1];
+
+/**
+ * Handle to the DNS Stub resolver.
+ */
+static struct GNUNET_DNSSTUB_Context *dnsstub;
+
+/**
+ * Are we an IPv4-exit?
+ */
+static int ipv4_exit;
+
+/**
+ * Are we an IPv6-exit?
+ */
+static int ipv6_exit;
+
+/**
+ * Do we support IPv4 at all on the TUN interface?
+ */
+static int ipv4_enabled;
+
+/**
+ * Do we support IPv6 at all on the TUN interface?
+ */
+static int ipv6_enabled;
+
+
+/**
+ * We got a reply from DNS for a request of a MESH tunnel. Send it
+ * via the tunnel (after changing the request ID back).
+ *
+ * @param cls the 'struct TunnelState'
+ * @param size number of bytes available in buf
+ * @param buf where to copy the reply
+ * @return number of bytes written to buf
+ */
+static size_t
+transmit_reply_to_mesh (void *cls,
+ size_t size,
+ void *buf)
+{
+ struct TunnelState *ts = cls;
+ size_t off;
+ size_t ret;
+ char *cbuf = buf;
+ struct GNUNET_MessageHeader hdr;
+ struct GNUNET_TUN_DnsHeader dns;
+
+ GNUNET_assert (GNUNET_YES == ts->is_dns);
+ ts->th = NULL;
+ GNUNET_assert (ts->specifics.dns.reply != NULL);
+ if (size == 0)
+ return 0;
+ ret = sizeof (struct GNUNET_MessageHeader) + ts->specifics.dns.reply_length;
+ GNUNET_assert (ret <= size);
+ hdr.size = htons (ret);
+ hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET);
+ memcpy (&dns, ts->specifics.dns.reply, sizeof (dns));
+ dns.id = ts->specifics.dns.original_id;
+ off = 0;
+ memcpy (&cbuf[off], &hdr, sizeof (hdr));
+ off += sizeof (hdr);
+ memcpy (&cbuf[off], &dns, sizeof (dns));
+ off += sizeof (dns);
+ memcpy (&cbuf[off], &ts->specifics.dns.reply[sizeof (dns)], ts->specifics.dns.reply_length - sizeof (dns));
+ off += ts->specifics.dns.reply_length - sizeof (dns);
+ GNUNET_free (ts->specifics.dns.reply);
+ ts->specifics.dns.reply = NULL;
+ ts->specifics.dns.reply_length = 0;
+ GNUNET_assert (ret == off);
+ return ret;
+}
+
+
+/**
+ * Callback called from DNSSTUB resolver when a resolution
+ * succeeded.
+ *
+ * @param cls NULL
+ * @param rs the socket that received the response
+ * @param dns the response itself
+ * @param r number of bytes in dns
+ */
+static void
+process_dns_result (void *cls,
+ struct GNUNET_DNSSTUB_RequestSocket *rs,
+ const struct GNUNET_TUN_DnsHeader *dns,
+ size_t r)
+{
+ struct TunnelState *ts;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Processing DNS result from stub resolver\n");
+ GNUNET_assert (NULL == cls);
+ /* Handle case that this is a reply to a request from a MESH DNS tunnel */
+ ts = tunnels[dns->id];
+ if ( (NULL == ts) ||
+ (ts->specifics.dns.rs != rs) )
+ return;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Got a response from the stub resolver for DNS request received via MESH!\n");
+ tunnels[dns->id] = NULL;
+ GNUNET_free_non_null (ts->specifics.dns.reply);
+ ts->specifics.dns.reply = GNUNET_malloc (r);
+ ts->specifics.dns.reply_length = r;
+ memcpy (ts->specifics.dns.reply, dns, r);
+ if (NULL != ts->th)
+ GNUNET_MESH_notify_transmit_ready_cancel (ts->th);
+ ts->th = GNUNET_MESH_notify_transmit_ready (ts->tunnel,
+ GNUNET_NO,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ NULL,
+ sizeof (struct GNUNET_MessageHeader) + r,
+ &transmit_reply_to_mesh,
+ ts);
+}
+
+
+/**
+ * Process a request via mesh to perform a DNS query.
+ *
+ * @param cls closure, NULL
+ * @param tunnel connection to the other end
+ * @param tunnel_ctx pointer to our 'struct TunnelState *'
+ * @param sender who sent the message
+ * @param message the actual message
+ * @param atsi performance data for the connection
+ * @return GNUNET_OK to keep the connection open,
+ * GNUNET_SYSERR to close it (signal serious error)
+ */
+static int
+receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ void **tunnel_ctx,
+ const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
+ const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
+{
+ struct TunnelState *ts = *tunnel_ctx;
+ const struct GNUNET_TUN_DnsHeader *dns;
+ size_t mlen = ntohs (message->size);
+ size_t dlen = mlen - sizeof (struct GNUNET_MessageHeader);
+ char buf[dlen] GNUNET_ALIGN;
+ struct GNUNET_TUN_DnsHeader *dout;
+
+ if (NULL == dnsstub)
+ return GNUNET_SYSERR;
+ if (GNUNET_NO == ts->is_dns)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_SYSERR == ts->is_dns)
+ {
+ /* tunnel is DNS from now on */
+ ts->is_dns = GNUNET_YES;
+ }
+ if (dlen < sizeof (struct GNUNET_TUN_DnsHeader))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ dns = (const struct GNUNET_TUN_DnsHeader *) &message[1];
+ ts->specifics.dns.original_id = dns->id;
+ if (tunnels[ts->specifics.dns.my_id] == ts)
+ tunnels[ts->specifics.dns.my_id] = NULL;
+ ts->specifics.dns.my_id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ UINT16_MAX + 1);
+ tunnels[ts->specifics.dns.my_id] = ts;
+ memcpy (buf, dns, dlen);
+ dout = (struct GNUNET_TUN_DnsHeader *) buf;
+ dout->id = ts->specifics.dns.my_id;
+ ts->specifics.dns.rs = GNUNET_DNSSTUB_resolve2 (dnsstub,
+ buf, dlen,
+ &process_dns_result,
+ NULL);
+ if (NULL == ts->specifics.dns.rs)
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+