/**
 * Function called for all addresses and peers to find the minimum and
 * maximum (averaged) values for a given quality property.  Given
 * those, we can then calculate the normalized score.
 *
 * @param cls the `struct PropertyRange`
 * @param h which peer are we looking at (ignored)
 * @param k the address for that peer
 * @return #GNUNET_OK (continue to iterate)
 */
static int
find_min_max_it (void *cls,
                 const struct GNUNET_PeerIdentity *h,
                 void *k)
{
  struct PropertyRange *pr = cls;
  const struct ATS_Address *a = k;

  pr->max.utilization_out = GNUNET_MAX (pr->max.utilization_out,
                                        a->properties.utilization_out);
  pr->max.utilization_in = GNUNET_MAX (pr->max.utilization_in,
                                       a->properties.utilization_in);
  pr->max.distance = GNUNET_MAX (pr->max.distance,
                                 a->properties.distance);
  pr->max.delay = GNUNET_TIME_relative_max (pr->max.delay,
                                            a->properties.delay);
  pr->min.utilization_out = GNUNET_MIN (pr->min.utilization_out,
                                        a->properties.utilization_out);
  pr->min.utilization_in = GNUNET_MIN (pr->min.utilization_in,
                                       a->properties.utilization_in);
  pr->min.distance = GNUNET_MIN (pr->min.distance,
                                 a->properties.distance);
  pr->min.delay = GNUNET_TIME_relative_min (pr->min.delay,
                                            a->properties.delay);
  return GNUNET_OK;
}
Example #2
0
/**
 * Grow an array.  Grows old by (*oldCount-newCount)*elementSize bytes
 * and sets *oldCount to newCount.
 *
 * @param old address of the pointer to the array
 *        *old may be NULL
 * @param elementSize the size of the elements of the array
 * @param oldCount address of the number of elements in the *old array
 * @param newCount number of elements in the new array, may be 0
 * @param filename where in the code was the call to GNUNET_array_grow()
 * @param linenumber where in the code was the call to GNUNET_array_grow()
 */
void
GNUNET_xgrow_ (void **old,
	       size_t elementSize,
	       unsigned int *oldCount,
         unsigned int newCount,
	       const char *filename,
	       int linenumber)
{
  void *tmp;
  size_t size;

  GNUNET_assert_at (INT_MAX / elementSize > newCount, filename, linenumber);
  size = newCount * elementSize;
  if (0 == size)
  {
    tmp = NULL;
  }
  else
  {
    tmp = GNUNET_xmalloc_ (size, filename, linenumber);
    if (NULL != *old)
    {
      GNUNET_memcpy (tmp, *old, elementSize * GNUNET_MIN(*oldCount, newCount));
    }
  }

  if (NULL != *old)
  {
    GNUNET_xfree_ (*old, filename, linenumber);
  }
  *old = tmp;
  *oldCount = newCount;
}
Example #3
0
/**
 * Compute the MIN of two bandwidth values.
 *
 * @param b1 first value
 * @param b2 second value
 * @return the min of b1 and b2
 */
struct GNUNET_BANDWIDTH_Value32NBO
GNUNET_BANDWIDTH_value_min (struct GNUNET_BANDWIDTH_Value32NBO b1,
                            struct GNUNET_BANDWIDTH_Value32NBO b2)
{
  return
      GNUNET_BANDWIDTH_value_init (GNUNET_MIN
                                   (ntohl (b1.value__), ntohl (b2.value__)));
}
Example #4
0
/**
 * Execute a transmission context.  If there is
 * an error in the transmission, the #GNUNET_SERVER_receive_done()
 * method will be called with an error code (#GNUNET_SYSERR),
 * otherwise with #GNUNET_OK.
 *
 * @param tc transmission context to use
 * @param timeout when to time out and abort the transmission
 */
