/**
 * The DNS request handler.  Called for every incoming DNS request.
 *
 * @param cls closure, unused
 * @param rh request handle to user for reply
 * @param request_length number of bytes in @a request
 * @param request UDP payload of the DNS request
 */
static void
handle_dns_request (void *cls,
		    struct GNUNET_DNS_RequestHandle *rh,
		    size_t request_length,
		    const char *request)
{
  struct GNUNET_DNSPARSER_Packet *p;
  struct InterceptLookupHandle *ilh;

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
	      "Hijacked a DNS request. Processing.\n");
  if (NULL == (p = GNUNET_DNSPARSER_parse (request, request_length)))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                "Received malformed DNS packet, leaving it untouched.\n");
    GNUNET_DNS_request_forward (rh);
    GNUNET_DNSPARSER_free_packet (p);
    return;
  }

  /* Check TLD and decide if we or legacy dns is responsible */
  if (1 != p->num_queries)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Not exactly one query in DNS packet. Forwarding untouched.\n");
    GNUNET_DNS_request_forward (rh);
    GNUNET_DNSPARSER_free_packet(p);
    return;
  }

  /* Check for GNS TLDs. */
  if ( (GNUNET_YES == is_gnu_tld (p->queries[0].name)) ||
       (GNUNET_YES == is_zkey_tld (p->queries[0].name)) ||
       (0 == strcmp (p->queries[0].name, GNUNET_GNS_TLD)) )
  {
    /* Start resolution in GNS */
    ilh = GNUNET_new (struct InterceptLookupHandle);
    GNUNET_CONTAINER_DLL_insert (ilh_head, ilh_tail, ilh);
    ilh->packet = p;
    ilh->request_handle = rh;
    ilh->lookup = GNS_resolver_lookup (&zone,
				       p->queries[0].type,
				       p->queries[0].name,
				       NULL /* FIXME: enable shorten for DNS intercepts? */,
				       GNUNET_NO,
				       &reply_to_dns, ilh);
    return;
  }
/**
 * Signature of a function that is called whenever the DNS service
 * encounters a DNS request and needs to do something with it.  The
 * function has then the chance to generate or modify the response by
 * calling one of the three "GNUNET_DNS_request_*" continuations.
 *
 * When a request is intercepted, this function is called first to
 * give the client a chance to do the complete address resolution;
 * "rdata" will be NULL for this first call for a DNS request, unless
 * some other client has already filled in a response.
 *
 * If multiple clients exist, all of them are called before the global
 * DNS.  The global DNS is only called if all of the clients'
 * functions call GNUNET_DNS_request_forward.  Functions that call
 * GNUNET_DNS_request_forward will be called again before a final
 * response is returned to the application.  If any of the clients'
 * functions call GNUNET_DNS_request_drop, the response is dropped.
 *
 * @param cls closure
 * @param rh request handle to user for reply
 * @param request_length number of bytes in request
 * @param request udp payload of the DNS request
 */
static void 
modify_request (void *cls,
		struct GNUNET_DNS_RequestHandle *rh,
		size_t request_length,
		const char *request)
{
  struct GNUNET_DNSPARSER_Packet *p;
  unsigned int i;
  char *buf;
  size_t len;
  int ret;

  p = GNUNET_DNSPARSER_parse (request, request_length);
  if (NULL == p)
  {
    fprintf (stderr, "Received malformed DNS packet, leaving it untouched\n");
    GNUNET_DNS_request_forward (rh);
    return;
  }
  for (i=0;i<p->num_answers;i++)
    modify_record (&p->answers[i]);
  buf = NULL;
  ret = GNUNET_DNSPARSER_pack (p, 1024, &buf, &len);
  GNUNET_DNSPARSER_free_packet (p);
  if (GNUNET_OK != ret)
  {
    if (GNUNET_NO == ret)
      fprintf (stderr, 
	       "Modified DNS response did not fit, keeping old response\n");
    else
      GNUNET_break (0); /* our modifications should have been sane! */
    GNUNET_DNS_request_forward (rh);
  }
  else
  {
    if (verbosity > 0)
      fprintf (stdout,
	       "Injecting modified DNS response\n");
    GNUNET_DNS_request_answer (rh, len, buf);
  }
  GNUNET_free_non_null (buf);      
}
/**
 * This function is called AFTER we got an IP address for a 
 * DNS request.  Now, the PT daemon has the chance to substitute
 * the IP address with one from the VPN range to tunnel requests
 * destined for this IP address via VPN and MESH.
 *
 * @param cls closure
 * @param rh request handle to user for reply
 * @param request_length number of bytes in request
 * @param request udp payload of the DNS request
 */
static void 
dns_post_request_handler (void *cls,
			  struct GNUNET_DNS_RequestHandle *rh,
			  size_t request_length,
			  const char *request)
{
  struct GNUNET_DNSPARSER_Packet *dns;
  struct ReplyContext *rc;
  int work;

  GNUNET_STATISTICS_update (stats,
			    gettext_noop ("# DNS replies intercepted"),
			    1, GNUNET_NO);
  dns = GNUNET_DNSPARSER_parse (request, request_length);
  if (NULL == dns)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
		_("Failed to parse DNS request.  Dropping.\n"));
    GNUNET_DNS_request_drop (rh);
    return;
  }
  work = GNUNET_NO;
  work |= work_test (dns->answers, dns->num_answers);
  work |= work_test (dns->authority_records, dns->num_authority_records);
  work |= work_test (dns->additional_records, dns->num_additional_records);
  if (! work)
  {
    GNUNET_DNS_request_forward (rh);
    GNUNET_DNSPARSER_free_packet (dns);
    return;
  }
  rc = GNUNET_malloc (sizeof (struct ReplyContext));
  rc->rh = rh;
  rc->dns = dns;
  rc->offset = 0;
  rc->group = ANSWERS;
  submit_request (rc);
}