/** * Route the given request via the DHT. This includes updating * the bloom filter and retransmission times, building the P2P * message and initiating the routing operation. */ static void transmit_request (struct ClientQueryRecord *cqr) { int32_t reply_bf_mutator; struct GNUNET_CONTAINER_BloomFilter *reply_bf; struct GNUNET_CONTAINER_BloomFilter *peer_bf; GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# GET requests from clients injected"), 1, GNUNET_NO); reply_bf_mutator = (int32_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX); reply_bf = GNUNET_BLOCK_construct_bloomfilter (reply_bf_mutator, cqr->seen_replies, cqr->seen_replies_count); peer_bf = GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, GNUNET_CONSTANTS_BLOOMFILTER_K); LOG (GNUNET_ERROR_TYPE_DEBUG, "Initiating GET for %s, replication %u, already have %u replies\n", GNUNET_h2s(&cqr->key), cqr->replication, cqr->seen_replies_count); GDS_NEIGHBOURS_handle_get (cqr->type, cqr->msg_options, cqr->replication, 0 /* hop count */ , &cqr->key, cqr->xquery, cqr->xquery_size, reply_bf, reply_bf_mutator, peer_bf); GNUNET_CONTAINER_bloomfilter_free (reply_bf); GNUNET_CONTAINER_bloomfilter_free (peer_bf); /* exponential back-off for retries. * max GNUNET_TIME_STD_EXPONENTIAL_BACKOFF_THRESHOLD (15 min) */ cqr->retry_frequency = GNUNET_TIME_STD_BACKOFF (cqr->retry_frequency); cqr->retry_time = GNUNET_TIME_relative_to_absolute (cqr->retry_frequency); }
/** * Try to combine multiple recent requests for the same value * (if they come from the same peer). * * @param cls the new 'struct RecentRequest' (to discard upon successful combination) * @param key the query * @param value the existing 'struct RecentRequest' (to update upon successful combination) * @return GNUNET_OK (continue to iterate), * GNUNET_SYSERR if the request was successfully combined */ static int try_combine_recent (void *cls, const struct GNUNET_HashCode * key, void *value) { struct RecentRequest *in = cls; struct RecentRequest *rr = value; if ( (0 != memcmp (&in->peer, &rr->peer, sizeof (struct GNUNET_PeerIdentity))) || (in->type != rr->type) || (in->xquery_size != rr->xquery_size) || (0 != memcmp (in->xquery, rr->xquery, in->xquery_size)) ) return GNUNET_OK; if (in->reply_bf_mutator != rr->reply_bf_mutator) { rr->reply_bf_mutator = in->reply_bf_mutator; GNUNET_CONTAINER_bloomfilter_free (rr->reply_bf); rr->reply_bf = in->reply_bf; } else { GNUNET_CONTAINER_bloomfilter_or2 (rr->reply_bf, in->reply_bf, GNUNET_CONTAINER_bloomfilter_get_size (in->reply_bf)); GNUNET_CONTAINER_bloomfilter_free (in->reply_bf); } GNUNET_free (in); return GNUNET_SYSERR; }
/** * Find a peer that would be reasonable for advertising. * * @param cls closure * @param pid identity of a peer * @param value 'struct Peer*' for the peer we are considering * @return GNUNET_YES (continue iteration) */ static int find_advertisable_hello (void *cls, const struct GNUNET_HashCode * pid, void *value) { struct FindAdvHelloContext *fah = cls; struct Peer *pos = value; struct GNUNET_TIME_Relative rst_time; size_t hs; if (pos == fah->peer) return GNUNET_YES; if (pos->hello == NULL) return GNUNET_YES; rst_time = GNUNET_TIME_absolute_get_remaining (pos->filter_expiration); if (0 == rst_time.rel_value) { /* time to discard... */ GNUNET_CONTAINER_bloomfilter_free (pos->filter); setup_filter (pos); } fah->next_adv = GNUNET_TIME_relative_min (rst_time, fah->next_adv); hs = GNUNET_HELLO_size (pos->hello); if (hs > fah->max_size) return GNUNET_YES; if (GNUNET_NO == GNUNET_CONTAINER_bloomfilter_test (pos->filter, &fah->peer->pid.hashPubKey)) fah->result = pos; return GNUNET_YES; }
/** * PEERINFO calls this function to let us know about a possible peer * that we might want to connect to. * * @param cls closure (not used) * @param peer potential peer to connect to * @param hello HELLO for this peer (or NULL) * @param err_msg NULL if successful, otherwise contains error message */ static void process_peer (void *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Message *hello, const char *err_msg) { struct Peer *pos; if (err_msg != NULL) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Error in communication with PEERINFO service: %s\n"), err_msg); GNUNET_PEERINFO_notify_cancel (peerinfo_notify); peerinfo_notify = GNUNET_PEERINFO_notify (cfg, &process_peer, NULL); return; } GNUNET_assert (peer != NULL); if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity))) return; /* that's me! */ if (hello == NULL) { /* free existing HELLO, if any */ pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey); if (NULL != pos) { GNUNET_free_non_null (pos->hello); pos->hello = NULL; if (pos->filter != NULL) { GNUNET_CONTAINER_bloomfilter_free (pos->filter); pos->filter = NULL; } if ((GNUNET_NO == pos->is_connected) && (GNUNET_NO == pos->is_friend) && (0 == GNUNET_TIME_absolute_get_remaining (pos-> greylisted_until).rel_value)) free_peer (NULL, &pos->pid.hashPubKey, pos); } return; } consider_for_advertising (hello); pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey); if (pos == NULL) pos = make_peer (peer, hello, GNUNET_NO); GNUNET_assert (NULL != pos); if (GNUNET_YES == pos->is_connected) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Already connected to peer `%s'\n", GNUNET_i2s (peer)); return; } if (GNUNET_TIME_absolute_get_remaining (pos->greylisted_until).rel_value > 0) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Already tried peer `%s' recently\n", GNUNET_i2s (peer)); return; /* peer still greylisted */ } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Considering connecting to peer `%s'\n", GNUNET_i2s (peer)); schedule_attempt_connect (pos); }
/** * Free all resources associated with the given peer. * * @param cls closure (not used) * @param pid identity of the peer * @param value peer to free * @return #GNUNET_YES (always: continue to iterate) */ static int free_peer (void *cls, const struct GNUNET_PeerIdentity * pid, void *value) { struct Peer *pos = value; GNUNET_break (NULL == pos->mq); GNUNET_break (GNUNET_OK == GNUNET_CONTAINER_multipeermap_remove (peers, pid, pos)); if (NULL != pos->hello_delay_task) { GNUNET_SCHEDULER_cancel (pos->hello_delay_task); pos->hello_delay_task = NULL; } if (NULL != pos->sh) { GNUNET_ATS_connectivity_suggest_cancel (pos->sh); pos->sh = NULL; } if (NULL != pos->hello) { GNUNET_free_non_null (pos->hello); pos->hello = NULL; } if (NULL != pos->filter) { GNUNET_CONTAINER_bloomfilter_free (pos->filter); pos->filter = NULL; } GNUNET_free (pos); return GNUNET_YES; }
/** * PEERINFO calls this function to let us know about a possible peer * that we might want to connect to. * * @param cls closure (not used) * @param peer potential peer to connect to * @param hello HELLO for this peer (or NULL) * @param err_msg NULL if successful, otherwise contains error message */ static void process_peer (void *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Message *hello, const char *err_msg) { struct Peer *pos; if (NULL != err_msg) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Error in communication with PEERINFO service: %s\n"), err_msg); GNUNET_PEERINFO_notify_cancel (peerinfo_notify); peerinfo_notify = GNUNET_PEERINFO_notify (cfg, GNUNET_NO, &process_peer, NULL); return; } GNUNET_assert (NULL != peer); if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity))) return; /* that's me! */ if (NULL == hello) { /* free existing HELLO, if any */ pos = GNUNET_CONTAINER_multipeermap_get (peers, peer); if (NULL != pos) { GNUNET_free_non_null (pos->hello); pos->hello = NULL; if (NULL != pos->filter) { GNUNET_CONTAINER_bloomfilter_free (pos->filter); pos->filter = NULL; } if ( (NULL == pos->mq) && (GNUNET_NO == pos->is_friend) ) free_peer (NULL, &pos->pid, pos); } return; } consider_for_advertising (hello); pos = GNUNET_CONTAINER_multipeermap_get (peers, peer); if (NULL == pos) pos = make_peer (peer, hello, GNUNET_NO); attempt_connect (pos); }
/** * We've gotten a HELLO from another peer. Consider it for * advertising. * * @param hello the HELLO we got */ static void consider_for_advertising (const struct GNUNET_HELLO_Message *hello) { int have_address; struct GNUNET_PeerIdentity pid; struct GNUNET_TIME_Absolute dt; struct GNUNET_HELLO_Message *nh; struct Peer *peer; uint16_t size; if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid)) { GNUNET_break (0); return; } if (0 == memcmp (&pid, &my_identity, sizeof (struct GNUNET_PeerIdentity))) return; /* that's me! */ have_address = GNUNET_NO; GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &address_iterator, &have_address); if (GNUNET_NO == have_address) return; /* no point in advertising this one... */ peer = GNUNET_CONTAINER_multihashmap_get (peers, &pid.hashPubKey); if (NULL == peer) { peer = make_peer (&pid, hello, GNUNET_NO); } else if (peer->hello != NULL) { dt = GNUNET_HELLO_equals (peer->hello, hello, GNUNET_TIME_absolute_get ()); if (dt.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value) return; /* nothing new here */ } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found `%s' from peer `%s' for advertising\n", "HELLO", GNUNET_i2s (&pid)); if (peer->hello != NULL) { nh = GNUNET_HELLO_merge (peer->hello, hello); GNUNET_free (peer->hello); peer->hello = nh; } else { size = GNUNET_HELLO_size (hello); peer->hello = GNUNET_malloc (size); memcpy (peer->hello, hello, size); } if (peer->filter != NULL) GNUNET_CONTAINER_bloomfilter_free (peer->filter); setup_filter (peer); /* since we have a new HELLO to pick from, re-schedule all * HELLO requests that are not bound by the HELLO send rate! */ GNUNET_CONTAINER_multihashmap_iterate (peers, &reschedule_hellos, peer); }
/** * Remove the oldest entry from the DHT routing table. Must only * be called if it is known that there is at least one entry * in the heap and hashmap. */ static void expire_oldest_entry () { struct RecentRequest *recent_req; GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# Entries removed from routing table"), 1, GNUNET_NO); recent_req = GNUNET_CONTAINER_heap_peek (recent_heap); GNUNET_assert (recent_req != NULL); GNUNET_CONTAINER_heap_remove_node (recent_req->heap_node); GNUNET_CONTAINER_bloomfilter_free (recent_req->reply_bf); GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (recent_map, &recent_req->key, recent_req)); GNUNET_free (recent_req); }
/** * Destroy a data cache (and free associated resources). * * @param h handle to the datastore */ void GNUNET_DATACACHE_destroy (struct GNUNET_DATACACHE_Handle *h) { if (NULL != h->filter) GNUNET_CONTAINER_bloomfilter_free (h->filter); if (h->api != NULL) GNUNET_break (NULL == GNUNET_PLUGIN_unload (h->lib_name, h->api)); GNUNET_free (h->lib_name); GNUNET_free (h->short_name); GNUNET_free (h->section); if (h->bloom_name != NULL) { if (0 != UNLINK (h->bloom_name)) GNUNET_log_from_strerror_file (GNUNET_ERROR_TYPE_WARNING, "datacache", "unlink", h->bloom_name); GNUNET_free (h->bloom_name); } GNUNET_STATISTICS_destroy (h->stats, GNUNET_NO); GNUNET_free (h); }
/** * Free all resources associated with the given peer. * * @param cls closure (not used) * @param pid identity of the peer * @param value peer to free * @return GNUNET_YES (always: continue to iterate) */ static int free_peer (void *cls, const struct GNUNET_HashCode * pid, void *value) { struct Peer *pos = value; GNUNET_break (GNUNET_NO == pos->is_connected); GNUNET_break (GNUNET_OK == GNUNET_CONTAINER_multihashmap_remove (peers, pid, pos)); if (pos->hello_req != NULL) GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req); if (pos->hello_delay_task != GNUNET_SCHEDULER_NO_TASK) GNUNET_SCHEDULER_cancel (pos->hello_delay_task); if (pos->attempt_connect_task != GNUNET_SCHEDULER_NO_TASK) GNUNET_SCHEDULER_cancel (pos->attempt_connect_task); if (pos->greylist_clean_task != GNUNET_SCHEDULER_NO_TASK) GNUNET_SCHEDULER_cancel (pos->greylist_clean_task); GNUNET_free_non_null (pos->hello); if (pos->filter != NULL) GNUNET_CONTAINER_bloomfilter_free (pos->filter); GNUNET_free (pos); return GNUNET_YES; }
/** * Handler for PUT messages. * * @param cls closure for the service * @param client the client we received this message from * @param message the actual message received */ static void handle_dht_local_put (void *cls, struct GNUNET_SERVER_Client *client, const struct GNUNET_MessageHeader *message) { const struct GNUNET_DHT_ClientPutMessage *dht_msg; struct GNUNET_CONTAINER_BloomFilter *peer_bf; uint16_t size; struct PendingMessage *pm; struct GNUNET_DHT_ClientPutConfirmationMessage *conf; size = ntohs (message->size); if (size < sizeof (struct GNUNET_DHT_ClientPutMessage)) { GNUNET_break (0); GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } GNUNET_STATISTICS_update (GDS_stats, gettext_noop ("# PUT requests received from clients"), 1, GNUNET_NO); dht_msg = (const struct GNUNET_DHT_ClientPutMessage *) message; LOG_TRAFFIC (GNUNET_ERROR_TYPE_DEBUG, "XDHT CLIENT-PUT %s @ %u\n", GNUNET_h2s (&dht_msg->key), getpid ()); /* give to local clients */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Handling local PUT of %u-bytes for query %s\n", size - sizeof (struct GNUNET_DHT_ClientPutMessage), GNUNET_h2s (&dht_msg->key)); GDS_CLIENTS_handle_reply (GNUNET_TIME_absolute_ntoh (dht_msg->expiration), &dht_msg->key, 0, NULL, 0, NULL, ntohl (dht_msg->type), size - sizeof (struct GNUNET_DHT_ClientPutMessage), &dht_msg[1]); /* store locally */ GDS_DATACACHE_handle_put (GNUNET_TIME_absolute_ntoh (dht_msg->expiration), &dht_msg->key, 0, NULL, ntohl (dht_msg->type), size - sizeof (struct GNUNET_DHT_ClientPutMessage), &dht_msg[1]); /* route to other peers */ peer_bf = GNUNET_CONTAINER_bloomfilter_init (NULL, DHT_BLOOM_SIZE, GNUNET_CONSTANTS_BLOOMFILTER_K); GDS_NEIGHBOURS_handle_put (ntohl (dht_msg->type), ntohl (dht_msg->options), ntohl (dht_msg->desired_replication_level), GNUNET_TIME_absolute_ntoh (dht_msg->expiration), 0 /* hop count */ , peer_bf, &dht_msg->key, 0, NULL, &dht_msg[1], size - sizeof (struct GNUNET_DHT_ClientPutMessage)); GDS_CLIENTS_process_put (ntohl (dht_msg->options), ntohl (dht_msg->type), 0, ntohl (dht_msg->desired_replication_level), 1, GDS_NEIGHBOURS_get_id(), GNUNET_TIME_absolute_ntoh (dht_msg->expiration), &dht_msg->key, &dht_msg[1], size - sizeof (struct GNUNET_DHT_ClientPutMessage)); GNUNET_CONTAINER_bloomfilter_free (peer_bf); pm = GNUNET_malloc (sizeof (struct PendingMessage) + sizeof (struct GNUNET_DHT_ClientPutConfirmationMessage)); conf = (struct GNUNET_DHT_ClientPutConfirmationMessage *) &pm[1]; conf->header.size = htons (sizeof (struct GNUNET_DHT_ClientPutConfirmationMessage)); conf->header.type = htons (GNUNET_MESSAGE_TYPE_DHT_CLIENT_PUT_OK); conf->reserved = htonl (0); conf->unique_id = dht_msg->unique_id; pm->msg = &conf->header; add_pending_message (find_active_client (client), pm); GNUNET_SERVER_receive_done (client, GNUNET_OK); }
int main (int argc, char *argv[]) { struct GNUNET_CONTAINER_BloomFilter *bf; struct GNUNET_CONTAINER_BloomFilter *bfi; GNUNET_HashCode tmp; int i; int ok1; int ok2; int falseok; char buf[SIZE]; struct stat sbuf; GNUNET_log_setup ("test-container-bloomfilter", "WARNING", NULL); GNUNET_CRYPTO_seed_weak_random (1); if (0 == STAT (TESTFILE, &sbuf)) if (0 != UNLINK (TESTFILE)) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "unlink", TESTFILE); bf = GNUNET_CONTAINER_bloomfilter_load (TESTFILE, SIZE, K); for (i = 0; i < 200; i++) { nextHC (&tmp); GNUNET_CONTAINER_bloomfilter_add (bf, &tmp); } GNUNET_CRYPTO_seed_weak_random (1); ok1 = 0; for (i = 0; i < 200; i++) { nextHC (&tmp); if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES) ok1++; } if (ok1 != 200) { printf ("Got %d elements out of" "200 expected after insertion.\n", ok1); GNUNET_CONTAINER_bloomfilter_free (bf); return -1; } if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_get_raw_data (bf, buf, SIZE)) { GNUNET_CONTAINER_bloomfilter_free (bf); return -1; } GNUNET_CONTAINER_bloomfilter_free (bf); bf = GNUNET_CONTAINER_bloomfilter_load (TESTFILE, SIZE, K); GNUNET_assert (bf != NULL); bfi = GNUNET_CONTAINER_bloomfilter_init (buf, SIZE, K); GNUNET_assert (bfi != NULL); GNUNET_CRYPTO_seed_weak_random (1); ok1 = 0; ok2 = 0; for (i = 0; i < 200; i++) { nextHC (&tmp); if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES) ok1++; if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES) ok2++; } if (ok1 != 200) { printf ("Got %d elements out of 200 " "expected after reloading.\n", ok1); GNUNET_CONTAINER_bloomfilter_free (bf); GNUNET_CONTAINER_bloomfilter_free (bfi); return -1; } if (ok2 != 200) { printf ("Got %d elements out of 200 " "expected after initialization.\n", ok2); GNUNET_CONTAINER_bloomfilter_free (bf); GNUNET_CONTAINER_bloomfilter_free (bfi); return -1; } GNUNET_CRYPTO_seed_weak_random (1); for (i = 0; i < 100; i++) { nextHC (&tmp); GNUNET_CONTAINER_bloomfilter_remove (bf, &tmp); GNUNET_CONTAINER_bloomfilter_remove (bfi, &tmp); } GNUNET_CRYPTO_seed_weak_random (1); ok1 = 0; ok2 = 0; for (i = 0; i < 200; i++) { nextHC (&tmp); if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES) ok1++; if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES) ok2++; } if (ok1 != 100) { printf ("Expected 100 elements in loaded filter" " after adding 200 and deleting 100, got %d\n", ok1); GNUNET_CONTAINER_bloomfilter_free (bf); GNUNET_CONTAINER_bloomfilter_free (bfi); return -1; } if (ok2 != 200) { printf ("Expected 200 elements in initialized filter" " after adding 200 and deleting 100 " "(which should do nothing for a filter not backed by a file), got %d\n", ok2); GNUNET_CONTAINER_bloomfilter_free (bf); GNUNET_CONTAINER_bloomfilter_free (bfi); return -1; } GNUNET_CRYPTO_seed_weak_random (3); GNUNET_CONTAINER_bloomfilter_clear (bf); falseok = 0; for (i = 0; i < 1000; i++) { nextHC (&tmp); if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES) falseok++; } if (falseok > 0) { GNUNET_CONTAINER_bloomfilter_free (bf); GNUNET_CONTAINER_bloomfilter_free (bfi); return -1; } if (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_or (bf, buf, SIZE)) { GNUNET_CONTAINER_bloomfilter_free (bf); GNUNET_CONTAINER_bloomfilter_free (bfi); return -1; } GNUNET_CRYPTO_seed_weak_random (2); i = 20; GNUNET_CONTAINER_bloomfilter_resize (bfi, &add_iterator, &i, SIZE * 2, K); GNUNET_CRYPTO_seed_weak_random (2); i = 20; GNUNET_CONTAINER_bloomfilter_resize (bf, &add_iterator, &i, SIZE * 2, K); GNUNET_CRYPTO_seed_weak_random (2); ok1 = 0; ok2 = 0; for (i = 0; i < 20; i++) { nextHC (&tmp); if (GNUNET_CONTAINER_bloomfilter_test (bf, &tmp) == GNUNET_YES) ok1++; if (GNUNET_CONTAINER_bloomfilter_test (bfi, &tmp) == GNUNET_YES) ok2++; } if (ok1 != 20) { printf ("Expected 20 elements in resized file-backed filter" " after adding 20, got %d\n", ok1); GNUNET_CONTAINER_bloomfilter_free (bf); GNUNET_CONTAINER_bloomfilter_free (bfi); return -1; } if (ok2 != 20) { printf ("Expected 20 elements in resized filter" " after adding 20, got %d\n", ok2); GNUNET_CONTAINER_bloomfilter_free (bf); GNUNET_CONTAINER_bloomfilter_free (bfi); return -1; } GNUNET_CONTAINER_bloomfilter_free (bf); GNUNET_CONTAINER_bloomfilter_free (bfi); GNUNET_break (0 == UNLINK (TESTFILE)); return 0; }