void
GNUNET_SERVER_transmit_context_run (struct GNUNET_SERVER_TransmitContext *tc,
                                    struct GNUNET_TIME_Relative timeout)
{
  tc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
  if (NULL ==
      GNUNET_SERVER_notify_transmit_ready (tc->client,
                                           GNUNET_MIN (MIN_BLOCK_SIZE,
                                                       tc->total), timeout,
                                           &transmit_response, tc))
  {
    GNUNET_break (0);
    GNUNET_SERVER_transmit_context_destroy (tc, GNUNET_SYSERR);
  }
}
/**
 * Callback method used with libcurl
 * Method is called when libcurl needs to read data during sending
 *
 * @param stream pointer where to write data
 * @param size size of an individual element
 * @param nmemb count of elements that can be written to the buffer
 * @param cls source pointer, passed to the libcurl handle
 * @return bytes written to stream, returning 0 will terminate connection!
 */
static size_t
client_send_cb (void *stream, size_t size, size_t nmemb, void *cls)
{
  struct Session *s = cls;
  struct Plugin *plugin = s->plugin;
  struct HTTP_Message *msg = s->msg_head;
  size_t len;

  if (GNUNET_YES != exist_session(plugin, s))
  {
    GNUNET_break (0);
    return 0;
  }
  if (NULL == msg)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                     "Client: %p Nothing to send! Suspending PUT handle!\n",
                     s->client_put);
    s->client_put_paused = GNUNET_YES;
    return CURL_READFUNC_PAUSE;
  }
  /* data to send */
  GNUNET_assert (msg->pos < msg->size);
  /* calculate how much fits in buffer */
  len = GNUNET_MIN (msg->size - msg->pos,
		    size * nmemb);
  memcpy (stream, &msg->buf[msg->pos], len);
  msg->pos += len;
  if (msg->pos == msg->size)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                     "Client: %p Message with %u bytes sent, removing message from queue\n",
                     s->client_put, msg->size, msg->pos);
    /* Calling transmit continuation  */
    GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
    if (NULL != msg->transmit_cont)
      msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK);
    GNUNET_free (msg);
  }
  return len;
}
Example #6
0
/**
 * Helper function for incremental transmission of the response.
 */
static size_t
transmit_response (void *cls, size_t size, void *buf)
{
  struct GNUNET_SERVER_TransmitContext *tc = cls;
  size_t msize;

  if (NULL == buf)
  {
    GNUNET_SERVER_transmit_context_destroy (tc, GNUNET_SYSERR);
    return 0;
  }
  if (tc->total - tc->off > size)
    msize = size;
  else
    msize = tc->total - tc->off;
  memcpy (buf, &tc->buf[tc->off], msize);
  tc->off += msize;
  if (tc->total == tc->off)
  {
    GNUNET_SERVER_receive_done (tc->client, GNUNET_OK);
    GNUNET_SERVER_client_drop (tc->client);
    GNUNET_free_non_null (tc->buf);
    GNUNET_free (tc);
  }
  else
  {
    if (NULL ==
        GNUNET_SERVER_notify_transmit_ready (tc->client,
                                             GNUNET_MIN (MIN_BLOCK_SIZE,
                                                         tc->total - tc->off),
                                             GNUNET_TIME_absolute_get_remaining
                                             (tc->timeout), &transmit_response,
                                             tc))
    {
      GNUNET_break (0);
      GNUNET_SERVER_transmit_context_destroy (tc, GNUNET_SYSERR);
    }
  }
  return msize;
}
Example #7
0
/**
 * Function called by the tree encoder to obtain
 * a block of plaintext data (for the lowest level
 * of the tree).
 *
 * @param cls our publishing context
 * @param offset identifies which block to get
 * @param max (maximum) number of bytes to get; returning
 *        fewer will also cause errors
 * @param buf where to copy the plaintext buffer
 * @param emsg location to store an error message (on error)
 * @return number of bytes copied to buf, 0 on error
 */
