* @brief tool to manipulate DNS and VPN services to perform protocol translation (IPvX over GNUnet)
* @author Christian Grothoff
*
+ * TODO:
+ * - add statistics
+ * - add logging?
+ * - figure out how/where/when/who tunnels DNS over mesh when necessary!
*/
#include "platform.h"
#include "gnunet_util_lib.h"
#include "gnunet_statistics_service.h"
+/**
+ * After how long do we time out if we could not get an IP from VPN?
+ */
+#define TIMEOUT GNUNET_TIME_UNIT_MINUTES
+
+
+/**
+ * Which group of DNS records are we currently processing?
+ */
+enum RequestGroup
+ {
+ /**
+ * DNS answers
+ */
+ ANSWERS = 0,
+
+ /**
+ * DNS authority records
+ */
+ AUTHORITY_RECORDS = 1,
+
+ /**
+ * DNS additional records
+ */
+ ADDITIONAL_RECORDS = 2,
+ /**
+ * We're done processing.
+ */
+ END = 3
+ };
+
+
+/**
+ * Information tracked per DNS request that we are processing.
+ */
+struct RequestContext
+{
+ /**
+ * Handle to submit the final result.
+ */
+ struct GNUNET_DNS_RequestHandle *rh;
+
+ /**
+ * DNS packet that is being modified.
+ */
+ struct GNUNET_DNSPARSER_Packet *dns;
+
+ /**
+ * Active redirection request with the VPN.
+ */
+ struct GNUNET_VPN_RedirectionRequest *rr;
+
+ /**
+ * Record for which we have an active redirection request.
+ */
+ struct GNUNET_DNSPARSER_Record *rec;
+
+ /**
+ * Offset in the current record group that is being modified.
+ */
+ unsigned int offset;
+
+ /**
+ * Group that is being modified
+ */
+ enum RequestGroup group;
+
+};
+
+
/**
* The handle to the configuration used throughout the process
*/
static int ipv6_pt;
+/**
+ * We're done modifying all records in the response. Submit the reply
+ * and free the resources of the rc.
+ *
+ * @param rc context to process
+ */
+static void
+finish_request (struct RequestContext *rc)
+{
+ char *buf;
+ size_t buf_len;
+
+ if (GNUNET_SYSERR ==
+ GNUNET_DNSPARSER_pack (rc->dns,
+ 16 * 1024,
+ &buf,
+ &buf_len))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Failed to pack DNS request. Dropping.\n"));
+ GNUNET_DNS_request_drop (rc->rh);
+ }
+ else
+ {
+ GNUNET_DNS_request_answer (rc->rh,
+ buf_len, buf);
+ GNUNET_free (buf);
+ }
+ GNUNET_DNSPARSER_free_packet (rc->dns);
+ GNUNET_free (rc);
+}
+
+
+/**
+ * Process the next record of the given request context.
+ * When done, submit the reply and free the resources of
+ * the rc.
+ *
+ * @param rc context to process
+ */
+static void
+submit_request (struct RequestContext *rc);
+
+
+/**
+ * Callback invoked from the VPN service once a redirection is
+ * available. Provides the IP address that can now be used to
+ * reach the requested destination. We substitute the active
+ * record and then continue with 'submit_request' to look at
+ * the other records.
+ *
+ * @param cls our 'struct RequestContext'
+ * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
+ * will match 'result_af' from the request
+ * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
+ * that the VPN allocated for the redirection;
+ * traffic to this IP will now be redirected to the
+ * specified target peer; NULL on error
+ */
+static void
+vpn_allocation_callback (void *cls,
+ int af,
+ const void *address)
+{
+ struct RequestContext *rc = cls;
+
+ rc->rr = NULL;
+ if (af == AF_UNSPEC)
+ {
+ GNUNET_DNS_request_drop (rc->rh);
+ GNUNET_DNSPARSER_free_packet (rc->dns);
+ GNUNET_free (rc);
+ return;
+ }
+ switch (rc->rec->type)
+ {
+ case GNUNET_DNSPARSER_TYPE_A:
+ GNUNET_assert (AF_INET == af);
+ memcpy (rc->rec->data.raw.data, address, sizeof (struct in_addr));
+ break;
+ case GNUNET_DNSPARSER_TYPE_AAAA:
+ GNUNET_assert (AF_INET6 == af);
+ memcpy (rc->rec->data.raw.data, address, sizeof (struct in6_addr));
+ break;
+ default:
+ GNUNET_assert (0);
+ return;
+ }
+ rc->rec = NULL;
+ submit_request (rc);
+}
+
+
+/**
+ * Modify the given DNS record by asking VPN to create a tunnel
+ * to the given address. When done, continue with submitting
+ * other records from the request context ('submit_request' is
+ * our continuation).
+ *
+ * @param rc context to process
+ * @param rec record to modify
+ */
+static void
+modify_address (struct RequestContext *rc,
+ struct GNUNET_DNSPARSER_Record *rec)
+{
+ int af;
+
+ switch (rec->type)
+ {
+ case GNUNET_DNSPARSER_TYPE_A:
+ af = AF_INET;
+ GNUNET_assert (rec->data.raw.data_len == sizeof (struct in_addr));
+ break;
+ case GNUNET_DNSPARSER_TYPE_AAAA:
+ af = AF_INET6;
+ GNUNET_assert (rec->data.raw.data_len == sizeof (struct in6_addr));
+ break;
+ default:
+ GNUNET_assert (0);
+ return;
+ }
+ rc->rec = rec;
+ rc->rr = GNUNET_VPN_redirect_to_ip (vpn_handle,
+ af, af,
+ rec->data.raw.data,
+ GNUNET_NO /* nac */,
+ GNUNET_TIME_relative_to_absolute (TIMEOUT),
+ &vpn_allocation_callback,
+ rc);
+}
+
+
+/**
+ * Process the next record of the given request context.
+ * When done, submit the reply and free the resources of
+ * the rc.
+ *
+ * @param rc context to process
+ */
+static void
+submit_request (struct RequestContext *rc)
+{
+ struct GNUNET_DNSPARSER_Record *ra;
+ unsigned int ra_len;
+ unsigned int i;
+
+ while (1)
+ {
+ switch (rc->group)
+ {
+ case ANSWERS:
+ ra = rc->dns->answers;
+ ra_len = rc->dns->num_answers;
+ break;
+ case AUTHORITY_RECORDS:
+ ra = rc->dns->authority_records;
+ ra_len = rc->dns->num_authority_records;
+ break;
+ case ADDITIONAL_RECORDS:
+ ra = rc->dns->additional_records;
+ ra_len = rc->dns->num_additional_records;
+ break;
+ case END:
+ finish_request (rc);
+ return;
+ default:
+ GNUNET_assert (0);
+ }
+ for (i=rc->offset;i<ra_len;i++)
+ {
+ switch (ra[i].type)
+ {
+ case GNUNET_DNSPARSER_TYPE_A:
+ if (ipv4_pt)
+ {
+ rc->offset = i + 1;
+ modify_address (rc, &ra[i]);
+ return;
+ }
+ break;
+ case GNUNET_DNSPARSER_TYPE_AAAA:
+ if (ipv6_pt)
+ {
+ rc->offset = i + 1;
+ modify_address (rc, &ra[i]);
+ return;
+ }
+ break;
+ }
+ }
+ rc->group++;
+ }
+}
+
+
/**
* Test if any of the given records need protocol-translation work.
*
const char *request)
{
struct GNUNET_DNSPARSER_Packet *dns;
+ struct RequestContext *rc;
int work;
dns = GNUNET_DNSPARSER_parse (request, request_length);
GNUNET_DNS_request_forward (rh);
return;
}
- /* FIXME: translate A/AAAA records using VPN! */
+ rc = GNUNET_malloc (sizeof (struct RequestContext));
+ rc->rh = rh;
+ rc->dns = dns;
+ rc->offset = 0;
+ rc->group = ANSWERS;
+ submit_request (rc);
}