/**
 * FIXME: Change the name of variable. 
 * Ensure that everywhere in this file you are using destination as the key.
 * Do we need prev field in routing table?
 * Add a new entry to our routing table.
 * @param source peer
 * @param destintation
 * @param next_hop
 */
void
GDS_ROUTING_add (struct GNUNET_PeerIdentity *source,
                 struct GNUNET_PeerIdentity *dest,
                 struct GNUNET_PeerIdentity *next_hop)
{
  struct RoutingTrail *new_routing_entry;
    
  new_routing_entry = GNUNET_malloc (sizeof (struct RoutingTrail));
  new_routing_entry->source = source;
  new_routing_entry->next_hop = next_hop;
  new_routing_entry->destination = dest;
    
  /* If dest is already present in the routing table, then exit.*/
  if (GNUNET_YES ==
    GNUNET_CONTAINER_multipeermap_contains (routing_table,
                                              dest))
  {
    GNUNET_break (0);
    return;
  }

  GNUNET_assert (GNUNET_OK ==
    GNUNET_CONTAINER_multipeermap_put (routing_table,
                                           dest, new_routing_entry,
                                           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
}
Beispiel #2
0
/**
 * Check if the given peer is currently connected. This function is for special
 * cirumstances (GNUNET_TESTBED uses it), normal users of the CORE API are
 * expected to track which peers are connected based on the connect/disconnect
 * callbacks from GNUNET_CORE_connect.  This function is NOT part of the
 * 'versioned', 'official' API. The difference between this function and the
 * function GNUNET_CORE_is_peer_connected() is that this one returns
 * synchronously after looking in the CORE API cache. The function
 * GNUNET_CORE_is_peer_connected() sends a message to the CORE service and hence
 * its response is given asynchronously.
 *
 * @param h the core handle
 * @param pid the identity of the peer to check if it has been connected to us
 * @return GNUNET_YES if the peer is connected to us; GNUNET_NO if not
 */
int
GNUNET_CORE_is_peer_connected_sync (const struct GNUNET_CORE_Handle *h,
                                    const struct GNUNET_PeerIdentity *pid)
{
  GNUNET_assert (NULL != h);
  GNUNET_assert (NULL != pid);
  return GNUNET_CONTAINER_multipeermap_contains (h->peers, pid);
}
/**
 * Is the given peer in the list of peers for which we
 * have an address request?
 *
 * @param cls unused, NULL
 * @param peer peer to query for
 * @return #GNUNET_YES if so, #GNUNET_NO if not
 */
unsigned int
GAS_connectivity_has_peer (void *cls,
                           const struct GNUNET_PeerIdentity *peer)
{
  if (NULL == connection_requests)
    return 0;
  /* TODO: return sum of 'strength's of connectivity requests */
  return GNUNET_CONTAINER_multipeermap_contains (connection_requests,
                                                 peer);
}
/**
 * Method called whenever a given peer connects.
 *
 * @param cls closure
 * @param peer peer identity this notification is about
 */
static void
core_connect_handler (void *cls,
		      const struct GNUNET_PeerIdentity *peer)
{
  if (GNUNET_YES == is_me(peer))
    return;

  GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connected to peer %s\n"),
	      GNUNET_i2s (peer));

  if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (nodes_requested, peer))
    return; /* We already sent a request */

  if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (nodes_active, peer))
    return; /* This peer is known as active  */

  if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (nodes_inactive, peer))
    return; /* This peer is known as inactive  */

  send_experimentation_request (peer);
}
/**
 * @brief Get the #PeerContext associated with a peer
 *
 * @param peer the peer id
 *
 * @return the #PeerContext
 */
static struct PeerContext *
get_peer_ctx (const struct GNUNET_PeerIdentity *peer)
{
  struct PeerContext *ctx;
  int ret;

  ret = GNUNET_CONTAINER_multipeermap_contains (peer_map, peer);
  GNUNET_assert (GNUNET_YES == ret);
  ctx = GNUNET_CONTAINER_multipeermap_get (peer_map, peer);
  GNUNET_assert (NULL != ctx);
  return ctx;
}
/**
 * Function that decides if a connection is acceptable or not.
 *
 * @param cls closure
 * @param pid peer to approve or disapproave
 * @return GNUNET_OK if the connection is allowed, GNUNET_SYSERR if not
 */