static size_t
unindex_reader (void *cls,
                uint64_t offset,
                size_t max,
                void *buf,
                char **emsg)
{
  struct GNUNET_FS_UnindexContext *uc = cls;
  size_t pt_size;

  pt_size = GNUNET_MIN (max, uc->file_size - offset);
  if (offset != GNUNET_DISK_file_seek (uc->fh, offset, GNUNET_DISK_SEEK_SET))
  {
    *emsg = GNUNET_strdup (_("Failed to find given position in file"));
    return 0;
  }
  if (pt_size != GNUNET_DISK_file_read (uc->fh, buf, pt_size))
  {
    *emsg = GNUNET_strdup (_("Failed to read file"));
    return 0;
  }
  return pt_size;
}
static void
measurement_stop (void *cls,
                  const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  unsigned long long delta;
  unsigned long long throughput_out;
  unsigned long long throughput_in;
  unsigned long long max_quota_in;
  unsigned long long max_quota_out;
  unsigned long long quota_delta;
  enum GNUNET_ErrorType kind = GNUNET_ERROR_TYPE_DEBUG;

  measure_task = NULL;
  FPRINTF (stdout, "%s",  "\n");
  running = GNUNET_NO;

  delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value_us;

  throughput_out = total_bytes_sent * 1000000LL / delta;     /* convert to bytes/s */
  throughput_in = total_bytes_recv * 1000000LL / delta;      /* convert to bytes/s */

  max_quota_in = GNUNET_MIN (current_quota_p1_in, current_quota_p2_in);
  max_quota_out = GNUNET_MIN (current_quota_p1_out, current_quota_p2_out);
  if (max_quota_out < max_quota_in)
    quota_delta = max_quota_in / 3;
  else
    quota_delta = max_quota_out / 3;

  if ((throughput_out > (max_quota_out + quota_delta)) ||
      (throughput_in > (max_quota_in + quota_delta)))
    ok = 1; /* fail */
  else
    ok = 0; /* pass */
  GNUNET_STATISTICS_get (p1.stats, "core", "# discarded CORE_SEND requests",
                         GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p1);

  GNUNET_STATISTICS_get (p1.stats, "core",
                         "# discarded CORE_SEND request bytes",
                         GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p1);
  GNUNET_STATISTICS_get (p1.stats, "core",
                         "# discarded lower priority CORE_SEND requests",
                         GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, NULL);
  GNUNET_STATISTICS_get (p1.stats, "core",
                         "# discarded lower priority CORE_SEND request bytes",
                         GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p1);
  GNUNET_STATISTICS_get (p2.stats, "core", "# discarded CORE_SEND requests",
                         GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p2);

  GNUNET_STATISTICS_get (p2.stats, "core",
                         "# discarded CORE_SEND request bytes",
                         GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p2);
  GNUNET_STATISTICS_get (p2.stats, "core",
                         "# discarded lower priority CORE_SEND requests",
                         GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p2);
  GNUNET_STATISTICS_get (p2.stats, "core",
                         "# discarded lower priority CORE_SEND request bytes",
                         GNUNET_TIME_UNIT_FOREVER_REL, NULL, &print_stat, &p2);

  if (ok != 0)
    kind = GNUNET_ERROR_TYPE_ERROR;
  switch (test)
  {
  case SYMMETRIC:
    GNUNET_log (kind, "Core quota compliance test with symmetric quotas: %s\n",
                (0 == ok) ? "PASSED" : "FAILED");
    break;
  case ASYMMETRIC_SEND_LIMITED:
    GNUNET_log (kind,
                "Core quota compliance test with limited sender quota: %s\n",
                (0 == ok) ? "PASSED" : "FAILED");
    break;
  case ASYMMETRIC_RECV_LIMITED:
    GNUNET_log (kind,
                "Core quota compliance test with limited receiver quota: %s\n",
                (0 == ok) ? "PASSED" : "FAILED");
    break;
  };
  GNUNET_log (kind, "Peer 1 send  rate: %llu b/s (%llu bytes in %llu ms)\n",
              throughput_out, total_bytes_sent, delta);
  GNUNET_log (kind, "Peer 1 send quota: %llu b/s\n", current_quota_p1_out);
  GNUNET_log (kind, "Peer 2 receive  rate: %llu b/s (%llu bytes in %llu ms)\n",
              throughput_in, total_bytes_recv, delta);
  GNUNET_log (kind, "Peer 2 receive quota: %llu b/s\n", current_quota_p2_in);
/*
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Max. inbound  quota allowed: %llu b/s\n",max_quota_in );
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Max. outbound quota allowed: %llu b/s\n",max_quota_out);
*/
  GNUNET_SCHEDULER_cancel (err_task);
  err_task = GNUNET_SCHEDULER_add_now (&terminate_task, NULL);

}
/**
 * Consider using the path @a p for the tunnel @a t.
 * The tunnel destination is at offset @a off in path @a p.
 *
 * @param cls our tunnel
 * @param path a path to our destination
 * @param off offset of the destination on path @a path
 * @return #GNUNET_YES (should keep iterating)
 */
