/**
 * Task run to check for messages that need to be sent to a subscriber.
 *
 * @param client a RemoteSubscriberInfo struct, containing the channel
 *               handle and any messages to be sent to it
 */
static void
process_pending_subscriber_messages (struct RemoteSubscriberInfo *subscriber)
{
  struct GNUNET_MessageHeader *msg;

  if ((subscriber->pending_head == NULL) ||
      (subscriber->transmit_handle != NULL))
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
                "Not asking for transmission to %s now: %s\n",
                GNUNET_i2s (&subscriber->id),
                subscriber->pending_head ==
                NULL ? "no more messages" : "request already pending");
    return;
  }

  msg = subscriber->pending_head->msg;
  LOG (GNUNET_ERROR_TYPE_DEBUG,
              "asking for transmission of %u bytes to client %s\n",
              ntohs (msg->size), GNUNET_i2s (&subscriber->id));

  subscriber->transmit_handle =
    GNUNET_MESH_notify_transmit_ready (subscriber->channel,
                                       GNUNET_NO,
                                       GNUNET_TIME_UNIT_FOREVER_REL,
                                       ntohs (msg->size),
                                       send_msg_to_subscriber, subscriber);
}
/**
 * @brief Send an echo request to the remote peer.
 *
 * @param cls Closure (NULL).
 * @param tc Task context.
 */
static void
send_echo (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  GNUNET_MESH_notify_transmit_ready (ch, GNUNET_NO,
                                     GNUNET_TIME_UNIT_FOREVER_REL,
                                     sizeof (struct GNUNET_MessageHeader),
                                     &data_ready, NULL);
}
/**
 * This function is called *before* the DNS request has been 
 * given to a "local" DNS resolver.  Tunneling for DNS requests
 * was enabled, so we now need to send the request via some MESH
 * tunnel to a DNS EXIT for resolution.
 *
 * @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_pre_request_handler (void *cls,
			 struct GNUNET_DNS_RequestHandle *rh,
			 size_t request_length,
			 const char *request)
{
  struct RequestContext *rc;
  size_t mlen;
  struct GNUNET_MessageHeader hdr;
  struct GNUNET_TUN_DnsHeader dns;

  GNUNET_STATISTICS_update (stats,
			    gettext_noop ("# DNS requests intercepted"),
			    1, GNUNET_NO);
  if (0 == dns_exit_available)
  {
    GNUNET_STATISTICS_update (stats,
			      gettext_noop ("# DNS requests dropped (DNS mesh tunnel down)"),
			      1, GNUNET_NO);
    GNUNET_DNS_request_drop (rh);
    return;
  }
  if (request_length < sizeof (dns))
  {
    GNUNET_STATISTICS_update (stats,
			      gettext_noop ("# DNS requests dropped (malformed)"),
			      1, GNUNET_NO);
    GNUNET_DNS_request_drop (rh);
    return;
  }
  memcpy (&dns, request, sizeof (dns));
  GNUNET_assert (NULL != mesh_tunnel);
  mlen = sizeof (struct GNUNET_MessageHeader) + request_length;
  rc = GNUNET_malloc (sizeof (struct RequestContext) + mlen);
  rc->rh = rh;
  rc->mesh_message = (const struct GNUNET_MessageHeader*) &rc[1];
  rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
						   &timeout_request,
						   rc);
  rc->dns_id = dns.id;
  hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET);
  hdr.size = htons (mlen);
  memcpy (&rc[1], &hdr, sizeof (struct GNUNET_MessageHeader));
  memcpy (&(((char*)&rc[1])[sizeof (struct GNUNET_MessageHeader)]),
	  request,
	  request_length);
  GNUNET_CONTAINER_DLL_insert_tail (transmit_queue_head,
				    transmit_queue_tail,
				    rc);
  if (NULL == mesh_th)
    mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel,
						 GNUNET_NO, 0,
						 TIMEOUT,
						 NULL, mlen,
						 &transmit_dns_request_to_mesh,
						 NULL);
}
/**
 * Function called whenever a message is received.
 *
 * Each time the function must call #GNUNET_MESH_receive_done on the channel
 * in order to receive the next message. This doesn't need to be immediate:
 * can be delayed if some processing is done on the message.
 *
 * @param cls Closure (set from #GNUNET_MESH_connect).
 * @param channel Connection to the other end.
 * @param channel_ctx Place to store local state associated with the channel.
 * @param message The actual message.
 * @return #GNUNET_OK to keep the channel open,
 *         #GNUNET_SYSERR to close it (signal serious error).
 */