static int
check_access (void *cls, const struct GNUNET_PeerIdentity * pid)
{
    int contains;

    GNUNET_assert (NULL != map);
    contains = GNUNET_CONTAINER_multipeermap_contains (map, pid);
    if (GNUNET_YES == contains)
    {
        DEBUG ("Permitting `%s'\n", GNUNET_i2s (pid));
        return GNUNET_OK;
    }
    DEBUG ("Not permitting `%s'\n", GNUNET_i2s (pid));
    return GNUNET_SYSERR;
}
Beispiel #7
0
/**
 * Clear the custom peer map
 *
 * @param c_peer_map the custom peer map to look in
 *
 * @return size of the map
 */
void
View_clear ()
{
    uint32_t i;
    uint32_t *index;

    for (i = 0; 0 < View_size (); i++)
    {   /* Need to free indices stored at peers */
        GNUNET_assert (GNUNET_YES ==
                       GNUNET_CONTAINER_multipeermap_contains (mpm, &array[i]));
        index = GNUNET_CONTAINER_multipeermap_get (mpm, &array[i]);
        GNUNET_assert (NULL != index);
        GNUNET_free (index);
        GNUNET_CONTAINER_multipeermap_remove_all (mpm, &array[i]);
    }
    GNUNET_assert (0 == View_size ());
}
/**
 * Remove experimentation request due to timeout
 *
 * @param cls the related node
 * @param tc scheduler's task context
 */
static void
remove_request (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
  struct Node *n = cls;

  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
	      "Removing request for peer %s due to timeout\n",
	      GNUNET_i2s (&n->id));
  if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (nodes_requested, &n->id))
  {
    GNUNET_break (0 == GNUNET_CONTAINER_multipeermap_remove (nodes_requested, &n->id, n));
    update_stats (nodes_requested);
    GNUNET_CONTAINER_multipeermap_put (nodes_inactive, &n->id, n,
				       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
    update_stats (nodes_inactive);
  }
  n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
}
/**
 * @brief Check if peer is removable.
 *
 * Check if
 *  - a recv channel exists
 *  - there are pending messages
 *  - there is no pending pull reply
 *
 * @param peer the peer in question
 * @return #GNUNET_YES    if peer is removable
 *         #GNUNET_NO     if peer is NOT removable
 *         #GNUNET_SYSERR if peer is not known
 */
int
Peers_check_removable (const struct GNUNET_PeerIdentity *peer)
{
  struct PeerContext *peer_ctx;

  if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (peer_map, peer))
  {
    return GNUNET_SYSERR;
  }

  peer_ctx = get_peer_ctx (peer);
  if ( (NULL != peer_ctx->recv_channel) ||
       (NULL != peer_ctx->pending_messages_head) ||
       (GNUNET_NO == check_peer_flag_set (peer_ctx, Peers_PULL_REPLY_PENDING)) )
  {
    return GNUNET_NO;
  }
  return GNUNET_YES;
}
/**
 * Send a message to all of our current clients that have the right
 * options set.
 *
 * @param partner origin (or destination) of the message (used to check that this peer is
 *        known to be connected to the respective client)
 * @param msg message to multicast
 * @param can_drop can this message be discarded if the queue is too long
 * @param options mask to use
 * @param type type of the embedded message, 0 for none
 */
