예제 #1
0
/**
 * Removes a the give peer from the peer array
 *
 * @param peer the peer to be removed
 */
static void
peer_list_remove (struct Peer *peer)
{
  unsigned int orig_size;
  uint32_t id;

  if (GNUNET_NO == peer->is_remote)
    GST_num_local_peers--;
  GST_peer_list[peer->id] = NULL;
  orig_size = GST_peer_list_size;
  while (GST_peer_list_size >= LIST_GROW_STEP)
  {
    for (id = GST_peer_list_size - 1;
         (id >= GST_peer_list_size - LIST_GROW_STEP) && (id != UINT32_MAX);
         id--)
      if (NULL != GST_peer_list[id])
        break;
    if (id != ((GST_peer_list_size - LIST_GROW_STEP) - 1))
      break;
    GST_peer_list_size -= LIST_GROW_STEP;
  }
  if (orig_size == GST_peer_list_size)
    return;
  GST_peer_list =
      GNUNET_realloc (GST_peer_list,
                      sizeof (struct Peer *) * GST_peer_list_size);
}
예제 #2
0
/**
 * Function to call to start a peer_create type operation once all
 * queues the operation is part of declare that the
 * operation can be activated.
 *
 * @param cls the closure from GNUNET_TESTBED_operation_create_()
 */
static void
opstart_peer_create (void *cls)
{
  struct OperationContext *opc = cls;
  struct PeerCreateData *data;
  struct GNUNET_TESTBED_PeerCreateMessage *msg;
  char *config;
  char *xconfig;
  size_t c_size;
  size_t xc_size;
  uint16_t msize;

  GNUNET_assert (OP_PEER_CREATE == opc->type);
  data = opc->data;
  GNUNET_assert (NULL != data);
  GNUNET_assert (NULL != data->peer);
  opc->state = OPC_STATE_STARTED;
  config = GNUNET_CONFIGURATION_serialize (data->cfg, &c_size);
  xc_size = GNUNET_TESTBED_compress_config_ (config, c_size, &xconfig);
  GNUNET_free (config);
  msize = xc_size + sizeof (struct GNUNET_TESTBED_PeerCreateMessage);
  msg = GNUNET_realloc (xconfig, msize);
  memmove (&msg[1], msg, xc_size);
  msg->header.size = htons (msize);
  msg->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_CREATEPEER);
  msg->operation_id = GNUNET_htonll (opc->id);
  msg->host_id = htonl (GNUNET_TESTBED_host_get_id_ (data->peer->host));
  msg->peer_id = htonl (data->peer->unique_id);
  msg->config_size = htonl (c_size);
  GNUNET_CONTAINER_DLL_insert_tail (opc->c->ocq_head, opc->c->ocq_tail, opc);
  GNUNET_TESTBED_queue_message_ (opc->c, &msg->header);
}
예제 #3
0
/**
 * Send data to be logged to the logger service.  The data will be buffered and
 * will be sent upon an explicit call to GNUNET_TESTBED_LOGGER_flush() or upon
 * exceeding a threshold size.
 *
 * @param h the logger handle
 * @param data the data to send;
 * @param size how many bytes of data to send
 */
void
GNUNET_TESTBED_LOGGER_write (struct GNUNET_TESTBED_LOGGER_Handle *h,
                             const void *data, size_t size)
{
  size_t fit_size;

  GNUNET_assert (0 != size);
  GNUNET_assert (NULL != data);
  GNUNET_assert (size < (GNUNET_SERVER_MAX_MESSAGE_SIZE
                         - sizeof (struct GNUNET_MessageHeader)));
  fit_size = sizeof (struct GNUNET_MessageHeader) + h->bs + size;
  if ( GNUNET_SERVER_MAX_MESSAGE_SIZE < fit_size )
    dispatch_buffer (h);
  if (NULL == h->buf)
  {
    h->buf = GNUNET_malloc (size);
    h->bs = size;
    memcpy (h->buf, data, size);
    return;
  }
  h->buf = GNUNET_realloc (h->buf, h->bs + size);
  memcpy (h->buf + h->bs, data, size);
  h->bs += size;
  return;
}
예제 #4
0
/**
 * Queue a message part for transmission.
 *
 * The message part is added to the current message buffer.
 * When this buffer is full, it is added to the transmission queue.
 *
 * @param tmit
 *        Transmission handle.
 * @param msg
 *        Message part, or NULL.
 * @param tmit_now
 *        Transmit message now, or wait for buffer to fill up?
 *        #GNUNET_YES or #GNUNET_NO.
 */
