/**
 * 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 ();
}
Esempio n. 12
0
/**
 * 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);
}
Esempio n. 14
0
/**
 * 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);
}
Esempio n. 15
0
/**
 * 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;
}
Esempio n. 21
0
/**
 * 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;
}