/** * Activity on our incoming socket. Read data from the * incoming connection. * * @param cls * @param tc scheduler context */ static void do_udp_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct GNUNET_NAT_AutoHandle *ah = cls; unsigned char reply_buf[1024]; ssize_t rlen; struct sockaddr_in answer; if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) && (GNUNET_NETWORK_fdset_isset (tc->read_ready, lsock4))) { rlen = GNUNET_NETWORK_socket_recv (lsock4, reply_buf, sizeof (reply_buf)); //Lets handle the packet memset(&answer, 0, sizeof(struct sockaddr_in)); if(ah->phase == AUTO_NAT_PUNCHED) { //Destroy the connection GNUNET_NETWORK_socket_close (lsock4); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "The external server was able to connect back"); ah->connected_back = GNUNET_YES; next_phase (ah); } else { if (GNUNET_OK == GNUNET_NAT_stun_handle_packet (reply_buf, rlen, &answer)) { //Process the answer process_stun_reply (&answer, ah); } else { next_phase (ah); } } } else { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "TIMEOUT while waiting for an answer\n"); if (ah->phase == AUTO_NAT_PUNCHED) { stop_stun(); } next_phase (ah); } }
/** * Determine our external IPv4 address and port using an external STUN server * * @param ah auto setup context */ static void test_stun (struct GNUNET_NAT_AutoHandle *ah) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running STUN test\n"); /* Get port from the configuration */ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (ah->cfg, "transport-udp", "PORT", &port)) { port = 2086; } //Lets create the socket lsock4 = bind_v4 (); if (NULL == lsock4) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind"); next_phase(ah); return; } else { //Lets call our function now when it accepts ltask4 = GNUNET_SCHEDULER_add_read_net (NAT_SERVER_TIMEOUT, lsock4, &do_udp_read, ah); } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "STUN service listens on port %u\n", port); if (GNUNET_NO == GNUNET_NAT_stun_make_request (stun_server, stun_port, lsock4, &request_callback, NULL)) { /*An error happened*/ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "STUN error, stopping\n"); stop_stun (); next_phase (ah); } }
/** * Determine our local IP addresses; detect internal IP & IPv6-support * * @param ah auto setup context */ static void test_local_ip (struct GNUNET_NAT_AutoHandle *ah) { ah->have_v6 = GNUNET_NO; ah->ret = GNUNET_NAT_ERROR_NO_VALID_IF_IP_COMBO; // reset to success if any of the IFs in below iterator has a valid IP GNUNET_OS_network_interfaces_list (&process_if, ah); GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "DISABLEV6", (GNUNET_YES == ah->have_v6) ? "NO" : "YES"); next_phase (ah); }
/** * Determine our external IPv4 address. * * @param ah auto setup context */ static void test_external_ip (struct GNUNET_NAT_AutoHandle *ah) { if (GNUNET_NAT_ERROR_SUCCESS != ah->ret) next_phase (ah); // FIXME: CPS? /* try to detect external IP */ ah->eh = GNUNET_NAT_mini_get_external_ipv4 (TIMEOUT, &set_external_ipv4, ah); }
/** * Set our external IPv4 address based on the UPnP. * * * @param cls closure with our setup context * @param addr the address, NULL on errors * @param emsg NULL on success, otherwise an error message */ static void set_external_ipv4 (void *cls, const struct in_addr *addr, enum GNUNET_NAT_StatusCode ret) { struct GNUNET_NAT_AutoHandle *ah = cls; char buf[INET_ADDRSTRLEN]; ah->eh = NULL; ah->ret = ret; if (GNUNET_NAT_ERROR_SUCCESS != ret) { next_phase (ah); return; } /* enable 'behind nat' */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Detected external IP `%s'\n"), inet_ntop (AF_INET, addr, buf, sizeof (buf))); GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "BEHIND_NAT", "YES"); /* set external IP address */ if (NULL == inet_ntop (AF_INET, addr, buf, sizeof (buf))) { GNUNET_break (0); /* actually, this should never happen, as the caller already executed just * this check, but for consistency (eg: future changes in the caller) * we still need to report this error... */ ah->ret = GNUNET_NAT_ERROR_EXTERNAL_IP_ADDRESS_INVALID; next_phase (ah); return; } GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS", buf); ah->upnp_set_external_address = GNUNET_YES; next_phase (ah); }
static void process_stun_reply(struct sockaddr_in* answer, struct GNUNET_NAT_AutoHandle *ah) { ah->stun_ip = inet_ntoa(answer->sin_addr); ah->stun_port = ntohs(answer->sin_port); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "External IP is: %s , with port %d\n", ah->stun_ip, ah->stun_port); next_phase (ah); }
/** * Test if NAT has been punched * * @param ah auto setup context */ static void test_nat_punched (struct GNUNET_NAT_AutoHandle *ah) { struct GNUNET_CLIENT_Connection *client; struct GNUNET_NAT_TestMessage msg; if (ah->stun_ip) { LOG (GNUNET_ERROR_TYPE_INFO, "Asking gnunet-nat-server to connect to `%s'\n", ah->stun_ip); msg.header.size = htons (sizeof (struct GNUNET_NAT_TestMessage)); msg.header.type = htons (GNUNET_MESSAGE_TYPE_NAT_TEST); msg.dst_ipv4 = inet_addr(ah->stun_ip); msg.dport = htons(ah->stun_port); msg.data = port; msg.is_tcp = htonl ((uint32_t) GNUNET_NO); client = GNUNET_CLIENT_connect ("gnunet-nat-server", ah->cfg); if (NULL == client) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to connect to `gnunet-nat-server'\n")); return; } GNUNET_break (GNUNET_OK == GNUNET_CLIENT_transmit_and_get_response (client, &msg.header, NAT_SERVER_TIMEOUT, GNUNET_YES, NULL, NULL)); if (NULL != ltask4) { GNUNET_SCHEDULER_cancel (ltask4); ltask4 = GNUNET_SCHEDULER_add_read_net (NAT_SERVER_TIMEOUT, lsock4, &do_udp_read, ah); } } else { LOG (GNUNET_ERROR_TYPE_INFO, "We don't have a STUN IP"); next_phase(ah); } }
/** * Test if UPnPC works. * * @param ah auto setup context */ static void test_upnpc (struct GNUNET_NAT_AutoHandle *ah) { int have_upnpc; if (GNUNET_NAT_ERROR_SUCCESS != ah->ret) next_phase (ah); // test if upnpc is available have_upnpc = (GNUNET_SYSERR != GNUNET_OS_check_helper_binary ("upnpc", GNUNET_NO, NULL)); //FIXME: test if upnpc is actually working, that is, if transports start to work once we use UPnP GNUNET_log (GNUNET_ERROR_TYPE_INFO, (have_upnpc) ? _("upnpc found, enabling its use\n") : _("upnpc not found\n")); GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_UPNP", (GNUNET_YES == have_upnpc) ? "YES" : "NO"); next_phase (ah); }
/** * Test if ICMP server is working * * @param ah auto setup context */ static void test_icmp_server (struct GNUNET_NAT_AutoHandle *ah) { int ext_ip; int nated; int binary; char *tmp; char *helper; ext_ip = GNUNET_NO; nated = GNUNET_NO; binary = GNUNET_NO; tmp = NULL; helper = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-server"); if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (ah->cfg, "nat", "EXTERNAL_ADDRESS", &tmp)) && (0 < strlen (tmp))){ ext_ip = GNUNET_OK; GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("test_icmp_server not possible, as we have no public IPv4 address\n")); } else goto err; if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, "nat", "BEHIND_NAT")){ nated = GNUNET_YES; GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("test_icmp_server not possible, as we are not behind NAT\n")); } else goto err; if (GNUNET_YES == GNUNET_OS_check_helper_binary (helper, GNUNET_YES, "-d 127.0.0.1" )){ binary = GNUNET_OK; // use localhost as source for that one udp-port, ok for testing GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("No working gnunet-helper-nat-server found\n")); } err: GNUNET_free_non_null (tmp); GNUNET_free (helper); if (GNUNET_OK == ext_ip && GNUNET_YES == nated && GNUNET_OK == binary) ah->task = GNUNET_SCHEDULER_add_now (&reversal_test, ah); else next_phase (ah); }
static gboolean io_cb(GIOChannel *channel, GIOCondition condition, gpointer user_data) { TyonXceleratorCalibrationAssistant *assistant = (TyonXceleratorCalibrationAssistant *)user_data; TyonXceleratorCalibrationAssistantPrivate *priv = assistant->priv; GError *local_error = NULL; gint value; GIOStatus status; gsize length; TyonSpecial event; if (condition & G_IO_HUP) return FALSE; status = g_io_channel_read_chars(channel, (gchar *)&event, sizeof(TyonSpecial), &length, &local_error); if (status == G_IO_STATUS_AGAIN) return TRUE; if (status == G_IO_STATUS_ERROR) { show_error_summary(assistant, _("Could not read io_channel")); g_error_free(local_error); return FALSE; } if (status == G_IO_STATUS_EOF) { show_error_summary(assistant, _("Could not read io_channel")); return FALSE; } if (length < sizeof(TyonSpecial) || event.report_id != TYON_REPORT_ID_SPECIAL || event.type != TYON_SPECIAL_TYPE_XCELERATOR) return TRUE; value = event.action; tyon_2d_positional_set(priv->positional, 0.0, (gdouble)(value - 127) / 127.0); switch(priv->phase) { case PHASE_MID: if (in_range(value, priv->last_value, 10)) { priv->mid += value; ++priv->count; gtk_progress_bar_set_fraction(priv->progress_bar, 1.0 / valid_count * priv->count); if (priv->count == valid_count) { priv->mid = priv->mid >> AVERAGE_SHIFT; next_phase(assistant); } } else {
/** * Function called by NAT to report the outcome of the nat-test. * Clean up and update GUI. * * @param cls the auto handle * @param success currently always #GNUNET_OK * @param emsg NULL on success, otherwise an error message */ static void result_callback (void *cls, enum GNUNET_NAT_StatusCode ret) { struct GNUNET_NAT_AutoHandle *ah = cls; if (GNUNET_NAT_ERROR_SUCCESS == ret) GNUNET_NAT_test_stop (ah->tst); ah->tst = NULL; ah->ret = ret; GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_NAT_ERROR_SUCCESS == ret ? _("NAT traversal with ICMP Server succeeded.\n") : _("NAT traversal with ICMP Server failed.\n")); GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "ENABLE_ICMP_SERVER", GNUNET_NAT_ERROR_SUCCESS == ret ? "NO" : "YES"); next_phase (ah); }
/** * Start auto-configuration routine. The resolver service should * be available when this function is called. * * @param cfg initial configuration * @param cb function to call with autoconfiguration result * @param cb_cls closure for @a cb * @return handle to cancel operation */ struct GNUNET_NAT_AutoHandle * GNUNET_NAT_autoconfig_start (const struct GNUNET_CONFIGURATION_Handle *cfg, GNUNET_NAT_AutoResultCallback cb, void *cb_cls) { struct GNUNET_NAT_AutoHandle *ah; ah = GNUNET_new (struct GNUNET_NAT_AutoHandle); ah->fin_cb = cb; ah->fin_cb_cls = cb_cls; ah->ret = GNUNET_NAT_ERROR_SUCCESS; ah->cfg = GNUNET_CONFIGURATION_dup (cfg); ah->initial_cfg = GNUNET_CONFIGURATION_dup (cfg); /* never use loopback addresses if user wanted autoconfiguration */ GNUNET_CONFIGURATION_set_value_string (ah->cfg, "nat", "USE_LOCALADDR", "NO"); next_phase (ah); return ah; }
/** * Test if ICMP client is working * * @param ah auto setup context */ static void test_icmp_client (struct GNUNET_NAT_AutoHandle *ah) { char *tmp; char *helper; tmp = NULL; helper = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-nat-client"); if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (ah->cfg, "nat", "INTERNAL_ADDRESS", &tmp)) && (0 < strlen (tmp))) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("test_icmp_client not possible, as we have no internal IPv4 address\n")); } else goto err; if (GNUNET_YES != GNUNET_CONFIGURATION_get_value_yesno (ah->cfg, "nat", "BEHIND_NAT")){ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("test_icmp_server not possible, as we are not behind NAT\n")); } else goto err; if (GNUNET_YES == GNUNET_OS_check_helper_binary (helper, GNUNET_YES, "-d 127.0.0.1 127.0.0.2 42")){ // none of these parameters are actually used in privilege testing mode GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("No working gnunet-helper-nat-server found\n")); } err: GNUNET_free_non_null (tmp); GNUNET_free (helper); next_phase (ah); }
/* * Note that |extra| points to the correct client/server configuration * within |test_ctx|. When configuring the handshake, general mode settings * are taken from |test_ctx|, and client/server-specific settings should be * taken from |extra|. * * The configuration code should never reach into |test_ctx->extra| or * |test_ctx->resume_extra| directly. * * (We could refactor test mode settings into a substructure. This would result * in cleaner argument passing but would complicate the test configuration * parsing.) */ static HANDSHAKE_RESULT *do_handshake_internal( SSL_CTX *server_ctx, SSL_CTX *server2_ctx, SSL_CTX *client_ctx, const SSL_TEST_CTX *test_ctx, const SSL_TEST_EXTRA_CONF *extra, SSL_SESSION *session_in, SSL_SESSION **session_out) { PEER server, client; BIO *client_to_server, *server_to_client; HANDSHAKE_EX_DATA server_ex_data, client_ex_data; CTX_DATA client_ctx_data, server_ctx_data, server2_ctx_data; HANDSHAKE_RESULT *ret = HANDSHAKE_RESULT_new(); int client_turn = 1; connect_phase_t phase = HANDSHAKE; handshake_status_t status = HANDSHAKE_RETRY; const unsigned char* tick = NULL; size_t tick_len = 0; SSL_SESSION* sess = NULL; const unsigned char *proto = NULL; /* API dictates unsigned int rather than size_t. */ unsigned int proto_len = 0; memset(&server_ctx_data, 0, sizeof(server_ctx_data)); memset(&server2_ctx_data, 0, sizeof(server2_ctx_data)); memset(&client_ctx_data, 0, sizeof(client_ctx_data)); memset(&server, 0, sizeof(server)); memset(&client, 0, sizeof(client)); configure_handshake_ctx(server_ctx, server2_ctx, client_ctx, test_ctx, extra, &server_ctx_data, &server2_ctx_data, &client_ctx_data); /* Setup SSL and buffers; additional configuration happens below. */ create_peer(&server, server_ctx); create_peer(&client, client_ctx); server.bytes_to_write = client.bytes_to_read = test_ctx->app_data_size; client.bytes_to_write = server.bytes_to_read = test_ctx->app_data_size; configure_handshake_ssl(server.ssl, client.ssl, extra); if (session_in != NULL) { /* In case we're testing resumption without tickets. */ TEST_check(SSL_CTX_add_session(server_ctx, session_in)); TEST_check(SSL_set_session(client.ssl, session_in)); } memset(&server_ex_data, 0, sizeof(server_ex_data)); memset(&client_ex_data, 0, sizeof(client_ex_data)); ret->result = SSL_TEST_INTERNAL_ERROR; client_to_server = BIO_new(BIO_s_mem()); server_to_client = BIO_new(BIO_s_mem()); TEST_check(client_to_server != NULL); TEST_check(server_to_client != NULL); /* Non-blocking bio. */ BIO_set_nbio(client_to_server, 1); BIO_set_nbio(server_to_client, 1); SSL_set_connect_state(client.ssl); SSL_set_accept_state(server.ssl); /* The bios are now owned by the SSL object. */ SSL_set_bio(client.ssl, server_to_client, client_to_server); TEST_check(BIO_up_ref(server_to_client) > 0); TEST_check(BIO_up_ref(client_to_server) > 0); SSL_set_bio(server.ssl, client_to_server, server_to_client); ex_data_idx = SSL_get_ex_new_index(0, "ex data", NULL, NULL, NULL); TEST_check(ex_data_idx >= 0); TEST_check(SSL_set_ex_data(server.ssl, ex_data_idx, &server_ex_data) == 1); TEST_check(SSL_set_ex_data(client.ssl, ex_data_idx, &client_ex_data) == 1); SSL_set_info_callback(server.ssl, &info_cb); SSL_set_info_callback(client.ssl, &info_cb); client.status = server.status = PEER_RETRY; /* * Half-duplex handshake loop. * Client and server speak to each other synchronously in the same process. * We use non-blocking BIOs, so whenever one peer blocks for read, it * returns PEER_RETRY to indicate that it's the other peer's turn to write. * The handshake succeeds once both peers have succeeded. If one peer * errors out, we also let the other peer retry (and presumably fail). */ for(;;) { if (client_turn) { do_connect_step(&client, phase); status = handshake_status(client.status, server.status, 1 /* client went last */); } else { do_connect_step(&server, phase); status = handshake_status(server.status, client.status, 0 /* server went last */); } switch (status) { case HANDSHAKE_SUCCESS: phase = next_phase(phase); if (phase == CONNECTION_DONE) { ret->result = SSL_TEST_SUCCESS; goto err; } else { client.status = server.status = PEER_RETRY; /* * For now, client starts each phase. Since each phase is * started separately, we can later control this more * precisely, for example, to test client-initiated and * server-initiated shutdown. */ client_turn = 1; break; } case CLIENT_ERROR: ret->result = SSL_TEST_CLIENT_FAIL; goto err; case SERVER_ERROR: ret->result = SSL_TEST_SERVER_FAIL; goto err; case INTERNAL_ERROR: ret->result = SSL_TEST_INTERNAL_ERROR; goto err; case HANDSHAKE_RETRY: /* Continue. */ client_turn ^= 1; break; } } err: ret->server_alert_sent = server_ex_data.alert_sent; ret->server_num_fatal_alerts_sent = server_ex_data.num_fatal_alerts_sent; ret->server_alert_received = client_ex_data.alert_received; ret->client_alert_sent = client_ex_data.alert_sent; ret->client_num_fatal_alerts_sent = client_ex_data.num_fatal_alerts_sent; ret->client_alert_received = server_ex_data.alert_received; ret->server_protocol = SSL_version(server.ssl); ret->client_protocol = SSL_version(client.ssl); ret->servername = server_ex_data.servername; if ((sess = SSL_get0_session(client.ssl)) != NULL) SSL_SESSION_get0_ticket(sess, &tick, &tick_len); if (tick == NULL || tick_len == 0) ret->session_ticket = SSL_TEST_SESSION_TICKET_NO; else ret->session_ticket = SSL_TEST_SESSION_TICKET_YES; ret->session_ticket_do_not_call = server_ex_data.session_ticket_do_not_call; #ifndef OPENSSL_NO_NEXTPROTONEG SSL_get0_next_proto_negotiated(client.ssl, &proto, &proto_len); ret->client_npn_negotiated = dup_str(proto, proto_len); SSL_get0_next_proto_negotiated(server.ssl, &proto, &proto_len); ret->server_npn_negotiated = dup_str(proto, proto_len); #endif SSL_get0_alpn_selected(client.ssl, &proto, &proto_len); ret->client_alpn_negotiated = dup_str(proto, proto_len); SSL_get0_alpn_selected(server.ssl, &proto, &proto_len); ret->server_alpn_negotiated = dup_str(proto, proto_len); ret->client_resumed = SSL_session_reused(client.ssl); ret->server_resumed = SSL_session_reused(server.ssl); if (session_out != NULL) *session_out = SSL_get1_session(client.ssl); ctx_data_free_data(&server_ctx_data); ctx_data_free_data(&server2_ctx_data); ctx_data_free_data(&client_ctx_data); peer_free_data(&server); peer_free_data(&client); return ret; }