From: Nathan S. Evans Date: Fri, 8 Jul 2011 17:10:45 +0000 (+0000) Subject: initial nse commit X-Git-Tag: initial-import-from-subversion-38251~17928 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=5058b5b92455e26dd740b5c0344f78e95cd0fc2e;p=oweals%2Fgnunet.git initial nse commit --- diff --git a/src/include/gnunet_nse_service.h b/src/include/gnunet_nse_service.h new file mode 100644 index 000000000..012a61c22 --- /dev/null +++ b/src/include/gnunet_nse_service.h @@ -0,0 +1,115 @@ +/* + This file is part of GNUnet + (C) 2009, 2010 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +#ifndef GNUNET_NSE_SERVICE_H_ +#define GNUNET_NSE_SERVICE_H_ + +/** + * @file include/gnunet_nse_service.h + * @brief API to retrieve the current network size estimate, + * also to register for notifications whenever a new + * network size estimate is calculated. + * + * @author Nathan Evans + */ + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + +#include "gnunet_common.h" +#include "gnunet_configuration_lib.h" +#include "gnunet_scheduler_lib.h" + +/** + * Version of the network size estimation API. + */ +#define GNUNET_NSE_VERSION 0x00000000 + +/** + * Interval for sending network size estimation flood requests. + * Number is in milliseconds. + * This needs to be a factor of the number milliseconds in + * a day, as the base time used is midnight each day offset + * by this amount. + * + * There are 86400000 milliseconds in a day. + */ +#define GNUNET_NSE_INTERVAL 3600000 /* Once per hour */ + +/** + * Number of bits + */ +#define GNUNET_NSE_BITS + +/** + * Handle for the network size estimation service. + */ +struct GNUNET_NSE_Handle; + + +/** + * Callback to call when network size estimate is updated. + * + * @param cls closure + * @param estimate the value of the current network size estimate + * @param std_dev standard deviation (rounded down to nearest integer) + * of the size estimation values seen + * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration + */ +typedef int +(*GNUNET_NSE_Callback) (void *cls, double estimate, double std_dev); + +/** + * Connect to the network size estimation service. + * + * @param cfg the configuration to use + * @param func funtion to call with network size estimate + * @param func_cls closure to pass for network size estimate callback + * + * @return handle to use + */ +struct GNUNET_NSE_Handle * +GNUNET_NSE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_NSE_Callback func, void *func_cls); + + +/** + * Disconnect from network size estimation service + * + * @param h handle to destroy + * + */ +void +GNUNET_NSE_disconnect (struct GNUNET_NSE_Handle *h); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +#endif /* GNUNET_NSE_SERVICE_H_ */ diff --git a/src/nse/Makefile.am b/src/nse/Makefile.am new file mode 100644 index 000000000..bb8e5f509 --- /dev/null +++ b/src/nse/Makefile.am @@ -0,0 +1,65 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + + +lib_LTLIBRARIES = libgnunetnse.la + +libgnunetnse_la_SOURCES = \ + nse_api.c nse.h +libgnunetnse_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) $(XLIB) +libgnunetnse_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + + +bin_PROGRAMS = \ + gnunet-service-nse + +# gnunet_nse_SOURCES = \ +# gnunet-nse.c +# gnunet_nse_LDADD = \ +# $(top_builddir)/src/nse/libgnunetnse.la \ +# $(top_builddir)/src/util/libgnunetutil.la \ +# $(GN_LIBINTL) +# gnunet_nse_DEPENDENCIES = \ +# libgnunetnse.la + +gnunet_service_nse_SOURCES = \ + gnunet-service-nse.c +gnunet_service_nse_LDADD = \ + $(top_builddir)/src/nse/libgnunetnse.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(GN_LIBINTL) +gnunet_service_nse_DEPENDENCIES = \ + libgnunetnse.la + +check_PROGRAMS = \ + test_nse_api + +if ENABLE_TEST_RUN +TESTS = $(check_PROGRAMS) $(check_SCRIPTS) +endif + +test_nse_api_SOURCES = \ + test_nse_api.c +test_nse_api_LDADD = \ + $(top_builddir)/src/nse/libgnunetnse.la \ + $(top_builddir)/src/util/libgnunetutil.la + + +EXTRA_DIST = \ + test_nse_api_data.conf \ + $(check_SCRIPTS) + + diff --git a/src/nse/gnunet-service-nse.c b/src/nse/gnunet-service-nse.c new file mode 100644 index 000000000..6c4466bc7 --- /dev/null +++ b/src/nse/gnunet-service-nse.c @@ -0,0 +1,435 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file nse/gnunet-service-nse.c + * @brief network size estimation service + * @author Nathan Evans + * + * The purpose of this service is to estimate the size of the network. + * Given a specified interval, each peer hashes the most recent + * timestamp which is evenly divisible by that interval. This hash + * is compared in distance to the peer identity to choose an offset. + * The closer the peer identity to the hashed timestamp, the earlier + * the peer sends out a "nearest peer" message. The closest peer's + * message should thus be received before any others, which stops + * those peer from sending their messages at a later duration. So + * every peer should receive the same nearest peer message, and + * from this can calculate the expected number of peers in the + * network. + * + */ +#include "platform.h" +#include "gnunet_client_lib.h" +#include "gnunet_constants.h" +#include "gnunet_container_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_service_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_core_service.h" +#include "gnunet_time_lib.h" +#include "gnunet_nse_service.h" +#include "nse.h" + +#define DEFAULT_HISTORY_SIZE 10 + +#define DEFAULT_CORE_QUEUE_SIZE 32 + +#define MILLISECONDS_PER_DAY 86400000 + +/** + * Entry in the list of clients which + * should be notified upon a new network + * size estimate calculation. + */ +struct ClientListEntry +{ + /** + * Pointer to previous entry + */ + struct ClientListEntry *prev; + + /** + * Pointer to next entry + */ + struct ClientListEntry *next; + + /** + * Client to notify. + */ + struct GNUNET_SERVER_Client *client; +}; + +/** + * Handle to our current configuration. + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Handle to the core service. + */ +struct GNUNET_CORE_Handle *coreAPI; + +/** + * Copy of this peer's identity. + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * Head of global list of clients. + */ +static struct ClientListEntry *cle_head; + +/** + * Tail of global list of clients. + */ +static struct ClientListEntry *cle_tail; + +/** + * The current network size estimate. + */ +static double current_size_estimate; + +/** + * The standard deviation of the last + * DEFAULT_HISTORY_SIZE network size estimates. + */ +static double current_std_dev; + +/** + * Array of the last DEFAULT_HISTORY_SIZE + * network size estimates. + */ +//static double *size_estimates[DEFAULT_HISTORY_SIZE]; + +/** + * Task scheduled to send flood message. + */ +static GNUNET_SCHEDULER_TaskIdentifier flood_task; + +/** + * Notification context, simplifies client broadcasts. + */ +static struct GNUNET_SERVER_NotificationContext *nc; + +/** + * The previous major time. + */ +struct GNUNET_TIME_Absolute previous_timestamp; + +/** + * The next major time. + */ +static struct GNUNET_TIME_Absolute next_timestamp; + +/** + * Base increment of time to add to send time. + */ +static struct GNUNET_TIME_Relative increment; + +/** + * The current network size estimate message. + */ +static struct GNUNET_NSE_ClientMessage current_estimate_message; + +/** + * Handler for START message from client, triggers an + * immediate current network estimate notification. + * Also, we remember the client for updates upon future + * estimate measurements. + * + * @param cls unused + * @param client who sent the message + * @param message the message received + */ +static void +handle_start_message (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + if ((ntohs (message->size) != sizeof(struct GNUNET_MessageHeader)) + || (ntohs (message->type) != GNUNET_MESSAGE_TYPE_NSE_START)) + return; + +#if DEBUG_NSE + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "NSE", "Received START message from client\n"); +#endif + GNUNET_SERVER_notification_context_add (nc, client); + GNUNET_SERVER_notification_context_unicast (nc, client, + ¤t_estimate_message.header, + GNUNET_NO); + GNUNET_SERVER_receive_done(client, GNUNET_OK); +} + +/** + * Core handler for size estimate flooding messages. + * + * @param cls closure + * @param message message + * @param peer peer identity this notification is about + * @param atsi performance data + * + */ +static int +handle_p2p_size_estimate (void *cls, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_MessageHeader *message, + const struct GNUNET_TRANSPORT_ATS_Information + *atsi) +{ + + return GNUNET_OK; +} + + +/** + * Send a flood message containing our peer's public key + * and the hashed current timestamp. + */ +static void +send_flood_message (void *cls, + const struct GNUNET_SCHEDULER_TaskContext * tc) +{ + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + return; +} + +/** + * A client disconnected. Remove it from the + * global DLL of clients. + * + * @param cls closure, NULL + * @param client identification of the client + */ +static void +handle_client_disconnect (void *cls, + struct GNUNET_SERVER_Client* client) +{ + struct ClientListEntry *cle; + + while (NULL != (cle = cle_head)) + cle = cle->next; + + if (cle != NULL) + { + GNUNET_SERVER_client_drop(cle->client); + GNUNET_CONTAINER_DLL_remove(cle_head, + cle_tail, + cle); + GNUNET_free(cle); + } + if (coreAPI != NULL) + { + GNUNET_CORE_disconnect(coreAPI); + coreAPI = NULL; + } +} + +/** + * Task run during shutdown. + * + * @param cls unused + * @param tc unused + */ +static void +shutdown_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct ClientListEntry *cle; + + GNUNET_SERVER_notification_context_destroy (nc); + nc = NULL; + while (NULL != (cle = cle_head)) + { + GNUNET_SERVER_client_drop (cle->client); + GNUNET_CONTAINER_DLL_remove (cle_head, + cle_tail, + cle); + GNUNET_free (cle); + } + + if (coreAPI != NULL) + { + GNUNET_CORE_disconnect(coreAPI); + coreAPI = NULL; + } + +} + + +/** + * Task to schedule a flood message to be sent. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +static void schedule_flood_message (void *cls, + const struct + GNUNET_SCHEDULER_TaskContext * tc) +{ + GNUNET_HashCode timestamp_hash; + struct GNUNET_TIME_Absolute curr_time; + unsigned int matching_bits; + + /* Get the current UTC time */ + curr_time = GNUNET_TIME_absolute_get(); + /* Find the previous interval start time */ + previous_timestamp.abs_value = (curr_time.abs_value / GNUNET_NSE_INTERVAL) * GNUNET_NSE_INTERVAL; + /* Find the next interval start time */ + next_timestamp.abs_value = (curr_time.abs_value / GNUNET_NSE_INTERVAL) * (GNUNET_NSE_INTERVAL + 1); + + GNUNET_CRYPTO_hash(&next_timestamp.abs_value, sizeof(uint64_t), ×tamp_hash); + matching_bits = GNUNET_CRYPTO_hash_matching_bits(×tamp_hash, &my_identity.hashPubKey); + + GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_relative_add ( + GNUNET_TIME_relative_multiply ( + increment, + matching_bits), + GNUNET_TIME_absolute_get_remaining ( + next_timestamp)), + &send_flood_message, NULL); +} + +/** + * Called on core init/fail. + * + * @param cls service closure + * @param server handle to the server for this service + * @param identity the public identity of this peer + * @param publicKey the public key of this peer + */ +void +core_init (void *cls, + struct GNUNET_CORE_Handle *server, + const struct GNUNET_PeerIdentity *identity, + const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *publicKey) +{ + if (server == NULL) + { +#if DEBUG_NSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%s: Connection to core FAILED!\n", "nse", + GNUNET_i2s (identity)); +#endif + GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); + return; + } +#if DEBUG_NSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%s: Core connection initialized, I am peer: %s\n", "nse", + GNUNET_i2s (identity)); +#endif + + /* Copy our identity so we can use it */ + memcpy (&my_identity, identity, sizeof (struct GNUNET_PeerIdentity)); + + flood_task = GNUNET_SCHEDULER_add_now(&schedule_flood_message, NULL); +} + +/** + * Handle network size estimate clients. + * + * @param cls closure + * @param server the initialized server + * @param c configuration to use + */ +static void +run (void *cls, + struct GNUNET_SERVER_Handle *server, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + static const struct GNUNET_SERVER_MessageHandler handlers[] = { + {&handle_start_message, NULL, GNUNET_MESSAGE_TYPE_NSE_START, 0}, + {NULL, NULL, 0, 0} + }; + + static const struct GNUNET_CORE_MessageHandler core_handlers[] = { + {&handle_p2p_size_estimate, GNUNET_MESSAGE_TYPE_NSE_P2P_FLOOD, 0}, + {NULL, 0, 0} + }; + + cfg = c; + GNUNET_SERVER_add_handlers (server, handlers); + nc = GNUNET_SERVER_notification_context_create (server, 16); + GNUNET_SERVER_disconnect_notify (server, + &handle_client_disconnect, + NULL); + + /** Connect to core service and register core handlers */ + coreAPI = GNUNET_CORE_connect (cfg, /* Main configuration */ + DEFAULT_CORE_QUEUE_SIZE, /* queue size */ + NULL, /* Closure passed to functions */ + &core_init, /* Call core_init once connected */ + NULL, /* Handle connects */ + NULL, /* Handle disconnects */ + NULL, /* Do we care about "status" updates? */ + NULL, /* Don't want notified about all incoming messages */ + GNUNET_NO, /* For header only inbound notification */ + NULL, /* Don't want notified about all outbound messages */ + GNUNET_NO, /* For header only outbound notification */ + core_handlers); /* Register these handlers */ + + if (coreAPI == NULL) + { + GNUNET_SCHEDULER_add_now (&shutdown_task, NULL); + return; + } + + increment + = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, + GNUNET_NSE_INTERVAL + / (sizeof(GNUNET_HashCode) + * 8)); + /* Set we have no idea defaults for network size estimate */ + current_size_estimate = NAN; + current_std_dev = NAN; + + current_estimate_message.header.size = htons(sizeof(struct GNUNET_NSE_ClientMessage)); + current_estimate_message.header.type = htons(GNUNET_MESSAGE_TYPE_NSE_ESTIMATE); + current_estimate_message.size_estimate = current_size_estimate; + current_estimate_message.std_deviation = current_std_dev; + + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, + &shutdown_task, + NULL); +} + + +/** + * The main function for the statistics service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + return (GNUNET_OK == + GNUNET_SERVICE_run (argc, + argv, + "nse", + GNUNET_SERVICE_OPTION_NONE, + &run, NULL)) ? 0 : 1; +} + +/* End of gnunet-service-nse.c */ + diff --git a/src/nse/nse.h b/src/nse/nse.h new file mode 100644 index 000000000..ee9832dc6 --- /dev/null +++ b/src/nse/nse.h @@ -0,0 +1,119 @@ +/* + This file is part of GNUnet. + (C) 2001-2011 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @author Nathan Evans + * @file nse/nse.h + * + * @brief Common type definitions for the network size estimation + * service and API. + */ +#ifndef NSE_H +#define NSE_H + +#include "gnunet_common.h" + +#define DEBUG_NSE GNUNET_YES + +#define SIGNED_TIMESTAMP_SIZE sizeof(struct GNUNET_TIME_Absolute) + +/** FIXME: define NSE message types here. */ + +struct GNUNET_Signed_Timestamp +{ + char data[SIGNED_TIMESTAMP_SIZE]; +}; + +/** + * Network size estimate sent from the service + * to clients. Contains the current size estimate + * (or 0 if none has been calculated) and the + * standard deviation of known estimates. + * + */ +struct GNUNET_NSE_ClientMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_NSE_UPDATE + */ + struct GNUNET_MessageHeader header; + + /* + * The current estimated network size. + */ + double size_estimate; + + /** + * The standard deviation (rounded down + * to the nearest integer) of size + * estimations. + */ + double std_deviation; +}; + +/** + * Network size estimate reply; sent when "this" + * peer's timer has run out before receiving a + * valid reply from another peer. + * + * FIXME: Is this the right way to do this? + * I think we need to include both the public + * key and the timestamp signed by the private + * key. This way a recipient + * can verify that the peer at least generated + * the public/private key pair, and that the + * timestamp matches what the current peer + * believes it should be. The receiving peer + * would then check whether the XOR of the peer + * identity and the timestamp is within a + * reasonable range of the current time + * (+/- N seconds). If a closer message which + * also verifies hasn't been received (or this + * message is a duplicate), the peer + * calculates the size estimate and forwards + * the request to all other peers. + * + * Hmm... Is it enought to *just* send the peer + * identity? Obviously this is smaller, but it + * doesn't allow us to verify that the + * public/private key pair were generated, right? + */ +struct GNUNET_NSE_ReplyMessage +{ + /** + * Type: GNUNET_MESSAGE_TYPE_NSE_REPLY + */ + struct GNUNET_MessageHeader header; + + /** + * The current timestamp value (which all + * peers should agree on) signed by the + * private key of the initiating peer. + */ + struct GNUNET_Signed_Timestamp timestamp; + + /** + * Public key of the originator, signed timestamp + * is decrypted by this. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey; +}; + +#endif diff --git a/src/nse/nse_api.c b/src/nse/nse_api.c new file mode 100644 index 000000000..20c28e94f --- /dev/null +++ b/src/nse/nse_api.c @@ -0,0 +1,299 @@ +/* + This file is part of GNUnet. + (C) 2009, 2010, 2011 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file nse/nse_api.c + * @brief api to get information from the network size estimation service + * @author Nathan Evans + * + * TODO: + */ +#include "platform.h" +#include "gnunet_client_lib.h" +#include "gnunet_constants.h" +#include "gnunet_container_lib.h" +#include "gnunet_arm_service.h" +#include "gnunet_hello_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_server_lib.h" +#include "gnunet_time_lib.h" +#include "gnunet_nse_service.h" +#include "nse.h" + + +/** + * Handle for the service. + */ +struct GNUNET_NSE_Handle +{ + /** + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Socket (if available). + */ + struct GNUNET_CLIENT_Connection *client; + + /** + * Currently pending transmission request. + */ + struct GNUNET_CLIENT_TransmitHandle *th; + + /** + * Task doing exponential back-off trying to reconnect. + */ + GNUNET_SCHEDULER_TaskIdentifier reconnect_task; + + /** + * Time for next connect retry. + */ + struct GNUNET_TIME_Relative reconnect_delay; + + /** + * Should this handle auto-destruct once all actions have + * been processed? + */ + int do_destroy; + + /** + * Are we currently receiving from the service? + */ + int receiving; + + /** + * Callback function to call when message is received. + */ + GNUNET_NSE_Callback recv_cb; + + /** + * Closure to pass to callback. + */ + void *recv_cb_cls; + +}; + + +/** + * Type of a function to call when we receive a message + * from the service. + * + * @param cls closure + * @param msg message received, NULL on timeout or fatal error + */ +void message_handler (void *cls, + const struct GNUNET_MessageHeader * msg) +{ + struct GNUNET_NSE_Handle *h = cls; + struct GNUNET_NSE_ClientMessage *client_msg; + + if ((ntohs (msg->size) < sizeof(struct GNUNET_NSE_ClientMessage)) + || (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_NSE_ESTIMATE)) + { +#if DEBUG_NSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%s: received incorrect message (size %d < %d) from service!", + "NSE API", ntohs (msg->size), + sizeof(struct GNUNET_NSE_ClientMessage)); +#endif + return; + } + + client_msg = (struct GNUNET_NSE_ClientMessage *)msg; + + h->recv_cb (h->recv_cb_cls, client_msg->size_estimate, + client_msg->std_deviation); + + GNUNET_CLIENT_receive (h->client, + &message_handler, h, GNUNET_TIME_UNIT_FOREVER_REL); +} + +static void +reconnect (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc); + +/** + * Reschedule a connect attempt to the service. + * + * @param h transport service to reconnect + */ +static void +reschedule_connect (struct GNUNET_NSE_Handle *h) +{ + GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK); + + if (NULL != h->th) + { + GNUNET_CLIENT_notify_transmit_ready_cancel (h->th); + h->th = NULL; + } + if (NULL != h->client) + { + GNUNET_CLIENT_disconnect (h->client, GNUNET_NO); + h->client = NULL; + } + +#if DEBUG_NSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling task to reconnect to nse service in %llu ms.\n", + h->reconnect_delay.rel_value); +#endif + h->reconnect_task + = GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, + &reconnect, h); + if (h->reconnect_delay.rel_value == 0) + { + h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS; + } + else + { + h->reconnect_delay = GNUNET_TIME_relative_multiply (h->reconnect_delay, 2); + h->reconnect_delay = GNUNET_TIME_relative_min (GNUNET_TIME_UNIT_SECONDS, + h->reconnect_delay); + } +} + +/** + * Transmit START message to service. + * + * @param cls unused + * @param size number of bytes available in buf + * @param buf where to copy the message + * @return number of bytes copied to buf + */ +static size_t +send_start (void *cls, size_t size, void *buf) +{ + struct GNUNET_NSE_Handle *h = cls; + struct GNUNET_MessageHeader *msg; + + h->th = NULL; + if (buf == NULL) + { + /* Connect error... */ +#if DEBUG_NSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Shutdown while trying to transmit `%s' request.\n", + "START"); +#endif + reschedule_connect(h); + return 0; + } +#if DEBUG_NSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Transmitting `%s' request.\n", "START"); +#endif + GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); + + msg = (struct GNUNET_MessageHeader *)buf; + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + msg->type = htons (GNUNET_MESSAGE_TYPE_NSE_START); + GNUNET_CLIENT_receive (h->client, + &message_handler, h, GNUNET_TIME_UNIT_FOREVER_REL); + return sizeof (struct GNUNET_MessageHeader); +} + +/** + * Try again to connect to network size estimation service. + * + * @param cls the handle to the transport service + * @param tc scheduler context + */ +static void +reconnect (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_NSE_Handle *h = cls; + + h->reconnect_task = GNUNET_SCHEDULER_NO_TASK; + if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) + { + /* shutdown, just give up */ + return; + } +#if DEBUG_NSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connecting to network size estimation service.\n"); +#endif + GNUNET_assert (h->client == NULL); + h->client = GNUNET_CLIENT_connect ("nse", h->cfg); + GNUNET_assert (h->client != NULL); + + h->th = + GNUNET_CLIENT_notify_transmit_ready (h->client, + sizeof(struct GNUNET_MessageHeader), + GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_NO, + &send_start, + h); + GNUNET_assert(h->th != NULL); +} + +/** + * Connect to the network size estimation service. + * + * @param cfg the configuration to use + * @param func funtion to call with network size estimate + * @param func_cls closure to pass for network size estimate callback + * + * @return handle to use + */ +struct GNUNET_NSE_Handle * +GNUNET_NSE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, + GNUNET_NSE_Callback func, void *func_cls) +{ + struct GNUNET_NSE_Handle *ret; + + ret = GNUNET_malloc (sizeof (struct GNUNET_NSE_Handle)); + + if (func == NULL) + return NULL; + + ret->cfg = cfg; + ret->recv_cb = func; + ret->recv_cb_cls = func_cls; + ret->reconnect_delay = GNUNET_TIME_UNIT_ZERO; + ret->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, ret); + return ret; +} + +/** + * Disconnect from network size estimation service + * + * @param h handle to destroy + * + */ +void +GNUNET_NSE_disconnect (struct GNUNET_NSE_Handle *h) +{ + GNUNET_assert(h != NULL); + if (h->reconnect_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(h->reconnect_task); + h->reconnect_task = GNUNET_SCHEDULER_NO_TASK; + } + if (h->th != NULL) + GNUNET_CLIENT_notify_transmit_ready_cancel(h->th); + if (h->client != NULL) + GNUNET_CLIENT_disconnect(h->client, GNUNET_NO); + + GNUNET_free(h); +} diff --git a/src/nse/test_nse.conf b/src/nse/test_nse.conf new file mode 100644 index 000000000..554cc4c0f --- /dev/null +++ b/src/nse/test_nse.conf @@ -0,0 +1,37 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-nse/ +DEFAULTCONFIG = test_nse.conf + +[nse] +PORT = 22353 +UNIXPATH = /tmp/test-nse-service-nse.unix +AUTOSTART = YES +DEBUG = YES + +[arm] +PORT = 22354 +DEFAULTSERVICES = nse +UNIXPATH = /tmp/test-nse-service-arm.unix +#DEBUG = YES + +[fs] +AUTOSTART = NO + +[datastore] +AUTOSTART = NO + +[dht] +AUTOSTART = NO + +[transport] +AUTOSTART = NO + +[core] +AUTOSTART = YES + +[peerinfo] +AUTOSTART = NO + +[dns] +AUTOSTART = NO + diff --git a/src/nse/test_nse_api.c b/src/nse/test_nse_api.c new file mode 100644 index 000000000..f7274640a --- /dev/null +++ b/src/nse/test_nse_api.c @@ -0,0 +1,182 @@ +/* + This file is part of GNUnet. + (C) 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file nse/test_nse_api.c + * @brief testcase for nse_api.c + */ +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_program_lib.h" +#include "gnunet_scheduler_lib.h" +#include "gnunet_nse_service.h" + +#define DEBUG_NSE GNUNET_YES + +#define START_ARM GNUNET_YES + +static struct GNUNET_NSE_Handle *h; + +GNUNET_SCHEDULER_TaskIdentifier die_task; + +struct PeerContext +{ + struct GNUNET_CONFIGURATION_Handle *cfg; +#if START_ARM + struct GNUNET_OS_Process *arm_proc; +#endif +}; + +static struct PeerContext p1; + +/** + * Signature of the main function of a task. + * + * @param cls closure + * @param tc context information (why was this task triggered now) + */ +static void end_test (void *cls, + const struct GNUNET_SCHEDULER_TaskContext * tc) +{ + if (h != NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Disconnecting from NSE service.\n"); + GNUNET_NSE_disconnect (h); + } +} + +/** + * Callback to call when network size estimate is updated. + * + * @param cls unused + * @param estimate the value of the current network size estimate + * @param std_dev standard deviation (rounded down to nearest integer) + * of the size estimation values seen + * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration + */ +static int +check_nse_message (void *cls, double estimate, double std_dev) +{ + int *ok = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received NSE message, estimate %f, standard deviation %f.\n"); + /* Fantastic check below. Expect NaN, the only thing not equal to itself. */ + if ((estimate != estimate) && (std_dev != std_dev)) + (*ok) = 0; + + if (die_task != GNUNET_SCHEDULER_NO_TASK) + GNUNET_SCHEDULER_cancel(die_task); + GNUNET_SCHEDULER_add_now(&end_test, NULL); + return GNUNET_OK; +} + +static void +setup_peer (struct PeerContext *p, const char *cfgname) +{ + p->cfg = GNUNET_CONFIGURATION_create (); +#if START_ARM + p->arm_proc = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm", + "gnunet-service-arm", +#if VERBOSE_ARM + "-L", "DEBUG", +#endif + "-c", cfgname, NULL); +#endif + GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname)); + +} + +static void +stop_arm (struct PeerContext *p) +{ +#if START_ARM + if (0 != GNUNET_OS_process_kill (p->arm_proc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); + GNUNET_OS_process_wait (p->arm_proc); + GNUNET_OS_process_close (p->arm_proc); + p->arm_proc = NULL; +#endif + GNUNET_CONFIGURATION_destroy (p->cfg); +} + +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + die_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply + (GNUNET_TIME_UNIT_MINUTES, 1), + &end_test, NULL); + + setup_peer (&p1, cfgfile); + h = GNUNET_NSE_connect (cfg, &check_nse_message, cls); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connecting to NSE service.\n"); + GNUNET_assert (h != NULL); +} + +static int +check () +{ + int ok = 1; + char *const argv[] = { "test-nse-api", + "-c", + "test_nse.conf", +#if DEBUG_NSE + "-L", "DEBUG", +#else + "-L", "WARNING", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + + GNUNET_PROGRAM_run (5, argv, "test-nse-api", "nohelp", + options, &run, &ok); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stopping arm.\n"); + stop_arm (&p1); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test_nse_api", +#if DEBUG_NSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + return ret; +} + +/* end of test_nse_api.c */