static void
transmit_queue_insert (struct GNUNET_PSYC_TransmitHandle *tmit,
                       const struct GNUNET_MessageHeader *msg,
                       uint8_t tmit_now)
{
  uint16_t size = (NULL != msg) ? ntohs (msg->size) : 0;

  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Queueing message part of type %u and size %u (tmit_now: %u)).\n",
       NULL != msg ? ntohs (msg->type) : 0, size, tmit_now);

  if (NULL != tmit->msg)
  {
    if (NULL == msg
        || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < tmit->msg->size + size)
    {
      /* End of message or buffer is full, add it to transmission queue
       * and start with empty buffer */
      tmit->msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
      tmit->msg->size = htons (tmit->msg->size);
      GNUNET_CLIENT_MANAGER_transmit (tmit->client, tmit->msg);
      tmit->msg = NULL;
      tmit->acks_pending++;
    }
    else
    {
      /* Message fits in current buffer, append */
      tmit->msg = GNUNET_realloc (tmit->msg, tmit->msg->size + size);
      memcpy ((char *) tmit->msg + tmit->msg->size, msg, size);
      tmit->msg->size += size;
    }
  }

  if (NULL == tmit->msg && NULL != msg)
  {
    /* Empty buffer, copy over message. */
    tmit->msg = GNUNET_malloc (sizeof (*tmit->msg) + size);
    tmit->msg->size = sizeof (*tmit->msg) + size;
    memcpy (&tmit->msg[1], msg, size);
  }

  if (NULL != tmit->msg
      && (GNUNET_YES == tmit_now
          || (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD
              < tmit->msg->size + sizeof (struct GNUNET_MessageHeader))))
  {
    /* End of message or buffer is full, add it to transmission queue. */
    tmit->msg->type = htons (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE);
    tmit->msg->size = htons (tmit->msg->size);
    GNUNET_CLIENT_MANAGER_transmit (tmit->client, tmit->msg);
    tmit->msg = NULL;
    tmit->acks_pending++;
  }
}
예제 #5
0
/**
 * Send the buffered data to the service
 *
 * @param h the logger handle
 */
static void
dispatch_buffer (struct GNUNET_TESTBED_LOGGER_Handle *h)
{
  struct GNUNET_MessageHeader *msg;
  size_t msize;

  msize = sizeof (struct GNUNET_MessageHeader) + h->bs;
  msg = GNUNET_realloc (h->buf, msize);
  h->buf = NULL;
  memmove (&msg[1], msg, h->bs);
  h->bs = 0;
  msg->type = htons (GNUNET_MESSAGE_TYPE_TESTBED_LOGGER_MSG);
  msg->size = htons (msize);
  queue_message (h, msg);
}
예제 #6
0
/**
 * Append a message to the transmission context.
 * All messages in the context will be sent by
 * the transmit_context_run method.
 *
 * @param tc context to use
 * @param msg message to append
 */
void
GNUNET_SERVER_transmit_context_append_message (struct
                                               GNUNET_SERVER_TransmitContext
                                               *tc,
                                               const struct GNUNET_MessageHeader
                                               *msg)
{
  struct GNUNET_MessageHeader *m;
  uint16_t size;

  size = ntohs (msg->size);
  tc->buf = GNUNET_realloc (tc->buf, tc->total + size);
  m = (struct GNUNET_MessageHeader *) &tc->buf[tc->total];
  tc->total += size;
  memcpy (m, msg, size);
}
예제 #7
0
/**
 * Handler for GNUNET_MESSAGE_TYPE_TESTBED_GETSLAVECONFIG messages
 *
 * @param cls NULL
 * @param client identification of the client
 * @param message the actual message
 */