static int
consider_path_cb (void *cls,
                  struct CadetPeerPath *path,
                  unsigned int off)
{
  struct CadetTunnel *t = cls;
  unsigned int min_length = UINT_MAX;
  GNUNET_CONTAINER_HeapCostType max_desire = 0;
  struct CadetTConnection *ct;

  /* Check if we care about the new path. */
  for (ct = t->connection_head;
       NULL != ct;
       ct = ct->next)
  {
    struct CadetPeerPath *ps;

    ps = GCC_get_path (ct->cc);
    if (ps == path)
      return GNUNET_YES; /* duplicate */
    min_length = GNUNET_MIN (min_length,
                             GCPP_get_length (ps));
    max_desire = GNUNET_MAX (max_desire,
                             GCPP_get_desirability (ps));
  }

  /* FIXME: not sure we should really just count
     'num_connections' here, as they may all have
     consistently failed to connect. */

  /* We iterate by increasing path length; if we have enough paths and
     this one is more than twice as long than what we are currently
     using, then ignore all of these super-long ones! */
  if ( (t->num_connections > DESIRED_CONNECTIONS_PER_TUNNEL) &&
       (min_length * 2 < off) )
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Ignoring paths of length %u, they are way too long.\n",
                min_length * 2);
    return GNUNET_NO;
  }
  /* If we have enough paths and this one looks no better, ignore it. */
  if ( (t->num_connections >= DESIRED_CONNECTIONS_PER_TUNNEL) &&
       (min_length < GCPP_get_length (path)) &&
       (max_desire > GCPP_get_desirability (path)) )
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Ignoring path (%u/%llu) to %s, got something better already.\n",
                GCPP_get_length (path),
                (unsigned long long) GCPP_get_desirability (path),
                GCP_2s (t->destination));
    return GNUNET_YES;
  }

  /* Path is interesting (better by some metric, or we don't have
     enough paths yet). */
  ct = GNUNET_new (struct CadetTConnection);
  ct->created = GNUNET_TIME_absolute_get ();
  ct->t = t;
  ct->cc = GCC_create (t->destination,
                       path,
                       ct,
                       &connection_ready_cb,
                       t);
  /* FIXME: schedule job to kill connection (and path?)  if it takes
     too long to get ready! (And track performance data on how long
     other connections took with the tunnel!)
     => Note: to be done within 'connection'-logic! */
  GNUNET_CONTAINER_DLL_insert (t->connection_head,
                               t->connection_tail,
                               ct);
  t->num_connections++;
  return GNUNET_YES;
}
Example #10
0
/**
 * Add incoming data to the receive buffer and call the
 * callback for all complete messages.
 *
 * @param mst tokenizer to use
 * @param client_identity ID of client for which this is a buffer
 * @param buf input data to add
 * @param size number of bytes in buf
 * @param purge should any excess bytes in the buffer be discarded
 *       (i.e. for packet-based services like UDP)
 * @param one_shot only call callback once, keep rest of message in buffer
 * @return GNUNET_OK if we are done processing (need more data)
 *         GNUNET_NO if one_shot was set and we have another message ready
 *         GNUNET_SYSERR if the data stream is corrupt
 */