static void
send_to_all_clients (const struct GNUNET_PeerIdentity *partner,
                     const struct GNUNET_MessageHeader *msg,
                     int can_drop,
                     uint32_t options,
                     uint16_t type)
{
  struct GSC_Client *c;
  int tm;

  for (c = client_head; NULL != c; c = c->next)
  {
    tm = type_match (type, c);
    if (!  ( (0 != (c->options & options)) ||
	     ( (0 != (options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) &&
	       (GNUNET_YES == tm) ) ) )
      continue;  /* neither options nor type match permit the message */
    if ( (0 != (options & GNUNET_CORE_OPTION_SEND_HDR_INBOUND)) &&
	 ( (0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ||
	   (GNUNET_YES == tm) ) )
      continue;
    if ( (0 != (options & GNUNET_CORE_OPTION_SEND_HDR_OUTBOUND)) &&
	 (0 != (c->options & GNUNET_CORE_OPTION_SEND_FULL_OUTBOUND)) )
      continue;
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                "Sending %u message with %u bytes to client interested in messages of type %u.\n",
		options,
		ntohs (msg->size),
                (unsigned int) type);
    GNUNET_assert ( (0 == (c->options & GNUNET_CORE_OPTION_SEND_FULL_INBOUND)) ||
		    (GNUNET_YES != tm) ||
		    (GNUNET_YES ==
		     GNUNET_CONTAINER_multipeermap_contains (c->connectmap,
							     partner)) );
    send_to_client (c, msg, can_drop);
  }
}
Beispiel #11
0
/**
 * Search callback function called when a subscribed peer is found.
 *
 * @param cls closure provided in GNUNET_REGEX_search()
 * @param id peer providing a regex that matches the string
 * @param get_path path of the get request
 * @param get_path_length length of @a get_path
 * @param put_path Path of the put request
 * @param put_path_length length of the @a put_path
 */
static void
subscribed_peer_found (void *cls, const struct GNUNET_PeerIdentity *id,
                       const struct GNUNET_PeerIdentity *get_path,
                       unsigned int get_path_length,
                       const struct GNUNET_PeerIdentity *put_path,
                       unsigned int put_path_length)
{
  struct PendingMessage *pm;
  struct RemoteSubscriberInfo *subscriber;
  struct GNUNET_MessageHeader *msg;
  struct RegexSearchContext *context = cls;
  size_t msg_len = ntohs (context->publish_msg->header.size);

  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "--------> Found an active subscription from %s\n",
	     GNUNET_i2s (id));

  /*
   * We may have delivered the message to the peer already if it has
   * other matching subscriptions; ignore this search result if that is
   * the case.
   */
  if (GNUNET_CONTAINER_multipeermap_contains (context->subscribers,
                                              id))
    return;

  GNUNET_CONTAINER_multipeermap_put (context->subscribers, id,
    NULL, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
  subscriber = GNUNET_CONTAINER_multipeermap_get (remote_subscribers,
                                                  id);

  if (0 == memcmp (id, &my_id, sizeof (struct GNUNET_PeerIdentity)))
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
         "fast tracking PUBLISH message to local subscribers\n");

    deliver_incoming_publish (context->publish_msg, context);
    return;
  }

  msg = GNUNET_malloc (msg_len);
  memcpy (msg, context->publish_msg, msg_len);

  pm = GNUNET_new (struct PendingMessage);
  pm->msg = msg;
  pm->context = context;

  if (NULL == subscriber)
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
                "creating a new channel to %s\n", GNUNET_i2s(id));

    subscriber = GNUNET_new (struct RemoteSubscriberInfo);

    subscriber->channel = GNUNET_MESH_channel_create (mesh_handle,
                                                      NULL,
                                                      id,
                                                      GNUNET_APPLICATION_TYPE_MQTT,
                                                      GNUNET_MESH_OPTION_RELIABLE);
    subscriber->peer_added = GNUNET_NO;
    subscriber->peer_connecting = GNUNET_NO;

    subscriber->id = *id;

    GNUNET_CONTAINER_multipeermap_put (remote_subscribers, id,
      subscriber, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
  }
Beispiel #12
0
/**
 * Check whether view contains a peer
 *
 * @param peer the peer to check for
 *
 * @return GNUNET_OK if view contains peer
 *         GNUNET_NO otherwise
 */
