X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=inline;f=src%2Ftransport%2Fgnunet-service-transport_validation.c;h=e61d1337d1c9130d3281b5ea24aa6ec1b6b04cdd;hb=86be3237abd7ff9bf06f1911802e8a38fb985650;hp=9a3914814921f40c19b6a8222fce1b88c1742ee1;hpb=5746309cb4be2073d550ad7a6885e918631dbc38;p=oweals%2Fgnunet.git diff --git a/src/transport/gnunet-service-transport_validation.c b/src/transport/gnunet-service-transport_validation.c index 9a3914814..e61d1337d 100644 --- a/src/transport/gnunet-service-transport_validation.c +++ b/src/transport/gnunet-service-transport_validation.c @@ -34,7 +34,7 @@ #include "gnunet_peerinfo_service.h" #include "gnunet_signatures.h" -// TODO: observe latency between PING/PONG and give information to ATS! +#define KEEP_093_COMPATIBILITY GNUNET_YES /** * How long is a PONG signature valid? We'll recycle a signature until @@ -53,11 +53,29 @@ #define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12) /** - * How long before an existing address expires should we again try to - * validate it? Must be (significantly) smaller than - * HELLO_ADDRESS_EXPIRATION. + * How often do we allow PINGing an address that we have not yet + * validated? This also determines how long we track an address that + * we cannot validate (because after this time we can destroy the + * validation record). */ -#define HELLO_REVALIDATION_START_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1) +#define UNVALIDATED_PING_KEEPALIVE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) + +/** + * How often do we PING an address that we have successfully validated + * in the past but are not actively using? Should be (significantly) + * smaller than HELLO_ADDRESS_EXPIRATION. + */ +#define VALIDATED_PING_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15) + +/** + * How often do we PING an address that we are currently using? + */ +#define CONNECTED_PING_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2) + +/** + * How much delay is acceptable for sending the PING or PONG? + */ +#define ACCEPTABLE_PING_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) /** * Size of the validation map hashmap. @@ -75,6 +93,8 @@ #define PONG_PRIORITY 4 +GNUNET_NETWORK_STRUCT_BEGIN + /** * Message used to ask a peer to validate receipt (to check an address * from a HELLO). Followed by the address we are trying to validate, @@ -149,7 +169,7 @@ struct TransportPongMessage uint32_t addrlen GNUNET_PACKED; }; - +GNUNET_NETWORK_STRUCT_END /** * Information about an address under validation @@ -158,15 +178,9 @@ struct ValidationEntry { /** - * Name of the transport. - */ - char *transport_name; - - /** - * The address, actually a pointer to the end - * of this struct. Do not free! + * The address. */ - const void *addr; + struct GNUNET_HELLO_Address *address; /** * Handle to the blacklist check (if we're currently in it). @@ -179,7 +193,7 @@ struct ValidationEntry struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded public_key; /** - * The identity of the peer. + * The identity of the peer. FIXME: duplicated (also in 'address') */ struct GNUNET_PeerIdentity pid; @@ -189,7 +203,12 @@ struct ValidationEntry GNUNET_SCHEDULER_TaskIdentifier timeout_task; /** - * At what time did we send the latest validation request? + * ID of task that will trigger address revalidation. + */ + GNUNET_SCHEDULER_TaskIdentifier revalidation_task; + + /** + * At what time did we send the latest validation request (PING)? */ struct GNUNET_TIME_Absolute send_time; @@ -205,7 +224,13 @@ struct ValidationEntry * ZERO if the address is considered valid (no validation needed) * otherwise a time in the future if we're currently denying re-validation */ - struct GNUNET_TIME_Absolute validation_block; + struct GNUNET_TIME_Absolute revalidation_block; + + /** + * Last observed latency for this address (round-trip), delay between + * last PING sent and PONG received; FOREVER if we never got a PONG. + */ + struct GNUNET_TIME_Relative latency; /** * Challenge number we used. @@ -213,10 +238,24 @@ struct ValidationEntry uint32_t challenge; /** - * Length of addr. + * When passing the address in 'add_valid_peer_address', did we + * copy the address to the HELLO yet? */ - size_t addrlen; + int copied; + /** + * Are we currently using this address for a connection? + */ + int in_use; + + /** + * Are we expecting a PONG message for this validation entry? + */ + int expecting_pong; + + /* FIXME: DEBUGGING */ + int last_line_set_to_no; + int last_line_set_to_yes; }; @@ -278,20 +317,11 @@ struct ValidationEntryMatchContext */ struct ValidationEntry *ve; - /** - * Transport name we're looking for. - */ - const char *transport_name; - /** * Address we're interested in. */ - const char *addr; + const struct GNUNET_HELLO_Address *address; - /** - * Number of bytes in 'addr'. - */ - size_t addrlen; }; @@ -305,14 +335,12 @@ struct ValidationEntryMatchContext * GNUNET_NO if the entry does match */ static int -validation_entry_match (void *cls, const GNUNET_HashCode * key, void *value) +validation_entry_match (void *cls, const struct GNUNET_HashCode * key, void *value) { struct ValidationEntryMatchContext *vemc = cls; struct ValidationEntry *ve = value; - if ((ve->addrlen == vemc->addrlen) && - (0 == memcmp (ve->addr, vemc->addr, ve->addrlen)) && - (0 == strcmp (ve->transport_name, vemc->transport_name))) + if (0 == GNUNET_HELLO_address_cmp (ve->address, vemc->address)) { vemc->ve = ve; return GNUNET_NO; @@ -321,52 +349,278 @@ validation_entry_match (void *cls, const GNUNET_HashCode * key, void *value) } +/** + * Iterate over validation entries and free them. + * + * @param cls (unused) + * @param key peer identity (unused) + * @param value a 'struct ValidationEntry' to clean up + * @return GNUNET_YES (continue to iterate) + */ +static int +cleanup_validation_entry (void *cls, const struct GNUNET_HashCode * key, void *value) +{ + struct ValidationEntry *ve = value; + + if (NULL != ve->bc) + { + GST_blacklist_test_cancel (ve->bc); + ve->bc = NULL; + } + GNUNET_break (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_remove (validation_map, + &ve->pid.hashPubKey, ve)); + GNUNET_HELLO_address_free (ve->address); + if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task) + { + GNUNET_SCHEDULER_cancel (ve->timeout_task); + ve->timeout_task = GNUNET_SCHEDULER_NO_TASK; + } + if (GNUNET_SCHEDULER_NO_TASK != ve->revalidation_task) + { + GNUNET_SCHEDULER_cancel (ve->revalidation_task); + ve->revalidation_task = GNUNET_SCHEDULER_NO_TASK; + } + GNUNET_free (ve); + return GNUNET_OK; +} + + +/** + * Address validation cleanup task. Assesses if the record is no + * longer valid and then possibly triggers its removal. + * + * @param cls the 'struct ValidationEntry' + * @param tc scheduler context (unused) + */ +static void +timeout_hello_validation (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ValidationEntry *ve = cls; + struct GNUNET_TIME_Absolute max; + struct GNUNET_TIME_Relative left; + + ve->timeout_task = GNUNET_SCHEDULER_NO_TASK; + max = GNUNET_TIME_absolute_max (ve->valid_until, ve->revalidation_block); + left = GNUNET_TIME_absolute_get_remaining (max); + if (left.rel_value > 0) + { + /* should wait a bit longer */ + ve->timeout_task = + GNUNET_SCHEDULER_add_delayed (left, &timeout_hello_validation, ve); + return; + } + GNUNET_STATISTICS_update (GST_stats, + gettext_noop ("# address records discarded"), 1, + GNUNET_NO); + cleanup_validation_entry (NULL, &ve->pid.hashPubKey, ve); +} + + +/** + * Function called with the result from blacklisting. + * Send a PING to the other peer if a communication is allowed. + * + * @param cls our 'struct ValidationEntry' + * @param pid identity of the other peer + * @param result GNUNET_OK if the connection is allowed, GNUNET_NO if not + */ +static void +transmit_ping_if_allowed (void *cls, const struct GNUNET_PeerIdentity *pid, + int result) +{ + struct ValidationEntry *ve = cls; + struct TransportPingMessage ping; + struct GNUNET_TRANSPORT_PluginFunctions *papi; + const struct GNUNET_MessageHeader *hello; + ssize_t ret; + size_t tsize; + size_t slen; + uint16_t hsize; + + ve->bc = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting plain PING to `%s' %s %s\n", + GNUNET_i2s (pid), GST_plugins_a2s (ve->address), ve->address->transport_name); + + slen = strlen (ve->address->transport_name) + 1; + hello = GST_hello_get (); + hsize = ntohs (hello->size); + tsize = + sizeof (struct TransportPingMessage) + ve->address->address_length + + slen + hsize; + + ping.header.size = + htons (sizeof (struct TransportPingMessage) + + ve->address->address_length + slen); + ping.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING); + ping.challenge = htonl (ve->challenge); + ping.target = *pid; + + if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Not transmitting `%s' with `%s', message too big (%u bytes!). This should not happen.\n"), + "HELLO", "PING", (unsigned int) tsize); + /* message too big (!?), get rid of HELLO */ + hsize = 0; + tsize = + sizeof (struct TransportPingMessage) + ve->address->address_length + + slen + hsize; + } + { + char message_buf[tsize]; + + /* build message with structure: + * [HELLO][TransportPingMessage][Transport name][Address] */ + memcpy (message_buf, hello, hsize); + memcpy (&message_buf[hsize], &ping, sizeof (struct TransportPingMessage)); + memcpy (&message_buf[sizeof (struct TransportPingMessage) + hsize], + ve->address->transport_name, slen); + memcpy (&message_buf[sizeof (struct TransportPingMessage) + slen + hsize], + ve->address->address, ve->address->address_length); + papi = GST_plugins_find (ve->address->transport_name); + if (papi == NULL) + ret = -1; + else + { + GNUNET_assert (papi->send != NULL); + GNUNET_assert (papi->get_session != NULL); + struct Session * session = papi->get_session(papi->cls, ve->address); + + if (session != NULL) + { + ret = papi->send (papi->cls, session, + message_buf, tsize, + PING_PRIORITY, ACCEPTABLE_PING_DELAY, + NULL, NULL); + } + else + { + /* Could not get a valid session */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Could not get a valid session for `%s' %s\n", + GNUNET_i2s (pid), GST_plugins_a2s (ve->address)); + ret = -1; + } + } + } + if (-1 != ret) + { + ve->send_time = GNUNET_TIME_absolute_get (); + GNUNET_STATISTICS_update (GST_stats, + gettext_noop + ("# PING without HELLO messages sent"), 1, + GNUNET_NO); + ve->expecting_pong = GNUNET_YES; + } +} + + +/** + * Do address validation again to keep address valid. + * + * @param cls the 'struct ValidationEntry' + * @param tc scheduler context (unused) + */ +static void +revalidate_address (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ValidationEntry *ve = cls; + struct GNUNET_TIME_Relative canonical_delay; + struct GNUNET_TIME_Relative delay; + struct GST_BlacklistCheck *bc; + uint32_t rdelay; + + ve->revalidation_task = GNUNET_SCHEDULER_NO_TASK; + delay = GNUNET_TIME_absolute_get_remaining (ve->revalidation_block); + /* How long until we can possibly permit the next PING? */ + canonical_delay = + (ve->in_use == + GNUNET_YES) ? CONNECTED_PING_FREQUENCY + : ((GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value > + 0) ? VALIDATED_PING_FREQUENCY : UNVALIDATED_PING_KEEPALIVE); + if (delay.rel_value > canonical_delay.rel_value * 2) + { + /* situation changed, recalculate delay */ + delay = canonical_delay; + ve->revalidation_block = GNUNET_TIME_relative_to_absolute (delay); + } + if (delay.rel_value > 0) + { + /* should wait a bit longer */ + ve->revalidation_task = + GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve); + return; + } + ve->revalidation_block = GNUNET_TIME_relative_to_absolute (canonical_delay); + + /* schedule next PINGing with some extra random delay to avoid synchronous re-validations */ + rdelay = + GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, + canonical_delay.rel_value); + delay = + GNUNET_TIME_relative_add (canonical_delay, + GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MILLISECONDS, rdelay)); + ve->revalidation_task = + GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve); + + /* start PINGing by checking blacklist */ + GNUNET_STATISTICS_update (GST_stats, + gettext_noop ("# address revalidations started"), 1, + GNUNET_NO); + bc = GST_blacklist_test_allowed (&ve->pid, ve->address->transport_name, + &transmit_ping_if_allowed, ve); + if (NULL != bc) + ve->bc = bc; /* only set 'bc' if 'transmit_ping_if_allowed' was not already + * called... */ +} + + /** * Find a ValidationEntry entry for the given neighbour that matches * the given address and transport. If none exists, create one (but * without starting any validation). * * @param public_key public key of the peer, NULL for unknown - * @param neighbour which peer we care about - * @param tname name of the transport plugin - * @param session session to look for, NULL for 'any'; otherwise - * can be used for the service to "learn" this session ID - * if 'addr' matches - * @param addr binary address - * @param addrlen length of addr + * @param address address to find * @return validation entry matching the given specifications, NULL * if we don't have an existing entry and no public key was given */ static struct ValidationEntry * find_validation_entry (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded - *public_key, const struct GNUNET_PeerIdentity *neighbour, - const char *tname, const char *addr, size_t addrlen) + *public_key, const struct GNUNET_HELLO_Address *address) { struct ValidationEntryMatchContext vemc; struct ValidationEntry *ve; vemc.ve = NULL; - vemc.transport_name = tname; - vemc.addr = addr; - vemc.addrlen = addrlen; + vemc.address = address; GNUNET_CONTAINER_multihashmap_get_multiple (validation_map, - &neighbour->hashPubKey, + &address->peer.hashPubKey, &validation_entry_match, &vemc); if (NULL != (ve = vemc.ve)) return ve; if (public_key == NULL) return NULL; - ve = GNUNET_malloc (sizeof (struct ValidationEntry) + addrlen); - ve->transport_name = GNUNET_strdup (tname); - ve->addr = (void *) &ve[1]; + ve = GNUNET_malloc (sizeof (struct ValidationEntry)); + ve->in_use = GNUNET_SYSERR; /* not defined */ + ve->last_line_set_to_no = 0; + ve->last_line_set_to_yes = 0; + ve->address = GNUNET_HELLO_address_copy (address); ve->public_key = *public_key; - ve->pid = *neighbour; + ve->pid = address->peer; + ve->latency = GNUNET_TIME_UNIT_FOREVER_REL; ve->challenge = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX); - memcpy (&ve[1], addr, addrlen); - ve->addrlen = addrlen; - GNUNET_CONTAINER_multihashmap_put (validation_map, &neighbour->hashPubKey, ve, + ve->timeout_task = + GNUNET_SCHEDULER_add_delayed (UNVALIDATED_PING_KEEPALIVE, + &timeout_hello_validation, ve); + GNUNET_CONTAINER_multihashmap_put (validation_map, &address->peer.hashPubKey, + ve, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + ve->expecting_pong = GNUNET_NO; return ve; } @@ -376,16 +630,13 @@ find_validation_entry (const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded * addresses. * * @param cls original HELLO message - * @param tname name of the transport + * @param address the address * @param expiration expiration time - * @param addr the address - * @param addrlen length of the address * @return GNUNET_OK (keep the address) */ static int -add_valid_address (void *cls, const char *tname, - struct GNUNET_TIME_Absolute expiration, const void *addr, - uint16_t addrlen) +add_valid_address (void *cls, const struct GNUNET_HELLO_Address *address, + struct GNUNET_TIME_Absolute expiration) { const struct GNUNET_HELLO_Message *hello = cls; struct ValidationEntry *ve; @@ -400,21 +651,29 @@ add_valid_address (void *cls, const char *tname, GNUNET_break (0); return GNUNET_OK; /* invalid HELLO !? */ } - ve = find_validation_entry (&public_key, &pid, tname, addr, addrlen); + if (0 == memcmp (&GST_my_identity, &pid, sizeof (struct GNUNET_PeerIdentity))) + { + /* Peerinfo returned own identity, skip validation */ + return GNUNET_OK; + } + + ve = find_validation_entry (&public_key, address); ve->valid_until = GNUNET_TIME_absolute_max (ve->valid_until, expiration); - GNUNET_ATS_address_update (GST_ats, &pid, ve->valid_until, tname, NULL, addr, - addrlen, NULL, 0); + + if (GNUNET_SCHEDULER_NO_TASK == ve->revalidation_task) + ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve); + GNUNET_ATS_address_add (GST_ats, address, NULL, NULL, 0); return GNUNET_OK; } /** - * Function called for any HELLO known to PEERINFO. + * Function called for any HELLO known to PEERINFO. * * @param cls unused * @param peer id of the peer, NULL for last call * @param hello hello message for the peer (can be NULL) - * @param error message + * @param err_msg error message */ static void process_peerinfo_hello (void *cls, const struct GNUNET_PeerIdentity *peer, @@ -442,38 +701,6 @@ GST_validation_start () } -/** - * Iterate over validation entries and free them. - * - * @param cls (unused) - * @param key peer identity (unused) - * @param value a 'struct ValidationEntry' to clean up - * @return GNUNET_YES (continue to iterate) - */ -static int -cleanup_validation_entry (void *cls, const GNUNET_HashCode * key, void *value) -{ - struct ValidationEntry *ve = value; - - if (NULL != ve->bc) - { - GST_blacklist_test_cancel (ve->bc); - ve->bc = NULL; - } - GNUNET_break (GNUNET_OK == - GNUNET_CONTAINER_multihashmap_remove (validation_map, - &ve->pid.hashPubKey, ve)); - GNUNET_free (ve->transport_name); - if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task) - { - GNUNET_SCHEDULER_cancel (ve->timeout_task); - ve->timeout_task = GNUNET_SCHEDULER_NO_TASK; - } - GNUNET_free (ve); - return GNUNET_OK; -} - - /** * Stop the validation subsystem. */ @@ -495,60 +722,46 @@ GST_validation_stop () } -/** - * Address validation cleanup task (record no longer needed). - * - * @param cls the 'struct ValidationEntry' - * @param tc scheduler context (unused) - */ -static void -timeout_hello_validation (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct ValidationEntry *ve = cls; - - ve->timeout_task = GNUNET_SCHEDULER_NO_TASK; - GNUNET_STATISTICS_update (GST_stats, - gettext_noop ("# address records discarded"), 1, - GNUNET_NO); - cleanup_validation_entry (NULL, &ve->pid.hashPubKey, ve); -} - - /** * Send the given PONG to the given address. * * @param cls the PONG message * @param public_key public key for the peer, never NULL - * @param target peer this change is about, never NULL * @param valid_until is ZERO if we never validated the address, * otherwise a time up to when we consider it (or was) valid * @param validation_block is FOREVER if the address is for an unsupported plugin (from PEERINFO) * is ZERO if the address is considered valid (no validation needed) * otherwise a time in the future if we're currently denying re-validation - * @param plugin_name name of the plugin - * @param plugin_address binary address - * @param plugin_address_len length of address + * @param address target address */ static void multicast_pong (void *cls, const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded - *public_key, const struct GNUNET_PeerIdentity *target, - struct GNUNET_TIME_Absolute valid_until, + *public_key, struct GNUNET_TIME_Absolute valid_until, struct GNUNET_TIME_Absolute validation_block, - const char *plugin_name, const void *plugin_address, - size_t plugin_address_len) + const struct GNUNET_HELLO_Address *address) { struct TransportPongMessage *pong = cls; struct GNUNET_TRANSPORT_PluginFunctions *papi; - papi = GST_plugins_find (plugin_name); + papi = GST_plugins_find (address->transport_name); if (papi == NULL) return; - (void) papi->send (papi->cls, target, (const char *) pong, - ntohs (pong->header.size), PONG_PRIORITY, - HELLO_REVALIDATION_START_TIME, NULL, plugin_address, - plugin_address_len, GNUNET_YES, NULL, NULL); + + GNUNET_assert (papi->send != NULL); + GNUNET_assert (papi->get_session != NULL); + + struct Session * session = papi->get_session(papi->cls, address); + if (session == NULL) + { + GNUNET_break (0); + return; + } + + papi->send (papi->cls, session, + (const char *) pong, ntohs (pong->header.size), + PONG_PRIORITY, ACCEPTABLE_PING_DELAY, + NULL, NULL); } @@ -557,18 +770,14 @@ multicast_pong (void *cls, * * @param sender peer sending the PING * @param hdr the PING + * @param sender_address the sender address as we got it * @param session session we got the PING from - * @param plugin_name name of plugin that received the PING - * @param sender_address address of the sender as known to the plugin, NULL - * if we did not initiate the connection - * @param sender_address_len number of bytes in sender_address */ void GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, const struct GNUNET_MessageHeader *hdr, - const char *plugin_name, struct Session *session, - const void *sender_address, - size_t sender_address_len) + const struct GNUNET_HELLO_Address *sender_address, + struct Session *session) { const struct TransportPingMessage *ping; struct TransportPongMessage *pong; @@ -580,6 +789,8 @@ GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, size_t alen; size_t slen; ssize_t ret; + int buggy = GNUNET_NO; + struct GNUNET_HELLO_Address address; if (ntohs (hdr->size) < sizeof (struct TransportPingMessage)) { @@ -591,7 +802,10 @@ GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, memcmp (&ping->target, &GST_my_identity, sizeof (struct GNUNET_PeerIdentity))) { - GNUNET_break_op (0); + GNUNET_STATISTICS_update (GST_stats, + gettext_noop + ("# PING message for different peer received"), 1, + GNUNET_NO); return; } GNUNET_STATISTICS_update (GST_stats, @@ -602,24 +816,73 @@ GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, /* peer wants to confirm that this is one of our addresses, this is what is * used for address validation */ - addrend = memchr (addr, '\0', alen); - if (NULL == addrend) + sig_cache = NULL; + sig_cache_exp = NULL; + + if (0 < alen) { - GNUNET_break_op (0); - return; + addrend = memchr (addr, '\0', alen); + if (NULL == addrend) + { + GNUNET_break_op (0); + return; + } + addrend++; + slen = strlen (addr) + 1; + alen -= slen; + address.address = addrend; + address.address_length = alen; + address.transport_name = addr; + address.peer = GST_my_identity; + + + if (GNUNET_YES != GST_hello_test_address (&address, &sig_cache, &sig_cache_exp)) + { +#if KEEP_093_COMPATIBILITY + int idsize = sizeof (GST_my_identity); + if (alen <= idsize) + { + if (0 == memcmp (address.address, &GST_my_identity, alen)) + buggy = GNUNET_YES; + } + else if (alen <= (idsize + strlen (address.transport_name))) + { + char *achar = (char *) &address.address; + if ((0 == memcmp (address.address, &GST_my_identity, idsize)) && + (0 == memcmp (&achar[idsize], address.transport_name, alen - idsize))) + buggy = GNUNET_YES; + } + else + { + /* Not predicatable */ + return; + } +#endif + if (GNUNET_NO == buggy) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Not confirming PING from peer `%s' with address `%s' since I cannot confirm having this address.\n", + GNUNET_i2s (sender), + GST_plugins_a2s (&address)); + return; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Received a PING message with validation bug from `%s'\n"), + GNUNET_i2s (sender)); + } + } } - addrend++; - slen = strlen (addr); - alen -= slen; - - if (GNUNET_YES != - GST_hello_test_address (addr, addrend, alen, &sig_cache, &sig_cache_exp)) + else { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - _ - ("Not confirming PING with address `%s' since I cannot confirm having this address.\n"), - GST_plugins_a2s (addr, addrend, alen)); - return; + addrend = NULL; /* make gcc happy */ + slen = 0; + static struct GNUNET_CRYPTO_RsaSignature no_address_signature; + static struct GNUNET_TIME_Absolute no_address_signature_expiration; + + sig_cache = &no_address_signature; + sig_cache_exp = &no_address_signature_expiration; } pong = GNUNET_malloc (sizeof (struct TransportPongMessage) + alen + slen); @@ -634,38 +897,91 @@ GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, pong->challenge = ping->challenge; pong->addrlen = htonl (alen + slen); memcpy (&pong[1], addr, slen); - memcpy (&((char *) &pong[1])[slen], addrend, alen); - if (GNUNET_TIME_absolute_get_remaining (*sig_cache_exp).rel_value < - PONG_SIGNATURE_LIFETIME.rel_value / 4) +#if KEEP_093_COMPATIBILITY + if (GNUNET_YES == buggy) { - /* create / update cached sig */ -#if DEBUG_TRANSPORT - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Creating PONG signature to indicate ownership.\n"); -#endif - *sig_cache_exp = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME); - pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp); + int idsize = sizeof (GST_my_identity); + if (alen <= idsize) + { + memcpy (&((char *) &pong[1])[slen], &GST_my_identity, alen); + } + else if (alen <= (idsize + strlen (address.transport_name) + 1)) + { + memcpy (&((char *) &pong[1])[slen], &GST_my_identity, alen); + memcpy (&((char *) &pong[1])[slen + idsize], address.transport_name, alen-idsize); + } + else + { + /* If this would happen, we would have a inconsistent PING we cannot reproduce */ + GNUNET_free (pong); + return; + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating buggy PONG signature to indicate ownership.\n"); + pong->expiration = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME)); GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_rsa_sign (GST_my_private_key, &pong->purpose, - sig_cache)); + &pong->signature)); } else { - pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp); +#endif + memcpy (&((char *) &pong[1])[slen], addrend, alen); + if (GNUNET_TIME_absolute_get_remaining (*sig_cache_exp).rel_value < + PONG_SIGNATURE_LIFETIME.rel_value / 4) + { + /* create / update cached sig */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating PONG signature to indicate ownership.\n"); + *sig_cache_exp = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME); + pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_rsa_sign (GST_my_private_key, &pong->purpose, + sig_cache)); + } + else + { + pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp); + } + pong->signature = *sig_cache; + +#if KEEP_093_COMPATIBILITY } - pong->signature = *sig_cache; +#endif + + GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN, + &pong->purpose, &pong->signature, + &GST_my_public_key)); + + GNUNET_assert (sender_address != NULL); /* first see if the session we got this PING from can be used to transmit * a response reliably */ - papi = GST_plugins_find (plugin_name); + papi = GST_plugins_find (sender_address->transport_name); if (papi == NULL) ret = -1; else - ret = - papi->send (papi->cls, sender, (const char *) pong, - ntohs (pong->header.size), PONG_PRIORITY, - HELLO_REVALIDATION_START_TIME, session, sender_address, - sender_address_len, GNUNET_SYSERR, NULL, NULL); + { + GNUNET_assert (papi->send != NULL); + GNUNET_assert (papi->get_session != NULL); + + if (session == NULL) + { + session = papi->get_session (papi->cls, sender_address); + } + if (session == NULL) + { + GNUNET_break (0); + ret = -1; + } + else + { + ret = papi->send (papi->cls, session, + (const char *) pong, ntohs (pong->header.size), + PONG_PRIORITY, ACCEPTABLE_PING_DELAY, + NULL, NULL); + } + } if (ret != -1) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -707,167 +1023,50 @@ struct ValidateAddressContext }; -/** - * Function called with the result from blacklisting. - * Send a PING to the other peer if a communication is allowed. - * - * @param cls ou r'struct ValidationEntry' - * @param pid identity of the other peer - * @param result GNUNET_OK if the connection is allowed, GNUNET_NO if not - */ -static void -transmit_ping_if_allowed (void *cls, const struct GNUNET_PeerIdentity *pid, - int result) -{ - struct ValidationEntry *ve = cls; - struct TransportPingMessage ping; - struct GNUNET_TRANSPORT_PluginFunctions *papi; - const struct GNUNET_MessageHeader *hello; - ssize_t ret; - size_t tsize; - size_t slen; - uint16_t hsize; - - ve->bc = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Transmitting plain PING to `%s'\n", - GNUNET_i2s (pid)); - ping.header.size = htons (sizeof (struct TransportPingMessage)); - ping.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING); - ping.challenge = htonl (ve->challenge); - ping.target = *pid; - - slen = strlen (ve->transport_name) + 1; - hello = GST_hello_get (); - hsize = ntohs (hello->size); - tsize = sizeof (struct TransportPingMessage) + ve->addrlen + slen + hsize; - if (tsize >= GNUNET_SERVER_MAX_MESSAGE_SIZE) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Not transmitting `%s' with `%s', message too big (%u bytes!). This should not happen.\n"), - "HELLO", "PING", (unsigned int) tsize); - /* message too big (!?), get rid of HELLO */ - hsize = 0; - tsize = sizeof (struct TransportPingMessage) + ve->addrlen + slen + hsize; - } - { - char message_buf[tsize]; - - memcpy (message_buf, hello, hsize); - memcpy (&message_buf[hsize], &ping, sizeof (struct TransportPingMessage)); - memcpy (&message_buf[sizeof (struct TransportPingMessage) + hsize], - ve->transport_name, slen); - memcpy (&message_buf[sizeof (struct TransportPingMessage) + slen + hsize], - ve->addr, ve->addrlen); - papi = GST_plugins_find (ve->transport_name); - if (papi == NULL) - ret = -1; - else - ret = - papi->send (papi->cls, pid, message_buf, tsize, PING_PRIORITY, - HELLO_REVALIDATION_START_TIME, NULL /* no session */ , - ve->addr, ve->addrlen, GNUNET_YES, NULL, NULL); - } - if (-1 != ret) - { - ve->send_time = GNUNET_TIME_absolute_get (); - GNUNET_STATISTICS_update (GST_stats, - gettext_noop - ("# PING without HELLO messages sent"), 1, - GNUNET_NO); - } -} - - /** * Iterator callback to go over all addresses and try to validate them * (unless blocked or already validated). * * @param cls pointer to a 'struct ValidateAddressContext' - * @param tname name of the transport + * @param address the address * @param expiration expiration time - * @param addr the address - * @param addrlen length of the address * @return GNUNET_OK (keep the address) */ static int -validate_address (void *cls, const char *tname, - struct GNUNET_TIME_Absolute expiration, const void *addr, - uint16_t addrlen) +validate_address_iterator (void *cls, + const struct GNUNET_HELLO_Address *address, + struct GNUNET_TIME_Absolute expiration) { const struct ValidateAddressContext *vac = cls; - const struct GNUNET_PeerIdentity *pid = &vac->pid; struct ValidationEntry *ve; - struct GST_BlacklistCheck *bc; if (GNUNET_TIME_absolute_get_remaining (expiration).rel_value == 0) return GNUNET_OK; /* expired */ - ve = find_validation_entry (&vac->public_key, pid, tname, addr, addrlen); - if (GNUNET_TIME_absolute_get_remaining (ve->validation_block).rel_value > 0) - return GNUNET_OK; /* blocked */ - if ((GNUNET_SCHEDULER_NO_TASK != ve->timeout_task) && - (GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value > 0)) - return GNUNET_OK; /* revalidation task already scheduled & still valid */ - ve->validation_block = - GNUNET_TIME_relative_to_absolute (HELLO_REVALIDATION_START_TIME); - if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task) - GNUNET_SCHEDULER_cancel (ve->timeout_task); - ve->timeout_task = - GNUNET_SCHEDULER_add_delayed (HELLO_REVALIDATION_START_TIME, - &timeout_hello_validation, ve); - bc = GST_blacklist_test_allowed (pid, tname, &transmit_ping_if_allowed, ve); - if (NULL != bc) - ve->bc = bc; + ve = find_validation_entry (&vac->public_key, address); + if (GNUNET_SCHEDULER_NO_TASK == ve->revalidation_task) + ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve); return GNUNET_OK; } -/** - * Do address validation again to keep address valid. - * - * @param cls the 'struct ValidationEntry' - * @param tc scheduler context (unused) - */ -static void -revalidate_address (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct ValidationEntry *ve = cls; - struct GNUNET_TIME_Relative delay; - struct ValidateAddressContext vac; - - ve->timeout_task = GNUNET_SCHEDULER_NO_TASK; - delay = GNUNET_TIME_absolute_get_remaining (ve->validation_block); - if (delay.rel_value > 0) - { - /* should wait a bit longer */ - ve->timeout_task = - GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve); - return; - } - GNUNET_STATISTICS_update (GST_stats, - gettext_noop ("# address revalidations started"), 1, - GNUNET_NO); - vac.pid = ve->pid; - vac.public_key = ve->public_key; - validate_address (&vac, ve->transport_name, ve->valid_until, ve->addr, - (uint16_t) ve->addrlen); -} - - /** * Add the validated peer address to the HELLO. * * @param cls the 'struct ValidationEntry' with the validated address * @param max space in buf * @param buf where to add the address + * @return number of bytes written, 0 to signal the + * end of the iteration. */ static size_t add_valid_peer_address (void *cls, size_t max, void *buf) { struct ValidationEntry *ve = cls; - return GNUNET_HELLO_add_address (ve->transport_name, ve->valid_until, - ve->addr, ve->addrlen, buf, max); + if (GNUNET_YES == ve->copied) + return 0; /* terminate */ + ve->copied = GNUNET_YES; + return GNUNET_HELLO_add_address (ve->address, ve->valid_until, buf, max); } @@ -884,13 +1083,13 @@ GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender, { const struct TransportPongMessage *pong; struct ValidationEntry *ve; + const char *tname; const char *addr; - const char *addrend; - size_t alen; + size_t addrlen; size_t slen; - uint32_t rdelay; - struct GNUNET_TIME_Relative delay; + size_t size; struct GNUNET_HELLO_Message *hello; + struct GNUNET_HELLO_Address address; if (ntohs (hdr->size) < sizeof (struct TransportPongMessage)) { @@ -900,20 +1099,25 @@ GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender, GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# PONG messages received"), 1, GNUNET_NO); + pong = (const struct TransportPongMessage *) hdr; - addr = (const char *) &pong[1]; - alen = ntohs (hdr->size) - sizeof (struct TransportPongMessage); - addrend = memchr (addr, '\0', alen); - if (NULL == addrend) + tname = (const char *) &pong[1]; + size = ntohs (hdr->size) - sizeof (struct TransportPongMessage); + addr = memchr (tname, '\0', size); + if (NULL == addr) { GNUNET_break_op (0); return; } - addrend++; - slen = strlen (addr); - alen -= slen; - ve = find_validation_entry (NULL, sender, addr, addrend, alen); - if (NULL == ve) + addr++; + slen = strlen (tname) + 1; + addrlen = size - slen; + address.peer = *sender; + address.address = addr; + address.address_length = addrlen; + address.transport_name = tname; + ve = find_validation_entry (NULL, &address); + if ((NULL == ve) || (ve->expecting_pong == GNUNET_NO)) { GNUNET_STATISTICS_update (GST_stats, gettext_noop @@ -928,15 +1132,6 @@ GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender, return; } - if (GNUNET_TIME_absolute_get_remaining - (GNUNET_TIME_absolute_ntoh (pong->expiration)).rel_value == 0) - { - GNUNET_STATISTICS_update (GST_stats, - gettext_noop - ("# PONGs dropped, signature expired"), 1, - GNUNET_NO); - return; - } if (GNUNET_OK != GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN, &pong->purpose, &pong->signature, @@ -946,26 +1141,33 @@ GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender, return; } + if (GNUNET_TIME_absolute_get_remaining + (GNUNET_TIME_absolute_ntoh (pong->expiration)).rel_value == 0) + { + GNUNET_STATISTICS_update (GST_stats, + gettext_noop + ("# PONGs dropped, signature expired"), 1, + GNUNET_NO); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Address validated for peer `%s' with plugin `%s': `%s'\n", + GNUNET_i2s (sender), tname, GST_plugins_a2s (ve->address)); /* validity achieved, remember it! */ + ve->expecting_pong = GNUNET_NO; ve->valid_until = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION); - GNUNET_ATS_address_update (GST_ats, &ve->pid, ve->valid_until, ve->transport_name, NULL, ve->addr, ve->addrlen, NULL, 0); /* FIXME: compute and add latency here... */ - + ve->latency = GNUNET_TIME_absolute_get_duration (ve->send_time); + { + struct GNUNET_ATS_Information ats; + ats.type = htonl (GNUNET_ATS_QUALITY_NET_DELAY); + ats.value = htonl ((uint32_t) ve->latency.rel_value); + GNUNET_ATS_address_add (GST_ats, ve->address, NULL, &ats, 1); + } /* build HELLO to store in PEERINFO */ + ve->copied = GNUNET_NO; hello = GNUNET_HELLO_create (&ve->public_key, &add_valid_peer_address, ve); - GNUNET_PEERINFO_add_peer (GST_peerinfo, hello); + GNUNET_PEERINFO_add_peer (GST_peerinfo, hello, NULL, NULL); GNUNET_free (hello); - - if (GNUNET_SCHEDULER_NO_TASK != ve->timeout_task) - GNUNET_SCHEDULER_cancel (ve->timeout_task); - - /* randomly delay by up to 1h to avoid synchronous validations */ - rdelay = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 60 * 60); - delay = - GNUNET_TIME_relative_add (HELLO_REVALIDATION_START_TIME, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, rdelay)); - ve->timeout_task = - GNUNET_SCHEDULER_add_delayed (delay, &revalidate_address, ve); } @@ -981,6 +1183,7 @@ GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello) const struct GNUNET_HELLO_Message *hm = (const struct GNUNET_HELLO_Message *) hello; struct ValidateAddressContext vac; + struct GNUNET_HELLO_Message *h; if ((GNUNET_OK != GNUNET_HELLO_get_id (hm, &vac.pid)) || (GNUNET_OK != GNUNET_HELLO_get_key (hm, &vac.public_key))) @@ -989,9 +1192,22 @@ GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello) GNUNET_break (0); return; } + if (0 == + memcmp (&GST_my_identity, &vac.pid, sizeof (struct GNUNET_PeerIdentity))) + return; + /* Add peer identity without addresses to peerinfo service */ + h = GNUNET_HELLO_create (&vac.public_key, NULL, NULL); + GNUNET_PEERINFO_add_peer (GST_peerinfo, h, NULL, NULL); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Adding `%s' without addresses for peer `%s'\n"), "HELLO", + GNUNET_i2s (&vac.pid)); + + GNUNET_free (h); GNUNET_assert (NULL == GNUNET_HELLO_iterate_addresses (hm, GNUNET_NO, - &validate_address, &vac)); + &validate_address_iterator, + &vac)); } @@ -1022,13 +1238,13 @@ struct IteratorContext * @return GNUNET_OK (continue to iterate) */ static int -iterate_addresses (void *cls, const GNUNET_HashCode * key, void *value) +iterate_addresses (void *cls, const struct GNUNET_HashCode * key, void *value) { struct IteratorContext *ic = cls; struct ValidationEntry *ve = value; - ic->cb (ic->cb_cls, &ve->public_key, &ve->pid, ve->valid_until, - ve->validation_block, ve->transport_name, ve->addr, ve->addrlen); + ic->cb (ic->cb_cls, &ve->public_key, ve->valid_until, ve->revalidation_block, + ve->address); return GNUNET_OK; } @@ -1038,12 +1254,8 @@ iterate_addresses (void *cls, const GNUNET_HashCode * key, void *value) * Can either give a snapshot (synchronous API) or be continuous. * * @param target peer information is requested for - * @param snapshot_only GNUNET_YES to iterate over addresses once, GNUNET_NO to - * continue to give information about addresses as it evolves * @param cb function to call; will not be called after this function returns - * if snapshot_only is GNUNET_YES * @param cb_cls closure for 'cb' - * @return context to cancel, NULL if 'snapshot_only' is GNUNET_YES */ void GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target, @@ -1059,4 +1271,100 @@ GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target, } +/** + * Update if we are using an address for a connection actively right now. + * Based on this, the validation module will measure latency for the + * address more or less often. + * + * @param address the address + * @param session the session + * @param in_use GNUNET_YES if we are now using the address for a connection, + * GNUNET_NO if we are no longer using the address for a connection + * @param line line of caller just for DEBUGGING! + */ +void +GST_validation_set_address_use (const struct GNUNET_HELLO_Address *address, + struct Session *session, + int in_use, + int line) +{ + struct ValidationEntry *ve; + + if (NULL != address) + ve = find_validation_entry (NULL, address); + else + ve = NULL; /* FIXME: lookup based on session... */ + if (NULL == ve) + { + /* this can happen for inbound connections (sender_address_len == 0); */ + return; + } + if (ve->in_use == in_use) + { + + if (GNUNET_YES == in_use) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error setting address in use for peer `%s' `%s' to USED: set last time by %i, called now by %i\n", + GNUNET_i2s (&address->peer), GST_plugins_a2s (address), + ve->last_line_set_to_yes, line); + } + if (GNUNET_NO == in_use) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Error setting address in use for peer `%s' `%s' to NOT_USED: set last time by %i, called now by %i\n", + GNUNET_i2s (&address->peer), GST_plugins_a2s (address), + ve->last_line_set_to_no, line); + } + } + + if (GNUNET_YES == in_use) + { + ve->last_line_set_to_yes = line; + } + if (GNUNET_NO == in_use) + { + ve->last_line_set_to_no = line; + } + + GNUNET_break (ve->in_use != in_use); /* should be different... */ + ve->in_use = in_use; + if (in_use == GNUNET_YES) + { + /* from now on, higher frequeny, so reschedule now */ + GNUNET_SCHEDULER_cancel (ve->revalidation_task); + ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve); + } +} + + +/** + * Query validation about the latest observed latency on a given + * address. + * + * @param sender peer + * @param address the address + * @param session session + * @return observed latency of the address, FOREVER if the address was + * never successfully validated + */ +struct GNUNET_TIME_Relative +GST_validation_get_address_latency (const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_HELLO_Address *address, + struct Session *session) +{ + struct ValidationEntry *ve; + + if (NULL == address) + { + GNUNET_break (0); // FIXME: support having latency only with session... + return GNUNET_TIME_UNIT_FOREVER_REL; + } + ve = find_validation_entry (NULL, address); + if (NULL == ve) + return GNUNET_TIME_UNIT_FOREVER_REL; + return ve->latency; +} + + /* end of file gnunet-service-transport_validation.c */