int
GNUNET_SERVER_mst_receive (struct GNUNET_SERVER_MessageStreamTokenizer *mst,
                           void *client_identity, const char *buf, size_t size,
                           int purge, int one_shot)
{
  const struct GNUNET_MessageHeader *hdr;
  size_t delta;
  uint16_t want;
  char *ibuf;
  int need_align;
  unsigned long offset;
  int ret;

  GNUNET_assert (mst->off <= mst->pos);
  GNUNET_assert (mst->pos <= mst->curr_buf);
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Server-mst receives %u bytes with %u bytes already in private buffer\n",
       (unsigned int) size, (unsigned int) (mst->pos - mst->off));
  ret = GNUNET_OK;
  ibuf = (char *) mst->hdr;
  while (mst->pos > 0)
  {
do_align:
    GNUNET_assert (mst->pos >= mst->off);
    if ((mst->curr_buf - mst->off < sizeof (struct GNUNET_MessageHeader)) ||
        (0 != (mst->off % ALIGN_FACTOR)))
    {
      /* need to align or need more space */
      mst->pos -= mst->off;
      memmove (ibuf, &ibuf[mst->off], mst->pos);
      mst->off = 0;
    }
    if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
    {
      delta =
          GNUNET_MIN (sizeof (struct GNUNET_MessageHeader) -
                      (mst->pos - mst->off), size);
      memcpy (&ibuf[mst->pos], buf, delta);
      mst->pos += delta;
      buf += delta;
      size -= delta;
    }
    if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
    {
      if (purge)
      {
        mst->off = 0;
        mst->pos = 0;
      }
      return GNUNET_OK;
    }
    hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
    want = ntohs (hdr->size);
    if (want < sizeof (struct GNUNET_MessageHeader))
    {
      GNUNET_break_op (0);
      return GNUNET_SYSERR;
    }
    if ( (mst->curr_buf - mst->off < want) &&
	 (mst->off > 0) )
    {
      /* can get more space by moving */
      mst->pos -= mst->off;
      memmove (ibuf, &ibuf[mst->off], mst->pos);
      mst->off = 0;
    }
    if (mst->curr_buf < want)
    {
      /* need to get more space by growing buffer */
      GNUNET_assert (0 == mst->off);
      mst->hdr = GNUNET_realloc (mst->hdr, want);
      ibuf = (char *) mst->hdr;
      mst->curr_buf = want;
    }
    hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
    if (mst->pos - mst->off < want)
    {
      delta = GNUNET_MIN (want - (mst->pos - mst->off), size);
      GNUNET_assert (mst->pos + delta <= mst->curr_buf);
      memcpy (&ibuf[mst->pos], buf, delta);
      mst->pos += delta;
      buf += delta;
      size -= delta;
    }
    if (mst->pos - mst->off < want)
    {
      if (purge)
      {
        mst->off = 0;
        mst->pos = 0;
      }
      return GNUNET_OK;
    }
    if (one_shot == GNUNET_SYSERR)
    {
      /* cannot call callback again, but return value saying that
       * we have another full message in the buffer */
      ret = GNUNET_NO;
      goto copy;
    }
    if (one_shot == GNUNET_YES)
      one_shot = GNUNET_SYSERR;
    mst->off += want;
    if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr))
      return GNUNET_SYSERR;
    if (mst->off == mst->pos)
    {
      /* reset to beginning of buffer, it's free right now! */
      mst->off = 0;
      mst->pos = 0;
    }
  }
  GNUNET_assert (0 == mst->pos);
  while (size > 0)
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
         "Server-mst has %u bytes left in inbound buffer\n",
         (unsigned int) size);
    if (size < sizeof (struct GNUNET_MessageHeader))
      break;
    offset = (unsigned long) buf;
    need_align = (0 != (offset % ALIGN_FACTOR)) ? GNUNET_YES : GNUNET_NO;
    if (GNUNET_NO == need_align)
    {
      /* can try to do zero-copy and process directly from original buffer */
      hdr = (const struct GNUNET_MessageHeader *) buf;
      want = ntohs (hdr->size);
      if (want < sizeof (struct GNUNET_MessageHeader))
      {
        GNUNET_break_op (0);
        mst->off = 0;
        return GNUNET_SYSERR;
      }
      if (size < want)
        break;                  /* or not: buffer incomplete, so copy to private buffer... */
      if (one_shot == GNUNET_SYSERR)
      {
        /* cannot call callback again, but return value saying that
         * we have another full message in the buffer */
        ret = GNUNET_NO;
        goto copy;
      }
      if (one_shot == GNUNET_YES)
        one_shot = GNUNET_SYSERR;
      if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr))
        return GNUNET_SYSERR;
      buf += want;
      size -= want;
    }
    else
    {
      /* need to copy to private buffer to align;
       * yes, we go a bit more spagetti than usual here */
      goto do_align;
    }
  }
