From 71c69f178dd8ad00e917c8724c4f2417f8d9648f Mon Sep 17 00:00:00 2001 From: "Nathan S. Evans" Date: Tue, 19 Jan 2010 13:23:04 +0000 Subject: [PATCH] moderate udp support, not really tested (: --- src/include/gnunet_network_lib.h | 26 + src/include/gnunet_protocols.h | 9 + src/include/gnunet_scheduler_lib.h | 4 +- src/transport/Makefile.am | 17 +- src/transport/plugin_transport_tcp.c | 6 +- src/transport/plugin_transport_udp.c | 1087 ++++++++++------- .../test_plugin_transport_data_udp.conf | 24 + src/transport/test_plugin_transport_udp.c | 358 ++++++ src/util/network.c | 62 + src/util/scheduler.c | 10 +- 10 files changed, 1179 insertions(+), 424 deletions(-) create mode 100644 src/transport/test_plugin_transport_data_udp.conf create mode 100644 src/transport/test_plugin_transport_udp.c diff --git a/src/include/gnunet_network_lib.h b/src/include/gnunet_network_lib.h index 513787e08..71c8d4346 100644 --- a/src/include/gnunet_network_lib.h +++ b/src/include/gnunet_network_lib.h @@ -123,6 +123,27 @@ int GNUNET_NETWORK_socket_getsockopt(const struct GNUNET_NETWORK_Handle *desc, i */ int GNUNET_NETWORK_socket_listen (const struct GNUNET_NETWORK_Handle *desc, int backlog); +/** + * How much data is available to be read on this descriptor? + * @param desc socket + */ +unsigned int +GNUNET_NETWORK_socket_recvfrom_amount (const struct GNUNET_NETWORK_Handle * desc); + +/** + * Read data from a connected socket (always non-blocking). + * @param desc socket + * @param buffer buffer + * @param length length of buffer + * @param src_addr either the source to recv from, or all zeroes + * to be filled in by recvfrom + * @param addrlen length of the addr + */ +ssize_t +GNUNET_NETWORK_socket_recvfrom (const struct GNUNET_NETWORK_Handle * desc, + void *buffer, size_t length, + struct sockaddr *src_addr, socklen_t *addrlen); + /** * Read data from a connected socket (always non-blocking). * @@ -248,6 +269,11 @@ void GNUNET_NETWORK_fdset_add (struct GNUNET_NETWORK_FDSet *dst, void GNUNET_NETWORK_fdset_copy(struct GNUNET_NETWORK_FDSet *to, const struct GNUNET_NETWORK_FDSet *from); +/* + * Return file descriptor for this network handle + */ +int +GNUNET_NETWORK_get_fd (struct GNUNET_NETWORK_Handle *desc); /** * Copy a native fd set * @param to destination diff --git a/src/include/gnunet_protocols.h b/src/include/gnunet_protocols.h index 51442958c..cdf5d0df3 100644 --- a/src/include/gnunet_protocols.h +++ b/src/include/gnunet_protocols.h @@ -242,6 +242,15 @@ extern "C" */ #define GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_DATA 43 +/* + * UDP Ping message + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_PING 51 + +/* + * UDP Pong message + */ +#define GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_PONG 52 /** * Initial setup message from core client to core. diff --git a/src/include/gnunet_scheduler_lib.h b/src/include/gnunet_scheduler_lib.h index 9ba4399ad..3205729a3 100644 --- a/src/include/gnunet_scheduler_lib.h +++ b/src/include/gnunet_scheduler_lib.h @@ -290,7 +290,7 @@ GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched, * @param sched scheduler to use * @param prerequisite_task run this task after the task with the given * task identifier completes (and any of our other - * conditions, such as delay, read or write-readyness + * conditions, such as delay, read or write-readiness * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency * on completion of other tasks (this will cause the task to run as * soon as possible). @@ -480,7 +480,7 @@ GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle *sched, * @param prio how important is this task? * @param prerequisite_task run this task after the task with the given * task identifier completes (and any of our other - * conditions, such as delay, read or write-readyness + * conditions, such as delay, read or write-readiness * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency * on completion of other tasks. * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever", diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index 02c346ef0..7d1f89a6e 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -48,6 +48,7 @@ gnunet_service_transport_LDADD = \ plugin_LTLIBRARIES = \ libgnunet_plugin_transport_tcp.la \ + libgnunet_plugin_transport_udp.la \ libgnunet_plugin_transport_template.la # TODO: add udp, http, nat, etc. @@ -67,10 +68,19 @@ libgnunet_plugin_transport_template_la_LIBADD = \ libgnunet_plugin_transport_template_la_LDFLAGS = \ $(GN_PLUGIN_LDFLAGS) +libgnunet_plugin_transport_udp_la_SOURCES = \ + plugin_transport_udp.c +libgnunet_plugin_transport_udp_la_LIBADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/util/libgnunetutil.la +libgnunet_plugin_transport_udp_la_LDFLAGS = \ + $(GN_PLUGIN_LDFLAGS) check_PROGRAMS = \ test_transport_api \ - test_plugin_transport + test_plugin_transport \ + test_plugin_transport_udp # TODO: add tests for tcp, udp, http, nat, etc. TESTS = $(check_PROGRAMS) @@ -88,6 +98,11 @@ test_plugin_transport_LDADD = \ $(top_builddir)/src/transport/libgnunettransport.la \ $(top_builddir)/src/util/libgnunetutil.la +test_plugin_transport_udp_SOURCES = \ + test_plugin_transport_udp.c +test_plugin_transport_udp_LDADD = \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la EXTRA_DIST = \ test_transport_api_data.conf \ diff --git a/src/transport/plugin_transport_tcp.c b/src/transport/plugin_transport_tcp.c index 8c5f79fd3..2694b3f1e 100644 --- a/src/transport/plugin_transport_tcp.c +++ b/src/transport/plugin_transport_tcp.c @@ -218,7 +218,7 @@ struct PendingMessage /** * Continuation function to call once the message - * has been sent. Can be NULL if there is no + * has been sent. Can be NULL if there is no * continuation to call. */ GNUNET_TRANSPORT_TransmitContinuation transmit_cont; @@ -514,7 +514,7 @@ static void process_pending_messages (struct Session *session); /** * Function called to notify a client about the socket - * begin ready to queue more data. "buf" will be + * being ready to queue more data. "buf" will be * NULL and "size" zero if the socket was closed for * writing in the meantime. * @@ -1073,7 +1073,7 @@ session_try_connect (void *cls, static void tcp_plugin_send (void *cls, const struct GNUNET_PeerIdentity *target, - unsigned int priority, + unsigned int priority, const struct GNUNET_MessageHeader *msg, struct GNUNET_TIME_Relative timeout, GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls) diff --git a/src/transport/plugin_transport_udp.c b/src/transport/plugin_transport_udp.c index 624ca0933..d2839762e 100644 --- a/src/transport/plugin_transport_udp.c +++ b/src/transport/plugin_transport_udp.c @@ -22,15 +22,36 @@ * @file transport/plugin_transport_udp.c * @brief Implementation of the UDP transport service * @author Christian Grothoff + * @author Nathan Evans? */ +/* Notes for Nate: + * - Use simple network api to open ports and do select and whatnot. + * - For sending, just find session and send away! + * - All that is required is a simple linked list of known peers and + * how to send to them. Each will have a socket associated with it + * and that's pretty much it! May want to re-start over with 0.8 + * code now that we have a better idea. + * - How often to do select loop? Do with a recall thing since we + * don't want to use cron jobs? + * - First thing, set up server which listens on a UDP port. + * - Use plugin->env->receive to notify server of message receipt + * + */ #include "platform.h" -#include "gnunet_util.h" +#include "gnunet_hello_lib.h" +#include "gnunet_connection_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_peerinfo_service.h" #include "gnunet_protocols.h" -#include "gnunet_transport.h" -#include "gnunet_stats_service.h" -#include "gnunet_upnp_service.h" -#include "ip.h" +#include "gnunet_resolver_service.h" +#include "gnunet_server_lib.h" +#include "gnunet_service_lib.h" +#include "gnunet_signatures.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" +#include "plugin_transport.h" +#include "transport.h" #define DEBUG_UDP GNUNET_YES @@ -40,151 +61,201 @@ */ #define MESSAGE_SIZE 1472 +/** + * Handle for request of hostname resolution, non-NULL if pending. + */ +static struct GNUNET_RESOLVER_RequestHandle *hostname_dns; + /** * Message-Packet header. */ -typedef struct +struct UDPMessage { /** * size of the message, in bytes, including this header. */ - GNUNET_MessageHeader header; + struct GNUNET_MessageHeader header; /** * What is the identity of the sender (GNUNET_hash of public key) */ - GNUNET_PeerIdentity sender; + struct GNUNET_PeerIdentity sender; -} UDPMessage; +}; -#define MY_TRANSPORT_NAME "UDP" -#include "common.c" +/* Forward definition */ +struct Plugin; -/* *********** globals ************* */ +/** + * Session handle for UDP connections. + */ +struct Session +{ -static int stat_bytesReceived; + /** + * Stored in a linked list. + */ + struct Session *next; -static int stat_bytesSent; + /** + * Pointer to the global plugin struct. + */ + struct Plugin *plugin; -static int stat_bytesDropped; + /** + * To whom are we talking to (set to our identity + */ + struct GNUNET_PeerIdentity target; -static int stat_udpConnected; + /** + * Address of the other peer if WE initiated the connection + * (and hence can be sure what it is), otherwise NULL. + */ + void *connect_addr; -/** - * thread that listens for inbound messages - */ -static struct GNUNET_SelectHandle *selector; + /** + * Length of connect_addr, can be 0. + */ + size_t connect_alen; -/** - * the socket that we transmit all data with - */ -static struct GNUNET_SocketHandle *udp_sock; + /* + * Random challenge number for validation + */ + int challenge; -static struct GNUNET_LoadMonitor *load_monitor; + /* + * Have we received validation (performed ping/pong) from this peer? + */ + unsigned int validated; +}; /** - * The socket of session has data waiting, process! - * - * This function may only be called if the tcplock is - * already held by the caller. + * Encapsulation of all of the state of the plugin. */ -static int -select_message_handler (void *mh_cls, - struct GNUNET_SelectHandle *sh, - struct GNUNET_SocketHandle *sock, - void *sock_ctx, const GNUNET_MessageHeader * msg) +struct Plugin { - unsigned int len; - GNUNET_TransportPacket *mp; - const UDPMessage *um; + /** + * Our environment. + */ + struct GNUNET_TRANSPORT_PluginEnvironment *env; - len = ntohs (msg->size); - if (len <= sizeof (UDPMessage)) - { - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK, - _("Received malformed message via %s. Ignored.\n"), - "UDP"); - return GNUNET_SYSERR; - } - um = (const UDPMessage *) msg; - mp = GNUNET_malloc (sizeof (GNUNET_TransportPacket)); - mp->msg = GNUNET_malloc (len - sizeof (UDPMessage)); - memcpy (mp->msg, &um[1], len - sizeof (UDPMessage)); - mp->sender = um->sender; - mp->size = len - sizeof (UDPMessage); - mp->tsession = NULL; - coreAPI->receive (mp); - if (stats != NULL) - stats->change (stat_bytesReceived, len); - return GNUNET_OK; -} + /** + * List of open TCP sessions. + */ + struct Session *sessions; -static void * -select_accept_handler (void *ah_cls, - struct GNUNET_SelectHandle *sh, - struct GNUNET_SocketHandle *sock, - const void *addr, unsigned int addr_len) -{ - static int nonnullpointer; + /** + * Handle for the statistics service. + */ + struct GNUNET_STATISTICS_Handle *statistics; - if (GNUNET_NO != is_rejected_tester (addr, addr_len)) - return NULL; - return &nonnullpointer; -} + /** + * Handle to the network service. + */ + struct GNUNET_SERVICE_Context *service; + + /** + * ID of task used to update our addresses when one expires. + */ + GNUNET_SCHEDULER_TaskIdentifier address_update_task; + + /** + * ID of select task + */ + GNUNET_SCHEDULER_TaskIdentifier select_task; + + /** + * Port that we are actually listening on. + */ + uint16_t open_port; + + /** + * Port that the user said we would have visible to the + * rest of the world. + */ + uint16_t adv_port; + + /* + * FD Read set + */ + struct GNUNET_NETWORK_FDSet * rs; + +}; /** - * Select has been forced to close a connection. - * Free the associated context. + * Message used to ask a peer to validate receipt (to check an address + * from a HELLO). Followed by the address used. Note that the + * recipients response does not affirm that he has this address, + * only that he got the challenge message. */ -static void -select_close_handler (void *ch_cls, - struct GNUNET_SelectHandle *sh, - struct GNUNET_SocketHandle *sock, void *sock_ctx) +struct UDPPingMessage { - /* do nothing */ -} + + /** + * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_PING + */ + struct GNUNET_MessageHeader header; + + /** + * Random challenge number (in network byte order). + */ + uint32_t challenge GNUNET_PACKED; + + + +}; + /** - * Establish a connection to a remote node. + * Message used to validate a HELLO. The challenge is included in the + * confirmation to make matching of replies to requests possible. The + * signature signs the original challenge number, our public key, the + * sender's address (so that the sender can check that the address we + * saw is plausible for him and possibly detect a MiM attack) and a + * timestamp (to limit replay).

* - * @param hello the hello-Message for the target node - * @param tsessionPtr the session handle that is to be set - * @param may_reuse are we allowed to re-use an existing connection (ignored for UDP) - * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + * This message is followed by the address of the + * client that we are observing (which is part of what + * is being signed). */ -static int -udp_connect (const GNUNET_MessageHello * hello, - GNUNET_TSession ** tsessionPtr, int may_reuse) +struct UDPPongMessage { - GNUNET_TSession *tsession; - - tsession = GNUNET_malloc (sizeof (GNUNET_TSession)); - memset (tsession, 0, sizeof (GNUNET_TSession)); - tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello)); - memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello)); - tsession->ttype = myAPI.protocol_number; - tsession->peer = hello->senderIdentity; - *tsessionPtr = tsession; - if (stats != NULL) - stats->change (stat_udpConnected, 1); - return GNUNET_OK; -} + /** + * Type will be GNUNET_MESSAGE_TYPE_TRANSPORT_TCP_PONG + */ + struct GNUNET_MessageHeader header; + + /** + * Random challenge number (in network byte order). + */ + uint32_t challenge GNUNET_PACKED; + + /* Length of addr, appended to end of message */ + unsigned int addrlen; +}; + +/* *********** globals ************* */ + +/** + * the socket that we transmit all data with + */ +static struct GNUNET_NETWORK_Handle *udp_sock; + /** * A (core) Session is to be associated with a transport session. The * transport service may want to know in order to call back on the * core if the connection is being closed. * - * @param tsession the session handle passed along + * @param session the session handle passed along * from the call to receive that was made by the transport * layer * @return GNUNET_OK if the session could be associated, * GNUNET_SYSERR if not. */ int -udp_associate (GNUNET_TSession * tsession) +udp_associate (struct Session * session) { return GNUNET_SYSERR; /* UDP connections can never be associated */ } @@ -195,18 +266,12 @@ udp_associate (GNUNET_TSession * tsession) * @param tsession the session that is closed * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed */ -static int -udp_disconnect (GNUNET_TSession * tsession) +void +udp_disconnect (void *cls, + const struct GNUNET_PeerIdentity * + target) { - if (tsession != NULL) - { - if (tsession->internal != NULL) - GNUNET_free (tsession->internal); - GNUNET_free (tsession); - if (stats != NULL) - stats->change (stat_udpConnected, -1); - } - return GNUNET_OK; + return; } /** @@ -214,385 +279,581 @@ udp_disconnect (GNUNET_TSession * tsession) * restarted later! */ static int -udp_transport_server_stop () +udp_transport_server_stop (void *cls) { - GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL); - if (selector != NULL) + struct Plugin *plugin = cls; + GNUNET_assert (udp_sock != NULL); + if (plugin->select_task != GNUNET_SCHEDULER_NO_TASK) { - GNUNET_select_destroy (selector); - selector = NULL; + GNUNET_SCHEDULER_cancel (plugin->env->sched, plugin->select_task); + plugin->select_task = GNUNET_SCHEDULER_NO_TASK; } - GNUNET_socket_destroy (udp_sock); + + GNUNET_NETWORK_socket_close (udp_sock); udp_sock = NULL; return GNUNET_OK; } +static struct Session * +find_session(void *cls, struct Session * session_list, const struct GNUNET_PeerIdentity *peer) +{ + struct Plugin *plugin = cls; + struct Session *pos; + pos = session_list; + + while (pos != NULL) + { + if (memcmp(peer, &pos->target, sizeof(struct GNUNET_PeerIdentity)) == 0) + return pos; + pos = pos->next; + } + + return NULL; +} + /** - * Test if the transport would even try to send - * a message of the given size and importance - * for the given session.
- * This function is used to check if the core should - * even bother to construct (and encrypt) this kind - * of message. + * Function that can be used by the transport service to transmit + * a message using the plugin. * - * @return GNUNET_YES if the transport would try (i.e. queue - * the message or call the OS to send), - * GNUNET_NO if the transport would just drop the message, - * GNUNET_SYSERR if the size/session is invalid + * @param cls closure + * @param service_context value passed to the transport-service + * to identify the neighbour + * @param target who should receive this message + * @param priority how important is the message + * @param msg the message to transmit + * @param timeout when should we time out (give up) if we can not transmit? + * @param cont continuation to call once the message has + * been transmitted (or if the transport is ready + * for the next transmission call; or if the + * peer disconnected...) + * @param cont_cls closure for cont */ -static int -udp_test_would_try (GNUNET_TSession * tsession, unsigned int size, - int important) +static void +udp_plugin_send (void *cls, + const struct GNUNET_PeerIdentity *target, + unsigned int priority, + const struct GNUNET_MessageHeader *msg, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls) { - const GNUNET_MessageHello *hello; + struct Plugin *plugin = cls; + struct Session *session; + struct UDPMessage *message; + int ssize; + size_t sent; - if (udp_sock == NULL) - return GNUNET_SYSERR; - if (size == 0) - { - GNUNET_GE_BREAK (coreAPI->ectx, 0); - return GNUNET_SYSERR; - } - if (size > myAPI.mtu) + session = find_session(plugin, plugin->sessions, target); + + if ((session == NULL) || (udp_sock == NULL)) + return; + + /* Build the message to be sent */ + message = GNUNET_malloc(sizeof(struct UDPMessage) + ntohs(msg->size)); + ssize = sizeof(struct UDPMessage) + ntohs(msg->size); + +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("In udp_send, ssize is %d\n"), ssize); +#endif + message->header.size = htons(ssize); + message->header.type = htons(0); + memcpy (&message->sender, plugin->env->my_identity, sizeof(struct GNUNET_PeerIdentity)); + memcpy (&message[1], msg, ntohs (msg->size)); + + /* Actually send the message */ + sent = GNUNET_NETWORK_socket_sendto (udp_sock, message, ssize, session->connect_addr, + session->connect_alen); + + if (cont != NULL) { - GNUNET_GE_BREAK (coreAPI->ectx, 0); - return GNUNET_SYSERR; + if (sent == GNUNET_SYSERR) + cont(cont_cls, target, GNUNET_SYSERR); + else + cont(cont_cls, target, GNUNET_OK); } - hello = (const GNUNET_MessageHello *) tsession->internal; - if (hello == NULL) - return GNUNET_SYSERR; - return GNUNET_YES; + + return; } /** - * Create a UDP socket. If possible, use IPv6, otherwise - * try IPv4. Update available_protocols accordingly. + * We've received a PING from this peer via UDP. + * Send back our PONG. + * + * @param cls closure + * @param sender the Identity of the sender + * @param message the actual message */ -static struct GNUNET_NETWORK_Handle * -udp_create_socket () +static void +handle_udp_ping (void *cls, + struct GNUNET_PeerIdentity *sender, struct sockaddr_storage * addr, size_t addrlen, + const struct GNUNET_MessageHeader *message) { - struct GNUNET_NETWORK_Handle *desc; - - available_protocols = VERSION_AVAILABLE_NONE; - desc = NULL; - if (GNUNET_YES != - GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD", "DISABLE-IPV6", - GNUNET_YES)) - { - desc = GNUNET_net_socket (PF_INET6, SOCK_DGRAM, 17); - } - if (NULL == desc) - { - desc = GNUNET_net_socket (PF_INET, SOCK_DGRAM, 17); - if (NULL == desc) - { - GNUNET_GE_LOG_STRERROR (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | - GNUNET_GE_BULK, "socket"); - return GNUNET_SYSERR; - } - available_protocols = VERSION_AVAILABLE_IPV4; - } - else + struct Plugin *plugin = cls; + struct Session *head = plugin->sessions; + const struct UDPPingMessage *ping = (const struct UDPPingMessage *)message; + struct UDPPongMessage *pong; + struct Session *found; + +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("handling ping, challenge is %d\n"), ntohs(ping->challenge)); +#endif + found = find_session(plugin, head, sender); + if (found != NULL) { - available_protocols = VERSION_AVAILABLE_IPV6 | VERSION_AVAILABLE_IPV4; + pong = GNUNET_malloc(sizeof(struct UDPPongMessage) + addrlen); + pong->header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_PONG); + pong->header.size = htons(sizeof(struct UDPPongMessage) + addrlen); + pong->challenge = ping->challenge; + memcpy(&pong[1], addr, addrlen); + pong->addrlen = htons(addrlen); + + udp_plugin_send(plugin, sender, GNUNET_SCHEDULER_PRIORITY_DEFAULT, &pong->header, GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30), NULL, NULL); } - return desc; + + return; + } /** - * Send a message to the specified remote node. + * We've received a PONG from this peer via UDP. + * Great. Call validate func if we haven't already + * received a PONG. * - * @param tsession the GNUNET_MessageHello identifying the remote node - * @param message what to send - * @param size the size of the message - * @param important is this message "important" to override typical transmit limits? - * @return GNUNET_SYSERR on error, GNUNET_OK on success + * @param cls closure + * @param client identification of the client + * @param message the actual message */ -static int -udp_send (GNUNET_TSession * tsession, - const void *message, const unsigned int size, int important) +static void +handle_udp_pong (void *cls, + struct GNUNET_PeerIdentity *sender, + const struct GNUNET_MessageHeader *message) { - const GNUNET_MessageHello *hello; - const HostAddress *haddr; - UDPMessage *mp; - struct sockaddr_in serverAddrv4; - struct sockaddr_in6 serverAddrv6; - struct sockaddr *serverAddr; - socklen_t addrlen; - unsigned short available; - int ok; - int ssize; - size_t sent; - - GNUNET_GE_ASSERT (NULL, tsession != NULL); - if (udp_sock == NULL) - return GNUNET_SYSERR; - if (size == 0) + struct Plugin *plugin = cls; + const struct UDPPongMessage *pong = (struct UDPPongMessage *)message; + struct Session *found; + unsigned int addr_len; + struct sockaddr_storage addr; + +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("handling pong\n")); +#endif + found = find_session(plugin, plugin->sessions, sender); +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("found->challenge %d, pong->challenge %d\n"), found->challenge, ntohs(pong->challenge)); +#endif + if ((found != NULL) && (found->challenge == ntohs(pong->challenge))) { - GNUNET_GE_BREAK (coreAPI->ectx, 0); - return GNUNET_SYSERR; + found->validated = GNUNET_YES; + addr_len = ntohs(pong->addrlen); +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("found associated ping, addr is %u bytes\n"), addr_len); +#endif + memcpy(&addr, &pong[1], addr_len); + plugin->env->notify_validation(plugin->env->cls, "udp", sender, ntohs(pong->challenge), (char *)&addr); } - if (size > myAPI.mtu) + else { - GNUNET_GE_BREAK (coreAPI->ectx, 0); - return GNUNET_SYSERR; - } - hello = (const GNUNET_MessageHello *) tsession->internal; - if (hello == NULL) - return GNUNET_SYSERR; - haddr = (const HostAddress *) &hello[1]; - available = ntohs (haddr->availability) & available_protocols; - if (available == VERSION_AVAILABLE_NONE) - return GNUNET_SYSERR; - if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6)) - { - if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0) - available = VERSION_AVAILABLE_IPV4; - else - available = VERSION_AVAILABLE_IPV6; - } - ssize = size + sizeof (UDPMessage); - mp = GNUNET_malloc (ssize); - mp->header.size = htons (ssize); - mp->header.type = 0; - mp->sender = *(coreAPI->my_identity); - memcpy (&mp[1], message, size); - ok = GNUNET_SYSERR; - - if ((available & VERSION_AVAILABLE_IPV4) > 0) - { - memset (&serverAddrv4, 0, sizeof (serverAddrv4)); -#if HAVE_SOCKADDR_IN_SIN_LEN - serverAddrv4.sin_len = sizeof (serverAddrv4); +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("Session not found!\n")); #endif - serverAddrv4.sin_family = AF_INET; - serverAddrv4.sin_port = haddr->port; - memcpy (&serverAddrv4.sin_addr, &haddr->ipv4, sizeof (struct in_addr)); - addrlen = sizeof (serverAddrv4); - serverAddr = (struct sockaddr *) &serverAddrv4; } - else + return; +} + +static void +udp_plugin_select (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Plugin *plugin = cls; + struct GNUNET_TIME_Relative timeout = GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 500); + char * buf; + struct UDPMessage *msg; + const struct GNUNET_MessageHeader *hdr; + struct GNUNET_PeerIdentity *sender; + unsigned int buflen; + socklen_t fromlen; + struct sockaddr_storage addr; + ssize_t ret; + + do { - memset (&serverAddrv6, 0, sizeof (serverAddrv6)); -#if HAVE_SOCKADDR_IN_SIN_LEN - serverAddrv6.sin6_len = sizeof (serverAddrv6); + buflen = GNUNET_NETWORK_socket_recvfrom_amount(udp_sock); + +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("we expect to read %u bytes\n"), buflen); #endif - serverAddrv6.sin6_family = AF_INET; - serverAddrv6.sin6_port = haddr->port; - memcpy (&serverAddrv6.sin6_addr, &haddr->ipv6, - sizeof (struct in6_addr)); - addrlen = sizeof (serverAddrv6); - serverAddr = (struct sockaddr *) &serverAddrv6; - } -#ifndef MINGW - if (GNUNET_YES == GNUNET_socket_send_to (udp_sock, - GNUNET_NC_NONBLOCKING, - mp, - ssize, &sent, - (const char *) serverAddr, - addrlen)) -#else - sent = - win_ols_sendto (udp_sock, mp, ssize, (const char *) serverAddr, addrlen); - if (sent != SOCKET_ERROR) + + if (buflen == GNUNET_NO) + return; + + buf = GNUNET_malloc(buflen); + fromlen = sizeof(addr); + +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("src_addr_len is %u\n"), fromlen); #endif - { - ok = GNUNET_OK; - if (stats != NULL) - stats->change (stat_bytesSent, sent); - } - else - { - if (stats != NULL) - stats->change (stat_bytesDropped, ssize); + + memset(&addr, 0, fromlen); + ret = GNUNET_NETWORK_socket_recvfrom(udp_sock, buf, buflen, (struct sockaddr *)&addr, &fromlen); + +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("socket_recv returned %u, src_addr_len is %u\n"), ret, fromlen); +#endif + + if (ret <= 0) + { + GNUNET_free(buf); + return; + } + + msg = (struct UDPMessage *)buf; + +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("header reports message size of %d\n"), ntohs(msg->header.size)); + + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("header reports message type of %d\n"), ntohs(msg->header.type)); +#endif + /*if (ntohs(hdr->size) < sizeof(struct UDPMessage)) + { + GNUNET_free(buf); + GNUNET_NETWORK_fdset_zero(plugin->rs); + GNUNET_NETWORK_fdset_set(plugin->rs, udp_sock); + break; + }*/ + hdr = (const struct GNUNET_MessageHeader *)&msg[1]; + sender = GNUNET_malloc(sizeof(struct GNUNET_PeerIdentity)); + memcpy(sender, &msg->sender, sizeof(struct GNUNET_PeerIdentity)); + +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("msg reports message size of %d\n"), ntohs(hdr->size)); + + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("msg reports message type of %d\n"), ntohs(hdr->type)); +#endif + + if (ntohs(hdr->type) == GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_PING) + { + handle_udp_ping(plugin, sender, &addr, fromlen, hdr); + } + + if (ntohs(hdr->type) == GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_PONG) + { + handle_udp_pong(plugin, sender, hdr); + } + + GNUNET_free(buf); + } - GNUNET_free (mp); - return ok; + while (GNUNET_NETWORK_socket_select (plugin->rs, + NULL, + NULL, + timeout) > 0 && GNUNET_NETWORK_fdset_isset(plugin->rs, udp_sock)); + + plugin->select_task = GNUNET_SCHEDULER_add_select(plugin->env->sched, GNUNET_SCHEDULER_PRIORITY_DEFAULT, GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, plugin->rs, NULL, &udp_plugin_select, plugin); + } /** - * Start the server process to receive inbound traffic. - * - * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed + * Create a UDP socket. If possible, use IPv6, otherwise + * try IPv4. */ -static int -udp_transport_server_start () +static struct GNUNET_NETWORK_Handle * +udp_transport_server_start (void *cls) { + struct Plugin *plugin = cls; + struct GNUNET_NETWORK_Handle *desc; struct sockaddr_in serverAddrv4; struct sockaddr_in6 serverAddrv6; struct sockaddr *serverAddr; socklen_t addrlen; - GNUNET_NETWORK_Handle *desc; - const int on = 1; - unsigned short port; - - GNUNET_GE_ASSERT (coreAPI->ectx, selector == NULL); - /* initialize UDP network */ - port = get_port (); - if (port != 0) + + desc = NULL; + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, "GNUNETD", "DISABLE-IPV6")) { - desc = udp_create_socket (); + desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_DGRAM, 17); + if (desc != NULL) + { + memset (&serverAddrv6, 0, sizeof (serverAddrv6)); + #if HAVE_SOCKADDR_IN_SIN_LEN + serverAddrv6.sin6_len = sizeof (serverAddrv6); + #endif + serverAddrv6.sin6_family = AF_INET6; + serverAddrv6.sin6_addr = in6addr_any; + serverAddrv6.sin6_port = htons (plugin->open_port); + addrlen = sizeof (serverAddrv6); + serverAddr = (struct sockaddr *) &serverAddrv6; + } + } + if (NULL == desc) + { + desc = GNUNET_NETWORK_socket_create (PF_INET, SOCK_DGRAM, 17); if (NULL == desc) - return GNUNET_SYSERR; - if (GNUNET_net_setsockopt (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0) { - GNUNET_GE_DIE_STRERROR (coreAPI->ectx, - GNUNET_GE_FATAL | GNUNET_GE_ADMIN | - GNUNET_GE_IMMEDIATE, "setsockopt"); - return GNUNET_SYSERR; + GNUNET_log_from(GNUNET_ERROR_TYPE_DEBUG, + "udp", + "socket"); + return NULL; } - if (available_protocols == VERSION_AVAILABLE_IPV4) + else { memset (&serverAddrv4, 0, sizeof (serverAddrv4)); -#if HAVE_SOCKADDR_IN_SIN_LEN + #if HAVE_SOCKADDR_IN_SIN_LEN serverAddrv4.sin_len = sizeof (serverAddrv4); -#endif + #endif serverAddrv4.sin_family = AF_INET; serverAddrv4.sin_addr.s_addr = INADDR_ANY; - serverAddrv4.sin_port = htons (port); + serverAddrv4.sin_port = htons (plugin->open_port); addrlen = sizeof (serverAddrv4); serverAddr = (struct sockaddr *) &serverAddrv4; } - else - { - memset (&serverAddrv6, 0, sizeof (serverAddrv6)); -#if HAVE_SOCKADDR_IN_SIN_LEN - serverAddrv6.sin6_len = sizeof (serverAddrv6); -#endif - serverAddrv6.sin6_family = AF_INET6; - serverAddrv6.sin6_addr = in6addr_any; - serverAddrv6.sin6_port = htons (port); - addrlen = sizeof (serverAddrv6); - serverAddr = (struct sockaddr *) &serverAddrv6; - } - if (GNUNET_net_bind (desc, serverAddr, addrlen) < 0) - { - GNUNET_GE_LOG_STRERROR (coreAPI->ectx, - GNUNET_GE_FATAL | GNUNET_GE_ADMIN | - GNUNET_GE_IMMEDIATE, "bind"); - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_FATAL | GNUNET_GE_ADMIN | - GNUNET_GE_IMMEDIATE, - _("Failed to bind to %s port %d.\n"), - MY_TRANSPORT_NAME, port); - if (0 != GNUNET_net_close (&desc)) - GNUNET_GE_LOG_STRERROR (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_USER | - GNUNET_GE_ADMIN | GNUNET_GE_BULK, - "close"); - return GNUNET_SYSERR; - } - selector = GNUNET_select_create ("udp", GNUNET_YES, coreAPI->ectx, load_monitor, desc, addrlen, 0, /* timeout */ - &select_message_handler, - NULL, - &select_accept_handler, - NULL, - &select_close_handler, - NULL, 64 * 1024, - 16 /* max sockets */ ); - if (selector == NULL) - return GNUNET_SYSERR; } - desc = udp_create_socket (); - if (NULL == desc) + + if (desc != NULL) { - GNUNET_GE_LOG_STRERROR (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_ADMIN | - GNUNET_GE_BULK, "socket"); - GNUNET_select_destroy (selector); - selector = NULL; - return GNUNET_SYSERR; + GNUNET_assert(GNUNET_NETWORK_socket_bind(desc, serverAddr, addrlen) == GNUNET_OK); } - udp_sock = GNUNET_socket_create (coreAPI->ectx, load_monitor, desc); - GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL); + + plugin->rs = GNUNET_NETWORK_fdset_create (); + + GNUNET_NETWORK_fdset_zero(plugin->rs); + GNUNET_NETWORK_fdset_set(plugin->rs, desc); + + plugin->select_task = GNUNET_SCHEDULER_add_select(plugin->env->sched, GNUNET_SCHEDULER_PRIORITY_DEFAULT, GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_UNIT_FOREVER_REL, plugin->rs, NULL, &udp_plugin_select, plugin); + + return desc; +} + +/** + * Function that can be used by the transport service to validate that + * another peer is reachable at a particular address (even if we + * already have a connection to this peer, this function is required + * to establish a new one). + * + * @param cls closure + * @param target who should receive this message + * @param challenge challenge code to use + * @param addrlen length of the address + * @param addr the address + * @param timeout how long should we try to transmit these? + * @return GNUNET_OK if the transmission has been scheduled + */ +static int +udp_plugin_validate (void *cls, + const struct GNUNET_PeerIdentity *target, + uint32_t challenge, + struct GNUNET_TIME_Relative timeout, + const void *addr, size_t addrlen) +{ + struct Plugin *plugin = cls; + struct Session *new_session; + struct UDPPongMessage *msg; + + if (addrlen <= 0) + return GNUNET_SYSERR; + + new_session = GNUNET_malloc(sizeof(struct Session)); + new_session->connect_addr = GNUNET_malloc(addrlen); + memcpy(new_session->connect_addr, addr, addrlen); + new_session->connect_alen = addrlen; +#if DEBUG_UDP + if (memcmp(target, plugin->env->my_identity, sizeof(struct GNUNET_PeerIdentity)) == 0) + { + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("definitely adding self to session list... hmmm\n")); + } +#endif + memcpy(&new_session->target, target, sizeof(struct GNUNET_PeerIdentity)); + new_session->challenge = challenge; + new_session->validated = GNUNET_NO; + new_session->next = plugin->sessions; + plugin->sessions = new_session; + + msg = GNUNET_malloc (sizeof (struct UDPPongMessage)); + msg->header.size = htons(sizeof(struct UDPPongMessage)); + msg->header.type = htons(GNUNET_MESSAGE_TYPE_TRANSPORT_UDP_PING); + msg->challenge = htons(challenge); +#if DEBUG_UDP + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("In validate, header size is %d, type %d, challenge %u\n"), ntohs(msg->header.size), ntohs(msg->header.type), ntohl(msg->challenge)); +#endif + udp_plugin_send(plugin, target, GNUNET_SCHEDULER_PRIORITY_DEFAULT, &msg->header, timeout, NULL, NULL); + return GNUNET_OK; } +/** + * Convert the transports address to a nice, human-readable + * format. + * + * @param cls closure + * @param type name of the transport that generated the address + * @param addr one of the addresses of the host, NULL for the last address + * the specific address format depends on the transport + * @param addrlen length of the address + * @param numeric should (IP) addresses be displayed in numeric form? + * @param timeout after how long should we give up? + * @param asc function to call on each string + * @param asc_cls closure for asc + */ +static void +udp_plugin_address_pretty_printer (void *cls, + const char *type, + const void *addr, + size_t addrlen, + int numeric, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_AddressStringCallback asc, + void *asc_cls) +{ + +} + +/** + * Set a quota for receiving data from the given peer; this is a + * per-transport limit. The transport should limit its read/select + * calls to stay below the quota (in terms of incoming data). + * + * @param cls closure + * @param target the peer for whom the quota is given + * @param quota_in quota for receiving/sending data in bytes per ms + */ +static void +udp_plugin_set_receive_quota (void *cls, + const struct GNUNET_PeerIdentity *target, + uint32_t quota_in) +{ + +} + +/** + * Another peer has suggested an address for this + * peer and transport plugin. Check that this could be a valid + * address. If so, consider adding it to the list + * of addresses. + * + * @param cls closure + * @param addr pointer to the address + * @param addrlen length of addr + * @return GNUNET_OK if this is a plausible address for this peer + * and transport + */ +static int +udp_plugin_address_suggested (void *cls, const void *addr, size_t addrlen) +{ + + return GNUNET_SYSERR; +} + + /** * The exported method. Makes the core api available via a global and * returns the udp transport API. */ -GNUNET_TransportAPI * -inittransport_udp (GNUNET_CoreAPIForTransport * core) +void * +libgnunet_plugin_transport_udp_init (void *cls) { unsigned long long mtu; - cfg = core->cfg; - load_monitor = core->load_monitor; - GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (UDPMessage) == 68); - GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24); - coreAPI = core; - if (-1 == GNUNET_GC_get_configuration_value_number (cfg, - "UDP", - "MTU", - sizeof (UDPMessage) - + - GNUNET_P2P_MESSAGE_OVERHEAD - + - sizeof - (GNUNET_MessageHeader) + - 32, 65500, - MESSAGE_SIZE, &mtu)) + struct GNUNET_TRANSPORT_PluginEnvironment *env = cls; + struct GNUNET_TRANSPORT_PluginFunctions *api; + struct Plugin *plugin; + struct GNUNET_SERVICE_Context *service; + unsigned long long aport; + unsigned long long bport; + + service = GNUNET_SERVICE_start ("transport-udp", env->sched, env->cfg); + if (service == NULL) { + GNUNET_log_from(GNUNET_ERROR_TYPE_WARNING, "udp", _ + ("Failed to start service for `%s' transport plugin.\n"), "udp"); return NULL; } + aport = 0; + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (env->cfg, + "transport-udp", + "PORT", + &bport)) || + (bport > 65535) || + ((GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (env->cfg, + "transport-udp", + "ADVERTISED-PORT", + &aport)) && (aport > 65535))) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "udp", + _ + ("Require valid port number for service `%s' in configuration!\n"), + "transport-udp"); + GNUNET_SERVICE_stop (service); + return NULL; + } + if (aport == 0) + aport = bport; + + mtu = 1240; if (mtu < 1200) - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE, - _("MTU %llu for `%s' is probably too low!\n"), mtu, "UDP"); - lock = GNUNET_mutex_create (GNUNET_NO); - if (0 != - GNUNET_GC_attach_change_listener (cfg, &reload_configuration, NULL)) - { - GNUNET_mutex_destroy (lock); - lock = NULL; - return NULL; - } - if (GNUNET_GC_get_configuration_value_yesno (cfg, "UDP", "UPNP", GNUNET_YES) - == GNUNET_YES) - { - upnp = coreAPI->service_request ("upnp"); - if (upnp == NULL) - GNUNET_GE_LOG (coreAPI->ectx, - GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE, - "The UPnP service could not be loaded. To disable UPnP, set the " - "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n", - "UDP"); - } - stats = coreAPI->service_request ("stats"); - if (stats != NULL) - { - stat_bytesReceived - = stats->create (gettext_noop ("# bytes received via UDP")); - stat_bytesSent = stats->create (gettext_noop ("# bytes sent via UDP")); - stat_bytesDropped - = stats->create (gettext_noop ("# bytes dropped by UDP (outgoing)")); - stat_udpConnected - = stats->create (gettext_noop ("# UDP connections (right now)")); - } - myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_UDP; - myAPI.mtu = mtu - sizeof (UDPMessage); - myAPI.cost = 20000; - myAPI.hello_verify = &verify_hello; - myAPI.hello_create = &create_hello; - myAPI.connect = &udp_connect; - myAPI.send = &udp_send; - myAPI.associate = &udp_associate; - myAPI.disconnect = &udp_disconnect; - myAPI.server_start = &udp_transport_server_start; - myAPI.server_stop = &udp_transport_server_stop; - myAPI.hello_to_address = &hello_to_address; - myAPI.send_now_test = &udp_test_would_try; - - return &myAPI; + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, + "udp", + _("MTU %llu for `%s' is probably too low!\n"), mtu, "UDP"); + + plugin = GNUNET_malloc (sizeof (struct Plugin)); + plugin->open_port = bport; + plugin->adv_port = aport; + plugin->env = env; + plugin->statistics = NULL; + api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); + plugin->sessions = NULL; + api->cls = plugin; + + api->validate = &udp_plugin_validate; + api->send = &udp_plugin_send; + api->disconnect = &udp_disconnect; + api->address_pretty_printer = &udp_plugin_address_pretty_printer; + api->set_receive_quota = &udp_plugin_set_receive_quota; + api->address_suggested = &udp_plugin_address_suggested; + api->cost_estimate = 17; /* TODO: ATS */ + plugin->service = service; + + udp_sock = udp_transport_server_start(plugin); + + GNUNET_assert(udp_sock != NULL); + + return api; } -void -donetransport_udp () +void * +libgnunet_plugin_transport_udp_done (void *cls) { - do_shutdown (); + struct GNUNET_TRANSPORT_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + + udp_transport_server_stop(plugin); + if (NULL != hostname_dns) + { + GNUNET_RESOLVER_request_cancel (hostname_dns); + hostname_dns = NULL; + } + GNUNET_SERVICE_stop (plugin->service); + GNUNET_free (plugin); + GNUNET_free (api); + return NULL; } -/* end of udp.c */ +/* end of plugin_transport_udp.c */ diff --git a/src/transport/test_plugin_transport_data_udp.conf b/src/transport/test_plugin_transport_data_udp.conf new file mode 100644 index 000000000..7583a868b --- /dev/null +++ b/src/transport/test_plugin_transport_data_udp.conf @@ -0,0 +1,24 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunetd-plugin-transport/ + +[resolver] +PORT = 2364 + +[transport] +PORT = 2365 +PLUGINS = udp + +[arm] +PORT = 2366 + +[statistics] +PORT = 2367 + +[transport-udp] +PORT = 2368 + +[peerinfo] +PORT = 2369 + +[testing] +WEAKRANDOM = YES diff --git a/src/transport/test_plugin_transport_udp.c b/src/transport/test_plugin_transport_udp.c new file mode 100644 index 000000000..85740c256 --- /dev/null +++ b/src/transport/test_plugin_transport_udp.c @@ -0,0 +1,358 @@ +/* + 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 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. +*/ +/** + * @file transport/test_transport_api.c + * @brief testcase for transport_api.c + * @author Sailor Siraj + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_constants.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_hello_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_plugin_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_program_lib.h" +#include "gnunet_signatures.h" +#include "plugin_transport.h" +#include "transport.h" + +#define VERBOSE GNUNET_NO + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) + +/** + * Our public key. + */ +static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; + +/** + * Our identity. + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; + +/** + * Our scheduler. + */ +struct GNUNET_SCHEDULER_Handle *sched; + +/** + * Our configuration. + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Number of neighbours we'd like to have. + */ +static uint32_t max_connect_per_transport; + +/** + * Environment for this plugin. + */ +struct GNUNET_TRANSPORT_PluginEnvironment env; + +/** + *handle for the api provided by this plugin + */ +struct GNUNET_TRANSPORT_PluginFunctions *api; + +/** + * Did the test pass or fail? + */ +static int ok; + +/** + * Initialize Environment for this plugin + */ +static void +receive(void *cls, + struct GNUNET_TIME_Relative + latency, + const struct GNUNET_PeerIdentity + * peer, + const struct GNUNET_MessageHeader + * message) +{ + /* do nothing */ +} + +void notify_address(void *cls, + const char *name, + const void *addr, + size_t addrlen, + struct + GNUNET_TIME_Relative + expires) +{ +} + +/** + * Function called when the service shuts + * down. Unloads our plugins. + * + * @param cls closure + * @param cfg configuration to use + */ +static void +unload_plugins (void *cls, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + GNUNET_assert (NULL == GNUNET_PLUGIN_unload ("libgnunet_plugin_transport_udp",api)); + if (my_private_key != NULL) + GNUNET_CRYPTO_rsa_key_free (my_private_key); + +} + + +static void +unload_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_CONFIGURATION_Handle *cfg = cls; + unload_plugins (NULL, cfg); +} + + +static GNUNET_SCHEDULER_TaskIdentifier validation_timeout_task; + + +static void +validation_notification (void *cls, + const char *name, + const struct GNUNET_PeerIdentity *peer, + uint32_t challenge, + const char *sender_addr) +{ + if (validation_timeout_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (sched, validation_timeout_task); + validation_timeout_task = GNUNET_SCHEDULER_NO_TASK; + } + + GNUNET_assert (challenge == 42); + + ok = 0; /* if the last test succeeded, report success */ + + GNUNET_SCHEDULER_add_continuation (sched, + &unload_task, + (void*) cfg, + GNUNET_SCHEDULER_REASON_PREREQ_DONE); +} + + +static void +validation_failed (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + validation_timeout_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_break (0); /* output error */ + /* the "validation_notification" was not called + in a timely fashion; we should set an error + code for main and shut down */ + unload_plugins (NULL, cfg); +} + + +/** + * Simple example test that invokes + * the "validate" function of the plugin + * and tries to see if the plugin would + * succeed to validate its own address. + * (This test is not well-written since + * we hand-compile the address which + * kind-of works for TCP but would not + * work for other plugins; we should ask + * the plugin about its address instead...). + */ +/* FIXME: won't work on IPv6 enabled systems where IPv4 mapping + * isn't enabled (eg. FreeBSD > 4) + */ +static void +test_validation () +{ + struct sockaddr_in soaddr; + + memset (&soaddr, 0, sizeof(soaddr)); +#if HAVE_SOCKADDR_IN_SIN_LEN + soaddr.sin_len = sizeof (soaddr); +#endif + soaddr.sin_family = AF_INET; + soaddr.sin_port = htons(2368 /* FIXME: get from config! */); + soaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + /* add job to catch failure (timeout) */ + validation_timeout_task = + GNUNET_SCHEDULER_add_delayed (sched, + TIMEOUT, + &validation_failed, + NULL); + + api->validate (api->cls, + &my_identity, + 42, + TIMEOUT, + &soaddr, + sizeof(soaddr)); +} + + +static void setup_plugin_environment() +{ + env.cfg = cfg; + env.sched = sched; + env.my_public_key = &my_public_key; + env.my_private_key = my_private_key; + env.my_identity = &my_identity; + env.cls=&env; + env.receive=&receive; + env.notify_address=¬ify_address; + env.notify_validation = &validation_notification; + env.max_connections = max_connect_per_transport; +} +static int retx; + +/** + * Runs the test. + * + * @param cls closure + * @param s scheduler to use + * @param c configuration to use + */ +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *s, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + unsigned long long tneigh; + char *keyfile; + char *libname; + + sched = s; + cfg = c; + /* parse configuration */ + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, + "TRANSPORT", + "NEIGHBOUR_LIMIT", + &tneigh)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (c, + "GNUNETD", + "HOSTKEY", &keyfile))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Transport service is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (s); + return; + } + max_connect_per_transport = (uint32_t) tneigh; + my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); + GNUNET_free (keyfile); + if (my_private_key == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Transport service could not access hostkey. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (s); + return; + } + GNUNET_CRYPTO_rsa_key_get_public (my_private_key, + &my_public_key); + GNUNET_CRYPTO_hash (&my_public_key, + sizeof (my_public_key), + &my_identity.hashPubKey); + + /* load plugins... */ + setup_plugin_environment(); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("Loading udp transport plugin\n")); + GNUNET_asprintf (&libname, "libgnunet_plugin_transport_udp"); + + api = GNUNET_PLUGIN_load(libname, &env); + GNUNET_free (libname); + if (api == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to load transport plugin for udp\n")); + /* FIXME: set some error code for main */ + return; + } + test_validation (); +} + + +/** + * The main function for the transport 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) +{ + static struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + int ret; + char *const argv_prog[] = { + "test_plugin_transport", + "-c", + "test_plugin_transport_data_udp.conf", + "-L", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL + }; + GNUNET_log_setup ("test-plugin-transport", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ok = 1; /* set to fail */ + ret = (GNUNET_OK == + GNUNET_PROGRAM_run (5, + argv_prog, + "test-plugin-transport", + "testcase", + options, + &run, NULL)) ? ok : 1; + GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-plugin-transport"); + return ret; +} + +/* end of test_plugin_transport_udp.c */ diff --git a/src/util/network.c b/src/util/network.c index d889fa94b..398f2b51a 100644 --- a/src/util/network.c +++ b/src/util/network.c @@ -354,6 +354,62 @@ GNUNET_NETWORK_socket_listen (const struct GNUNET_NETWORK_Handle *desc, } +/** + * How much data is available to be read on this descriptor? + * + * Returns GNUNET_NO if no data is available, or on error! + * @param desc socket + */ +unsigned int +GNUNET_NETWORK_socket_recvfrom_amount (const struct GNUNET_NETWORK_Handle * desc) +{ + int error; + unsigned int pending; + + /* How much is there to be read? */ + error = ioctl(desc->fd, FIONREAD, &pending); + GNUNET_log_from(GNUNET_ERROR_TYPE_INFO, "udp", _ + ("pending is %u bytes, error is %d\n"), pending, error); + + if (error == 0) + return pending; + else + return GNUNET_NO; +} + +/** + * Read data from a connected socket (always non-blocking). + * @param desc socket + * @param buffer buffer + * @param length length of buffer + * @param src_addr either the source to recv from, or all zeroes + * to be filled in by recvfrom + * @param addrlen length of the addr + */ +ssize_t +GNUNET_NETWORK_socket_recvfrom (const struct GNUNET_NETWORK_Handle * desc, + void *buffer, size_t length, + struct sockaddr *src_addr, socklen_t *addrlen) +{ + int ret; + int flags; + flags = 0; + +#ifdef MSG_DONTWAIT + flags |= MSG_DONTWAIT; + +#endif /* */ + ret = recvfrom (desc->fd, buffer, length, flags, src_addr, addrlen); + +#ifdef MINGW + if (SOCKET_ERROR == ret) + SetErrnoFromWinsockError (WSAGetLastError ()); + +#endif /* */ + return ret; +} + + /** * Read data from a connected socket (always non-blocking). * @param desc socket @@ -643,6 +699,12 @@ GNUNET_NETWORK_fdset_copy (struct GNUNET_NETWORK_FDSet *to, #endif } +int +GNUNET_NETWORK_get_fd (struct GNUNET_NETWORK_Handle *desc) +{ + return desc->fd; +} + /** * Copy a native fd set * diff --git a/src/util/scheduler.c b/src/util/scheduler.c index a8e5add3e..6b12c7d1a 100644 --- a/src/util/scheduler.c +++ b/src/util/scheduler.c @@ -365,7 +365,7 @@ check_ready (struct GNUNET_SCHEDULER_Handle *handle, { #if DEBUG_TASKS GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Checking readyness of task: %llu / %p\n", + "Checking readiness of task: %llu / %p\n", pos->id, pos->callback_cls); #endif next = pos->next; @@ -406,7 +406,7 @@ GNUNET_SCHEDULER_shutdown (struct GNUNET_SCHEDULER_Handle *sched) pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN; /* we don't move the task into the ready queue yet; check_ready will do that later, possibly adding additional - readyness-factors */ + readiness-factors */ pos = pos->next; } for (i=0;ireason |= GNUNET_SCHEDULER_REASON_SHUTDOWN; /* we don't move the task into the ready queue yet; check_ready will do that later, possibly adding additional - readyness-factors */ + readiness-factors */ pos = pos->next; } } @@ -773,7 +773,7 @@ GNUNET_SCHEDULER_add_continuation (struct GNUNET_SCHEDULER_Handle *sched, * @param sched scheduler to use * @param prerequisite_task run this task after the task with the given * task identifier completes (and any of our other - * conditions, such as delay, read or write-readyness + * conditions, such as delay, read or write-readiness * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency * on completion of other tasks (this will cause the task to run as * soon as possible). @@ -1044,7 +1044,7 @@ GNUNET_SCHEDULER_add_write_file (struct GNUNET_SCHEDULER_Handle * sched, * @param prio how important is this task? * @param prerequisite_task run this task after the task with the given * task identifier completes (and any of our other - * conditions, such as delay, read or write-readyness + * conditions, such as delay, read or write-readiness * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency * on completion of other tasks. * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever", -- 2.25.1