+/**
+ * Callback function used to fill a buffer of max bytes with a list of
+ * addresses in the format used by HELLOs. Should use
+ * "GNUNET_HELLO_add_address" as a helper function.
+ *
+ * @param cls the 'struct AddValidatedAddressContext' with the validated address
+ * @param max maximum number of bytes that can be written to buf
+ * @param buf where to write the address information
+ * @return number of bytes written, 0 to signal the
+ * end of the iteration.
+ */
+static size_t
+add_validated_address (void *cls,
+ size_t max, void *buf)
+{
+ struct AddValidatedAddressContext *avac = cls;
+ const struct ValidationEntry *ve = avac->ve;
+
+ if (GNUNET_YES == avac->done)
+ return 0;
+ avac->done = GNUNET_YES;
+ return GNUNET_HELLO_add_address (ve->transport_name,
+ GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION),
+ ve->addr,
+ ve->addrlen,
+ buf,
+ max);
+}
+
+
+/**
+ * Iterator over hash map entries. Checks if the given
+ * validation entry is for the same challenge as what
+ * is given in the PONG.
+ *
+ * @param cls the 'struct TransportPongMessage*'
+ * @param key peer identity
+ * @param value value in the hash map ('struct ValidationEntry')
+ * @return GNUNET_YES if we should continue to
+ * iterate (mismatch), GNUNET_NO if not (entry matched)
+ */
+static int
+check_pending_validation (void *cls,
+ const GNUNET_HashCode * key,
+ void *value)
+{
+ const struct TransportPongMessage *pong = cls;
+ struct ValidationEntry *ve = value;
+ struct AddValidatedAddressContext avac;
+ unsigned int challenge = ntohl(pong->challenge);
+ struct GNUNET_HELLO_Message *hello;
+ struct GNUNET_PeerIdentity target;
+ struct NeighbourList *n;
+ struct ForeignAddressList *fal;
+
+ if (ve->challenge != challenge)
+ return GNUNET_YES;
+
+#if DEBUG_TRANSPORT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Confirmed validity of address, peer `%4s' has address `%s' (%s).\n",
+ GNUNET_h2s (key),
+ GNUNET_a2s ((const struct sockaddr *) ve->addr,
+ ve->addrlen),
+ ve->transport_name);
+#endif
+ /* create the updated HELLO */
+ GNUNET_CRYPTO_hash (&ve->publicKey,
+ sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded),
+ &target.hashPubKey);
+ avac.done = GNUNET_NO;
+ avac.ve = ve;
+ hello = GNUNET_HELLO_create (&ve->publicKey,
+ &add_validated_address,
+ &avac);
+ GNUNET_PEERINFO_add_peer (cfg, sched,
+ &target,
+ hello);
+ GNUNET_free (hello);
+ n = find_neighbour (&target);
+ if (n != NULL)
+ {
+ fal = add_peer_address (n, ve->transport_name,
+ ve->addr,
+ ve->addrlen);
+ fal->expires = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
+ fal->validated = GNUNET_YES;
+ fal->latency = GNUNET_TIME_absolute_get_duration (ve->send_time);
+ if (n->latency.value == GNUNET_TIME_UNIT_FOREVER_REL.value)
+ n->latency = fal->latency;
+ else
+ n->latency.value = (fal->latency.value + n->latency.value) / 2;
+ n->distance = fal->distance;
+ if (GNUNET_NO == n->received_pong)
+ {
+ notify_clients_connect (&target, n->latency, n->distance);
+ n->received_pong = GNUNET_YES;
+ }
+ if (n->retry_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sched,
+ n->retry_task);
+ n->retry_task = GNUNET_SCHEDULER_NO_TASK;
+ try_transmission_to_peer (n);
+ }
+ }
+
+ /* clean up validation entry */
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (validation_map,
+ key,
+ ve));
+ GNUNET_SCHEDULER_cancel (sched,
+ ve->timeout_task);
+ GNUNET_free (ve->transport_name);
+ GNUNET_free (ve);
+ return GNUNET_NO;
+}
+
+
+/**
+ * Function that will be called if we receive a validation
+ * of an address challenge that we transmitted to another
+ * peer. Note that the validation should only be considered
+ * acceptable if the challenge matches AND if the sender
+ * address is at least a plausible address for this peer
+ * (otherwise we may be seeing a MiM attack).
+ *
+ * @param cls closure
+ * @param message the pong message
+ * @param peer who responded to our challenge
+ * @param sender_address string describing our sender address (as observed
+ * by the other peer in binary format)
+ * @param sender_address_len number of bytes in 'sender_address'
+ */
+static void
+handle_pong (void *cls, const struct GNUNET_MessageHeader *message,
+ const struct GNUNET_PeerIdentity *peer,
+ const char *sender_address,
+ size_t sender_address_len)
+{
+#if DEBUG_TRANSPORT > 1
+ /* we get tons of these that just get discarded, only log
+ if we are quite verbose */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Receiving `%s' message from `%4s'.\n", "PONG",
+ GNUNET_i2s (peer));
+#endif
+ if (GNUNET_SYSERR !=
+ GNUNET_CONTAINER_multihashmap_get_multiple (validation_map,
+ &peer->hashPubKey,
+ &check_pending_validation,
+ (void*) message))
+ {
+ /* This is *expected* to happen a lot since we send
+ PONGs to *all* known addresses of the sender of
+ the PING, so most likely we get multiple PONGs
+ per PING, and all but the first PONG will end up
+ here. So really we should not print anything here
+ unless we want to be very, very verbose... */
+#if DEBUG_TRANSPORT > 2
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received `%s' message from `%4s' but have no record of a matching `%s' message. Ignoring.\n",
+ "PONG",
+ GNUNET_i2s (peer),
+ "PING");
+#endif
+ return;
+ }
+
+#if 0
+ /* FIXME: add given address to potential pool of our addresses
+ (for voting) */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
+ _("Another peer saw us using the address `%s' via `%s'.\n"),
+ GNUNET_a2s ((const struct sockaddr *) &pong[1],
+ ntohs(pong->addrlen)),
+ va->transport_name);
+#endif
+}
+
+
+static void
+neighbour_timeout_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct NeighbourList *n = cls;
+
+#if DEBUG_TRANSPORT
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
+ "Neighbour `%4s' has timed out!\n", GNUNET_i2s (&n->id));
+#endif
+ n->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ disconnect_neighbour (n, GNUNET_NO);
+}
+
+
+/**
+ * Create a fresh entry in our neighbour list for the given peer.
+ * Will try to transmit our current HELLO to the new neighbour. Also
+ * notifies our clients about the new "connection".
+ *
+ * @param peer the peer for which we create the entry
+ * @return the new neighbour list entry
+ */
+static struct NeighbourList *
+setup_new_neighbour (const struct GNUNET_PeerIdentity *peer)
+{
+ struct NeighbourList *n;
+ struct TransportPlugin *tp;
+ struct ReadyList *rl;
+
+ GNUNET_assert (our_hello != NULL);
+ n = GNUNET_malloc (sizeof (struct NeighbourList));
+ n->next = neighbours;
+ neighbours = n;
+ n->id = *peer;
+ n->last_quota_update = GNUNET_TIME_absolute_get ();
+ n->peer_timeout =
+ GNUNET_TIME_relative_to_absolute
+ (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
+ n->quota_in = (GNUNET_CONSTANTS_DEFAULT_BPM_IN_OUT + 59999) / (60 * 1000);
+ tp = plugins;
+ while (tp != NULL)
+ {
+ if (tp->api->send != NULL)
+ {
+ rl = GNUNET_malloc (sizeof (struct ReadyList));
+ rl->next = n->plugins;
+ n->plugins = rl;
+ rl->plugin = tp;
+ rl->addresses = NULL;
+ }
+ tp = tp->next;
+ }
+ n->latency = GNUNET_TIME_UNIT_FOREVER_REL;
+ n->distance = -1;
+ n->timeout_task = GNUNET_SCHEDULER_add_delayed (sched,
+ GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT,
+ &neighbour_timeout_task, n);
+ transmit_to_peer (NULL, NULL, 0,
+ HELLO_ADDRESS_EXPIRATION,
+ (const char *) our_hello, GNUNET_HELLO_size(our_hello),
+ GNUNET_NO, n);
+ return n;
+}
+
+
+/**
+ * Closure for 'check_address_exists'.
+ */
+struct CheckAddressExistsClosure
+{
+ /**
+ * Address to check for.
+ */
+ const void *addr;
+
+ /**
+ * Name of the transport.
+ */
+ const char *tname;
+
+ /**
+ * Length of addr.
+ */
+ size_t addrlen;
+
+ /**
+ * Set to GNUNET_YES if the address exists.
+ */
+ int exists;