static void
handle_slave_get_config (void *cls, struct GNUNET_SERVER_Client *client,
                         const struct GNUNET_MessageHeader *message)
{
  struct GNUNET_TESTBED_SlaveGetConfigurationMessage *msg;
  struct Slave *slave;
  struct GNUNET_TESTBED_SlaveConfiguration *reply;
  const struct GNUNET_CONFIGURATION_Handle *cfg;
  char *config;
  char *xconfig;
  size_t config_size;
  size_t xconfig_size;
  size_t reply_size;
  uint64_t op_id;
  uint32_t slave_id;

  msg = (struct GNUNET_TESTBED_SlaveGetConfigurationMessage *) message;
  slave_id = ntohl (msg->slave_id);
  op_id = GNUNET_ntohll (msg->operation_id);
  if ((GST_slave_list_size <= slave_id) || (NULL == GST_slave_list[slave_id]))
  {
    /* FIXME: Add forwardings for this type of message here.. */
    GST_send_operation_fail_msg (client, op_id, "Slave not found");
    GNUNET_SERVER_receive_done (client, GNUNET_OK);
    return;
  }
  slave = GST_slave_list[slave_id];
  GNUNET_assert (NULL != (cfg = GNUNET_TESTBED_host_get_cfg_ (GST_host_list[slave->host_id])));
  config = GNUNET_CONFIGURATION_serialize (cfg, &config_size);
  xconfig_size =
      GNUNET_TESTBED_compress_config_ (config, config_size, &xconfig);
  GNUNET_free (config);
  reply_size = xconfig_size + sizeof (struct GNUNET_TESTBED_SlaveConfiguration);
  GNUNET_break (reply_size <= UINT16_MAX);
  GNUNET_break (config_size <= UINT16_MAX);
  reply = GNUNET_realloc (xconfig, reply_size);
  (void) memmove (&reply[1], reply, xconfig_size);
  reply->header.type = htons (GNUNET_MESSAGE_TYPE_TESTBED_SLAVE_CONFIGURATION);
  reply->header.size = htons ((uint16_t) reply_size);
  reply->slave_id = msg->slave_id;
  reply->operation_id = msg->operation_id;
  reply->config_size = htons ((uint16_t) config_size);
  GST_queue_message (client, &reply->header);
  GNUNET_SERVER_receive_done (client, GNUNET_OK);
}
예제 #8
0
/**
 * Append a message to the transmission context.
 * All messages in the context will be sent by
 * the transmit_context_run method.
 *
 * @param tc context to use
 * @param data what to append to the result message
 * @param length length of data
 * @param type type of the message
 */
void
GNUNET_SERVER_transmit_context_append_data (struct GNUNET_SERVER_TransmitContext
                                            *tc, const void *data,
                                            size_t length, uint16_t type)
{
  struct GNUNET_MessageHeader *msg;
  size_t size;

  GNUNET_assert (length < GNUNET_SERVER_MAX_MESSAGE_SIZE);
  size = length + sizeof (struct GNUNET_MessageHeader);
  GNUNET_assert (size > length);
  tc->buf = GNUNET_realloc (tc->buf, tc->total + size);
  msg = (struct GNUNET_MessageHeader *) &tc->buf[tc->total];
  tc->total += size;
  msg->size = htons (size);
  msg->type = htons (type);
  memcpy (&msg[1], data, length);
}
예제 #9
0
/**
 * Destroy a host handle.  Must only be called once everything
 * running on that host has been stopped.
 *
 * @param host handle to destroy
 */
