/** * Try to read the HELLO in the given filename and discard expired addresses. * * @param fn name of the file * @return HELLO of the file, NULL on error */ static struct GNUNET_HELLO_Message * read_host_file (const char *fn) { char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN; const struct GNUNET_HELLO_Message *hello; struct GNUNET_HELLO_Message *hello_clean; int size; struct GNUNET_TIME_Absolute now; if (GNUNET_YES != GNUNET_DISK_file_test (fn)) return NULL; size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer)); hello = (const struct GNUNET_HELLO_Message *) buffer; if ((size < sizeof (struct GNUNET_MessageHeader)) || (size != ntohs ((((const struct GNUNET_MessageHeader *) hello)->size))) || (size != GNUNET_HELLO_size (hello))) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to parse HELLO in file `%s'\n"), fn); return NULL; } now = GNUNET_TIME_absolute_get (); hello_clean = GNUNET_HELLO_iterate_addresses (hello, GNUNET_YES, &discard_expired, &now); return hello_clean; }
/** * 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; }
/** * Check that a peerinfo information message is well-formed. * * @param cls closure * @param im message received * @return #GNUNET_OK if the message is well-formed */ static int check_notification (void *cls, const struct InfoMessage *im) { uint16_t ms = ntohs (im->header.size) - sizeof (*im); if (ms >= sizeof (struct GNUNET_MessageHeader)) { const struct GNUNET_HELLO_Message *hello; hello = (const struct GNUNET_HELLO_Message *) &im[1]; if (ms != GNUNET_HELLO_size (hello)) { GNUNET_break (0); return GNUNET_SYSERR; } return GNUNET_OK; } if (0 != ms) { GNUNET_break (0); return GNUNET_SYSERR; } return GNUNET_OK; /* odd... */ }
/** * Function to process paths received for a new peer addition. The recorded * paths form the initial tunnel, which can be optimized later. * Called on each result obtained for the DHT search. * * @param cls closure * @param exp when will this value expire * @param key key of the result * @param get_path path of the get request * @param get_path_length lenght of @a get_path * @param put_path path of the put request * @param put_path_length length of the @a put_path * @param type type of the result * @param size number of bytes in data * @param data pointer to the result data */ static void dht_get_id_handler (void *cls, struct GNUNET_TIME_Absolute exp, const struct GNUNET_HashCode *key, const struct GNUNET_PeerIdentity *get_path, unsigned int get_path_length, const struct GNUNET_PeerIdentity *put_path, unsigned int put_path_length, enum GNUNET_BLOCK_Type type, size_t size, const void *data) { const struct GNUNET_HELLO_Message *hello = data; struct CadetPeer *peer; GCPP_try_path_from_dht (get_path, get_path_length, put_path, put_path_length); if ( (size >= sizeof (struct GNUNET_HELLO_Message)) && (ntohs (hello->header.size) == size) && (size == GNUNET_HELLO_size (hello)) ) { peer = GCP_get (&put_path[0], GNUNET_YES); LOG (GNUNET_ERROR_TYPE_DEBUG, "Got HELLO for %s\n", GCP_2s (peer)); GCP_set_hello (peer, hello); } }
/** * Construct our HELLO message from all of the addresses of * all of the transports. * * @param cls unused * @param tc scheduler context */ static void refresh_hello_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GeneratorContext gc; int friend_only; hello_task = GNUNET_SCHEDULER_NO_TASK; gc.addr_pos = oal_head; gc.expiration = GNUNET_TIME_relative_to_absolute (hello_expiration); friend_only = GNUNET_HELLO_is_friend_only (our_hello); GNUNET_free (our_hello); our_hello = GNUNET_HELLO_create (&GST_my_public_key, &address_generator, &gc, friend_only); GNUNET_assert (NULL != our_hello); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Refreshed my %s `%s', new size is %d\n", (GNUNET_YES == GNUNET_HELLO_is_friend_only (our_hello)) ? "friend-only" : "public", "HELLO", GNUNET_HELLO_size (our_hello)); GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# refreshed my HELLO"), 1, GNUNET_NO); if (NULL != hello_cb) hello_cb (hello_cb_cls, GST_hello_get ()); GNUNET_PEERINFO_add_peer (GST_peerinfo, our_hello, NULL, NULL); hello_task = GNUNET_SCHEDULER_add_delayed (HELLO_REFRESH_PERIOD, &refresh_hello_task, NULL); }
/** * Do transmit info about peer to given host. * * @param cls NULL to hit all hosts, otherwise specifies a particular target * @param key hostID * @param value information to transmit * @return GNUNET_YES (continue to iterate) */ static int add_to_tc (void *cls, const GNUNET_HashCode * key, void *value) { struct GNUNET_SERVER_TransmitContext *tc = cls; struct HostEntry *pos = value; struct InfoMessage *im; uint16_t hs; char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN; hs = 0; im = (struct InfoMessage *) buf; if (pos->hello != NULL) { hs = GNUNET_HELLO_size (pos->hello); GNUNET_assert (hs < GNUNET_SERVER_MAX_MESSAGE_SIZE - sizeof (struct InfoMessage)); memcpy (&im[1], pos->hello, hs); } im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO); im->header.size = htons (sizeof (struct InfoMessage) + hs); im->reserved = htonl (0); im->peer = pos->identity; GNUNET_SERVER_transmit_context_append_message (tc, &im->header); return GNUNET_YES; }
static void try_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct ConnectingContext *cc = cls; struct PeerContext *p1 = cc->p1; struct PeerContext *p2 = cc->p2; cc->tct = GNUNET_SCHEDULER_NO_TASK; if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) return; GNUNET_assert (cc != NULL); GNUNET_assert (cc->p1 != NULL); GNUNET_assert (cc->p2 != NULL); char *p2_s = GNUNET_strdup (GNUNET_i2s (&p2->id)); GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-testing", "Asking peer %u (`%s') to connect peer %u (`%s'), providing HELLO with %u bytes\n", p1->no, GNUNET_i2s (&p1->id), p2->no, p2_s, GNUNET_HELLO_size (cc->p2->hello)); GNUNET_free (p2_s); GNUNET_TRANSPORT_offer_hello (cc->th_p1, (const struct GNUNET_MessageHeader *) cc-> p2->hello, NULL, NULL); GNUNET_TRANSPORT_try_connect (cc->th_p1, &p2->id, NULL, NULL); /*FIXME TRY_CONNECT change */ cc->tct = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &try_connect, cc); }
/** * Calculate when we would like to send the next HELLO to this * peer and ask for it. * * @param cls for which peer to schedule the HELLO */ static void schedule_next_hello (void *cls) { struct Peer *pl = cls; struct FindAdvHelloContext fah; struct GNUNET_MQ_Envelope *env; size_t want; struct GNUNET_TIME_Relative delay; struct GNUNET_HashCode hc; pl->hello_delay_task = NULL; GNUNET_assert (NULL != pl->mq); /* find applicable HELLOs */ fah.peer = pl; fah.result = NULL; fah.max_size = GNUNET_SERVER_MAX_MESSAGE_SIZE - 1; fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL; GNUNET_CONTAINER_multipeermap_iterate (peers, &find_advertisable_hello, &fah); pl->hello_delay_task = GNUNET_SCHEDULER_add_delayed (fah.next_adv, &schedule_next_hello, pl); if (NULL == fah.result) return; delay = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed); if (0 != delay.rel_value_us) return; want = GNUNET_HELLO_size (fah.result->hello); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending HELLO with %u bytes", (unsigned int) want); env = GNUNET_MQ_msg_copy (&fah.result->hello->header); GNUNET_MQ_send (pl->mq, env); /* avoid sending this one again soon */ GNUNET_CRYPTO_hash (&pl->pid, sizeof (struct GNUNET_PeerIdentity), &hc); GNUNET_CONTAINER_bloomfilter_add (fah.result->filter, &hc); GNUNET_STATISTICS_update (stats, gettext_noop ("# HELLO messages gossipped"), 1, GNUNET_NO); /* prepare to send the next one */ if (NULL != pl->hello_delay_task) GNUNET_SCHEDULER_cancel (pl->hello_delay_task); pl->next_hello_allowed = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY); pl->hello_delay_task = GNUNET_SCHEDULER_add_now (&schedule_next_hello, pl); }
/** * Bind a host address (hello) to a hostId. * * @param peer the peer for which this is a hello * @param hello the verified (!) hello message */ static void bind_address (const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Message *hello) { char *fn; struct HostEntry *host; struct GNUNET_HELLO_Message *mrg; struct GNUNET_TIME_Absolute delta; add_host_to_known_hosts (peer); host = GNUNET_CONTAINER_multihashmap_get (hostmap, &peer->hashPubKey); GNUNET_assert (host != NULL); if (host->hello == NULL) { host->hello = GNUNET_malloc (GNUNET_HELLO_size (hello)); memcpy (host->hello, hello, GNUNET_HELLO_size (hello)); } else { mrg = GNUNET_HELLO_merge (host->hello, hello); delta = GNUNET_HELLO_equals (mrg, host->hello, GNUNET_TIME_absolute_get ()); if (delta.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value) { GNUNET_free (mrg); return; } GNUNET_free (host->hello); host->hello = mrg; } fn = get_host_filename (peer); if (GNUNET_OK == GNUNET_DISK_directory_create_for_file (fn)) { if (GNUNET_SYSERR == GNUNET_DISK_fn_write (fn, host->hello, GNUNET_HELLO_size (host->hello), GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ)) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "write", fn); } GNUNET_free (fn); notify_all (host); }
/** * 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); }
/** * Create a new entry in the peer list. * * @param peer identity of the new entry * @param hello hello message, can be NULL * @param is_friend is the new entry for a friend? * @return the new entry */ static struct Peer * make_peer (const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Message *hello, int is_friend) { struct Peer *ret; ret = GNUNET_malloc (sizeof (struct Peer)); ret->pid = *peer; ret->is_friend = is_friend; if (hello != NULL) { ret->hello = GNUNET_malloc (GNUNET_HELLO_size (hello)); memcpy (ret->hello, hello, GNUNET_HELLO_size (hello)); } GNUNET_break (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (peers, &peer->hashPubKey, ret, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); return ret; }
/** * Periodically announce self id in the DHT * * @param cls closure * @param tc task context */ static void announce_id (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_HashCode phash; const struct GNUNET_HELLO_Message *hello; size_t size; struct GNUNET_TIME_Absolute expiration; struct GNUNET_TIME_Relative retry_time; if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) { announce_id_task = NULL; return; } LOG (GNUNET_ERROR_TYPE_DEBUG, "Announce ID\n"); /* TODO * - Set data expiration in function of X * - Adapt X to churn */ hello = GCH_get_mine (); if (NULL == hello || (size = GNUNET_HELLO_size (hello)) == 0) { /* Peerinfo gave us no hello yet, try again in a second. */ announce_id_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &announce_id, cls); LOG (GNUNET_ERROR_TYPE_DEBUG, " no hello, waiting!\n"); GNUNET_STATISTICS_update (stats, "# DHT announce skipped (no hello)", 1, GNUNET_NO); return; } expiration = GNUNET_HELLO_get_last_expiration (hello); retry_time = GNUNET_TIME_absolute_get_remaining (expiration); LOG (GNUNET_ERROR_TYPE_DEBUG, "Hello %p size: %u\n", hello, size); GNUNET_STATISTICS_update (stats, "# DHT announce", 1, GNUNET_NO); memset (&phash, 0, sizeof (phash)); memcpy (&phash, &my_full_id, sizeof (my_full_id)); GNUNET_DHT_put (dht_handle, /* DHT handle */ &phash, /* Key to use */ dht_replication_level, /* Replication level */ GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, /* DHT options */ GNUNET_BLOCK_TYPE_DHT_HELLO, /* Block type */ size, /* Size of the data */ (const char *) hello, /* Data itself */ expiration, /* Data expiration */ retry_time, /* Retry time */ NULL, /* Continuation */ NULL); /* Continuation closure */ announce_id_task = GNUNET_SCHEDULER_add_delayed (id_announce_time, &announce_id, cls); }
/** * Notify all clients in the notify list about the * given host entry changing. * * @param he entry of the host for which we generate a notification * @return generated notification message */ static struct InfoMessage * make_info_message (const struct HostEntry *he) { struct InfoMessage *im; size_t hs; hs = (he->hello == NULL) ? 0 : GNUNET_HELLO_size (he->hello); im = GNUNET_malloc (sizeof (struct InfoMessage) + hs); im->header.size = htons (hs + sizeof (struct InfoMessage)); im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO); im->peer = he->identity; if (he->hello != NULL) memcpy (&im[1], he->hello, hs); return im; }
/** * Receive a peerinfo information message, process it and * go for more. * * @param cls closure * @param msg message received, NULL on timeout or fatal error */ static void process_notification (void *cls, const struct GNUNET_MessageHeader *msg) { struct GNUNET_PEERINFO_NotifyContext *nc = cls; const struct InfoMessage *im; const struct GNUNET_HELLO_Message *hello; uint16_t ms; if (msg == NULL) { GNUNET_CLIENT_disconnect (nc->client); reconnect (nc, NULL); return; } ms = ntohs (msg->size); if ((ms < sizeof (struct InfoMessage)) || (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO)) { GNUNET_break (0); GNUNET_CLIENT_disconnect (nc->client); nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg); request_notifications (nc); return; } im = (const struct InfoMessage *) msg; hello = NULL; if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader)) { hello = (const struct GNUNET_HELLO_Message *) &im[1]; if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello)) { GNUNET_break (0); GNUNET_CLIENT_disconnect (nc->client); nc->client = GNUNET_CLIENT_connect ("peerinfo", nc->cfg); request_notifications (nc); return; } } LOG (GNUNET_ERROR_TYPE_DEBUG, "Received information about peer `%s' from peerinfo database\n", GNUNET_i2s (&im->peer)); nc->callback (nc->callback_cls, &im->peer, hello, NULL); receive_notifications (nc); }
/** * Notify all clients in the notify list about the * given host entry changing. * * @param he entry of the host for which we generate a notification * @param include_friend_only create public of friend-only message * @return generated notification message */ static struct InfoMessage * make_info_message (const struct HostEntry *he, int include_friend_only) { struct InfoMessage *im; struct GNUNET_HELLO_Message *src; size_t hs; if (GNUNET_YES == include_friend_only) src = he->friend_only_hello; else src = he->hello; hs = (NULL == src) ? 0 : GNUNET_HELLO_size (src); im = GNUNET_malloc (sizeof (struct InfoMessage) + hs); im->header.size = htons (hs + sizeof (struct InfoMessage)); im->header.type = htons (GNUNET_MESSAGE_TYPE_PEERINFO_INFO); im->peer = he->identity; if (NULL != src) memcpy (&im[1], src, hs); return im; }
static unsigned int prepare_beacon (struct Plugin *plugin, struct UDP_Beacon_Message *msg) { uint16_t hello_size; uint16_t msg_size; const struct GNUNET_MessageHeader *hello; hello = plugin->env->get_our_hello (); if (NULL == hello) return 0; hello_size = GNUNET_HELLO_size ((struct GNUNET_HELLO_Message *) hello); msg_size = hello_size + sizeof (struct UDP_Beacon_Message); if (hello_size < (sizeof (struct GNUNET_MessageHeader)) || (msg_size > (UDP_MTU))) return 0; msg->sender = *(plugin->env->my_identity); msg->header.size = htons (msg_size); msg->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BROADCAST_BEACON); memcpy (&msg[1], hello, hello_size); return msg_size; }
/** * @brief delete expired HELLO entries in data/hosts/ * * @param cls pointer to current time (struct GNUNET_TIME_Absolute) * @param fn filename to test to see if the HELLO expired * @return GNUNET_OK (continue iteration) */ static int discard_hosts_helper (void *cls, const char *fn) { struct GNUNET_TIME_Absolute *now = cls; char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN; const struct GNUNET_HELLO_Message *hello; struct GNUNET_HELLO_Message *new_hello; int size; size = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer)); if (size < sizeof (struct GNUNET_MessageHeader)) { if (0 != UNLINK (fn)) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "unlink", fn); return GNUNET_OK; } hello = (const struct GNUNET_HELLO_Message *) buffer; new_hello = GNUNET_HELLO_iterate_addresses (hello, GNUNET_YES, &discard_expired, now); if (new_hello != NULL) { GNUNET_DISK_fn_write (fn, new_hello, GNUNET_HELLO_size (new_hello), GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE | GNUNET_DISK_PERM_GROUP_READ | GNUNET_DISK_PERM_OTHER_READ); GNUNET_free (new_hello); } else { if (0 != UNLINK (fn)) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "unlink", fn); } return GNUNET_OK; }
/** * Calculate when we would like to send the next HELLO to this * peer and ask for it. * * @param cls for which peer to schedule the HELLO * @param tc task context */ static void schedule_next_hello (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct Peer *pl = cls; struct FindAdvHelloContext fah; size_t next_want; struct GNUNET_TIME_Relative delay; pl->hello_delay_task = GNUNET_SCHEDULER_NO_TASK; GNUNET_assert (GNUNET_YES == pl->is_connected); if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) return; /* we're out of here */ if (pl->hello_req != NULL) return; /* did not finish sending the previous one */ /* find applicable HELLOs */ fah.peer = pl; fah.result = NULL; fah.max_size = GNUNET_SERVER_MAX_MESSAGE_SIZE - 1; fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL; GNUNET_CONTAINER_multihashmap_iterate (peers, &find_advertisable_hello, &fah); pl->hello_delay_task = GNUNET_SCHEDULER_add_delayed (fah.next_adv, &schedule_next_hello, pl); if (fah.result == NULL) return; next_want = GNUNET_HELLO_size (fah.result->hello); delay = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed); if (delay.rel_value == 0) { /* now! */ pl->hello_req = GNUNET_CORE_notify_transmit_ready (handle, GNUNET_YES, 0, GNUNET_CONSTANTS_SERVICE_TIMEOUT, &pl->pid, next_want, &hello_advertising_ready, pl); } }
/** * Add a host to the persistent list. This method operates in * semi-reliable mode: if the transmission is not completed by * the time 'GNUNET_PEERINFO_disconnect' is called, it will be * aborted. Furthermore, if a second HELLO is added for the * same peer before the first one was transmitted, PEERINFO may * merge the two HELLOs prior to transmission to the service. * * @param h handle to the peerinfo service * @param hello the verified (!) HELLO message * @param cont continuation to call when done, NULL is allowed * @param cont_cls closure for 'cont' * @return handle to cancel add operation; all pending * 'add' operations will be cancelled automatically * on disconnect, so it is not necessary to keep this * handle (unless 'cont' is NULL and at some point * calling 'cont' must be prevented) */ struct GNUNET_PEERINFO_AddContext * GNUNET_PEERINFO_add_peer (struct GNUNET_PEERINFO_Handle *h, const struct GNUNET_HELLO_Message *hello, GNUNET_PEERINFO_Continuation cont, void *cont_cls) { uint16_t hs = GNUNET_HELLO_size (hello); struct GNUNET_PEERINFO_AddContext *ac; struct GNUNET_PeerIdentity peer; GNUNET_assert (GNUNET_OK == GNUNET_HELLO_get_id (hello, &peer)); LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding peer `%s' to PEERINFO database (%u bytes of `%s')\n", GNUNET_i2s (&peer), hs, "HELLO"); ac = GNUNET_malloc (sizeof (struct GNUNET_PEERINFO_AddContext) + hs); ac->h = h; ac->size = hs; ac->cont = cont; ac->cont_cls = cont_cls; memcpy (&ac[1], hello, hs); GNUNET_CONTAINER_DLL_insert_tail (h->ac_head, h->ac_tail, ac); trigger_transmit (h); return ac; }
/** * Function to fill send buffer with HELLO. * * @param cls 'struct Peer' of the target peer * @param size number of bytes available in buf * @param buf where the callee should write the message * @return number of bytes written to buf */ static size_t hello_advertising_ready (void *cls, size_t size, void *buf) { struct Peer *pl = cls; struct FindAdvHelloContext fah; size_t want; pl->hello_req = NULL; GNUNET_assert (GNUNET_YES == pl->is_connected); /* find applicable HELLOs */ fah.peer = pl; fah.result = NULL; fah.max_size = size; fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL; GNUNET_CONTAINER_multihashmap_iterate (peers, &find_advertisable_hello, &fah); want = 0; if (fah.result != NULL) { want = GNUNET_HELLO_size (fah.result->hello); GNUNET_assert (want <= size); memcpy (buf, fah.result->hello, want); GNUNET_CONTAINER_bloomfilter_add (fah.result->filter, &pl->pid.hashPubKey); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending `%s' with %u bytes", "HELLO", (unsigned int) want); GNUNET_STATISTICS_update (stats, gettext_noop ("# HELLO messages gossipped"), 1, GNUNET_NO); } if (pl->hello_delay_task != GNUNET_SCHEDULER_NO_TASK) GNUNET_SCHEDULER_cancel (pl->hello_delay_task); pl->next_hello_allowed = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY); pl->hello_delay_task = GNUNET_SCHEDULER_add_now (&schedule_next_hello, pl); return want; }
/** * Type of a function to call when we receive a message from the * service. Call the iterator with the result and (if applicable) * continue to receive more messages or trigger processing the next * event (if applicable). * * @param cls closure * @param msg message received, NULL on timeout or fatal error */ static void peerinfo_handler (void *cls, const struct GNUNET_MessageHeader *msg) { struct GNUNET_PEERINFO_Handle *h = cls; struct GNUNET_PEERINFO_IteratorContext *ic = h->ic_head; const struct InfoMessage *im; const struct GNUNET_HELLO_Message *hello; GNUNET_PEERINFO_Processor cb; struct GNUNET_PeerIdentity id; void *cb_cls; uint16_t ms; GNUNET_assert (NULL != ic); h->in_receive = GNUNET_NO; ic->in_receive = GNUNET_NO; cb = ic->callback; cb_cls = ic->callback_cls; if (NULL == msg) { /* peerinfo service died, signal error */ GNUNET_PEERINFO_iterate_cancel (ic); reconnect (h); if (NULL != cb) cb (cb_cls, NULL, NULL, _("Failed to receive response from `PEERINFO' service.")); return; } if (GNUNET_MESSAGE_TYPE_PEERINFO_INFO_END == ntohs (msg->type)) { /* normal end of list of peers, signal end, process next pending request */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Received end of list of peers from `%s' service\n", "PEERINFO"); GNUNET_PEERINFO_iterate_cancel (ic); trigger_transmit (h); if (NULL != cb) cb (cb_cls, NULL, NULL, NULL); return; } ms = ntohs (msg->size); if ((ms < sizeof (struct InfoMessage)) || (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_PEERINFO_INFO)) { /* malformed message */ GNUNET_break (0); GNUNET_PEERINFO_iterate_cancel (ic); reconnect (h); if (NULL != cb) cb (cb_cls, NULL, NULL, _("Received invalid message from `PEERINFO' service.")); return; } im = (const struct InfoMessage *) msg; GNUNET_break (0 == ntohl (im->reserved)); if ( (GNUNET_YES == ic->have_peer) && (0 != memcmp (&ic->peer, &im->peer, sizeof (struct GNUNET_PeerIdentity))) ) { /* bogus message (from a different iteration call?); out of sequence! */ LOG (GNUNET_ERROR_TYPE_ERROR, "Received HELLO for peer `%s', expected peer `%s'\n", GNUNET_h2s (&im->peer.hashPubKey), GNUNET_i2s (&ic->peer)); GNUNET_break (0); GNUNET_PEERINFO_iterate_cancel (ic); reconnect (h); if (NULL != cb) cb (cb_cls, NULL, NULL, _("Received invalid message from `PEERINFO' service.")); return; } hello = NULL; if (ms > sizeof (struct InfoMessage) + sizeof (struct GNUNET_MessageHeader)) { hello = (const struct GNUNET_HELLO_Message *) &im[1]; if (ms != sizeof (struct InfoMessage) + GNUNET_HELLO_size (hello)) { /* malformed message */ GNUNET_break (0); GNUNET_PEERINFO_iterate_cancel (ic); reconnect (h); if (NULL != cb) cb (cb_cls, NULL, NULL, _("Received invalid message from `PEERINFO' service.")); return; } if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &id)) { /* malformed message */ GNUNET_break (0); GNUNET_PEERINFO_iterate_cancel (ic); reconnect (h); if (NULL != cb) cb (cb_cls, NULL, NULL, _("Received invalid message from `PEERINFO' service.")); return; } if (0 != memcmp (&im->peer, &id, sizeof (struct GNUNET_PeerIdentity))) { /* malformed message */ GNUNET_break (0); GNUNET_PEERINFO_iterate_cancel (ic); reconnect (h); if (NULL != cb) cb (cb_cls, NULL, NULL, _("Received invalid message from `PEERINFO' service.")); return; } } /* normal data message */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Received %u bytes of `%s' information about peer `%s' from `%s' service\n", (hello == NULL) ? 0 : (unsigned int) GNUNET_HELLO_size (hello), "HELLO", GNUNET_i2s (&im->peer), "PEERINFO"); h->in_receive = GNUNET_YES; ic->in_receive = GNUNET_YES; GNUNET_CLIENT_receive (h->client, &peerinfo_handler, h, GNUNET_TIME_absolute_get_remaining (ic->timeout)); if (NULL != cb) cb (cb_cls, &im->peer, hello, NULL); }
/** * Iterate over all of the addresses in the HELLO. * * @param msg HELLO to iterate over * @param return_modified if a modified copy should be returned, * otherwise NULL will be returned * @param it iterator to call on each address * @param it_cls closure for it */ struct GNUNET_HELLO_Message * GNUNET_HELLO_iterate_addresses (const struct GNUNET_HELLO_Message *msg, int return_modified, GNUNET_HELLO_AddressIterator it, void *it_cls) { struct GNUNET_HELLO_Address address; uint16_t msize; struct GNUNET_HELLO_Message *ret; const char *inptr; size_t insize; size_t esize; size_t wpos; char *woff; uint16_t alen; struct GNUNET_TIME_AbsoluteNBO expire; int iret; msize = GNUNET_HELLO_size (msg); if ((msize < sizeof (struct GNUNET_HELLO_Message)) || (ntohs (msg->header.type) != GNUNET_MESSAGE_TYPE_HELLO)) return NULL; ret = NULL; if (return_modified) { ret = GNUNET_malloc (msize); memcpy (ret, msg, msize); } inptr = (const char *) &msg[1]; insize = msize - sizeof (struct GNUNET_HELLO_Message); wpos = 0; woff = (ret != NULL) ? (char *) &ret[1] : NULL; GNUNET_CRYPTO_hash (&msg->publicKey, sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded), &address.peer.hashPubKey); while (insize > 0) { esize = get_hello_address_size (inptr, insize, &alen); if (esize == 0) { GNUNET_break (0); GNUNET_free_non_null (ret); return NULL; } memcpy (&expire, &inptr[esize - alen - sizeof (struct GNUNET_TIME_AbsoluteNBO)], sizeof (struct GNUNET_TIME_AbsoluteNBO)); address.address = &inptr[esize - alen]; address.address_length = alen; address.transport_name = inptr; iret = it (it_cls, &address, GNUNET_TIME_absolute_ntoh (expire)); if (iret == GNUNET_SYSERR) { if (ret != NULL) ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos); return ret; } if ((iret == GNUNET_OK) && (ret != NULL)) { memcpy (woff, inptr, esize); woff += esize; wpos += esize; } insize -= esize; inptr += esize; } if (ret != NULL) ret->header.size = ntohs (sizeof (struct GNUNET_HELLO_Message) + wpos); return ret; }
int main (int argc, char *argv[]) { struct GNUNET_DISK_FileHandle *fh; struct GNUNET_HELLO_Message *orig; struct GNUNET_HELLO_Message *result; struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pk; uint64_t fsize; GNUNET_log_setup ("gnunet-hello", "INFO", NULL); if (argc != 2) { FPRINTF (stderr, "%s", _("Call with name of HELLO file to modify.\n")); return 1; } if (GNUNET_OK != GNUNET_DISK_file_size (argv[1], &fsize, GNUNET_YES, GNUNET_YES)) { FPRINTF (stderr, _("Error accessing file `%s': %s\n"), argv[1], STRERROR (errno)); return 1; } if (fsize > 65536) { FPRINTF (stderr, _("File `%s' is too big to be a HELLO\n"), argv[1]); return 1; } if (fsize < sizeof (struct GNUNET_MessageHeader)) { FPRINTF (stderr, _("File `%s' is too small to be a HELLO\n"), argv[1]); return 1; } fh = GNUNET_DISK_file_open (argv[1], GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_USER_READ); if (NULL == fh) { FPRINTF (stderr, _("Error opening file `%s': %s\n"), argv[1], STRERROR (errno)); return 1; } { char buf[fsize] GNUNET_ALIGN; GNUNET_assert (fsize == GNUNET_DISK_file_read (fh, buf, fsize)); GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh)); orig = (struct GNUNET_HELLO_Message *) buf; if ( (fsize != GNUNET_HELLO_size (orig)) || (GNUNET_OK != GNUNET_HELLO_get_key (orig, &pk)) ) { FPRINTF (stderr, _("Did not find well-formed HELLO in file `%s'\n"), argv[1]); return 1; } result = GNUNET_HELLO_create (&pk, &add_from_hello, &orig); GNUNET_assert (NULL != result); fh = GNUNET_DISK_file_open (argv[1], GNUNET_DISK_OPEN_WRITE, GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); if (NULL == fh) { FPRINTF (stderr, _("Error opening file `%s': %s\n"), argv[1], STRERROR (errno)); GNUNET_free (result); return 1; } fsize = GNUNET_HELLO_size (result); if (fsize != GNUNET_DISK_file_write (fh, result, fsize)) { FPRINTF (stderr, _("Error writing HELLO to file `%s': %s\n"), argv[1], STRERROR (errno)); (void) GNUNET_DISK_file_close (fh); return 1; } GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh)); } return 0; }
/** * Try to read the HELLOs in the given filename and discard expired * addresses. Removes the file if one the HELLO is malformed. If all * addresses are expired, the HELLO is also removed (but the HELLO * with the public key is still returned if it was found and valid). * The file can contain multiple HELLO messages. * * @param fn name of the file * @param unlink_garbage if #GNUNET_YES, try to remove useless files * @param r ReadHostFileContext to store the resutl */ static void read_host_file (const char *fn, int unlink_garbage, struct ReadHostFileContext *r) { char buffer[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN; unsigned int size_total; struct GNUNET_TIME_Absolute now; unsigned int left; const struct GNUNET_HELLO_Message *hello; struct GNUNET_HELLO_Message *hello_clean; unsigned read_pos; int size_hello; r->friend_only_hello = NULL; r->hello = NULL; if (GNUNET_YES != GNUNET_DISK_file_test (fn)) return; size_total = GNUNET_DISK_fn_read (fn, buffer, sizeof (buffer)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes from `%s'\n", size_total, fn); if (size_total < sizeof (struct GNUNET_MessageHeader)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to parse HELLO in file `%s': %s\n"), fn, "Fail has invalid size"); if ( (GNUNET_YES == unlink_garbage) && (0 != UNLINK (fn)) && (ENOENT != errno) ) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); return; } read_pos = 0; while (read_pos < size_total) { hello = (const struct GNUNET_HELLO_Message *) &buffer[read_pos]; size_hello = GNUNET_HELLO_size (hello); if ( (0 == size_hello) || (size_total - read_pos < size_hello) ) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to parse HELLO in file `%s'\n"), fn); if (0 == read_pos) { if ((GNUNET_YES == unlink_garbage) && (0 != UNLINK (fn)) && (ENOENT != errno) ) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); } else { if ((GNUNET_YES == unlink_garbage) && (0 != TRUNCATE (fn, read_pos)) && (ENOENT != errno) ) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "truncate", fn); } return; } now = GNUNET_TIME_absolute_get (); hello_clean = GNUNET_HELLO_iterate_addresses (hello, GNUNET_YES, &discard_expired, &now); if (NULL == hello_clean) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to parse HELLO in file `%s'\n"), fn); if ((GNUNET_YES == unlink_garbage) && (0 != UNLINK (fn)) && (ENOENT != errno) ) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); return; } left = 0; (void) GNUNET_HELLO_iterate_addresses (hello_clean, GNUNET_NO, &count_addresses, &left); if (0 == left) { GNUNET_free (hello_clean); break; } if (GNUNET_NO == GNUNET_HELLO_is_friend_only (hello_clean)) { if (NULL == r->hello) r->hello = hello_clean; else { GNUNET_break (0); GNUNET_free (r->hello); r->hello = hello_clean; } } else { if (NULL == r->friend_only_hello) r->friend_only_hello = hello_clean; else { GNUNET_break (0); GNUNET_free (r->friend_only_hello); r->friend_only_hello = hello_clean; } } read_pos += size_hello; } if (0 == left) { /* no addresses left, remove from disk */ if ( (GNUNET_YES == unlink_garbage) && (0 != UNLINK (fn)) ) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn); } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found `%s' and `%s' HELLO message in file\n", (NULL != r->hello) ? "public" : "NON-public", (NULL != r->friend_only_hello) ? "friend only" : "NO friend only"); }
/** * Periodically announce self id in the DHT * * @param cls closure */ static void announce_id (void *cls) { struct GNUNET_HashCode phash; const struct GNUNET_HELLO_Message *hello; size_t size; struct GNUNET_TIME_Absolute expiration; struct GNUNET_TIME_Relative next_put; hello = GCH_get_mine (); size = (NULL != hello) ? GNUNET_HELLO_size (hello) : 0; if (0 == size) { expiration = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), announce_delay); announce_delay = GNUNET_TIME_STD_BACKOFF (announce_delay); } else { expiration = GNUNET_HELLO_get_last_expiration (hello); announce_delay = GNUNET_TIME_UNIT_SECONDS; } /* Call again in id_announce_time, unless HELLO expires first, * but wait at least 1s. */ next_put = GNUNET_TIME_absolute_get_remaining (expiration); next_put = GNUNET_TIME_relative_min (next_put, id_announce_time); next_put = GNUNET_TIME_relative_max (next_put, GNUNET_TIME_UNIT_SECONDS); announce_id_task = GNUNET_SCHEDULER_add_delayed (next_put, &announce_id, cls); GNUNET_STATISTICS_update (stats, "# DHT announce", 1, GNUNET_NO); memset (&phash, 0, sizeof (phash)); GNUNET_memcpy (&phash, &my_full_id, sizeof (my_full_id)); LOG (GNUNET_ERROR_TYPE_DEBUG, "Announcing my HELLO (%u bytes) in the DHT\n", size); GNUNET_DHT_put (dht_handle, /* DHT handle */ &phash, /* Key to use */ dht_replication_level, /* Replication level */ GNUNET_DHT_RO_RECORD_ROUTE | GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE, /* DHT options */ GNUNET_BLOCK_TYPE_DHT_HELLO, /* Block type */ size, /* Size of the data */ (const char *) hello, /* Data itself */ expiration, /* Data expiration */ NULL, /* Continuation */ NULL); /* Continuation closure */ }
/** * Callback that processes each of the known HELLOs for the * hostlist response construction. * * @param cls closure, NULL * @param peer id of the peer, NULL for last call * @param hello hello message for the peer (can be NULL) * @param err_msg message */ static void host_processor (void *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Message *hello, const char *err_msg) { size_t old; size_t s; int has_addr; if (NULL != err_msg) { GNUNET_assert (NULL == peer); builder->pitr = NULL; GNUNET_free_non_null (builder->data); GNUNET_free (builder); builder = NULL; GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Error in communication with PEERINFO service: %s\n"), err_msg); return; } if (NULL == peer) { builder->pitr = NULL; finish_response (); return; } if (NULL == hello) return; has_addr = GNUNET_NO; GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &check_has_addr, &has_addr); if (GNUNET_NO == has_addr) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "HELLO for peer `%4s' has no address, not suitable for hostlist!\n", GNUNET_i2s (peer)); GNUNET_STATISTICS_update (stats, gettext_noop ("HELLOs without addresses encountered (ignored)"), 1, GNUNET_NO); return; } old = builder->size; s = GNUNET_HELLO_size (hello); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received %u bytes of `%s' from peer `%s' for hostlist.\n", (unsigned int) s, "HELLO", GNUNET_i2s (peer)); if ( (old + s >= GNUNET_MAX_MALLOC_CHECKED) || (old + s >= MAX_BYTES_PER_HOSTLISTS) ) { /* too large, skip! */ GNUNET_STATISTICS_update (stats, gettext_noop ("bytes not included in hostlist (size limit)"), s, GNUNET_NO); return; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Adding peer `%s' to hostlist (%u bytes)\n", GNUNET_i2s (peer), (unsigned int) s); GNUNET_array_grow (builder->data, builder->size, old + s); memcpy (&builder->data[old], hello, s); }