+ * @param result status code
+ */
+static void
+result_callback (void *cls,
+ enum GNUNET_NAT_StatusCode result)
+{
+ struct TestContext *tc = cls;
+
+ display_test_result (tc,
+ result);
+}
+
+
+/**
+ * Resolve address we got a validation state for to a string.
+ *
+ * @param address the address itself
+ * @param numeric #GNUNET_YES to disable DNS, #GNUNET_NO to try reverse lookup
+ * @param last_validation when was the address validated last
+ * @param valid_until until when is the address valid
+ * @param next_validation when will we try to revalidate the address next
+ * @param state where are we in the validation state machine
+ */
+static void
+resolve_validation_address (const struct GNUNET_HELLO_Address *address,
+ int numeric,
+ struct GNUNET_TIME_Absolute last_validation,
+ struct GNUNET_TIME_Absolute valid_until,
+ struct GNUNET_TIME_Absolute next_validation,
+ enum GNUNET_TRANSPORT_ValidationState state);
+
+
+/**
+ * Function to call with a textual representation of an address. This
+ * function will be called several times with different possible
+ * textual representations, and a last time with @a address being NULL
+ * to signal the end of the iteration. Note that @a address NULL
+ * always is the last call, regardless of the value in @a res.
+ *
+ * @param cls closure
+ * @param address NULL on end of iteration,
+ * otherwise 0-terminated printable UTF-8 string,
+ * in particular an empty string if @a res is #GNUNET_NO
+ * @param res result of the address to string conversion:
+ * if #GNUNET_OK: conversion successful
+ * if #GNUNET_NO: address was invalid (or not supported)
+ * if #GNUNET_SYSERR: communication error (IPC error)
+ */
+static void
+process_validation_string (void *cls,
+ const char *address,
+ int res)
+{
+ struct ValidationResolutionContext *vc = cls;
+ char *s_valid;
+ char *s_last;
+ char *s_next;
+
+ if (NULL != address)
+ {
+ if (GNUNET_SYSERR == res)
+ {
+ FPRINTF (stderr,
+ "Failed to convert address for peer `%s' plugin `%s' length %u to string \n",
+ GNUNET_i2s (&vc->addrcp->peer),
+ vc->addrcp->transport_name,
+ (unsigned int) vc->addrcp->address_length);
+ }
+ if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->valid_until.abs_value_us)
+ s_valid = GNUNET_strdup ("never");
+ else
+ s_valid = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string (vc->valid_until));
+
+ if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->last_validation.abs_value_us)
+ s_last = GNUNET_strdup ("never");
+ else
+ s_last = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string (vc->last_validation));
+
+ if (GNUNET_TIME_UNIT_ZERO_ABS.abs_value_us == vc->next_validation.abs_value_us)
+ s_next = GNUNET_strdup ("never");
+ else
+ s_next = GNUNET_strdup (GNUNET_STRINGS_absolute_time_to_string (vc->next_validation));
+
+ FPRINTF (stdout,
+ _("Peer `%s' %s %s\n\t%s%s\n\t%s%s\n\t%s%s\n"),
+ GNUNET_i2s (&vc->addrcp->peer),
+ (GNUNET_OK == res) ? address : "<invalid address>",
+ (monitor_validation) ? GNUNET_TRANSPORT_vs2s (vc->state) : "",
+ "Valid until : ", s_valid,
+ "Last validation: ",s_last,
+ "Next validation: ", s_next);
+ GNUNET_free (s_valid);
+ GNUNET_free (s_last);
+ GNUNET_free (s_next);
+ vc->printed = GNUNET_YES;
+ return;
+ }
+ /* last call, we are done */
+ GNUNET_assert (address_resolutions > 0);
+ address_resolutions--;
+ if ( (GNUNET_SYSERR == res) &&
+ (GNUNET_NO == vc->printed))
+ {
+ if (numeric == GNUNET_NO)
+ {
+ /* Failed to resolve address, try numeric lookup
+ (note: this should be unnecessary, as
+ transport should fallback to numeric lookup
+ internally if DNS takes too long anyway) */
+ resolve_validation_address (vc->addrcp,
+ GNUNET_NO,
+ vc->last_validation,
+ vc->valid_until,
+ vc->next_validation,
+ vc->state);
+ }
+ else
+ {
+ FPRINTF (stdout,
+ _("Peer `%s' %s `%s' \n"),
+ GNUNET_i2s (&vc->addrcp->peer),
+ "<unable to resolve address>",
+ GNUNET_TRANSPORT_vs2s (vc->state));
+ }
+ }
+ GNUNET_free (vc->transport);
+ GNUNET_free (vc->addrcp);
+ GNUNET_CONTAINER_DLL_remove (vc_head, vc_tail, vc);
+ GNUNET_free (vc);
+ if ((0 == address_resolutions) && (iterate_validation))
+ {
+ if (NULL != end)
+ {
+ GNUNET_SCHEDULER_cancel (end);
+ end = NULL;
+ }
+ if (NULL != op_timeout)
+ {
+ GNUNET_SCHEDULER_cancel (op_timeout);
+ op_timeout = NULL;
+ }
+ ret = 0;
+ end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
+ }
+}
+
+
+/**
+ * Resolve address we got a validation state for to a string.
+ *
+ * @param address the address itself
+ * @param numeric #GNUNET_YES to disable DNS, #GNUNET_NO to try reverse lookup
+ * @param last_validation when was the address validated last
+ * @param valid_until until when is the address valid
+ * @param next_validation when will we try to revalidate the address next
+ * @param state where are we in the validation state machine
+ */
+static void
+resolve_validation_address (const struct GNUNET_HELLO_Address *address,
+ int numeric,
+ struct GNUNET_TIME_Absolute last_validation,
+ struct GNUNET_TIME_Absolute valid_until,
+ struct GNUNET_TIME_Absolute next_validation,
+ enum GNUNET_TRANSPORT_ValidationState state)
+{
+ struct ValidationResolutionContext *vc;
+
+ vc = GNUNET_new (struct ValidationResolutionContext);
+ GNUNET_assert(NULL != vc);
+ GNUNET_CONTAINER_DLL_insert(vc_head, vc_tail, vc);
+ address_resolutions++;
+
+ vc->transport = GNUNET_strdup(address->transport_name);
+ vc->addrcp = GNUNET_HELLO_address_copy (address);
+ vc->printed = GNUNET_NO;
+ vc->state = state;
+ vc->last_validation = last_validation;
+ vc->valid_until = valid_until;
+ vc->next_validation = next_validation;
+
+ /* Resolve address to string */
+ vc->asc = GNUNET_TRANSPORT_address_to_string (cfg,
+ address,
+ numeric,
+ RESOLUTION_TIMEOUT,
+ &process_validation_string, vc);
+}
+
+
+/**
+ * Resolve address we got a validation state for to a string.
+ *
+ * @param cls NULL
+ * @param address the address itself
+ * @param last_validation when was the address validated last
+ * @param valid_until until when is the address valid
+ * @param next_validation when will we try to revalidate the address next
+ * @param state where are we in the validation state machine
+ */
+static void
+process_validation_cb (void *cls,
+ 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)
+{
+ if (NULL == address)
+ {
+ if (monitor_validation)
+ {
+ FPRINTF (stdout,
+ "%s",
+ _("Monitor disconnected from transport service. Reconnecting.\n"));
+ return;
+ }
+ vic = NULL;
+ if (NULL != end)
+ GNUNET_SCHEDULER_cancel (end);
+ end = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
+ return;
+ }
+ resolve_validation_address (address,
+ numeric,
+ last_validation,
+ valid_until,
+ next_validation,
+ state);
+}
+
+
+static void
+run_nat_test ()
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Running test for plugin `%s' using bind port %u and advertised port %u \n",
+ head->name,
+ (uint16_t) head->bnd_port,
+ (uint16_t) head->adv_port);
+
+ head->tst = GNUNET_NAT_test_start (cfg,
+ (0 == strcasecmp (head->name, "udp"))
+ ? GNUNET_NO : GNUNET_YES,
+ (uint16_t) head->bnd_port,
+ (uint16_t) head->adv_port,
+ TIMEOUT,
+ &result_callback, head);
+}
+
+
+/**
+ * Test our plugin's configuration (NAT traversal, etc.).
+ *
+ * @param cfg configuration to test
+ */
+static void
+do_test_configuration (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ char *plugins;
+ char *tok;
+ unsigned long long bnd_port;
+ unsigned long long adv_port;
+ struct TestContext *tc;
+ char *binary;
+
+ if (GNUNET_OK
+ != GNUNET_CONFIGURATION_get_value_string (cfg, "transport", "plugins",
+ &plugins))
+ {
+ FPRINTF (stderr, "%s", _
+ ("No transport plugins configured, peer will never communicate\n"));
+ ret = 4;
+ return;
+ }
+
+ for (tok = strtok (plugins, " "); tok != NULL ; tok = strtok (NULL, " "))
+ {
+ char section[12 + strlen (tok)];
+ GNUNET_snprintf (section, sizeof(section), "transport-%s", tok);
+ if (GNUNET_OK
+ != GNUNET_CONFIGURATION_get_value_number (cfg, section, "PORT",
+ &bnd_port))
+ {
+ FPRINTF (stderr,
+ _("No port configured for plugin `%s', cannot test it\n"), tok);
+ continue;
+ }
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, section,
+ "ADVERTISED_PORT", &adv_port))
+ adv_port = bnd_port;
+
+ tc = GNUNET_new (struct TestContext);
+ tc->name = GNUNET_strdup (tok);
+ tc->adv_port = adv_port;
+ tc->bnd_port = bnd_port;
+ GNUNET_CONTAINER_DLL_insert_tail (head, tail, tc);
+ }
+ GNUNET_free(plugins);
+
+ if ((NULL != head) && (NULL == resolver))
+ {
+ binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver");
+ resolver = GNUNET_OS_start_process (GNUNET_YES,
+ GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
+ NULL, NULL, NULL,
+ binary,
+ "gnunet-service-resolver", NULL);
+ if (NULL == resolver)
+ {
+ FPRINTF (stderr, _("Failed to start resolver!\n"));
+ return;
+ }
+
+ GNUNET_free(binary);
+ GNUNET_RESOLVER_connect (cfg);
+ run_nat_test ();
+ }
+}
+
+
+/**
+ * Function called to notify a client about the socket
+ * begin ready to queue more data. @a buf will be
+ * NULL and @a size zero if the socket was closed for
+ * writing in the meantime.
+ *
+ * @param cls closure
+ * @param size number of bytes available in @a buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to @a buf
+ */
+static size_t
+transmit_data (void *cls,
+ size_t size,
+ void *buf)
+{
+ struct GNUNET_MessageHeader *m = buf;
+
+ if ((NULL == buf) || (0 == size))
+ {
+ th = NULL;
+ return 0;
+ }
+
+ GNUNET_assert(size >= sizeof(struct GNUNET_MessageHeader));
+ GNUNET_assert(size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
+ m->size = ntohs (size);
+ m->type = ntohs (GNUNET_MESSAGE_TYPE_DUMMY);
+ memset (&m[1], 52, size - sizeof(struct GNUNET_MessageHeader));
+ traffic_sent += size;
+ th = GNUNET_TRANSPORT_notify_transmit_ready (handle, &pid,
+ BLOCKSIZE * 1024,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &transmit_data, NULL);
+ if (verbosity > 0)
+ FPRINTF (stdout, _("Transmitting %u bytes to %s\n"), (unsigned int) size,
+ GNUNET_i2s (&pid));
+ return size;
+}
+
+
+/**
+ * Function called to notify transport users that another
+ * peer connected to us.
+ *
+ * @param cls closure
+ * @param peer the peer that connected
+ */
+static void
+notify_connect (void *cls,
+ const struct GNUNET_PeerIdentity *peer)
+{
+ if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
+ return;
+ ret = 0;
+ if (benchmark_send)
+ {
+ if (NULL != op_timeout)
+ {
+ GNUNET_SCHEDULER_cancel (op_timeout);
+ op_timeout = NULL;
+ }
+ if (verbosity > 0)
+ FPRINTF (stdout,
+ _("Successfully connected to `%s', starting to send benchmark data in %u Kb blocks\n"),
+ GNUNET_i2s (&pid), BLOCKSIZE);
+ start_time = GNUNET_TIME_absolute_get ();
+ if (NULL == th)
+ th = GNUNET_TRANSPORT_notify_transmit_ready (handle, peer,
+ BLOCKSIZE * 1024,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &transmit_data,
+ NULL);
+ else
+ GNUNET_break(0);
+ return;
+ }
+}
+
+
+/**
+ * Function called to notify transport users that another
+ * peer disconnected from us.
+ *
+ * @param cls closure
+ * @param peer the peer that disconnected
+ */
+static void
+notify_disconnect (void *cls,
+ const struct GNUNET_PeerIdentity *peer)
+{
+ if (0 != memcmp (&pid, peer, sizeof(struct GNUNET_PeerIdentity)))
+ return;
+
+ if (NULL != th)
+ {
+ GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
+ th = NULL;
+ }
+ if (benchmark_send)
+ {
+ FPRINTF (stdout, _("Disconnected from peer `%s' while benchmarking\n"),
+ GNUNET_i2s (&pid));
+ if (NULL != end)
+ GNUNET_SCHEDULER_cancel (end);
+ return;
+ }
+}
+
+
+/**
+ * Function called to notify transport users that another
+ * peer connected to us.
+ *
+ * @param cls closure
+ * @param peer the peer that connected
+ */
+static void
+monitor_notify_connect (void *cls,
+ const struct GNUNET_PeerIdentity *peer)
+{
+ monitor_connect_counter++;
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+ const char *now_str = GNUNET_STRINGS_absolute_time_to_string (now);
+
+ FPRINTF (stdout,
+ _("%24s: %-17s %4s (%u connections in total)\n"),
+ now_str,
+ _("Connected to"),
+ GNUNET_i2s (peer),
+ monitor_connect_counter);
+}
+
+
+/**
+ * Function called to notify transport users that another
+ * peer disconnected from us.
+ *
+ * @param cls closure
+ * @param peer the peer that disconnected