+ return GNUNET_OK;
+}
+
+
+/**
+ * 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;
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ struct sockaddr *so;
+ socklen_t salen;
+
+ if (dlen < sizeof (struct GNUNET_TUN_DnsHeader))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ dns = (const struct GNUNET_TUN_DnsHeader *) &message[1];
+ ts->original_id = dns->id;
+ if (tunnels[ts->my_id] == ts)
+ tunnels[ts->my_id] = NULL;
+ ts->my_id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ UINT16_MAX + 1);
+ tunnels[ts->my_id] = ts;
+ memcpy (buf, dns, dlen);
+ dout = (struct GNUNET_TUN_DnsHeader*) buf;
+ dout->id = ts->my_id;
+ memset (&v4, 0, sizeof (v4));
+ memset (&v6, 0, sizeof (v6));
+ if (1 == inet_pton (AF_INET, dns_exit, &v4.sin_addr))
+ {
+ salen = sizeof (v4);
+ v4.sin_family = AF_INET;
+ v4.sin_port = htons (53);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v4.sin_len = (u_char) salen;
+#endif
+ so = (struct sockaddr *) &v4;
+ ts->dnsout = get_request_socket (AF_INET);
+ }
+ else if (1 == inet_pton (AF_INET6, dns_exit, &v6.sin6_addr))
+ {
+ salen = sizeof (v6);
+ v6.sin6_family = AF_INET6;
+ v6.sin6_port = htons (53);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v6.sin6_len = (u_char) salen;
+#endif
+ so = (struct sockaddr *) &v6;
+ ts->dnsout = get_request_socket (AF_INET6);
+ }
+ else
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == ts->dnsout)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Configured DNS exit `%s' is not working / valid.\n"),
+ dns_exit);
+ return GNUNET_SYSERR;
+ }
+ memcpy (&ts->addr,
+ so,
+ salen);
+ ts->addrlen = salen;
+ GNUNET_NETWORK_socket_sendto (ts->dnsout,
+ buf, dlen, so, salen);
+ ts->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Callback from GNUNET_MESH for new tunnels.
+ *
+ * @param cls closure
+ * @param tunnel new handle to the tunnel
+ * @param initiator peer that started the tunnel
+ * @param ats performance information for the tunnel
+ * @return initial tunnel context for the tunnel
+ */
+static void *
+accept_dns_tunnel (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
+ const struct GNUNET_PeerIdentity *initiator GNUNET_UNUSED,
+ const struct GNUNET_ATS_Information *ats GNUNET_UNUSED)
+{
+ struct TunnelState *ts = GNUNET_malloc (sizeof (struct TunnelState));
+
+ GNUNET_STATISTICS_update (stats,
+ gettext_noop ("# Inbound MESH tunnels created"),
+ 1, GNUNET_NO);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received inbound tunnel from `%s'\n",
+ GNUNET_i2s (initiator));
+ ts->tunnel = tunnel;
+ return ts;
+}
+
+
+/**
+ * Function called by mesh whenever an inbound tunnel is destroyed.
+ * Should clean up any associated state.
+ *
+ * @param cls closure (set from GNUNET_MESH_connect)
+ * @param tunnel connection to the other end (henceforth invalid)
+ * @param tunnel_ctx place where local state associated
+ * with the tunnel is stored
+ */
+static void
+destroy_dns_tunnel (void *cls GNUNET_UNUSED,
+ const struct GNUNET_MESH_Tunnel *tunnel,
+ void *tunnel_ctx)
+{
+ struct TunnelState *ts = tunnel_ctx;
+
+ if (tunnels[ts->my_id] == ts)
+ tunnels[ts->my_id] = NULL;
+ if (NULL != ts->th)
+ GNUNET_MESH_notify_transmit_ready_cancel (ts->th);
+ GNUNET_free_non_null (ts->reply);
+ GNUNET_free (ts);