void
GNUNET_TESTBED_host_destroy (struct GNUNET_TESTBED_Host *host)
{
  struct RegisteredController *rc;
  uint32_t id;

  GNUNET_assert (host->id < host_list_size);
  GNUNET_assert (host_list[host->id] == host);
  host_list[host->id] = NULL;
  /* clear registered controllers list */
  for (rc = host->rc_head; NULL != rc; rc = host->rc_head)
  {
    GNUNET_CONTAINER_DLL_remove (host->rc_head, host->rc_tail, rc);
    GNUNET_free (rc);
  }
  GNUNET_free_non_null ((char *) host->username);
  GNUNET_free_non_null ((char *) host->hostname);
  GNUNET_TESTBED_operation_queue_destroy_
      (host->opq_parallel_overlay_connect_operations);
  GNUNET_CONFIGURATION_destroy (host->cfg);
  GNUNET_free (host);
  while (host_list_size >= HOST_LIST_GROW_STEP)
  {
    for (id = host_list_size - 1; id > host_list_size - HOST_LIST_GROW_STEP;
         id--)
      if (NULL != host_list[id])
        break;
    if (id != host_list_size - HOST_LIST_GROW_STEP)
      break;
    if (NULL != host_list[id])
      break;
    host_list_size -= HOST_LIST_GROW_STEP;
  }
  host_list =
      GNUNET_realloc (host_list,
                      sizeof (struct GNUNET_TESTBED_Host *) * host_list_size);
}
예제 #10
0
static int
check ()
{
#define MAX_TESTVAL 1024
  char *ptrs[MAX_TESTVAL];
  int i;
  int j;
  int k;
  unsigned int ui;

  /* GNUNET_malloc/GNUNET_free test */
  k = 352;                      /* random start value */
  for (i = 1; i < MAX_TESTVAL; i++)
  {
    ptrs[i] = GNUNET_malloc (i);
    for (j = 0; j < i; j++)
      ptrs[i][j] = k++;
  }

  for (i = MAX_TESTVAL - 1; i >= 1; i--)
  {
    for (j = i - 1; j >= 0; j--)
      if (ptrs[i][j] != (char) --k)
        return 1;
    GNUNET_free (ptrs[i]);
  }

  /* GNUNET_free_non_null test */
  GNUNET_free_non_null (NULL);
  GNUNET_free_non_null (GNUNET_malloc (4));

  /* GNUNET_strdup tests */
  ptrs[0] = GNUNET_strdup ("bar");
  if (0 != strcmp (ptrs[0], "bar"))
    return 3;
  /* now realloc */
  ptrs[0] = GNUNET_realloc (ptrs[0], 12);
  strcpy (ptrs[0], "Hello World");

  GNUNET_free (ptrs[0]);
  GNUNET_asprintf (&ptrs[0], "%s %s", "Hello", "World");
  GNUNET_assert (strlen (ptrs[0]) == 11);
  GNUNET_free (ptrs[0]);

  /* GNUNET_array_grow tests */
  ptrs[0] = NULL;
  ui = 0;
  GNUNET_array_grow (ptrs[0], ui, 42);
  if (ui != 42)
    return 4;
  GNUNET_array_grow (ptrs[0], ui, 22);
  if (ui != 22)
    return 5;
  for (j = 0; j < 22; j++)
    ptrs[0][j] = j;
  GNUNET_array_grow (ptrs[0], ui, 32);
  for (j = 0; j < 22; j++)
    if (ptrs[0][j] != j)
      return 6;
  for (j = 22; j < 32; j++)
    if (ptrs[0][j] != 0)
      return 7;
  GNUNET_array_grow (ptrs[0], ui, 0);
  if (i != 0)
    return 8;
  if (ptrs[0] != NULL)
    return 9;


  return 0;
}
예제 #11
0
/**
 * We are ready to transmit (or got a timeout).
 *
 * @param cls our connection handle
 * @param tc task context describing why we are here
 */