static int
data_callback (void *cls,
               struct GNUNET_MESH_Channel *channel,
               void **channel_ctx,
               const struct GNUNET_MessageHeader *message)
{
  uint16_t len;
  ssize_t done;
  uint16_t off;
  const char *buf;
  GNUNET_break (ch == channel);

  if (GNUNET_YES == echo)
  {
    if (0 != listen_port)
    {
      /* Just listening to echo incoming messages*/
      GNUNET_MESH_notify_transmit_ready (channel, GNUNET_NO,
                                        GNUNET_TIME_UNIT_FOREVER_REL,
                                        sizeof (struct GNUNET_MessageHeader),
                                        &data_ready, NULL);
      return GNUNET_OK;
    }
    else
    {
      struct GNUNET_TIME_Relative latency;

      latency = GNUNET_TIME_absolute_get_duration (echo_time);
      echo_time = GNUNET_TIME_UNIT_FOREVER_ABS;
      FPRINTF (stdout, "time: %s\n",
               GNUNET_STRINGS_relative_time_to_string (latency, GNUNET_NO));
      echo_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
                                                &send_echo, NULL);
    }
  }

  len = ntohs (message->size) - sizeof (*message);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got %u bytes\n", len);
  buf = (const char *) &message[1];
  off = 0;
  while (off < len)
  {
    done = write (1, &buf[off], len - off);
    if (done <= 0)
    {
      if (-1 == done)
        GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
                             "write");
      return GNUNET_SYSERR;
    }
    off += done;
  }
  return GNUNET_OK;
}
/**
 * Transmit a DNS request via MESH and move the request
 * handle to the receive queue.
 *
 * @param cls NULL
 * @param size number of bytes available in buf
 * @param buf where to copy the message
 * @return number of bytes written to buf
 */
static size_t
transmit_dns_request_to_mesh (void *cls,
			      size_t size,
			      void *buf)
{
  struct RequestContext *rc;
  size_t mlen;

  mesh_th = NULL;
  if (NULL == (rc = transmit_queue_head))
    return 0;
  mlen = ntohs (rc->mesh_message->size);
  if (mlen > size)
  {    
    mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel,
						 GNUNET_NO, 0,
						 TIMEOUT,
						 NULL, mlen,
						 &transmit_dns_request_to_mesh,
						 NULL);
    return 0;
  }
  GNUNET_assert (GNUNET_NO == rc->was_transmitted);
  memcpy (buf, rc->mesh_message, mlen);
  GNUNET_CONTAINER_DLL_remove (transmit_queue_head,
			       transmit_queue_tail,
			       rc);
  rc->was_transmitted = GNUNET_YES;
  GNUNET_CONTAINER_DLL_insert (receive_queue_head,
			       receive_queue_tail,
			       rc);
  rc = transmit_queue_head;
  if (NULL != rc)
    mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel,
						 GNUNET_NO, 0,
						 TIMEOUT,
						 NULL, ntohs (rc->mesh_message->size),
						 &transmit_dns_request_to_mesh,
						 NULL);
  return mlen;
}
/**
 * Task run in monitor mode when the user presses CTRL-C to abort.
 * Stops monitoring activity.
 *
 * @param cls Closure (unused).
 * @param tc scheduler context
 */
static void
read_stdio (void *cls,
            const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  static char buf[60000];

  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
  {
    return;
  }

  data_size = read (0, buf, 60000);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "stdio read %u bytes\n", data_size);
  if (data_size < 1)
  {
    GNUNET_SCHEDULER_shutdown();
    return;
  }
  GNUNET_MESH_notify_transmit_ready (ch, GNUNET_NO,
                                     GNUNET_TIME_UNIT_FOREVER_REL,
                                     data_size
                                     + sizeof (struct GNUNET_MessageHeader),
                                     &data_ready, buf);
}