copy:
  if ((size > 0) && (!purge))
  {
    if (size + mst->pos > mst->curr_buf)
    {
      mst->hdr = GNUNET_realloc (mst->hdr, size + mst->pos);
      ibuf = (char *) mst->hdr;
      mst->curr_buf = size + mst->pos;
    }
    GNUNET_assert (size + mst->pos <= mst->curr_buf);
    memcpy (&ibuf[mst->pos], buf, size);
    mst->pos += size;
  }
  if (purge)
  {
    mst->off = 0;
    mst->pos = 0;
  }
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Server-mst leaves %u bytes in private buffer\n",
       (unsigned int) (mst->pos - mst->off));
  return ret;
}
/**
 * Callback method used with libcurl
 * Method is called when libcurl needs to read data during sending
 *
 * @param stream pointer where to write data
 * @param size size of an individual element
 * @param nmemb count of elements that can be written to the buffer
 * @param cls source pointer, passed to the libcurl handle
 * @return bytes written to stream, returning 0 will terminate connection!
 */
static size_t
client_send_cb (void *stream, size_t size, size_t nmemb, void *cls)
{
  struct Session *s = cls;
  struct HTTP_Client_Plugin *plugin = s->plugin;
  struct HTTP_Message *msg = s->msg_head;
  size_t len;
  char *stat_txt;

  if (GNUNET_YES != client_exist_session (plugin, s))
  {
    GNUNET_break (0);
    return 0;
  }
  if (GNUNET_YES == s->put_tmp_disconnecting)
  {

      GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
                       "Session %p/connection %p: disconnect due to inactivity\n",
                       s, s->client_put);
      return 0;
  }

  if (NULL == msg)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                     "Session %p/connection %p: nothing to send, suspending\n",
                     s, s->client_put);
    s->put_disconnect_task = GNUNET_SCHEDULER_add_delayed (PUT_DISCONNECT_TIMEOUT, &client_put_disconnect, s);
    s->put_paused = GNUNET_YES;
    return CURL_READFUNC_PAUSE;
  }
  /* data to send */
  GNUNET_assert (msg->pos < msg->size);
  /* calculate how much fits in buffer */
  len = GNUNET_MIN (msg->size - msg->pos,
                    size * nmemb);
  memcpy (stream, &msg->buf[msg->pos], len);
  msg->pos += len;
  if (msg->pos == msg->size)
  {
    GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
                     "Session %p/connection %p: sent message with %u bytes sent, removing message from queue\n",
                     s, s->client_put, msg->size, msg->pos);
    /* Calling transmit continuation  */
    GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
    if (NULL != msg->transmit_cont)
      msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK,
                          msg->size, msg->size + s->overhead);
    s->overhead = 0;
    GNUNET_free (msg);
  }

  GNUNET_asprintf (&stat_txt, "# bytes currently in %s_client buffers", plugin->protocol);
  GNUNET_STATISTICS_update (plugin->env->stats,
                            stat_txt, -len, GNUNET_NO);
  GNUNET_free (stat_txt);

  GNUNET_asprintf (&stat_txt, "# bytes transmitted via %s_client", plugin->protocol);
  GNUNET_STATISTICS_update (plugin->env->stats,
                            stat_txt, len, GNUNET_NO);
  GNUNET_free (stat_txt);

  client_reschedule_session_timeout (s);
  return len;
}