/** * 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); } }
/** * Add or remove an address from this peer's HELLO message. * * @param addremove GNUNET_YES to add, GNUNET_NO to remove * @param address address to add or remove */ void GST_hello_modify_addresses (int addremove, const struct GNUNET_HELLO_Address *address) { struct OwnAddressList *al; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, (addremove == GNUNET_YES) ? "Adding `%s' to the set of our addresses\n" : "Removing `%s' from the set of our addresses\n", GST_plugins_a2s (address)); GNUNET_assert (address != NULL); if (GNUNET_NO == addremove) { for (al = oal_head; al != NULL; al = al->next) if (0 == GNUNET_HELLO_address_cmp (address, al->address)) { GNUNET_CONTAINER_DLL_remove (oal_head, oal_tail, al); GNUNET_HELLO_address_free (al->address); GNUNET_free (al); refresh_hello (); return; } /* address to be removed not found!? */ GNUNET_break (0); return; } al = GNUNET_malloc (sizeof (struct OwnAddressList)); GNUNET_CONTAINER_DLL_insert (oal_head, oal_tail, al); al->address = GNUNET_HELLO_address_copy (address); refresh_hello (); }
/** * Output information of neighbours to the given client. * * @param cls the 'struct PeerIterationContext' * @param peer identity of the neighbour * @param address the address * @param state current state this peer is in * @param state_timeout timeout for the current state of the peer * @param bandwidth_in inbound quota in NBO * @param bandwidth_out outbound quota in NBO */ static void send_peer_information (void *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Address *address, enum GNUNET_TRANSPORT_PeerState state, struct GNUNET_TIME_Absolute state_timeout, struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in, struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out) { struct IterationContext *pc = cls; struct PeerIterateResponseMessage *msg; if ( (GNUNET_YES == pc->all) || (0 == memcmp (peer, &pc->id, sizeof (pc->id))) ) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending information about `%s' using address `%s' in state `%s'\n", GNUNET_i2s(peer), (address != NULL) ? GST_plugins_a2s (address) : "<none>", GNUNET_TRANSPORT_ps2s (state)); msg = compose_address_iterate_response_message (peer, address); msg->state = htonl (state); msg->state_timeout = GNUNET_TIME_absolute_hton(state_timeout); GNUNET_SERVER_transmit_context_append_message (pc->tc, &msg->header); GNUNET_free (msg); } }
/** * Output information of validation entries to the given client. * * @param cls the 'struct IterationContext' * @param peer identity of the neighbour * @param address the address * @param last_validation point in time when last validation was performed * @param valid_until point in time how long address is valid * @param next_validation point in time when next validation will be performed * @param state state of validation notification */ static void send_validation_information (void *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Address *address, struct GNUNET_TIME_Absolute last_validation, struct GNUNET_TIME_Absolute valid_until, struct GNUNET_TIME_Absolute next_validation, enum GNUNET_TRANSPORT_ValidationState state) { struct IterationContext *pc = cls; struct ValidationIterateResponseMessage *msg; if ( (GNUNET_YES == pc->all) || (0 == memcmp (peer, &pc->id, sizeof (pc->id))) ) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending information about for validation entry for peer `%s' using address `%s'\n", GNUNET_i2s(peer), (address != NULL) ? GST_plugins_a2s (address) : "<none>"); msg = compose_validation_iterate_response_message (peer, address); msg->last_validation = GNUNET_TIME_absolute_hton(last_validation); msg->valid_until = GNUNET_TIME_absolute_hton(valid_until); msg->next_validation = GNUNET_TIME_absolute_hton(next_validation); msg->state = htonl ((uint32_t) state); GNUNET_SERVER_transmit_context_append_message (pc->tc, &msg->header); GNUNET_free (msg); } }
/** * Notify ATS about utilization changes to an @a address. * Does nothing if the @a address is not known to us. * * @param address our information about the address * @param bps_in new utilization inbound * @param bps_out new utilization outbound */ void GST_ats_update_utilization (const struct GNUNET_HELLO_Address *address, uint32_t bps_in, uint32_t bps_out) { struct AddressInfo *ai; ai = find_ai_no_session (address); if (NULL == ai) { /* We do not know about this address, do nothing. */ return; } LOG (GNUNET_ERROR_TYPE_DEBUG, "Updating utilization for peer `%s' address %s: %u/%u\n", GNUNET_i2s (&address->peer), GST_plugins_a2s (address), (unsigned int) bps_in, (unsigned int) bps_out); ai->properties.utilization_in = bps_in; ai->properties.utilization_out = bps_out; /* Give manipulation its chance to change metrics */ GST_manipulation_manipulate_metrics (address, ai->session, &ai->properties); /* Address may be blocked, only give ATS if address is currently active. */ if (NULL != ai->ar) GNUNET_ATS_address_update (ai->ar, &ai->properties); }
/** * Notify ATS that the address has expired and thus cannot * be used any longer. This function must only be called * if the corresponding session is already gone. * * @param address the address */ void GST_ats_expire_address (const struct GNUNET_HELLO_Address *address) { struct AddressInfo *ai; if (0 == memcmp (&GST_my_identity, &address->peer, sizeof (struct GNUNET_PeerIdentity))) return; /* our own, ignore! */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Address %s of peer %s expired\n", GST_plugins_a2s (address), GNUNET_i2s (&address->peer)); ai = find_ai_no_session (address); if (NULL == ai) { GNUNET_assert (0); return; } if (NULL != ai->session) { /* Got an active session, just remember the expiration and act upon it when the session goes down. */ ai->expired = GNUNET_YES; return; } /* Address expired, no session, free resources */ destroy_ai (ai); }
/** * Notify ATS about a new inbound @a address. The @a address in * combination with the @a session must be new, but this function will * perform a santiy check. If the @a address is indeed new, make it * available to ATS. * * @param address the address * @param session the session * @param prop performance information */ void GST_ats_add_inbound_address (const struct GNUNET_HELLO_Address *address, struct GNUNET_ATS_Session *session, const struct GNUNET_ATS_Properties *prop) { struct GNUNET_ATS_AddressRecord *ar; struct AddressInfo *ai; if (0 == memcmp (&GST_my_identity, &address->peer, sizeof (struct GNUNET_PeerIdentity))) return; /* our own, ignore! */ /* Sanity checks for a valid inbound address */ if (NULL == address->transport_name) { GNUNET_break(0); return; } GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != prop->scope); GNUNET_assert (GNUNET_YES == GNUNET_HELLO_address_check_option (address, GNUNET_HELLO_ADDRESS_INFO_INBOUND)); GNUNET_assert (NULL != session); ai = find_ai (address, session); if (NULL != ai) { /* This should only be called for new sessions, and thus we should not already have the address */ GNUNET_break (0); return; } /* Is indeed new, let's tell ATS */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Notifying ATS about peer `%s''s new inbound address `%s' session %p in network %s\n", GNUNET_i2s (&address->peer), GST_plugins_a2s (address), session, GNUNET_ATS_print_network_type (prop->scope)); ar = GNUNET_ATS_address_add (GST_ats, address, session, prop); GNUNET_assert (NULL != ar); ai = GNUNET_new (struct AddressInfo); ai->address = GNUNET_HELLO_address_copy (address); ai->session = session; ai->properties = *prop; ai->ar = ar; (void) GNUNET_CONTAINER_multipeermap_put (p2a, &ai->address->peer, ai, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); publish_p2a_stat_update (); }
/** * Add or remove an address from this peer's HELLO message. * * @param addremove #GNUNET_YES to add, #GNUNET_NO to remove * @param address address to add or remove */ void GST_hello_modify_addresses (int addremove, const struct GNUNET_HELLO_Address *address) { struct OwnAddressList *al; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, (GNUNET_YES == addremove) ? "Adding `%s' to the set of our addresses\n" : "Removing `%s' from the set of our addresses\n", GST_plugins_a2s (address)); GNUNET_assert (NULL != address); for (al = oal_head; al != NULL; al = al->next) if (0 == GNUNET_HELLO_address_cmp (address, al->address)) break; if (GNUNET_NO == addremove) { if (NULL == al) { /* address to be removed not found!? */ GNUNET_break (0); return; } al->rc--; if (0 != al->rc) return; /* RC not yet zero */ GNUNET_CONTAINER_DLL_remove (oal_head, oal_tail, al); GNUNET_HELLO_address_free (al->address); GNUNET_free (al); refresh_hello (); return; } if (NULL != al) { /* address added twice or more */ al->rc++; return; } al = GNUNET_new (struct OwnAddressList); al->rc = 1; GNUNET_CONTAINER_DLL_insert (oal_head, oal_tail, al); al->address = GNUNET_HELLO_address_copy (address); refresh_hello (); }
/** * Plugin tells transport service about a new inbound session * * @param cls unused * @param address the address * @param session the new session * @param scope network scope information */ static void plugin_env_session_start (void *cls, const struct GNUNET_HELLO_Address *address, struct GNUNET_ATS_Session *session, enum GNUNET_ATS_Network_Type scope) { struct GNUNET_ATS_Properties prop; if (NULL == address) { GNUNET_break(0); return; } if (NULL == session) { GNUNET_break(0); return; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Notification from plugin `%s' about new session from peer `%s' address `%s'\n", address->transport_name, GNUNET_i2s (&address->peer), GST_plugins_a2s (address)); if (GNUNET_YES == GNUNET_HELLO_address_check_option (address, GNUNET_HELLO_ADDRESS_INFO_INBOUND)) { /* inbound is always new, but outbound MAY already be known, but for example for UNIX, we have symmetric connections and thus we may not know the address yet; add if necessary! */ /* FIXME: maybe change API here so we just pass scope? */ memset (&prop, 0, sizeof (prop)); GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != scope); prop.scope = scope; GST_ats_add_inbound_address (address, session, &prop); } /* Do blacklist check if communication with this peer is allowed */ (void) GST_blacklist_test_allowed (&address->peer, address->transport_name, &plugin_env_session_start_bl_check_cont, NULL, address, session); }
/** * Notify ATS about the new address including the network this address is * located in. The address must NOT be inbound and must be new to ATS. * * @param address the address * @param prop performance information */ void GST_ats_add_address (const struct GNUNET_HELLO_Address *address, const struct GNUNET_ATS_Properties *prop) { struct GNUNET_ATS_AddressRecord *ar; struct AddressInfo *ai; if (0 == memcmp (&GST_my_identity, &address->peer, sizeof (struct GNUNET_PeerIdentity))) return; /* our own, ignore! */ /* validadte address */ if (NULL == address->transport_name) { GNUNET_break(0); return; } GNUNET_assert (GNUNET_YES != GNUNET_HELLO_address_check_option (address, GNUNET_HELLO_ADDRESS_INFO_INBOUND)); ai = find_ai_no_session (address); GNUNET_assert (NULL == ai); GNUNET_break (GNUNET_ATS_NET_UNSPECIFIED != prop->scope); /* address seems sane, let's tell ATS */ LOG (GNUNET_ERROR_TYPE_INFO, "Notifying ATS about peer %s's new address `%s'\n", GNUNET_i2s (&address->peer), GST_plugins_a2s (address)); ar = GNUNET_ATS_address_add (GST_ats, address, NULL, prop); GNUNET_assert (NULL != ar); ai = GNUNET_new (struct AddressInfo); ai->address = GNUNET_HELLO_address_copy (address); ai->ar = ar; ai->properties = *prop; (void) GNUNET_CONTAINER_multipeermap_put (p2a, &ai->address->peer, ai, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); publish_p2a_stat_update (); }
/** * The blocking time for an address has expired, allow ATS to * suggest it again. * * @param cls the `struct AddressInfo` of the address to unblock * @param tc unused */ static void unblock_address (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct AddressInfo *ai = cls; ai->unblock_task = NULL; LOG (GNUNET_ERROR_TYPE_DEBUG, "Unblocking address %s of peer %s\n", GST_plugins_a2s (ai->address), GNUNET_i2s (&ai->address->peer)); ai->ar = GNUNET_ATS_address_add (GST_ats, ai->address, ai->session, &ai->properties); GNUNET_break (NULL != ai->ar); num_blocked--; publish_p2a_stat_update (); }
/** * Function that will be called whenever the plugin internally * cleans up a session pointer and hence the service needs to * discard all of those sessions as well. Plugins that do not * use sessions can simply omit calling this function and always * use NULL wherever a session pointer is needed. This function * should be called BEFORE a potential "TransmitContinuation" * from the "TransmitFunction". * * @param cls closure * @param address which address was the session for * @param session which session is being destoyed */ static void plugin_env_session_end (void *cls, const struct GNUNET_HELLO_Address *address, struct GNUNET_ATS_Session *session) { struct GNUNET_ATS_SessionKiller *sk; if (NULL == address) { GNUNET_break (0); return; } if (NULL == session) { GNUNET_break (0); return; } GNUNET_assert (strlen (address->transport_name) > 0); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Notification from plugin about terminated session %p from peer `%s' address `%s'\n", session, GNUNET_i2s (&address->peer), GST_plugins_a2s (address)); GST_neighbours_session_terminated (&address->peer, session); GST_ats_del_session (address, session); GST_blacklist_abort_matching (address, session); for (sk = sk_head; NULL != sk; sk = sk->next) { if (sk->session == session) { GNUNET_CONTAINER_DLL_remove (sk_head, sk_tail, sk); GNUNET_SCHEDULER_cancel (sk->task); GNUNET_free(sk); break; } } }
/** * Broadcast the new validation changes to all clients monitoring the peer. * * @param peer peer this update is about (never NULL) * @param address address, NULL on disconnect * @param last_validation point in time when last validation was performed * @param valid_until point in time how long address is valid * @param next_validation point in time when next validation will be performed * @param state state of validation notification */ void GST_clients_broadcast_validation_notification ( const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Address *address, struct GNUNET_TIME_Absolute last_validation, struct GNUNET_TIME_Absolute valid_until, struct GNUNET_TIME_Absolute next_validation, enum GNUNET_TRANSPORT_ValidationState state) { struct ValidationIterateResponseMessage *msg; struct MonitoringClient *mc; static struct GNUNET_PeerIdentity all_zeros; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending information about for validation entry for peer `%s' using address `%s'\n", GNUNET_i2s(peer), (address != NULL) ? GST_plugins_a2s (address) : "<none>"); msg = compose_validation_iterate_response_message (peer, address); msg->last_validation = GNUNET_TIME_absolute_hton(last_validation); msg->valid_until = GNUNET_TIME_absolute_hton(valid_until); msg->next_validation = GNUNET_TIME_absolute_hton(next_validation); msg->state = htonl ((uint32_t) state); mc = val_monitoring_clients_head; while (mc != NULL) { if ((0 == memcmp (&mc->peer, &all_zeros, sizeof (struct GNUNET_PeerIdentity))) || (0 == memcmp (&mc->peer, peer, sizeof (struct GNUNET_PeerIdentity)))) { GNUNET_SERVER_notification_context_unicast (val_nc, mc->client, &msg->header, GNUNET_NO); } mc = mc->next; } GNUNET_free (msg); }
/** * Black list check result for try_connect call * If connection to the peer is allowed request adddress and ??? * * @param cls the message * @param peer the peer * @param address the address * @param session the session * @param result the result */ static void connect_bl_check_cont (void *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Address *address, struct GNUNET_ATS_Session *session, int result) { struct GNUNET_MessageHeader *msg = cls; if (GNUNET_OK == result) { /* Blacklist allows to speak to this peer, forward SYN to neighbours */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Received SYN message from peer `%s' at `%s'\n", GNUNET_i2s (peer), GST_plugins_a2s (address)); if (GNUNET_OK != GST_neighbours_handle_session_syn (msg, peer)) { GST_blacklist_abort_matching (address, session); kill_session (address->transport_name, session); } GNUNET_free (msg); return; } GNUNET_free (msg); if (GNUNET_SYSERR == result) return; /* check was aborted, session destroyed */ /* Blacklist denies to speak to this peer */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Discarding SYN message from `%s' due to denied blacklist check\n", GNUNET_i2s (peer)); kill_session (address->transport_name, session); }
/** * Black list check result from blacklist check triggered when a * plugin gave us a new session in #plugin_env_session_start(). If * connection to the peer is disallowed, kill the session. * * @param cls NULL * @param peer the peer * @param address address associated with the request * @param session session associated with the request * @param result the result */ static void plugin_env_session_start_bl_check_cont (void *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_HELLO_Address *address, struct GNUNET_ATS_Session *session, int result) { if (GNUNET_OK != result) { kill_session (address->transport_name, session); return; } if (GNUNET_YES != GNUNET_HELLO_address_check_option (address, GNUNET_HELLO_ADDRESS_INFO_INBOUND)) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Informing verifier about inbound session's address `%s'\n", GST_plugins_a2s (address)); GST_validation_handle_address (address); } }
/** * We've received a PONG. Check if it matches a pending PING and * mark the respective address as confirmed. * * @param sender peer sending the PONG * @param hdr the PONG */ void GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender, const struct GNUNET_MessageHeader *hdr) { const struct TransportPongMessage *pong; struct ValidationEntry *ve; const char *tname; const char *addr; size_t addrlen; size_t slen; size_t size; struct GNUNET_HELLO_Message *hello; struct GNUNET_HELLO_Address address; if (ntohs (hdr->size) < sizeof (struct TransportPongMessage)) { GNUNET_break_op (0); return; } GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# PONG messages received"), 1, GNUNET_NO); pong = (const struct TransportPongMessage *) hdr; 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; } 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 ("# PONGs dropped, no matching pending validation"), 1, GNUNET_NO); return; } /* now check that PONG is well-formed */ if (0 != memcmp (&ve->pid, sender, sizeof (struct GNUNET_PeerIdentity))) { GNUNET_break_op (0); return; } if (GNUNET_OK != GNUNET_CRYPTO_rsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN, &pong->purpose, &pong->signature, &ve->public_key)) { GNUNET_break_op (0); 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); 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_update (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, NULL, NULL); GNUNET_free (hello); }
/** * 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\n", GNUNET_i2s (pid), GST_plugins_a2s (ve->address)); 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, 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; } }
/** * We've received a PING. If appropriate, generate a PONG. * * @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 */ void GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender, const struct GNUNET_MessageHeader *hdr, const struct GNUNET_HELLO_Address *sender_address, struct Session *session) { const struct TransportPingMessage *ping; struct TransportPongMessage *pong; struct GNUNET_TRANSPORT_PluginFunctions *papi; struct GNUNET_CRYPTO_RsaSignature *sig_cache; struct GNUNET_TIME_Absolute *sig_cache_exp; const char *addr; const char *addrend; size_t alen; size_t slen; ssize_t ret; struct GNUNET_HELLO_Address address; if (ntohs (hdr->size) < sizeof (struct TransportPingMessage)) { GNUNET_break_op (0); return; } ping = (const struct TransportPingMessage *) hdr; if (0 != memcmp (&ping->target, &GST_my_identity, sizeof (struct GNUNET_PeerIdentity))) { GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# PING message for different peer received"), 1, GNUNET_NO); return; } GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# PING messages received"), 1, GNUNET_NO); addr = (const char *) &ping[1]; alen = ntohs (hdr->size) - sizeof (struct TransportPingMessage); /* peer wants to confirm that this is one of our addresses, this is what is * used for address validation */ sig_cache = NULL; sig_cache_exp = NULL; if (0 < alen) { 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 = *sender; if (GNUNET_YES != GST_hello_test_address (&address, &sig_cache, &sig_cache_exp)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Not confirming PING with address `%s' since I cannot confirm having this address.\n"), GST_plugins_a2s (&address)); return; } } else { 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); pong->header.size = htons (sizeof (struct TransportPongMessage) + alen + slen); pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG); pong->purpose.size = htonl (sizeof (struct GNUNET_CRYPTO_RsaSignaturePurpose) + sizeof (uint32_t) + sizeof (struct GNUNET_TIME_AbsoluteNBO) + alen + slen); pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN); 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) { /* 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; 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 (sender_address->transport_name); if (papi == NULL) ret = -1; else { 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, "Transmitted PONG to `%s' via reliable mechanism\n", GNUNET_i2s (sender)); /* done! */ GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# PONGs unicast via reliable transport"), 1, GNUNET_NO); GNUNET_free (pong); return; } /* no reliable method found, try transmission via all known addresses */ GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# PONGs multicast to all available addresses"), 1, GNUNET_NO); GST_validation_get_addresses (sender, &multicast_pong, pong); GNUNET_free (pong); }
/** * Temporarily block a valid address for use by ATS for address * suggestions. This function should be called if an address was * suggested by ATS but failed to perform (i.e. failure to establish a * session or to exchange the PING/PONG). * * @param address the address to block * @param session the session (can be NULL) */ void GST_ats_block_address (const struct GNUNET_HELLO_Address *address, struct GNUNET_ATS_Session *session) { struct AddressInfo *ai; if (0 == memcmp (&GST_my_identity, &address->peer, sizeof (struct GNUNET_PeerIdentity))) return; /* our own, ignore! */ ai = find_ai (address, session); if (NULL == ai) { GNUNET_assert (0); return; } if (NULL == ai->ar) { /* already blocked, how did it get used!? */ GNUNET_break (0); return; } ai->back_off = GNUNET_TIME_STD_BACKOFF (ai->back_off); if (GNUNET_YES == GNUNET_HELLO_address_check_option (address, GNUNET_HELLO_ADDRESS_INFO_INBOUND)) LOG (GNUNET_ERROR_TYPE_DEBUG, "Removing address %s of peer %s from use (inbound died)\n", GST_plugins_a2s (address), GNUNET_i2s (&address->peer)); else LOG (GNUNET_ERROR_TYPE_INFO, "Blocking address %s of peer %s from use for %s\n", GST_plugins_a2s (address), GNUNET_i2s (&address->peer), GNUNET_STRINGS_relative_time_to_string (ai->back_off, GNUNET_YES)); /* destroy session and address */ if ( (NULL == session) || (GNUNET_NO == GNUNET_ATS_address_del_session (ai->ar, session)) ) { GNUNET_ATS_address_destroy (ai->ar); } /* "ar" has been freed, regardless how the branch above played out: it was either freed in #GNUNET_ATS_address_del_session() because it was incoming, or explicitly in #GNUNET_ATS_address_del_session(). */ ai->ar = NULL; /* determine when the address should come back to life */ ai->blocked = GNUNET_TIME_relative_to_absolute (ai->back_off); ai->unblock_task = GNUNET_SCHEDULER_add_delayed (ai->back_off, &unblock_address, ai); num_blocked++; publish_p2a_stat_update (); }
/** * Function called by the transport for each received message. * This function should also be called with "NULL" for the * message to signal that the other peer disconnected. * * @param cls closure, const char* with the name of the plugin we received the message from * @param peer (claimed) identity of the other peer * @param message the message, NULL if we only care about * learning about the delay until we should receive again -- FIXME! * @param ats performance information * @param ats_count number of records in ats * @param session identifier used for this session (NULL for plugins * that do not offer bi-directional communication to the sender * using the same "connection") * @param sender_address binary address of the sender (if we established the * connection or are otherwise sure of it; should be NULL * for inbound TCP/UDP connections since it it not clear * that we could establish ourselves a connection to that * IP address and get the same system) * @param sender_address_len number of bytes in sender_address * @return how long the plugin should wait until receiving more data * (plugins that do not support this, can ignore the return value) */ static struct GNUNET_TIME_Relative plugin_env_receive_callback (void *cls, const struct GNUNET_PeerIdentity *peer, const struct GNUNET_MessageHeader *message, const struct GNUNET_ATS_Information *ats, uint32_t ats_count, struct Session *session, const char *sender_address, uint16_t sender_address_len) { const char *plugin_name = cls; struct GNUNET_TIME_Relative ret; struct GNUNET_HELLO_Address address; uint16_t type; address.peer = *peer; address.address = sender_address; address.address_length = sender_address_len; address.transport_name = plugin_name; ret = GNUNET_TIME_UNIT_ZERO; if (NULL == message) goto end; type = ntohs (message->type); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received Message with type %u from peer `%s'\n", type, GNUNET_i2s (peer)); GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# bytes total received"), ntohs (message->size), GNUNET_NO); switch (type) { case GNUNET_MESSAGE_TYPE_HELLO: GST_validation_handle_hello (message); return ret; case GNUNET_MESSAGE_TYPE_TRANSPORT_PING: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, "Processing `%s' from `%s'\n", "PING", (sender_address != NULL) ? GST_plugins_a2s (&address) : "<inbound>"); GST_validation_handle_ping (peer, message, &address, session); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, "Processing `%s' from `%s'\n", "PONG", (sender_address != NULL) ? GST_plugins_a2s (&address) : "<inbound>"); GST_validation_handle_pong (peer, message); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT: GST_neighbours_handle_connect (message, peer, &address, session, ats, ats_count); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_CONNECT_ACK: GST_neighbours_handle_connect_ack (message, peer, &address, session, ats, ats_count); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK: GST_neighbours_handle_session_ack (message, peer, &address, session, ats, ats_count); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT: GST_neighbours_handle_disconnect_message (peer, message); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE: GST_neighbours_keepalive (peer); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE: GST_neighbours_keepalive_response (peer, ats, ats_count); break; default: /* should be payload */ GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# bytes payload received"), ntohs (message->size), GNUNET_NO); ret = process_payload (peer, &address, session, message, ats, ats_count); break; } end: #if 1 /* FIXME: this should not be needed, and not sure it's good to have it, but without * this connections seem to go extra-slow */ GNUNET_ATS_address_update (GST_ats, &address, session, ats, ats_count); #endif GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Allowing receive from peer %s to continue in %llu ms\n", GNUNET_i2s (peer), (unsigned long long) ret.rel_value); return ret; }
/** * Function called by the transport for each received message. * * @param cls closure, const char* with the name of the plugin we received the message from * @param address address and (claimed) identity of the other peer * @param message the message, NULL if we only care about * learning about the delay until we should receive again * @param session identifier used for this session (NULL for plugins * that do not offer bi-directional communication to the sender * using the same "connection") * @return how long the plugin should wait until receiving more data * (plugins that do not support this, can ignore the return value) */ struct GNUNET_TIME_Relative GST_receive_callback (void *cls, const struct GNUNET_HELLO_Address *address, struct GNUNET_ATS_Session *session, const struct GNUNET_MessageHeader *message) { const char *plugin_name = cls; struct GNUNET_TIME_Relative ret; uint16_t type; ret = GNUNET_TIME_UNIT_ZERO; if (NULL == message) goto end; type = ntohs (message->type); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received message with type %u from peer `%s'\n", type, GNUNET_i2s (&address->peer)); GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# bytes total received"), ntohs (message->size), GNUNET_NO); GST_neighbours_notify_data_recv (address, message); switch (type) { case GNUNET_MESSAGE_TYPE_HELLO_LEGACY: /* Legacy HELLO message, discard */ return ret; case GNUNET_MESSAGE_TYPE_HELLO: if (GNUNET_OK != GST_validation_handle_hello (message)) { GNUNET_break_op (0); GST_blacklist_abort_matching (address, session); } return ret; case GNUNET_MESSAGE_TYPE_TRANSPORT_PING: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Processing PING from `%s'\n", GST_plugins_a2s (address)); if (GNUNET_OK != GST_validation_handle_ping (&address->peer, message, address, session)) { GST_blacklist_abort_matching (address, session); kill_session (plugin_name, session); } break; case GNUNET_MESSAGE_TYPE_TRANSPORT_PONG: GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Processing PONG from `%s'\n", GST_plugins_a2s (address)); if (GNUNET_OK != GST_validation_handle_pong (&address->peer, message)) { GNUNET_break_op (0); GST_blacklist_abort_matching (address, session); kill_session (plugin_name, session); } break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN: /* Do blacklist check if communication with this peer is allowed */ (void) GST_blacklist_test_allowed (&address->peer, NULL, &connect_bl_check_cont, GNUNET_copy_message (message), address, session); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_SYN_ACK: if (GNUNET_OK != GST_neighbours_handle_session_syn_ack (message, address, session)) { GST_blacklist_abort_matching (address, session); kill_session (plugin_name, session); } break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_ACK: if (GNUNET_OK != GST_neighbours_handle_session_ack (message, address, session)) { GNUNET_break_op(0); GST_blacklist_abort_matching (address, session); kill_session (plugin_name, session); } break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_DISCONNECT: GST_neighbours_handle_disconnect_message (&address->peer, message); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_QUOTA: GST_neighbours_handle_quota_message (&address->peer, message); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE: GST_neighbours_keepalive (&address->peer, message); break; case GNUNET_MESSAGE_TYPE_TRANSPORT_SESSION_KEEPALIVE_RESPONSE: GST_neighbours_keepalive_response (&address->peer, message); break; default: /* should be payload */ GNUNET_STATISTICS_update (GST_stats, gettext_noop ("# bytes payload received"), ntohs (message->size), GNUNET_NO); ret = process_payload (address, session, message); break; } end: GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Allowing receive from peer %s to continue in %s\n", GNUNET_i2s (&address->peer), GNUNET_STRINGS_relative_time_to_string (ret, GNUNET_YES)); return ret; }