static void
transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  struct GNUNET_CONNECTION_Handle *connection = cls;
  GNUNET_CONNECTION_TransmitReadyNotify notify;
  ssize_t ret;
  size_t have;

  LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_ready running (%p).\n", connection);
  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != connection->write_task);
  connection->write_task = GNUNET_SCHEDULER_NO_TASK;
  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == connection->nth.timeout_task);
  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
  {
    if (NULL != connection->sock)
      goto SCHEDULE_WRITE;      /* ignore shutdown, go again immediately */
    LOG (GNUNET_ERROR_TYPE_DEBUG,
         "Transmit to `%s' fails, shutdown happened (%p).\n",
         GNUNET_a2s (connection->addr, connection->addrlen), connection);
    notify = connection->nth.notify_ready;
    if (NULL != notify)
    {
      connection->nth.notify_ready = NULL;
      notify (connection->nth.notify_ready_cls, 0, NULL);
    }
    return;
  }
  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
         "Transmit to `%s' fails, time out reached (%p).\n",
         GNUNET_a2s (connection->addr, connection->addrlen), connection);
    notify = connection->nth.notify_ready;
    GNUNET_assert (NULL != notify);
    connection->nth.notify_ready = NULL;
    notify (connection->nth.notify_ready_cls, 0, NULL);
    return;
  }
  GNUNET_assert (NULL != connection->sock);
  if (NULL == tc->write_ready) 
  {
    /* special circumstances (in particular, PREREQ_DONE after
     * connect): not yet ready to write, but no "fatal" error either.
     * Hence retry.  */
    goto SCHEDULE_WRITE;
  }
  if (!GNUNET_NETWORK_fdset_isset (tc->write_ready, connection->sock))
  {
    GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == connection->write_task);
    /* special circumstances (in particular, shutdown): not yet ready
     * to write, but no "fatal" error either.  Hence retry.  */
    goto SCHEDULE_WRITE;
  }
  GNUNET_assert (connection->write_buffer_off >= connection->write_buffer_pos);
  if ((NULL != connection->nth.notify_ready) &&
      (connection->write_buffer_size < connection->nth.notify_size))
  {
    connection->write_buffer =
        GNUNET_realloc (connection->write_buffer, connection->nth.notify_size);
    connection->write_buffer_size = connection->nth.notify_size;
  }
  process_notify (connection);
  have = connection->write_buffer_off - connection->write_buffer_pos;
  if (0 == have)
  {
    /* no data ready for writing, terminate write loop */
    return;
  }
  GNUNET_assert (have <= connection->write_buffer_size);
  GNUNET_assert (have + connection->write_buffer_pos <= connection->write_buffer_size);
  GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size);
RETRY:
  ret =
      GNUNET_NETWORK_socket_send (connection->sock,
				  &connection->write_buffer[connection->write_buffer_pos],
				  have);
  if (-1 == ret)
  {
    if (EINTR == errno)
      goto RETRY;
    if (GNUNET_SCHEDULER_NO_TASK != connection->write_task)
    {
      GNUNET_SCHEDULER_cancel (connection->write_task);
      connection->write_task = GNUNET_SCHEDULER_NO_TASK;
    }
    signal_transmit_error (connection, errno);
    return;
  }
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Connection transmitted %u/%u bytes to `%s' (%p)\n",
       (unsigned int) ret, have, GNUNET_a2s (connection->addr, connection->addrlen), connection);
  connection->write_buffer_pos += ret;
  if (connection->write_buffer_pos == connection->write_buffer_off)
  {
    /* transmitted all pending data */
    connection->write_buffer_pos = 0;
    connection->write_buffer_off = 0;
  }
  if ((0 == connection->write_buffer_off) && (NULL == connection->nth.notify_ready))
    return;                     /* all data sent! */
  /* not done writing, schedule more */
SCHEDULE_WRITE:
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Re-scheduling transmit_ready (more to do) (%p).\n", connection);
  have = connection->write_buffer_off - connection->write_buffer_pos;
  GNUNET_assert ((NULL != connection->nth.notify_ready) || (have > 0));
  if (GNUNET_SCHEDULER_NO_TASK == connection->write_task)
    connection->write_task =
        GNUNET_SCHEDULER_add_write_net ((connection->nth.notify_ready ==
                                         NULL) ? GNUNET_TIME_UNIT_FOREVER_REL :
                                        GNUNET_TIME_absolute_get_remaining
                                        (connection->nth.transmit_timeout),
                                        connection->sock, &transmit_ready, connection);
}
예제 #12
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;
}