int
View_contains_peer (const struct GNUNET_PeerIdentity *peer)
{
    return GNUNET_CONTAINER_multipeermap_contains (mpm, peer);
}
/**
 * Handle #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST message.
 *
 * @param cls unused
 * @param client new client that sent a #GNUNET_MESSAGE_TYPE_CORE_SEND_REQUEST
 * @param message the `struct SendMessageRequest` (presumably)
 */
static void
handle_client_send_request (void *cls,
                            struct GNUNET_SERVER_Client *client,
                            const struct GNUNET_MessageHeader *message)
{
  const struct SendMessageRequest *req;
  struct GSC_Client *c;
  struct GSC_ClientActiveRequest *car;
  int is_loopback;

  req = (const struct SendMessageRequest *) message;
  c = find_client (client);
  if (NULL == c)
  {
    /* client did not send INIT first! */
    GNUNET_break (0);
    GNUNET_SERVER_receive_done (client,
                                GNUNET_SYSERR);
    return;
  }
  if (NULL == c->requests)
    c->requests = GNUNET_CONTAINER_multipeermap_create (16,
                                                        GNUNET_NO);
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Client asked for transmission to `%s'\n",
              GNUNET_i2s (&req->peer));
  is_loopback =
      (0 ==
       memcmp (&req->peer,
               &GSC_my_identity,
               sizeof (struct GNUNET_PeerIdentity)));
  if ((! is_loopback) &&
      (GNUNET_YES !=
       GNUNET_CONTAINER_multipeermap_contains (c->connectmap,
                                               &req->peer)))
  {
    /* neighbour must have disconnected since request was issued,
     * ignore (client will realize it once it processes the
     * disconnect notification) */
    GNUNET_STATISTICS_update (GSC_stats,
                              gettext_noop
                              ("# send requests dropped (disconnected)"), 1,
                              GNUNET_NO);
    GNUNET_SERVER_receive_done (client,
                                GNUNET_OK);
    return;
  }

  car = GNUNET_CONTAINER_multipeermap_get (c->requests,
                                           &req->peer);
  if (NULL == car)
  {
    /* create new entry */
    car = GNUNET_new (struct GSC_ClientActiveRequest);
    GNUNET_assert (GNUNET_OK ==
                   GNUNET_CONTAINER_multipeermap_put (c->requests,
                                                      &req->peer,
                                                      car,
                                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
    car->client_handle = c;
  }
/**
 * @brief Remove peer
 *
 * @param peer the peer to clean
 * @return #GNUNET_YES if peer was removed
 *         #GNUNET_NO  otherwise
 */
int
Peers_remove_peer (const struct GNUNET_PeerIdentity *peer)
{
  struct PeerContext *peer_ctx;

  if (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (peer_map, peer))
  {
    return GNUNET_NO;
  }

  peer_ctx = get_peer_ctx (peer);
  set_peer_flag (peer_ctx, Peers_TO_DESTROY);
  LOG (GNUNET_ERROR_TYPE_DEBUG,
       "Going to remove peer %s\n",
       GNUNET_i2s (&peer_ctx->peer_id));
  Peers_unset_peer_flag (peer, Peers_ONLINE);

  GNUNET_array_grow (peer_ctx->pending_ops, peer_ctx->num_pending_ops, 0);
  while (NULL != peer_ctx->pending_messages_head)
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Removing unsent %s\n",
        peer_ctx->pending_messages_head->type);
    remove_pending_message (peer_ctx->pending_messages_head);
  }
  /* If we are still waiting for notification whether this peer is live
   * cancel the according task */
  if (NULL != peer_ctx->liveliness_check_pending)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
         "Removing pending liveliness check for peer %s\n",
         GNUNET_i2s (&peer_ctx->peer_id));
    // TODO wait until cadet sets mq->cancel_impl
    //GNUNET_MQ_send_cancel (peer_ctx->liveliness_check_pending->ev);
    GNUNET_free (peer_ctx->liveliness_check_pending);
    peer_ctx->liveliness_check_pending = NULL;
  }
  if (NULL != peer_ctx->send_channel)
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Destroying send channel\n");
    GNUNET_CADET_channel_destroy (peer_ctx->send_channel);
    peer_ctx->send_channel = NULL;
  }
  if (NULL != peer_ctx->recv_channel)
  {
    LOG (GNUNET_ERROR_TYPE_DEBUG,
        "Destroying recv channel\n");
    GNUNET_CADET_channel_destroy (peer_ctx->recv_channel);
    peer_ctx->recv_channel = NULL;
  }
  if (NULL != peer_ctx->mq)
  {
    GNUNET_MQ_destroy (peer_ctx->mq);
    peer_ctx->mq = NULL;
  }

  GNUNET_free (peer_ctx->send_channel_flags);
  GNUNET_free (peer_ctx->recv_channel_flags);

  if (GNUNET_YES != GNUNET_CONTAINER_multipeermap_remove_all (peer_map, &peer_ctx->peer_id))
  {
    LOG (GNUNET_ERROR_TYPE_WARNING, "removing peer from peer_map failed\n");
  }
  GNUNET_free (peer_ctx);
  return GNUNET_YES;
}
/**
 * Signature of a function that is called with QoS information about an address.
 *
 * @param cls closure
 * @param address the address
 * @param address_active is this address actively used to maintain a connection
 * 				to a peer
 * @param bandwidth_out assigned outbound bandwidth for the connection
 * @param bandwidth_in assigned inbound bandwidth for the connection
 * @param ats performance data for the address (as far as known)
 * @param ats_count number of performance records in 'ats'
 */
static void
addr_info_cb (void *cls,
              const struct GNUNET_HELLO_Address *address,
              int address_active,
              struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
              struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
              const struct GNUNET_ATS_Information *ats,
              uint32_t ats_count)
{
  static const char *query_insert =
      "INSERT INTO ats_info("
      " id,"
      " val,"
      " timestamp"
      ") VALUES ("
      " ?1,"
      " ?2,"
      " datetime('now')"
      ");";
  struct Entry *entry;
  int latency;
  unsigned int cnt;

  GNUNET_assert (NULL != db);
  if (GNUNET_NO == address_active)
    return;
  for (cnt = 0; cnt < ats_count; cnt++)
  {
    if (GNUNET_ATS_QUALITY_NET_DELAY == ntohl (ats[cnt].type))
      goto insert;
  }
  return;

 insert:
  latency = (int) ntohl (ats[cnt].value);
  entry = NULL;
  if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (map,
                                                            &address->peer))
  {
    entry = GNUNET_CONTAINER_multipeermap_get (map, &address->peer);
    GNUNET_assert (NULL != entry);
    if (latency == entry->latency)
      return;
  }
  if (NULL == stmt_insert)
  {
    if (SQLITE_OK != sqlite3_prepare_v2 (db, query_insert, -1, &stmt_insert,
                                         NULL))
    {
      LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_prepare_v2");
      goto err_shutdown;
    }
  }
  if ( (SQLITE_OK != sqlite3_bind_text (stmt_insert, 1,
                                        GNUNET_i2s (&address->peer), -1,
                                        SQLITE_STATIC)) ||
        (SQLITE_OK != sqlite3_bind_int (stmt_insert, 2, latency)) )
  {
     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_bind_text");
     goto err_shutdown;
  }
  if (SQLITE_DONE != sqlite3_step (stmt_insert))
  {
    LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_step");
    goto err_shutdown;
  }
  if (SQLITE_OK != sqlite3_reset (stmt_insert))
  {
    LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_insert");
    goto err_shutdown;
  }
  if (NULL == entry)
  {
    entry = GNUNET_new (struct Entry);
    entry->id = address->peer;
    GNUNET_CONTAINER_multipeermap_put (map, 
                                       &entry->id, entry,
                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
  }
/**
 * @brief Check whether @a peer is actually a peer.
 *
 * A valid peer is a peer that we know exists eg. we were connected to once.
 *
 * @param peer peer in question
 *
 * @return #GNUNET_YES if peer is valid
 *         #GNUNET_NO  if peer is not valid
 */
int
Peers_check_peer_valid (const struct GNUNET_PeerIdentity *peer)
{
  return GNUNET_CONTAINER_multipeermap_contains (valid_peers, peer);
}
/**
 * @brief Check whether we have information about the given peer.
 *
 * FIXME probably deprecated. Make this the new _online.
 *
 * @param peer peer in question
 *
 * @return #GNUNET_YES if peer is known
 *         #GNUNET_NO  if peer is not knwon
 */
int
Peers_check_peer_known (const struct GNUNET_PeerIdentity *peer)
{
  return GNUNET_CONTAINER_multipeermap_contains (peer_map, peer);
}
static int
testMap (int i)
{
  struct GNUNET_CONTAINER_MultiPeerMap *m;
  struct GNUNET_PeerIdentity k1;
  struct GNUNET_PeerIdentity k2;
  struct GNUNET_CONTAINER_MultiPeerMapIterator *iter;
  struct GNUNET_PeerIdentity key_ret;
  const char *ret;
  int j;

  CHECK (NULL != (m = GNUNET_CONTAINER_multipeermap_create (i, GNUNET_NO)));
  memset (&k1, 0, sizeof (k1));
  memset (&k2, 1, sizeof (k2));
  CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (m, &k1));
  CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (m, &k2));
  CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_remove (m, &k1, NULL));
  CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_remove (m, &k2, NULL));
  CHECK (NULL == GNUNET_CONTAINER_multipeermap_get (m, &k1));
  CHECK (NULL == GNUNET_CONTAINER_multipeermap_get (m, &k2));
  CHECK (0 == GNUNET_CONTAINER_multipeermap_remove_all (m, &k1));
  CHECK (0 == GNUNET_CONTAINER_multipeermap_size (m));
  CHECK (0 == GNUNET_CONTAINER_multipeermap_iterate (m, NULL, NULL));
  CHECK (0 == GNUNET_CONTAINER_multipeermap_get_multiple (m, &k1, NULL, NULL));

  CHECK (GNUNET_OK ==
         GNUNET_CONTAINER_multipeermap_put (m, &k1, "v1",
                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
  CHECK (1 == GNUNET_CONTAINER_multipeermap_size (m));
  ret = GNUNET_CONTAINER_multipeermap_get (m, &k1);
  GNUNET_assert (ret != NULL);
  CHECK (0 == strcmp ("v1", ret));
  CHECK (GNUNET_NO ==
         GNUNET_CONTAINER_multipeermap_put (m, &k1, "v1",
                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE));
  CHECK (1 == GNUNET_CONTAINER_multipeermap_size (m));
  CHECK (GNUNET_OK ==
         GNUNET_CONTAINER_multipeermap_put (m, &k1, "v2",
                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
  CHECK (GNUNET_OK ==
         GNUNET_CONTAINER_multipeermap_put (m, &k1, "v3",
                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
  CHECK (3 == GNUNET_CONTAINER_multipeermap_size (m));
  CHECK (GNUNET_OK == GNUNET_CONTAINER_multipeermap_remove (m, &k1, "v3"));
  CHECK (2 == GNUNET_CONTAINER_multipeermap_size (m));
  CHECK (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (m, &k1));
  CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_contains (m, &k2));
  CHECK (2 == GNUNET_CONTAINER_multipeermap_get_multiple (m, &k1, NULL, NULL));
  CHECK (0 == GNUNET_CONTAINER_multipeermap_get_multiple (m, &k2, NULL, NULL));
  CHECK (2 == GNUNET_CONTAINER_multipeermap_iterate (m, NULL, NULL));
  iter = GNUNET_CONTAINER_multipeermap_iterator_create (m);
  CHECK (GNUNET_YES == GNUNET_CONTAINER_multipeermap_iterator_next (iter, &key_ret, (const void **)&ret));
  CHECK (0 == memcmp (&key_ret, &k1, sizeof (key_ret)));
  CHECK (GNUNET_YES == GNUNET_CONTAINER_multipeermap_iterator_next (iter, &key_ret, (const void **)&ret));
  CHECK (0 == memcmp (&key_ret, &k1, sizeof (key_ret)));
  CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_iterator_next (iter, NULL, NULL));
  GNUNET_free (iter);

  CHECK (2 == GNUNET_CONTAINER_multipeermap_remove_all (m, &k1));
  for (j = 0; j < 1024; j++)
    CHECK (GNUNET_OK ==
           GNUNET_CONTAINER_multipeermap_put (m, &k1, "v2",
                                              GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
  iter = GNUNET_CONTAINER_multipeermap_iterator_create (m);
  for (j = 0; j < GNUNET_CONTAINER_multipeermap_size (m); j++)
    CHECK (GNUNET_YES == GNUNET_CONTAINER_multipeermap_iterator_next (iter, NULL, NULL));
  CHECK (GNUNET_NO == GNUNET_CONTAINER_multipeermap_iterator_next (iter, NULL, NULL));
  GNUNET_free (iter);

  GNUNET_CONTAINER_multipeermap_destroy (m);
  return 0;
}
/**
 * Signature of a function that is called with QoS information about an address.
 *
 * @param cls closure
 * @param address the address
 * @param address_active #GNUNET_YES if this address is actively used
 *        to maintain a connection to a peer;
 *        #GNUNET_NO if the address is not actively used;
 *        #GNUNET_SYSERR if this address is no longer available for ATS
 * @param bandwidth_out assigned outbound bandwidth for the connection
 * @param bandwidth_in assigned inbound bandwidth for the connection
 * @param prop performance data for the address (as far as known)
 */
static void
addr_info_cb (void *cls,
              const struct GNUNET_HELLO_Address *address,
              int address_active,
              struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
              struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in,
              const struct GNUNET_ATS_Properties *prop)
{
  static const char *query_insert =
      "INSERT INTO ats_info("
      " id,"
      " val,"
      " timestamp"
      ") VALUES ("
      " ?1,"
      " ?2,"
      " datetime('now')"
      ");";
  struct Entry *entry;
  int latency; /* FIXME: type!? */

  if (NULL == address)
  {
    /* ATS service temporarily disconnected */
    return;
  }

  GNUNET_assert (NULL != db);
  if (GNUNET_YES != address_active)
    return;
  latency = (int) prop->delay.rel_value_us;
  entry = NULL;
  if (GNUNET_YES == GNUNET_CONTAINER_multipeermap_contains (map,
                                                            &address->peer))
  {
    entry = GNUNET_CONTAINER_multipeermap_get (map, &address->peer);
    GNUNET_assert (NULL != entry);
    if (latency == entry->latency)
      return;
  }
  if (NULL == stmt_insert)
  {
    if (SQLITE_OK != sqlite3_prepare_v2 (db, query_insert, -1, &stmt_insert,
                                         NULL))
    {
      LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_prepare_v2");
      goto err_shutdown;
    }
  }
  if ( (SQLITE_OK != sqlite3_bind_text (stmt_insert, 1,
                                        GNUNET_i2s (&address->peer), -1,
                                        SQLITE_STATIC)) ||
        (SQLITE_OK != sqlite3_bind_int (stmt_insert, 2, latency)) )
  {
     LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_bind_text");
     goto err_shutdown;
  }
  if (SQLITE_DONE != sqlite3_step (stmt_insert))
  {
    LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_step");
    goto err_shutdown;
  }
  if (SQLITE_OK != sqlite3_reset (stmt_insert))
  {
    LOG_SQLITE (db, NULL, GNUNET_ERROR_TYPE_ERROR, "sqlite3_insert");
    goto err_shutdown;
  }
  if (NULL == entry)
  {
    entry = GNUNET_new (struct Entry);
    entry->id = address->peer;
    GNUNET_CONTAINER_multipeermap_put (map,
                                       &entry->id, entry,
                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
  }