From d23a815951413af100c74b38cdd09a01ca1c280a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 16 Mar 2017 05:33:01 +0100 Subject: removing dead/legacy server/connection logic, except for in tcp/wlan/bt plugins (which will be updated 'later') --- po/POTFILES.in | 25 +- src/consensus/consensus_protocol.h | 6 +- src/conversation/gnunet-helper-audio-playback.c | 15 +- src/conversation/microphone.c | 2 - src/core/gnunet-service-core_kx.c | 152 +- src/dns/gnunet-service-dns.c | 3 +- src/exit/gnunet-daemon-exit.c | 2 - src/fs/fs_dirmetascan.c | 2 - src/include/Makefile.am | 2 - src/include/gnunet_connection_lib.h | 400 ----- src/include/gnunet_helper_lib.h | 8 +- src/include/gnunet_mq_lib.h | 1 + src/include/gnunet_server_lib.h | 887 ---------- src/include/gnunet_service_lib.h | 61 +- src/include/gnunet_util_lib.h | 2 - src/nat-auto/Makefile.am | 5 +- src/nat-auto/gnunet-nat-server.c | 212 +-- src/testbed/testbed_api_hosts.c | 5 +- src/transport/Makefile.am | 4 +- src/transport/gnunet-helper-transport-wlan-dummy.c | 30 +- src/transport/plugin_transport_http_client.c | 21 +- src/transport/plugin_transport_http_server.c | 23 +- src/transport/plugin_transport_tcp.c | 528 +++++- src/transport/plugin_transport_udp.c | 39 +- src/transport/plugin_transport_udp.h | 12 +- src/transport/plugin_transport_udp_broadcasting.c | 30 +- src/transport/plugin_transport_wlan.c | 29 +- src/transport/tcp_connection_legacy.c | 1647 ++++++++++++++++++ src/transport/tcp_server_legacy.c | 1748 +++++++++++++++++++ src/transport/tcp_server_mst_legacy.c | 311 ++++ src/transport/tcp_service_legacy.c | 1687 +++++++++++++++++++ src/transport/test_plugin_transport.c | 2 +- src/util/Makefile.am | 83 +- src/util/connection.c | 1648 ------------------ src/util/helper.c | 23 +- src/util/mq.c | 86 - src/util/server.c | 1752 -------------------- src/util/service.c | 1697 ------------------- src/util/test_connection.c | 167 -- src/util/test_connection_addressing.c | 186 --- src/util/test_connection_receive_cancel.c | 160 -- src/util/test_connection_timeout.c | 129 -- src/util/test_connection_timeout_no_connect.c | 76 - src/util/test_connection_transmit_cancel.c | 76 - src/util/test_server.c | 302 ---- src/util/test_server_disconnect.c | 166 -- src/util/test_server_mst_interrupt.c | 60 - src/util/test_server_with_client.c | 198 --- src/util/test_server_with_client_unix.c | 176 -- src/vpn/gnunet-service-vpn.c | 2 - 50 files changed, 6209 insertions(+), 8679 deletions(-) delete mode 100644 src/include/gnunet_connection_lib.h delete mode 100644 src/include/gnunet_server_lib.h create mode 100644 src/transport/tcp_connection_legacy.c create mode 100644 src/transport/tcp_server_legacy.c create mode 100644 src/transport/tcp_server_mst_legacy.c create mode 100644 src/transport/tcp_service_legacy.c delete mode 100644 src/util/connection.c delete mode 100644 src/util/server.c delete mode 100644 src/util/service.c delete mode 100644 src/util/test_connection.c delete mode 100644 src/util/test_connection_addressing.c delete mode 100644 src/util/test_connection_receive_cancel.c delete mode 100644 src/util/test_connection_timeout.c delete mode 100644 src/util/test_connection_timeout_no_connect.c delete mode 100644 src/util/test_connection_transmit_cancel.c delete mode 100644 src/util/test_server.c delete mode 100644 src/util/test_server_disconnect.c delete mode 100644 src/util/test_server_mst_interrupt.c delete mode 100644 src/util/test_server_with_client.c delete mode 100644 src/util/test_server_with_client_unix.c diff --git a/po/POTFILES.in b/po/POTFILES.in index aea7d5ef7..df5a298ac 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -37,31 +37,19 @@ src/block/block.c src/block/plugin_block_template.c src/block/plugin_block_test.c src/cadet/cadet_api.c -src/cadet/cadet_api_new.c -src/cadet/cadet_common.c -src/cadet/cadet_path.c src/cadet/cadet_test_lib.c -src/cadet/cadet_test_lib_new.c src/cadet/desirability_table.c src/cadet/gnunet-cadet.c src/cadet/gnunet-cadet-profiler.c src/cadet/gnunet-service-cadet.c src/cadet/gnunet-service-cadet_channel.c src/cadet/gnunet-service-cadet_connection.c +src/cadet/gnunet-service-cadet_core.c src/cadet/gnunet-service-cadet_dht.c src/cadet/gnunet-service-cadet_hello.c -src/cadet/gnunet-service-cadet_local.c -src/cadet/gnunet-service-cadet-new.c -src/cadet/gnunet-service-cadet-new_channel.c -src/cadet/gnunet-service-cadet-new_connection.c -src/cadet/gnunet-service-cadet-new_core.c -src/cadet/gnunet-service-cadet-new_dht.c -src/cadet/gnunet-service-cadet-new_hello.c -src/cadet/gnunet-service-cadet-new_paths.c -src/cadet/gnunet-service-cadet-new_peer.c -src/cadet/gnunet-service-cadet-new_tunnels.c +src/cadet/gnunet-service-cadet_paths.c src/cadet/gnunet-service-cadet_peer.c -src/cadet/gnunet-service-cadet_tunnel.c +src/cadet/gnunet-service-cadet_tunnels.c src/consensus/consensus_api.c src/consensus/gnunet-consensus-profiler.c src/consensus/gnunet-service-consensus.c @@ -165,7 +153,6 @@ src/fs/gnunet-service-fs_cadet_client.c src/fs/gnunet-service-fs_cadet_server.c src/fs/gnunet-service-fs_cp.c src/fs/gnunet-service-fs_indexing.c -src/fs/gnunet-service-fs_lc.c src/fs/gnunet-service-fs_pe.c src/fs/gnunet-service-fs_pr.c src/fs/gnunet-service-fs_push.c @@ -415,6 +402,9 @@ src/transport/plugin_transport_udp_broadcasting.c src/transport/plugin_transport_udp.c src/transport/plugin_transport_unix.c src/transport/plugin_transport_wlan.c +src/transport/tcp_connection_legacy.c +src/transport/tcp_server_legacy.c +src/transport/tcp_service_legacy.c src/transport/transport_api_address_to_string.c src/transport/transport_api_blacklist.c src/transport/transport_api_core.c @@ -438,7 +428,6 @@ src/util/common_endian.c src/util/common_logging.c src/util/configuration.c src/util/configuration_loader.c -src/util/connection.c src/util/container_bloomfilter.c src/util/container_heap.c src/util/container_meta_data.c @@ -485,11 +474,9 @@ src/util/plugin.c src/util/program.c src/util/resolver_api.c src/util/scheduler.c -src/util/server.c src/util/server_mst.c src/util/server_nc.c src/util/server_tc.c -src/util/service.c src/util/service_new.c src/util/signal.c src/util/socks.c diff --git a/src/consensus/consensus_protocol.h b/src/consensus/consensus_protocol.h index 320d460c7..f2933ed6f 100644 --- a/src/consensus/consensus_protocol.h +++ b/src/consensus/consensus_protocol.h @@ -109,7 +109,7 @@ struct ConsensusElement /** * Is this a marker element? */ - uint8_t marker GNUNET_PACKED; + uint8_t marker; /* rest: element data */ }; @@ -117,7 +117,7 @@ struct ConsensusElement struct ConsensusSizeElement { - struct ConsensusElement ce GNUNET_PACKED; + struct ConsensusElement ce; uint64_t size GNUNET_PACKED; uint8_t sender_index; @@ -125,7 +125,7 @@ struct ConsensusSizeElement struct ConsensusStuffedElement { - struct ConsensusElement ce GNUNET_PACKED; + struct ConsensusElement ce; struct GNUNET_HashCode rand GNUNET_PACKED; }; diff --git a/src/conversation/gnunet-helper-audio-playback.c b/src/conversation/gnunet-helper-audio-playback.c index e965cb2aa..4344e1d41 100644 --- a/src/conversation/gnunet-helper-audio-playback.c +++ b/src/conversation/gnunet-helper-audio-playback.c @@ -549,7 +549,6 @@ ogg_demux_and_decode () */ static int stdin_receiver (void *cls, - void *client, const struct GNUNET_MessageHeader *msg) { struct AudioMessage *audio; @@ -727,12 +726,14 @@ ogg_init () ogg_sync_init (&oy); } + static void drain_callback (pa_stream*s, int success, void *userdata) { pa_threaded_mainloop_signal (m, 0); } + /** * The main function for the playback helper. * @@ -746,7 +747,7 @@ main (int argc, char *argv[]) static unsigned long long toff; char readbuf[MAXLINE]; - struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst; + struct GNUNET_MessageStreamTokenizer *stdin_mst; char c; ssize_t ret; #ifdef DEBUG_READ_PURE_OGG @@ -762,7 +763,7 @@ main (int argc, char *argv[]) GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pipe"); return 1; } - stdin_mst = GNUNET_SERVER_mst_create (&stdin_receiver, NULL); + stdin_mst = GNUNET_MST_create (&stdin_receiver, NULL); ogg_init (); pa_init (); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -802,11 +803,11 @@ main (int argc, char *argv[]) } else #endif - GNUNET_SERVER_mst_receive (stdin_mst, NULL, - readbuf, ret, - GNUNET_NO, GNUNET_NO); + GNUNET_MST_from_buffer (stdin_mst, + readbuf, ret, + GNUNET_NO, GNUNET_NO); } - GNUNET_SERVER_mst_destroy (stdin_mst); + GNUNET_MST_destroy (stdin_mst); if (stream_out) { GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, diff --git a/src/conversation/microphone.c b/src/conversation/microphone.c index 94f52f8dc..7871433a3 100644 --- a/src/conversation/microphone.c +++ b/src/conversation/microphone.c @@ -64,13 +64,11 @@ struct Microphone * Function to process the audio from the record helper * * @param cls clsoure with our `struct Microphone` - * @param client NULL * @param msg the message from the helper * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ static int process_record_messages (void *cls, - void *client, const struct GNUNET_MessageHeader *msg) { struct Microphone *mic = cls; diff --git a/src/core/gnunet-service-core_kx.c b/src/core/gnunet-service-core_kx.c index 906898512..8a7cada5c 100644 --- a/src/core/gnunet-service-core_kx.c +++ b/src/core/gnunet-service-core_kx.c @@ -262,6 +262,11 @@ struct GSC_KeyExchangeInfo */ struct GNUNET_MQ_Handle *mq; + /** + * Our message stream tokenizer (for encrypted payload). + */ + struct GNUNET_MessageStreamTokenizer *mst; + /** * PING message we transmit to the other peer. */ @@ -369,11 +374,6 @@ static struct GNUNET_CRYPTO_EcdhePrivateKey *my_ephemeral_key; */ static struct EphemeralKeyMessage current_ekm; -/** - * Our message stream tokenizer (for encrypted payload). - */ -static struct GNUNET_SERVER_MessageStreamTokenizer *mst; - /** * DLL head. */ @@ -701,6 +701,55 @@ setup_fresh_ping (struct GSC_KeyExchangeInfo *kx) } +/** + * Deliver P2P message to interested clients. Invokes send twice, + * once for clients that want the full message, and once for clients + * that only want the header + * + * @param cls the `struct GSC_KeyExchangeInfo` + * @param m the message + */ +static int +deliver_message (void *cls, + const struct GNUNET_MessageHeader *m) +{ + struct GSC_KeyExchangeInfo *kx = cls; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted message of type %d from %s\n", + ntohs (m->type), + GNUNET_i2s (kx->peer)); + if (GNUNET_CORE_KX_STATE_UP != kx->status) + { + GNUNET_STATISTICS_update (GSC_stats, + gettext_noop ("# PAYLOAD dropped (out of order)"), + 1, + GNUNET_NO); + return GNUNET_OK; + } + switch (ntohs (m->type)) + { + case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP: + case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP: + GSC_SESSIONS_set_typemap (kx->peer, m); + return GNUNET_OK; + case GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP: + GSC_SESSIONS_confirm_typemap (kx->peer, m); + return GNUNET_OK; + default: + GSC_CLIENTS_deliver_message (kx->peer, + m, + ntohs (m->size), + GNUNET_CORE_OPTION_SEND_FULL_INBOUND); + GSC_CLIENTS_deliver_message (kx->peer, + m, + sizeof (struct GNUNET_MessageHeader), + GNUNET_CORE_OPTION_SEND_HDR_INBOUND); + } + return GNUNET_OK; +} + + /** * Function called by transport to notify us that * a peer connected to us (on the network level). @@ -727,6 +776,8 @@ handle_transport_notify_connect (void *cls, 1, GNUNET_NO); kx = GNUNET_new (struct GSC_KeyExchangeInfo); + kx->mst = GNUNET_MST_create (&deliver_message, + kx); kx->mq = mq; kx->peer = pid; kx->set_key_retry_frequency = INITIAL_SET_KEY_RETRY_FREQUENCY; @@ -801,6 +852,7 @@ handle_transport_notify_disconnect (void *cls, GNUNET_CONTAINER_DLL_remove (kx_head, kx_tail, kx); + GNUNET_MST_destroy (kx->mst); GNUNET_free (kx); } @@ -1416,24 +1468,6 @@ GSC_KX_encrypt_and_transmit (struct GSC_KeyExchangeInfo *kx, } -/** - * Closure for #deliver_message() - */ -struct DeliverMessageContext -{ - - /** - * Key exchange context. - */ - struct GSC_KeyExchangeInfo *kx; - - /** - * Sender of the message. - */ - const struct GNUNET_PeerIdentity *peer; -}; - - /** * We received an encrypted message. Check that it is * well-formed (size-wise). @@ -1475,7 +1509,6 @@ handle_encrypted (void *cls, struct GNUNET_TIME_Absolute t; struct GNUNET_CRYPTO_SymmetricInitializationVector iv; struct GNUNET_CRYPTO_AuthKey auth_key; - struct DeliverMessageContext dmc; uint16_t size = ntohs (m->header.size); char buf[size] GNUNET_ALIGN; @@ -1620,15 +1653,12 @@ handle_encrypted (void *cls, gettext_noop ("# bytes of payload decrypted"), size - sizeof (struct EncryptedMessage), GNUNET_NO); - dmc.kx = kx; - dmc.peer = kx->peer; if (GNUNET_OK != - GNUNET_SERVER_mst_receive (mst, - &dmc, - &buf[sizeof (struct EncryptedMessage)], - size - sizeof (struct EncryptedMessage), - GNUNET_YES, - GNUNET_NO)) + GNUNET_MST_from_buffer (kx->mst, + &buf[sizeof (struct EncryptedMessage)], + size - sizeof (struct EncryptedMessage), + GNUNET_YES, + GNUNET_NO)) GNUNET_break_op (0); } @@ -1655,57 +1685,6 @@ handle_transport_notify_excess_bw (void *cls, } -/** - * Deliver P2P message to interested clients. Invokes send twice, - * once for clients that want the full message, and once for clients - * that only want the header - * - * @param cls always NULL - * @param client who sent us the message (struct GSC_KeyExchangeInfo) - * @param m the message - */ -static int -deliver_message (void *cls, - void *client, - const struct GNUNET_MessageHeader *m) -{ - struct DeliverMessageContext *dmc = client; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Decrypted message of type %d from %s\n", - ntohs (m->type), - GNUNET_i2s (dmc->peer)); - if (GNUNET_CORE_KX_STATE_UP != dmc->kx->status) - { - GNUNET_STATISTICS_update (GSC_stats, - gettext_noop ("# PAYLOAD dropped (out of order)"), - 1, - GNUNET_NO); - return GNUNET_OK; - } - switch (ntohs (m->type)) - { - case GNUNET_MESSAGE_TYPE_CORE_BINARY_TYPE_MAP: - case GNUNET_MESSAGE_TYPE_CORE_COMPRESSED_TYPE_MAP: - GSC_SESSIONS_set_typemap (dmc->peer, m); - return GNUNET_OK; - case GNUNET_MESSAGE_TYPE_CORE_CONFIRM_TYPE_MAP: - GSC_SESSIONS_confirm_typemap (dmc->peer, m); - return GNUNET_OK; - default: - GSC_CLIENTS_deliver_message (dmc->peer, - m, - ntohs (m->size), - GNUNET_CORE_OPTION_SEND_FULL_INBOUND); - GSC_CLIENTS_deliver_message (dmc->peer, - m, - sizeof (struct GNUNET_MessageHeader), - GNUNET_CORE_OPTION_SEND_HDR_INBOUND); - } - return GNUNET_OK; -} - - /** * Setup the message that links the ephemeral key to our persistent * public key and generate the appropriate signature. @@ -1829,8 +1808,6 @@ GSC_KX_init (struct GNUNET_CRYPTO_EddsaPrivateKey *pk) rekey_task = GNUNET_SCHEDULER_add_delayed (REKEY_FREQUENCY, &do_rekey, NULL); - mst = GNUNET_SERVER_mst_create (&deliver_message, - NULL); transport = GNUNET_TRANSPORT_core_connect (GSC_cfg, &GSC_my_identity, @@ -1874,11 +1851,6 @@ GSC_KX_done () GNUNET_free (my_private_key); my_private_key = NULL; } - if (NULL != mst) - { - GNUNET_SERVER_mst_destroy (mst); - mst = NULL; - } if (NULL != nc) { GNUNET_notification_context_destroy (nc); diff --git a/src/dns/gnunet-service-dns.c b/src/dns/gnunet-service-dns.c index 74f595c5e..079f74ce4 100644 --- a/src/dns/gnunet-service-dns.c +++ b/src/dns/gnunet-service-dns.c @@ -882,11 +882,10 @@ handle_client_response (void *cls, * message is received by the tokenizer from the DNS hijack process. * * @param cls closure - * @param client identification of the client * @param message the actual message, a DNS request we should handle */ static int -process_helper_messages (void *cls GNUNET_UNUSED, void *client, +process_helper_messages (void *cls, const struct GNUNET_MessageHeader *message) { uint16_t msize; diff --git a/src/exit/gnunet-daemon-exit.c b/src/exit/gnunet-daemon-exit.c index a32cb3086..15a462ada 100644 --- a/src/exit/gnunet-daemon-exit.c +++ b/src/exit/gnunet-daemon-exit.c @@ -2905,12 +2905,10 @@ tcp_from_helper (const struct GNUNET_TUN_TcpHeader *tcp, * Receive packets from the helper-process * * @param cls unused - * @param client unsued * @param message message received from helper */ static int message_token (void *cls GNUNET_UNUSED, - void *client GNUNET_UNUSED, const struct GNUNET_MessageHeader *message) { const struct GNUNET_TUN_Layer2PacketHeader *pkt_tun; diff --git a/src/fs/fs_dirmetascan.c b/src/fs/fs_dirmetascan.c index 2f79c7c05..7b9f178fd 100644 --- a/src/fs/fs_dirmetascan.c +++ b/src/fs/fs_dirmetascan.c @@ -245,12 +245,10 @@ finish_scan (void *cls) * Calls the scanner progress handler. * * @param cls the closure (directory scanner object) - * @param client always NULL * @param msg message from the helper process */ static int process_helper_msgs (void *cls, - void *client, const struct GNUNET_MessageHeader *msg) { struct GNUNET_FS_DirScanner *ds = cls; diff --git a/src/include/Makefile.am b/src/include/Makefile.am index 4fb2577fd..b745da125 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -38,7 +38,6 @@ gnunetinclude_HEADERS = \ gnunet_common.h \ gnunet_constants.h \ gnunet_configuration_lib.h \ - gnunet_connection_lib.h \ gnunet_consensus_service.h \ gnunet_container_lib.h \ gnunet_conversation_service.h \ @@ -107,7 +106,6 @@ gnunetinclude_HEADERS = \ gnunet_scalarproduct_service.h \ gnunet_scheduler_lib.h \ gnunet_secretsharing_service.h \ - gnunet_server_lib.h \ gnunet_service_lib.h \ gnunet_set_service.h \ gnunet_signal_lib.h \ diff --git a/src/include/gnunet_connection_lib.h b/src/include/gnunet_connection_lib.h deleted file mode 100644 index e9dd95d1b..000000000 --- a/src/include/gnunet_connection_lib.h +++ /dev/null @@ -1,400 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @author Christian Grothoff - * - * @file include/gnunet_connection_lib.h - * Basic, low-level TCP networking interface - * - * @defgroup connection Connection library - * Basic, low-level TCP networking interface - * @{ - */ -#ifndef GNUNET_CONNECTION_LIB_H -#define GNUNET_CONNECTION_LIB_H - -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - -#include "gnunet_network_lib.h" -#include "gnunet_scheduler_lib.h" -#include "gnunet_time_lib.h" - -/** - * Timeout we use on TCP connect before trying another - * result from the DNS resolver. Actual value used - * is this value divided by the number of address families. - * Default is 5s. - */ -#define GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) - -/** - * @brief handle for a network connection - */ -struct GNUNET_CONNECTION_Handle; - - -/** - * Credentials for UNIX domain sockets. - */ -struct GNUNET_CONNECTION_Credentials -{ - /** - * UID of the other end of the connection. - */ - uid_t uid; - - /** - * GID of the other end of the connection. - */ - gid_t gid; -}; - - -/** - * Function to call for access control checks. - * - * @param cls closure - * @param ucred credentials, if available, otherwise NULL - * @param addr address - * @param addrlen length of address - * @return GNUNET_YES to allow, GNUNET_NO to deny, GNUNET_SYSERR - * for unknown address family (will be denied). - */ -typedef int (*GNUNET_CONNECTION_AccessCheck) (void *cls, - const struct - GNUNET_CONNECTION_Credentials * - ucred, - const struct sockaddr * addr, - socklen_t addrlen); - - -/** - * Callback function for data received from the network. Note that - * both "available" and "err" would be 0 if the read simply timed out. - * - * @param cls closure - * @param buf pointer to received data - * @param available number of bytes availabe in "buf", - * possibly 0 (on errors) - * @param addr address of the sender - * @param addrlen size of addr - * @param errCode value of errno (on errors receiving) - */ -typedef void (*GNUNET_CONNECTION_Receiver) (void *cls, const void *buf, - size_t available, - const struct sockaddr * addr, - socklen_t addrlen, int errCode); - -/** - * Set the persist option on this connection handle. Indicates - * that the underlying socket or fd should never really be closed. - * Used for indicating process death. - * - * @param connection the connection to set persistent - */ -void -GNUNET_CONNECTION_persist_ (struct GNUNET_CONNECTION_Handle *connection); - -/** - * Disable the "CORK" feature for communication with the given socket, - * forcing the OS to immediately flush the buffer on transmission - * instead of potentially buffering multiple messages. Essentially - * reduces the OS send buffers to zero. - * Used to make sure that the last messages sent through the connection - * reach the other side before the process is terminated. - * - * @param connection the connection to make flushing and blocking - * @return #GNUNET_OK on success - */ -int -GNUNET_CONNECTION_disable_corking (struct GNUNET_CONNECTION_Handle *connection); - - -/** - * Create a connection handle by (asynchronously) connecting to a host. - * This function returns immediately, even if the connection has not - * yet been established. This function only creates TCP connections. - * - * @param s socket to connect - * @param serv_addr server address - * @param addrlen length of server address - * @return the connection handle - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_connect_socket (struct GNUNET_NETWORK_Handle *s, - const struct sockaddr *serv_addr, - socklen_t addrlen); - - -/** - * Create a connection handle by boxing an existing OS socket. The OS - * socket should henceforth be no longer used directly. - * #GNUNET_CONNECTION_destroy() will close it. - * - * @param osSocket existing socket to box - * @return the boxed socket handle - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_existing (struct GNUNET_NETWORK_Handle *osSocket); - - -/** - * Create a connection handle by accepting on a listen socket. This - * function may block if the listen socket has no connection ready. - * - * @param access_cb function to use to check if access is allowed - * @param access_cb_cls closure for @a access_cb - * @param lsock listen socket - * @return the connection handle, NULL on error (for example, access refused) - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access_cb, - void *access_cb_cls, - struct GNUNET_NETWORK_Handle *lsock); - - -/** - * Create a connection handle by (asynchronously) connecting to a host. - * This function returns immediately, even if the connection has not - * yet been established. This function only creates TCP connections. - * - * @param cfg configuration to use - * @param hostname name of the host to connect to - * @param port port to connect to - * @return the connection handle - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_connect (const struct GNUNET_CONFIGURATION_Handle - *cfg, const char *hostname, - uint16_t port); - - -/** - * Create a connection handle by connecting to a UNIX domain service. - * This function returns immediately, even if the connection has not - * yet been established. This function only creates UNIX connections. - * - * @param cfg configuration to use - * @param unixpath path to connect to) - * @return the connection handle, NULL on systems without UNIX support - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_connect_to_unixpath (const struct - GNUNET_CONFIGURATION_Handle - *cfg, const char *unixpath); - - - - -/** - * Create a connection handle by (asynchronously) connecting to a host. - * This function returns immediately, even if the connection has not - * yet been established. This function only creates TCP connections. - * - * @param af_family address family to use - * @param serv_addr server address - * @param addrlen length of server address - * @return the connection handle - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_sockaddr (int af_family, - const struct sockaddr *serv_addr, - socklen_t addrlen); - -/** - * Check if connection is valid (no fatal errors have happened so far). - * Note that a connection that is still trying to connect is considered - * valid. - * - * @param connection handle to check - * @return GNUNET_YES if valid, GNUNET_NO otherwise - */ -int -GNUNET_CONNECTION_check (struct GNUNET_CONNECTION_Handle *connection); - - -/** - * Obtain the network address of the other party. - * - * @param connection the client to get the address for - * @param addr where to store the address - * @param addrlen where to store the length of the address - * @return GNUNET_OK on success - */ -int -GNUNET_CONNECTION_get_address (struct GNUNET_CONNECTION_Handle *connection, - void **addr, size_t * addrlen); - - -/** - * Close the connection and free associated resources. There must - * not be any pending requests for reading or writing to the - * connection at this time. - * - * @param connection connection to destroy - */ -void -GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *connection); - - -/** - * Receive data from the given connection. Note that this function will - * call "receiver" asynchronously using the scheduler. It will - * "immediately" return. Note that there MUST only be one active - * receive call per connection at any given point in time (so do not - * call receive again until the receiver callback has been invoked). - * - * @param connection connection handle - * @param max maximum number of bytes to read - * @param timeout maximum amount of time to wait - * @param receiver function to call with received data - * @param receiver_cls closure for receiver - */ -void -GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *connection, size_t max, - struct GNUNET_TIME_Relative timeout, - GNUNET_CONNECTION_Receiver receiver, - void *receiver_cls); - - -/** - * Cancel receive job on the given connection. Note that the - * receiver callback must not have been called yet in order - * for the cancellation to be valid. - * - * @param connection connection handle - * @return closure of the original receiver callback closure - */ -void * -GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *connection); - - -/** - * Function called to notify a client about the connection begin ready - * to queue more data. @a buf will be NULL and @a size zero if the - * connection was closed for writing in the meantime. - * - * @param cls closure - * @param size number of bytes available in @a buf - * @param buf where the callee should write the message - * @return number of bytes written to @a buf - */ -typedef size_t -(*GNUNET_CONNECTION_TransmitReadyNotify) (void *cls, - size_t size, - void *buf); - - -/** - * Opaque handle that can be used to cancel - * a transmit-ready notification. - */ -struct GNUNET_CONNECTION_TransmitHandle; - -/** - * Ask the connection to call us once the specified number of bytes - * are free in the transmission buffer. Will never call the @a notify - * callback in this task, but always first go into the scheduler. Note that - * this function will abort if "size" is greater than - * #GNUNET_SERVER_MAX_MESSAGE_SIZE. - * - * Note that "notify" will be called either when enough - * buffer space is available OR when the connection is destroyed. - * The size parameter given to notify is guaranteed to be - * larger or equal to size if the buffer is ready, or zero - * if the connection was destroyed (or at least closed for - * writing). Finally, any time before 'notify' is called, a - * client may call "notify_transmit_ready_cancel" to cancel - * the transmission request. - * - * Only one transmission request can be scheduled at the same - * time. Notify will be run with the same scheduler priority - * as that of the caller. - * - * @param connection connection - * @param size number of bytes to send - * @param timeout after how long should we give up (and call - * notify with buf NULL and size 0)? - * @param notify function to call when buffer space is available - * @param notify_cls closure for notify - * @return non-NULL if the notify callback was queued, - * NULL if we are already going to notify someone else (busy) - */ -struct GNUNET_CONNECTION_TransmitHandle * -GNUNET_CONNECTION_notify_transmit_ready (struct GNUNET_CONNECTION_Handle *connection, - size_t size, - struct GNUNET_TIME_Relative timeout, - GNUNET_CONNECTION_TransmitReadyNotify - notify, void *notify_cls); - - -/** - * Cancel the specified transmission-ready - * notification. - * - * @param th handle for notification to cancel - */ -void -GNUNET_CONNECTION_notify_transmit_ready_cancel (struct - GNUNET_CONNECTION_TransmitHandle - *th); - - -/** - * Create a connection to be proxied using a given connection. - * - * @param cph connection to proxy server - * @return connection to be proxied - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_proxied_from_handshake (struct GNUNET_CONNECTION_Handle *cph); - - -/** - * Activate proxied connection and destroy initial proxy handshake connection. - * There must not be any pending requests for reading or writing to the - * proxy hadshake connection at this time. - * - * @param proxied connection connection to proxy server - */ -void -GNUNET_CONNECTION_acivate_proxied (struct GNUNET_CONNECTION_Handle *proxied); - - -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif - -/* ifndef GNUNET_CONNECTION_LIB_H */ -#endif - -/** @} */ /* end of group */ - -/* end of gnunet_connection_lib.h */ diff --git a/src/include/gnunet_helper_lib.h b/src/include/gnunet_helper_lib.h index db0ca98aa..60b3ff681 100644 --- a/src/include/gnunet_helper_lib.h +++ b/src/include/gnunet_helper_lib.h @@ -38,7 +38,8 @@ #define GNUNET_HELPER_LIB_H #include "gnunet_scheduler_lib.h" -#include "gnunet_server_lib.h" +#include "gnunet_mst_lib.h" + /** * The handle to a helper process. @@ -52,7 +53,8 @@ struct GNUNET_HELPER_Handle; * * @param cls the closure from GNUNET_HELPER_start() */ -typedef void (*GNUNET_HELPER_ExceptionCallback) (void *cls); +typedef void +(*GNUNET_HELPER_ExceptionCallback) (void *cls); /** @@ -75,7 +77,7 @@ struct GNUNET_HELPER_Handle * GNUNET_HELPER_start (int with_control_pipe, const char *binary_name, char *const binary_argv[], - GNUNET_SERVER_MessageTokenizerCallback cb, + GNUNET_MessageTokenizerCallback cb, GNUNET_HELPER_ExceptionCallback exp_cb, void *cb_cls); diff --git a/src/include/gnunet_mq_lib.h b/src/include/gnunet_mq_lib.h index b05128ccc..ecee1b223 100644 --- a/src/include/gnunet_mq_lib.h +++ b/src/include/gnunet_mq_lib.h @@ -35,6 +35,7 @@ #ifndef GNUNET_MQ_LIB_H #define GNUNET_MQ_LIB_H +#include "gnunet_scheduler_lib.h" /** * Allocate an envelope, with extra space allocated after the space needed diff --git a/src/include/gnunet_server_lib.h b/src/include/gnunet_server_lib.h deleted file mode 100644 index 5da31bcd7..000000000 --- a/src/include/gnunet_server_lib.h +++ /dev/null @@ -1,887 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009-2013 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @author Christian Grothoff - * - * @file - * Library for building GNUnet network servers - - * @defgroup server Server library - * Library for building GNUnet network servers - * - * Provides functions for a server that communicates with clients. - * - * @see [Documentation](https://gnunet.org/ipc) - * - * @{ - */ - -#ifndef GNUNET_SERVER_LIB_H -#define GNUNET_SERVER_LIB_H - -#ifdef __cplusplus -extern "C" -{ -#if 0 /* keep Emacsens' auto-indent happy */ -} -#endif -#endif - -#include "gnunet_common.h" -#include "gnunet_connection_lib.h" - - -/** - * Largest supported message (to be precise, one byte more - * than the largest possible message, so tests involving - * this value should check for messages being smaller than - * this value). - */ -#define GNUNET_SERVER_MAX_MESSAGE_SIZE 65536 - -/** - * Smallest supported message. - */ -#define GNUNET_SERVER_MIN_BUFFER_SIZE sizeof (struct GNUNET_MessageHeader) - -/** - * @brief handle for a server - */ -struct GNUNET_SERVER_Handle; - -/** - * @brief opaque handle for a client of the server - */ -struct GNUNET_SERVER_Client; - -/** - * @brief opaque handle server returns for aborting transmission to a client. - */ -struct GNUNET_SERVER_TransmitHandle; - - -/** - * Functions with this signature are called whenever a message is - * received. - * - * @param cls closure - * @param client identification of the client - * @param message the actual message - */ -typedef void -(*GNUNET_SERVER_MessageCallback) (void *cls, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message); - - -/** - * Message handler. Each struct specifies how to handle on particular - * type of message received. - */ -struct GNUNET_SERVER_MessageHandler -{ - /** - * Function to call for messages of "type". - */ - GNUNET_SERVER_MessageCallback callback; - - /** - * Closure argument for @e callback. - */ - void *callback_cls; - - /** - * Type of the message this handler covers. - */ - uint16_t type; - - /** - * Expected size of messages of this type. Use 0 for - * variable-size. If non-zero, messages of the given - * type will be discarded (and the connection closed) - * if they do not have the right size. - */ - uint16_t expected_size; - -}; - - -/** - * Create a new server. - * - * @param access_cb function for access control - * @param access_cb_cls closure for @a access_cb - * @param lsocks NULL-terminated array of listen sockets - * @param idle_timeout after how long should we timeout idle connections? - * @param require_found if #GNUNET_YES, connections sending messages of unknown type - * will be closed - * @return handle for the new server, NULL on error - * (typically, "port" already in use) - */ -struct GNUNET_SERVER_Handle * -GNUNET_SERVER_create_with_sockets (GNUNET_CONNECTION_AccessCheck access_cb, - void *access_cb_cls, - struct GNUNET_NETWORK_Handle **lsocks, - struct GNUNET_TIME_Relative idle_timeout, - int require_found); - -/** - * Create a new server. - * - * @param access_cb function for access control - * @param access_cb_cls closure for @a access_cb - * @param server_addr address toes listen on (including port), NULL terminated array - * @param socklen lengths of respective @a server_addr - * @param idle_timeout after how long should we timeout idle connections? - * @param require_found if #GNUNET_YES, connections sending messages of unknown type - * will be closed - * @return handle for the new server, NULL on error - * (typically, "port" already in use) - */ -struct GNUNET_SERVER_Handle * -GNUNET_SERVER_create (GNUNET_CONNECTION_AccessCheck access_cb, - void *access_cb_cls, - struct sockaddr *const *server_addr, - const socklen_t *socklen, - struct GNUNET_TIME_Relative idle_timeout, - int require_found); - - -/** - * Suspend accepting connections from the listen socket temporarily. - * Resume activity using #GNUNET_SERVER_resume. - * - * @param server server to stop accepting connections. - */ -void -GNUNET_SERVER_suspend (struct GNUNET_SERVER_Handle *server); - - -/** - * Resume accepting connections from the listen socket. - * - * @param server server to resume accepting connections. - */ -void -GNUNET_SERVER_resume (struct GNUNET_SERVER_Handle *server); - - -/** - * Stop the listen socket and get ready to shutdown the server once - * only clients marked using #GNUNET_SERVER_client_mark_monitor are - * left. - * - * @param server server to stop listening on - */ -void -GNUNET_SERVER_stop_listening (struct GNUNET_SERVER_Handle *server); - - -/** - * Free resources held by this server. - * - * @param server server to destroy - */ -void -GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *server); - - -/** - * Add additional handlers to an existing server. - * - * @param server the server to add handlers to - * @param handlers array of message handlers for - * incoming messages; the last entry must - * have "NULL" for the "callback"; multiple - * entries for the same type are allowed, - * they will be called in order of occurence. - * These handlers can be removed later; - * the handlers array must exist until removed - * (or server is destroyed). - */ -void -GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server, - const struct GNUNET_SERVER_MessageHandler *handlers); - - -/** - * Notify us when the server has enough space to transmit - * a message of the given size to the given client. - * - * @param client client to transmit message to - * @param size requested amount of buffer space - * @param timeout after how long should we give up (and call - * notify with buf NULL and size 0)? - * @param callback function to call when space is available - * @param callback_cls closure for @a callback - * @return non-NULL if the notify callback was queued; can be used - * to cancel the request using - * #GNUNET_SERVER_notify_transmit_ready_cancel. - * NULL if we are already going to notify someone else (busy) - */ -struct GNUNET_SERVER_TransmitHandle * -GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, - size_t size, - struct GNUNET_TIME_Relative timeout, - GNUNET_CONNECTION_TransmitReadyNotify callback, - void *callback_cls); - - -/** - * Abort transmission request. - * - * @param th request to abort - */ -void -GNUNET_SERVER_notify_transmit_ready_cancel (struct GNUNET_SERVER_TransmitHandle *th); - - -/** - * Set the 'monitor' flag on this client. Clients which have been - * marked as 'monitors' won't prevent the server from shutting down - * once #GNUNET_SERVER_stop_listening has been invoked. The idea is - * that for "normal" clients we likely want to allow them to process - * their requests; however, monitor-clients are likely to 'never' - * disconnect during shutdown and thus will not be considered when - * determining if the server should continue to exist after - * #GNUNET_SERVER_destroy has been called. - * - * @param client the client to set the 'monitor' flag on - */ -void -GNUNET_SERVER_client_mark_monitor (struct GNUNET_SERVER_Client *client); - - -/** - * Set the persistent flag on this client, used to setup client - * connection to only be killed when the process of the service it's - * connected to is actually dead. This API is used during shutdown - * signalling within ARM, and it is not expected that typical users - * of the API would need this function. - * - * @param client the client to set the persistent flag on - */ -void -GNUNET_SERVER_client_persist_ (struct GNUNET_SERVER_Client *client); - - -/** - * Resume receiving from this client, we are done processing the - * current request. This function must be called from within each - * #GNUNET_SERVER_MessageCallback (or its respective continuations). - * - * @param client client we were processing a message of - * @param success #GNUNET_OK to keep the connection open and - * continue to receive - * #GNUNET_NO to close the connection (normal behavior) - * #GNUNET_SYSERR to close the connection (signal - * serious error) - */ -void -GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, - int success); - - -/** - * Change the timeout for a particular client. Decreasing the timeout - * may not go into effect immediately (only after the previous timeout - * times out or activity happens on the socket). - * - * @param client the client to update - * @param timeout new timeout for activities on the socket - */ -void -GNUNET_SERVER_client_set_timeout (struct GNUNET_SERVER_Client *client, - struct GNUNET_TIME_Relative timeout); - - -/** - * Return user context associated with the given client. - * Note: you should probably use the macro (call without the underscore). - * - * @param client client to query - * @param size number of bytes in user context struct (for verification only) - * @return pointer to user context - */ -void * -GNUNET_SERVER_client_get_user_context_ (struct GNUNET_SERVER_Client *client, - size_t size); - - -/** - * Set user context to be associated with the given client. - * Note: you should probably use the macro (call without the underscore). - * - * @param client client to query - * @param ptr pointer to user context - * @param size number of bytes in user context struct (for verification only) - */ -void -GNUNET_SERVER_client_set_user_context_ (struct GNUNET_SERVER_Client *client, - void *ptr, - size_t size); - - -/** - * Return user context associated with the given client. - * - * @param client client to query - * @param type expected return type (i.e. 'struct Foo') - * @return pointer to user context of type 'type *'. - */ -#define GNUNET_SERVER_client_get_user_context(client,type) \ - (type *) GNUNET_SERVER_client_get_user_context_ (client, sizeof (type)) - -/** - * Set user context to be associated with the given client. - * - * @param client client to query - * @param value pointer to user context - */ -#define GNUNET_SERVER_client_set_user_context(client,value) \ - GNUNET_SERVER_client_set_user_context_ (client, value, sizeof (*value)) - - -/** - * Disable the warning the server issues if a message is not acknowledged - * in a timely fashion. Use this call if a client is intentionally delayed - * for a while. Only applies to the current message. - * - * @param client client for which to disable the warning - */ -void -GNUNET_SERVER_disable_receive_done_warning (struct GNUNET_SERVER_Client - *client); - - -/** - * Inject a message into the server, pretend it came - * from the specified client. Delivery of the message - * will happen instantly (if a handler is installed; - * otherwise the call does nothing). - * - * @param server the server receiving the message - * @param sender the "pretended" sender of the message - * can be NULL! - * @param message message to transmit - * @return #GNUNET_OK if the message was OK and the - * connection can stay open - * #GNUNET_SYSERR if the connection to the - * client should be shut down - */ -int -GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server, - struct GNUNET_SERVER_Client *sender, - const struct GNUNET_MessageHeader *message); - - -/** - * Add a TCP socket-based connection to the set of handles managed by - * this server. Use this function for outgoing (P2P) connections that - * we initiated (and where this server should process incoming - * messages). - * - * @param server the server to use - * @param connection the connection to manage (client must - * stop using this connection from now on) - * @return the client handle - */ -struct GNUNET_SERVER_Client * -GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server, - struct GNUNET_CONNECTION_Handle *connection); - - -/** - * Notify the server that the given client handle should - * be kept (keeps the connection up if possible, increments - * the internal reference counter). - * - * @param client the client to keep - */ -void -GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client); - - -/** - * Notify the server that the given client handle is no - * longer required. Decrements the reference counter. If - * that counter reaches zero an inactive connection maybe - * closed. - * - * @param client the client to drop - */ -void -GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client); - - -/** - * Obtain the network address of the other party. - * - * @param client the client to get the address for - * @param addr where to store the address - * @param addrlen where to store the length of @a addr - * @return #GNUNET_OK on success - */ -int -GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client, - void **addr, size_t *addrlen); - - -/** - * Functions with this signature are called whenever a client - * is disconnected on the network level. - * - * @param cls closure - * @param client identification of the client; NULL - * for the last call when the server is destroyed - */ -typedef void -(*GNUNET_SERVER_DisconnectCallback) (void *cls, - struct GNUNET_SERVER_Client *client); - - -/** - * Functions with this signature are called whenever a client - * is connected on the network level. - * - * @param cls closure - * @param client identification of the client - */ -typedef void -(*GNUNET_SERVER_ConnectCallback) (void *cls, - struct GNUNET_SERVER_Client *client); - - -/** - * Ask the server to notify us whenever a client disconnects. - * This function is called whenever the actual network connection - * is closed; the reference count may be zero or larger than zero - * at this point. If the server is destroyed before this - * notification is explicitly cancelled, the 'callback' will - * once be called with a 'client' argument of NULL to indicate - * that the server itself is now gone (and that the callback - * won't be called anymore and also can no longer be cancelled). - * - * @param server the server manageing the clients - * @param callback function to call on disconnect - * @param callback_cls closure for @a callback - */ -void -GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server, - GNUNET_SERVER_DisconnectCallback callback, - void *callback_cls); - - -/** - * Ask the server to notify us whenever a client connects. - * This function is called whenever the actual network connection - * is opened. If the server is destroyed before this - * notification is explicitly cancelled, the @a callback will - * once be called with a 'client' argument of NULL to indicate - * that the server itself is now gone (and that the callback - * won't be called anymore and also can no longer be cancelled). - * - * @param server the server manageing the clients - * @param callback function to call on sconnect - * @param callback_cls closure for @a callback - */ -void -GNUNET_SERVER_connect_notify (struct GNUNET_SERVER_Handle *server, - GNUNET_SERVER_ConnectCallback callback, - void *callback_cls); - - -/** - * Ask the server to stop notifying us whenever a client disconnects. - * Arguments must match exactly those given to - * #GNUNET_SERVER_disconnect_notify. It is not necessary to call this - * function during shutdown of the server; in fact, most applications - * will never use this function. - * - * @param server the server manageing the clients - * @param callback function to call on disconnect - * @param callback_cls closure for @a callback - */ -void -GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server, - GNUNET_SERVER_DisconnectCallback callback, - void *callback_cls); - - -/** - * Ask the server to stop notifying us whenever a client connects. - * Arguments must match exactly those given to - * #GNUNET_SERVER_connect_notify. It is not necessary to call this - * function during shutdown of the server; in fact, most applications - * will never use this function. - * - * @param server the server manageing the clients - * @param callback function to call on connect - * @param callback_cls closure for @a callback - */ -void -GNUNET_SERVER_connect_notify_cancel (struct GNUNET_SERVER_Handle *server, - GNUNET_SERVER_ConnectCallback callback, - void *callback_cls); - - -/** - * Ask the server to disconnect from the given client. This is the - * same as passing #GNUNET_SYSERR to #GNUNET_SERVER_receive_done, - * except that it allows dropping of a client even when not handling a - * message from that client. - * - * @param client the client to disconnect from - */ -void -GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client); - - -/** - * Disable the "CORK" feature for communication with the given client, - * forcing the OS to immediately flush the buffer on transmission - * instead of potentially buffering multiple messages. - * - * @param client handle to the client - * @return #GNUNET_OK on success - */ -int -GNUNET_SERVER_client_disable_corking (struct GNUNET_SERVER_Client *client); - - -/** - * The tansmit context is the key datastructure for a conveniance API - * used for transmission of complex results to the client followed - * ONLY by signaling receive_done with success or error - */ -struct GNUNET_SERVER_TransmitContext; - - -/** - * Create a new transmission context for the given client. - * - * @param client client to create the context for. - * @return NULL on error - */ -struct GNUNET_SERVER_TransmitContext * -GNUNET_SERVER_transmit_context_create (struct GNUNET_SERVER_Client *client); - - -/** - * Append a message to the transmission context. - * All messages in the context will be sent by - * the #GNUNET_SERVER_transmit_context_run method. - * - * @param tc context to use - * @param data what to append to the result message - * @param length length of @a data - * @param type type of the message - */ -void -GNUNET_SERVER_transmit_context_append_data (struct GNUNET_SERVER_TransmitContext *tc, - const void *data, - size_t length, uint16_t type); - - -/** - * Append a message to the transmission context. - * All messages in the context will be sent by - * the transmit_context_run method. - * - * @param tc context to use - * @param msg message to append - */ -void -GNUNET_SERVER_transmit_context_append_message (struct GNUNET_SERVER_TransmitContext *tc, - const struct GNUNET_MessageHeader *msg); - - -/** - * Execute a transmission context. If there is an error in the - * transmission, the receive_done method will be called with an error - * code (#GNUNET_SYSERR), otherwise with #GNUNET_OK. - * - * @param tc transmission context to use - * @param timeout when to time out and abort the transmission - */ -void -GNUNET_SERVER_transmit_context_run (struct GNUNET_SERVER_TransmitContext *tc, - struct GNUNET_TIME_Relative timeout); - - -/** - * Destroy a transmission context. This function must not be called - * after #GNUNET_SERVER_transmit_context_run. - * - * @param tc transmission context to destroy - * @param success code to give to #GNUNET_SERVER_receive_done for - * the client: #GNUNET_OK to keep the connection open and - * continue to receive - * #GNUNET_NO to close the connection (normal behavior) - * #GNUNET_SYSERR to close the connection (signal - * serious error) - */ -void -GNUNET_SERVER_transmit_context_destroy (struct GNUNET_SERVER_TransmitContext *tc, - int success); - - -/** - * The notification context is the key datastructure for a conveniance - * API used for transmission of notifications to the client until the - * client disconnects or is disconnected (or the notification context - * is destroyed, in which case we disconnect these clients). - * Essentially, all (notification) messages are queued up until the - * client is able to read them. - */ -struct GNUNET_SERVER_NotificationContext; - - -/** - * Create a new notification context. - * - * @param server server for which this function creates the context - * @param queue_length maximum number of messages to keep in - * the notification queue; optional messages are dropped - * if the queue gets longer than this number of messages - * @return handle to the notification context - */ -struct GNUNET_SERVER_NotificationContext * -GNUNET_SERVER_notification_context_create (struct GNUNET_SERVER_Handle *server, - unsigned int queue_length); - - -/** - * Destroy the context, force disconnect for all clients. - * - * @param nc context to destroy. - */ -void -GNUNET_SERVER_notification_context_destroy (struct GNUNET_SERVER_NotificationContext *nc); - - -/** - * Add a client to the notification context. - * - * @param nc context to modify - * @param client client to add - */ -void -GNUNET_SERVER_notification_context_add (struct GNUNET_SERVER_NotificationContext *nc, - struct GNUNET_SERVER_Client *client); - - -/** - * Send a message to a particular client; must have - * already been added to the notification context. - * - * @param nc context to modify - * @param client client to transmit to - * @param msg message to send - * @param can_drop can this message be dropped due to queue length limitations - */ -void -GNUNET_SERVER_notification_context_unicast (struct GNUNET_SERVER_NotificationContext *nc, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *msg, - int can_drop); - - -/** - * Send a message to all clients of this context. - * - * @param nc context to modify - * @param msg message to send - * @param can_drop can this message be dropped due to queue length limitations - */ -void -GNUNET_SERVER_notification_context_broadcast (struct GNUNET_SERVER_NotificationContext *nc, - const struct GNUNET_MessageHeader *msg, - int can_drop); - - -/** - * Return active number of subscribers in this context. - * - * @param nc context to query - * @return number of current subscribers - */ -unsigned int -GNUNET_SERVER_notification_context_get_size (struct GNUNET_SERVER_NotificationContext *nc); - - -/** - * Create a message queue for a server's client. - * - * @param client the client - * @return the message queue - */ -struct GNUNET_MQ_Handle * -GNUNET_MQ_queue_for_server_client (struct GNUNET_SERVER_Client *client); - - -/** - * Handle to a message stream tokenizer. - */ -struct GNUNET_SERVER_MessageStreamTokenizer; - - -/** - * Functions with this signature are called whenever a - * complete message is received by the tokenizer. - * - * Do not call #GNUNET_SERVER_mst_destroy from within - * the scope of this callback. - * - * @param cls closure - * @param client identification of the client - * @param message the actual message - * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing - */ -typedef int -(*GNUNET_SERVER_MessageTokenizerCallback) (void *cls, - void *client, - const struct GNUNET_MessageHeader *message); - - -/** - * Create a message stream tokenizer. - * - * @param cb function to call on completed messages - * @param cb_cls closure for @a cb - * @return handle to tokenizer - */ -struct GNUNET_SERVER_MessageStreamTokenizer * -GNUNET_SERVER_mst_create (GNUNET_SERVER_MessageTokenizerCallback cb, - void *cb_cls); - - -/** - * Add incoming data to the receive buffer and call the - * callback for all complete messages. - * - * @param mst tokenizer to use - * @param client_identity ID of client for which this is a buffer, - * can be NULL (will be passed back to 'cb') - * @param buf input data to add - * @param size number of bytes in @a buf - * @param purge should any excess bytes in the buffer be discarded - * (i.e. for packet-based services like UDP) - * @param one_shot only call callback once, keep rest of message in buffer - * @return #GNUNET_OK if we are done processing (need more data) - * #GNUNET_NO if one_shot was set and we have another message ready - * #GNUNET_SYSERR if the data stream is corrupt - */ -int -GNUNET_SERVER_mst_receive (struct GNUNET_SERVER_MessageStreamTokenizer *mst, - void *client_identity, - const char *buf, size_t size, - int purge, int one_shot); - - -/** - * Destroys a tokenizer. - * - * @param mst tokenizer to destroy - */ -void -GNUNET_SERVER_mst_destroy (struct GNUNET_SERVER_MessageStreamTokenizer *mst); - - -/** - * Signature of a function to create a custom tokenizer. - * - * @param cls closure from #GNUNET_SERVER_set_callbacks - * @param client handle to client the tokenzier will be used for - * @return handle to custom tokenizer ('mst') - */ -typedef void* -(*GNUNET_SERVER_MstCreateCallback) (void *cls, - struct GNUNET_SERVER_Client *client); - - -/** - * Signature of a function to destroy a custom tokenizer. - * - * @param cls closure from #GNUNET_SERVER_set_callbacks - * @param mst custom tokenizer handle - */ -typedef void -(*GNUNET_SERVER_MstDestroyCallback) (void *cls, - void *mst); - - -/** - * Signature of a function to receive data for a custom tokenizer. - * - * @param cls closure from #GNUNET_SERVER_set_callbacks - * @param mst custom tokenizer handle - * @param client_identity ID of client for which this is a buffer, - * can be NULL (will be passed back to 'cb') - * @param buf input data to add - * @param size number of bytes in @a buf - * @param purge should any excess bytes in the buffer be discarded - * (i.e. for packet-based services like UDP) - * @param one_shot only call callback once, keep rest of message in buffer - * @return #GNUNET_OK if we are done processing (need more data) - * #GNUNET_NO if one_shot was set and we have another message ready - * #GNUNET_SYSERR if the data stream is corrupt - */ -typedef int -(*GNUNET_SERVER_MstReceiveCallback) (void *cls, void *mst, - struct GNUNET_SERVER_Client *client, - const char *buf, - size_t size, - int purge, - int one_shot); - - -/** - * Change functions used by the server to tokenize the message stream. - * (very rarely used). - * - * @param server server to modify - * @param create new tokenizer initialization function - * @param destroy new tokenizer destruction function - * @param receive new tokenizer receive function - * @param cls closure for @a create, @a receive and @a destroy - */ -void -GNUNET_SERVER_set_callbacks (struct GNUNET_SERVER_Handle *server, - GNUNET_SERVER_MstCreateCallback create, - GNUNET_SERVER_MstDestroyCallback destroy, - GNUNET_SERVER_MstReceiveCallback receive, - void *cls); - - -#if 0 /* keep Emacsens' auto-indent happy */ -{ -#endif -#ifdef __cplusplus -} -#endif - -/* ifndef GNUNET_SERVER_LIB_H */ -#endif - -/** @} */ /* end of group server */ - -/* end of gnunet_server_lib.h */ diff --git a/src/include/gnunet_service_lib.h b/src/include/gnunet_service_lib.h index 75b880530..c506fc6fa 100644 --- a/src/include/gnunet_service_lib.h +++ b/src/include/gnunet_service_lib.h @@ -44,22 +44,28 @@ extern "C" #endif #include "gnunet_configuration_lib.h" -#include "gnunet_server_lib.h" #include "gnunet_mq_lib.h" +/** + * Largest supported message (to be precise, one byte more + * than the largest possible message, so tests involving + * this value should check for messages being smaller than + * this value). NOTE: legacy name. + */ +#define GNUNET_SERVER_MAX_MESSAGE_SIZE 65536 /** - * Function called by the service's run - * method to run service-specific setup code. - * - * @param cls closure - * @param server the initialized server - * @param cfg configuration to use + * Smallest supported message. NOTE: legacy name. */ -typedef void -(*GNUNET_SERVICE_Main) (void *cls, - struct GNUNET_SERVER_Handle *server, - const struct GNUNET_CONFIGURATION_Handle *cfg); +#define GNUNET_SERVER_MIN_BUFFER_SIZE sizeof (struct GNUNET_MessageHeader) + +/** + * Timeout we use on TCP connect before trying another + * result from the DNS resolver. Actual value used + * is this value divided by the number of address families. + * Default is 5s. NOTE: legacy name. + */ +#define GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) /** @@ -88,27 +94,6 @@ enum GNUNET_SERVICE_Options }; -/** - * Run a standard GNUnet service startup sequence (initialize loggers - * and configuration, parse options). - * - * @param argc number of command line arguments in @a argv - * @param argv command line arguments - * @param service_name our service name - * @param options service options - * @param task main task of the service - * @param task_cls closure for @a task - * @return #GNUNET_SYSERR on error, #GNUNET_OK - * if we shutdown nicely - * @deprecated - */ -int -GNUNET_SERVICE_run (int argc, - char *const *argv, - const char *service_name, - enum GNUNET_SERVICE_Options options, - GNUNET_SERVICE_Main task, - void *task_cls); /** @@ -133,18 +118,6 @@ GNUNET_SERVICE_start (const char *service_name, enum GNUNET_SERVICE_Options options); -/** - * Obtain the server used by a service. Note that the server must NOT - * be destroyed by the caller. - * - * @param ctx the service context returned from the start function - * @return handle to the server for this service, NULL if there is none - * @deprecated - */ -struct GNUNET_SERVER_Handle * -GNUNET_SERVICE_get_server (struct GNUNET_SERVICE_Context *ctx); - - /** * Get the NULL-terminated array of listen sockets for this service. * diff --git a/src/include/gnunet_util_lib.h b/src/include/gnunet_util_lib.h index 52f5d8ab2..b42751d2a 100644 --- a/src/include/gnunet_util_lib.h +++ b/src/include/gnunet_util_lib.h @@ -41,7 +41,6 @@ extern "C" #include "gnunet_crypto_lib.h" #include "gnunet_bandwidth_lib.h" #include "gnunet_bio_lib.h" -#include "gnunet_connection_lib.h" #include "gnunet_client_lib.h" #include "gnunet_container_lib.h" #include "gnunet_getopt_lib.h" @@ -55,7 +54,6 @@ extern "C" #include "gnunet_plugin_lib.h" #include "gnunet_program_lib.h" #include "gnunet_protocols.h" -#include "gnunet_server_lib.h" #include "gnunet_service_lib.h" #include "gnunet_signal_lib.h" #include "gnunet_strings_lib.h" diff --git a/src/nat-auto/Makefile.am b/src/nat-auto/Makefile.am index 19695fabd..14f3f2330 100644 --- a/src/nat-auto/Makefile.am +++ b/src/nat-auto/Makefile.am @@ -37,11 +37,11 @@ lib_LTLIBRARIES = \ libgnunetnatauto_la_SOURCES = \ nat_auto_api.c \ - nat_auto_api_test.c + nat_auto_api_test.c libgnunetnatauto_la_LIBADD = \ $(top_builddir)/src/nat/libgnunetnatnew.la \ $(top_builddir)/src/util/libgnunetutil.la \ - $(GN_LIBINTL) @EXT_LIBS@ + $(GN_LIBINTL) @EXT_LIBS@ libgnunetnatauto_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) $(WINFLAGS) \ -version-info 0:0:0 @@ -55,4 +55,3 @@ gnunet_service_nat_auto_LDADD = \ $(LIBGCRYPT_LIBS) \ -lgcrypt \ $(GN_LIBINTL) - diff --git a/src/nat-auto/gnunet-nat-server.c b/src/nat-auto/gnunet-nat-server.c index 371e4b27e..590fad4d6 100644 --- a/src/nat-auto/gnunet-nat-server.c +++ b/src/nat-auto/gnunet-nat-server.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet. - Copyright (C) 2011 GNUnet e.V. + Copyright (C) 2011, 2017 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published @@ -31,9 +31,21 @@ /** - * Our server. + * Information we track per client. */ -static struct GNUNET_SERVER_Handle *server; +struct ClientData +{ + /** + * Timeout task. + */ + struct GNUNET_SCHEDULER_Task *tt; + + /** + * Client handle. + */ + struct GNUNET_SERVICE_Client *client; +}; + /** * Our configuration. @@ -248,21 +260,18 @@ try_send_udp (uint32_t dst_ipv4, * We've received a request to probe a NAT * traversal. Do it. * - * @param cls unused - * @param client handle to client (we always close) + * @param cls handle to client (we always close) * @param msg message with details about what to test */ static void -test (void *cls, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *msg) +handle_test (void *cls, + const struct GNUNET_NAT_AUTO_TestMessage *tm) { - const struct GNUNET_NAT_AUTO_TestMessage *tm; + struct ClientData *cd = cls; uint16_t dport; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received test request\n"); - tm = (const struct GNUNET_NAT_AUTO_TestMessage *) msg; dport = ntohs (tm->dport); if (0 == dport) try_anat (tm->dst_ipv4, @@ -276,126 +285,119 @@ test (void *cls, try_send_udp (tm->dst_ipv4, dport, tm->data); - GNUNET_SERVER_receive_done (client, - GNUNET_NO); + GNUNET_SERVICE_client_drop (cd->client); } /** - * Task run during shutdown. + * Main function that will be run. * - * @param cls unused + * @param cls closure + * @param c configuration + * @param srv service handle */ static void -shutdown_task (void *cls) +run (void *cls, + const struct GNUNET_CONFIGURATION_Handle *c, + struct GNUNET_SERVICE_Handle *srv) { - GNUNET_SERVER_destroy (server); - server = NULL; + cfg = c; } /** - * Main function that will be run. + * Forcefully drops client after 1s. * - * @param cls closure - * @param args remaining command-line arguments - * @param cfgfile name of the configuration file used (for saving, can be NULL!) - * @param c configuration + * @param cls our `struct ClientData` of a client to drop */ static void -run (void *cls, - char *const *args, - const char *cfgfile, - const struct GNUNET_CONFIGURATION_Handle *c) +force_timeout (void *cls) { - static const struct GNUNET_SERVER_MessageHandler handlers[] = { - {&test, NULL, GNUNET_MESSAGE_TYPE_NAT_TEST, - sizeof (struct GNUNET_NAT_AUTO_TestMessage)}, - {NULL, NULL, 0, 0} - }; - unsigned int port; - struct sockaddr_in in4; - struct sockaddr_in6 in6; - - socklen_t slen[] = { - sizeof (in4), - sizeof (in6), - 0 - }; - struct sockaddr *sa[] = { - (struct sockaddr *) &in4, - (struct sockaddr *) &in6, - NULL - }; + struct ClientData *cd = cls; - cfg = c; - if ( (NULL == args[0]) || - (1 != SSCANF (args[0], "%u", &port)) || - (0 == port) || - (65536 <= port) ) - { - FPRINTF (stderr, - _("Please pass valid port number as the first argument! (got `%s')\n"), - args[0]); - return; - } - memset (&in4, 0, sizeof (in4)); - memset (&in6, 0, sizeof (in6)); - in4.sin_family = AF_INET; - in4.sin_port = htons ((uint16_t) port); - in6.sin6_family = AF_INET6; - in6.sin6_port = htons ((uint16_t) port); -#if HAVE_SOCKADDR_IN_SIN_LEN - in4.sin_len = sizeof (in4); - in6.sin6_len = sizeof (in6); -#endif - server = GNUNET_SERVER_create (NULL, - NULL, - (struct sockaddr * const *) sa, - slen, - GNUNET_TIME_UNIT_SECONDS, - GNUNET_YES); - GNUNET_SERVER_add_handlers (server, - handlers); - GNUNET_SCHEDULER_add_shutdown (&shutdown_task, - NULL); + cd->tt = NULL; + GNUNET_SERVICE_client_drop (cd->client); } + /** - * Main function of gnunet-nat-server. + * Callback called when a client connects to the service. * - * @param argc number of command-line arguments - * @param argv command line - * @return 0 on success, -1 on error + * @param cls closure for the service + * @param c the new client that connected to the service + * @param mq the message queue used to send messages to the client + * @return our `struct ClientData` */ -int -main (int argc, char *const argv[]) +static void * +client_connect_cb (void *cls, + struct GNUNET_SERVICE_Client *c, + struct GNUNET_MQ_Handle *mq) { - static const struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_OPTION_END - }; - - if (GNUNET_OK != - GNUNET_STRINGS_get_utf8_args (argc, argv, - &argc, &argv)) - return 2; - - if (GNUNET_OK != - GNUNET_PROGRAM_run (argc, - argv, - "gnunet-nat-server [options] PORT", - _("GNUnet NAT traversal test helper daemon"), - options, - &run, - NULL)) - { - GNUNET_free ((void*) argv); - return 1; - } - GNUNET_free ((void*) argv); - return 0; + struct ClientData *cd; + + cd = GNUNET_new (struct ClientData); + cd->client = c; + cd->tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &force_timeout, + cd); + return cd; +} + + +/** + * Callback called when a client disconnected from the service + * + * @param cls closure for the service + * @param c the client that disconnected + * @param internal_cls our `struct ClientData` + */ +static void +client_disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *c, + void *internal_cls) +{ + struct ClientData *cd = internal_cls; + + if (NULL != cd->tt) + GNUNET_SCHEDULER_cancel (cd->tt); + GNUNET_free (cd); } +/** + * Define "main" method using service macro. + */ +GNUNET_SERVICE_MAIN +("nat-server", + GNUNET_SERVICE_OPTION_NONE, + &run, + &client_connect_cb, + &client_disconnect_cb, + NULL, + GNUNET_MQ_hd_fixed_size (test, + GNUNET_MESSAGE_TYPE_NAT_TEST, + struct GNUNET_NAT_AUTO_TestMessage, + NULL), + GNUNET_MQ_handler_end ()); + + +#if defined(LINUX) && defined(__GLIBC__) +#include + +/** + * MINIMIZE heap size (way below 128k) since this process doesn't need much. + */ +void __attribute__ ((constructor)) +GNUNET_ARM_memory_init () +{ + mallopt (M_TRIM_THRESHOLD, 4 * 1024); + mallopt (M_TOP_PAD, 1 * 1024); + malloc_trim (0); +} +#endif + + + + /* end of gnunet-nat-server.c */ diff --git a/src/testbed/testbed_api_hosts.c b/src/testbed/testbed_api_hosts.c index 731944bc4..5d2c1cc37 100644 --- a/src/testbed/testbed_api_hosts.c +++ b/src/testbed/testbed_api_hosts.c @@ -952,10 +952,11 @@ gen_rsh_suffix_args (const char * const *append_args) * @param client identification of the client * @param message the actual message * - * @return GNUNET_OK on success, GNUNET_SYSERR to stop further processing + * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing */ static int -helper_mst (void *cls, void *client, const struct GNUNET_MessageHeader *message) +helper_mst (void *cls, + const struct GNUNET_MessageHeader *message) { struct GNUNET_TESTBED_ControllerProc *cp = cls; const struct GNUNET_TESTBED_HelperReply *msg; diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index acc2557c6..7687f2348 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -191,6 +191,8 @@ libexec_PROGRAMS = \ $(BT_BIN) \ gnunet-service-transport + + bin_PROGRAMS = \ gnunet-transport \ gnunet-transport-certificate-creation @@ -561,7 +563,7 @@ TESTS = \ $(HTTP_API_TIMEOUT_TEST) \ $(HTTPS_API_TIMEOUT_TEST) \ $(WLAN_TIMEOUT_TEST) \ - $(BT_TIMEOUT_TEST) + $(BT_TIMEOUT_TEST) if HAVE_GETOPT_BINARY TESTS += \ test_transport_api_slow_ats diff --git a/src/transport/gnunet-helper-transport-wlan-dummy.c b/src/transport/gnunet-helper-transport-wlan-dummy.c index 684546314..63ed9c4b7 100644 --- a/src/transport/gnunet-helper-transport-wlan-dummy.c +++ b/src/transport/gnunet-helper-transport-wlan-dummy.c @@ -120,11 +120,11 @@ send_mac_to_plugin (char *buffer, struct GNUNET_TRANSPORT_WLAN_MacAddress *mac) * type to the output forward and copy it to the buffer for stdout. * * @param cls the 'struct SendBuffer' to copy the converted message to - * @param client unused * @param hdr inbound message from the FIFO */ static int -stdin_send (void *cls, void *client, const struct GNUNET_MessageHeader *hdr) +stdin_send (void *cls, + const struct GNUNET_MessageHeader *hdr) { struct SendBuffer *write_pout = cls; const struct GNUNET_TRANSPORT_WLAN_RadiotapSendMessage *in; @@ -166,11 +166,11 @@ stdin_send (void *cls, void *client, const struct GNUNET_MessageHeader *hdr) * We read a full message from stdin. Copy it to our send buffer. * * @param cls the 'struct SendBuffer' to copy to - * @param client unused * @param hdr the message we received to copy to the buffer */ static int -file_in_send (void *cls, void *client, const struct GNUNET_MessageHeader *hdr) +file_in_send (void *cls, + const struct GNUNET_MessageHeader *hdr) { struct SendBuffer *write_std = cls; uint16_t sendsize; @@ -213,8 +213,8 @@ main (int argc, char *argv[]) fd_set wfds; struct timeval tv; int retval; - struct GNUNET_SERVER_MessageStreamTokenizer *stdin_mst = NULL; - struct GNUNET_SERVER_MessageStreamTokenizer *file_in_mst = NULL; + struct GNUNET_MessageStreamTokenizer *stdin_mst = NULL; + struct GNUNET_MessageStreamTokenizer *file_in_mst = NULL; struct GNUNET_TRANSPORT_WLAN_MacAddress macaddr; int first; @@ -340,8 +340,8 @@ main (int argc, char *argv[]) write_std.pos = 0; write_pout.size = 0; write_pout.pos = 0; - stdin_mst = GNUNET_SERVER_mst_create (&stdin_send, &write_pout); - file_in_mst = GNUNET_SERVER_mst_create (&file_in_send, &write_std); + stdin_mst = GNUNET_MST_create (&stdin_send, &write_pout); + file_in_mst = GNUNET_MST_create (&file_in_send, &write_std); /* Send 'random' mac address */ macaddr.mac[0] = 0x13; @@ -453,8 +453,9 @@ main (int argc, char *argv[]) } else if (0 < readsize) { - GNUNET_SERVER_mst_receive (stdin_mst, NULL, readbuf, readsize, - GNUNET_NO, GNUNET_NO); + GNUNET_MST_from_buffer (stdin_mst, + readbuf, readsize, + GNUNET_NO, GNUNET_NO); } else @@ -475,8 +476,9 @@ main (int argc, char *argv[]) } else if (0 < readsize) { - GNUNET_SERVER_mst_receive (file_in_mst, NULL, readbuf, readsize, - GNUNET_NO, GNUNET_NO); + GNUNET_MST_from_buffer (file_in_mst, + readbuf, readsize, + GNUNET_NO, GNUNET_NO); } else { @@ -489,9 +491,9 @@ main (int argc, char *argv[]) end: /* clean up */ if (NULL != stdin_mst) - GNUNET_SERVER_mst_destroy (stdin_mst); + GNUNET_MST_destroy (stdin_mst); if (NULL != file_in_mst) - GNUNET_SERVER_mst_destroy (file_in_mst); + GNUNET_MST_destroy (file_in_mst); if (NULL != fpout) fclose (fpout); diff --git a/src/transport/plugin_transport_http_client.c b/src/transport/plugin_transport_http_client.c index ceed94af8..e91149289 100644 --- a/src/transport/plugin_transport_http_client.c +++ b/src/transport/plugin_transport_http_client.c @@ -221,7 +221,7 @@ struct GNUNET_ATS_Session /** * Message stream tokenizer for incoming data */ - struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk; + struct GNUNET_MessageStreamTokenizer *msg_tk; /** * Session timeout task @@ -528,7 +528,7 @@ client_delete_session (struct GNUNET_ATS_Session *s) GNUNET_TRANSPORT_SS_DONE); if (NULL != s->msg_tk) { - GNUNET_SERVER_mst_destroy (s->msg_tk); + GNUNET_MST_destroy (s->msg_tk); s->msg_tk = NULL; } GNUNET_HELLO_address_free (s->address); @@ -1158,13 +1158,11 @@ client_wake_up (void *cls) * Callback for message stream tokenizer * * @param cls the session - * @param client not used * @param message the message received * @return always #GNUNET_OK */ static int client_receive_mst_cb (void *cls, - void *client, const struct GNUNET_MessageHeader *message) { struct GNUNET_ATS_Session *s = cls; @@ -1274,14 +1272,13 @@ client_receive (void *stream, return CURL_WRITEFUNC_PAUSE; } if (NULL == s->msg_tk) - s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb, - s); - GNUNET_SERVER_mst_receive (s->msg_tk, - s, - stream, - len, - GNUNET_NO, - GNUNET_NO); + s->msg_tk = GNUNET_MST_create (&client_receive_mst_cb, + s); + GNUNET_MST_from_buffer (s->msg_tk, + stream, + len, + GNUNET_NO, + GNUNET_NO); return len; } diff --git a/src/transport/plugin_transport_http_server.c b/src/transport/plugin_transport_http_server.c index 63c67b81c..2d6f40d58 100644 --- a/src/transport/plugin_transport_http_server.c +++ b/src/transport/plugin_transport_http_server.c @@ -201,7 +201,7 @@ struct GNUNET_ATS_Session /** * Message stream tokenizer for incoming data */ - struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk; + struct GNUNET_MessageStreamTokenizer *msg_tk; /** * Client recv handle @@ -608,7 +608,7 @@ server_delete_session (struct GNUNET_ATS_Session *s) } if (NULL != s->msg_tk) { - GNUNET_SERVER_mst_destroy (s->msg_tk); + GNUNET_MST_destroy (s->msg_tk); s->msg_tk = NULL; } GNUNET_HELLO_address_free (s->address); @@ -1621,13 +1621,11 @@ server_send_callback (void *cls, * Callback called by MessageStreamTokenizer when a message has arrived * * @param cls current session as closure - * @param client client * @param message the message to be forwarded to transport service * @return #GNUNET_OK */ static int server_receive_mst_cb (void *cls, - void *client, const struct GNUNET_MessageHeader *message) { struct GNUNET_ATS_Session *s = cls; @@ -1847,13 +1845,16 @@ server_access_cb (void *cls, *upload_data_size); if (s->msg_tk == NULL) { - s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s); + s->msg_tk = GNUNET_MST_create (&server_receive_mst_cb, + s); } - GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data, *upload_data_size, - GNUNET_NO, GNUNET_NO); + GNUNET_MST_from_buffer (s->msg_tk, + upload_data, + *upload_data_size, + GNUNET_NO, GNUNET_NO); server_mhd_connection_timeout (plugin, s, - GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value_us / 1000LL - / 1000LL); + GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value_us / 1000LL + / 1000LL); (*upload_data_size) = 0; } else @@ -1935,7 +1936,7 @@ server_disconnect_cb (void *cls, sc->session->server_recv = NULL; if (NULL != sc->session->msg_tk) { - GNUNET_SERVER_mst_destroy (sc->session->msg_tk); + GNUNET_MST_destroy (sc->session->msg_tk); sc->session->msg_tk = NULL; } } @@ -2757,7 +2758,7 @@ server_start_report_addresses (struct HTTP_Server_Plugin *plugin) return; } - plugin->nat + plugin->nat = GNUNET_NAT_register (plugin->env->cfg, "transport-http_server", IPPROTO_TCP, diff --git a/src/transport/plugin_transport_tcp.c b/src/transport/plugin_transport_tcp.c index 34bbd00e0..10ea01cec 100644 --- a/src/transport/plugin_transport_tcp.c +++ b/src/transport/plugin_transport_tcp.c @@ -45,9 +45,494 @@ */ #define NAT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10) -GNUNET_NETWORK_STRUCT_BEGIN +/** + * Opaque handle that can be used to cancel + * a transmit-ready notification. + */ +struct GNUNET_CONNECTION_TransmitHandle; + +/** + * @brief handle for a server + */ +struct GNUNET_SERVER_Handle; + +/** + * @brief opaque handle for a client of the server + */ +struct GNUNET_SERVER_Client; + +/** + * @brief opaque handle server returns for aborting transmission to a client. + */ +struct GNUNET_SERVER_TransmitHandle; + +/** + * @brief handle for a network connection + */ +struct GNUNET_CONNECTION_Handle; + + +/** + * Function called to notify a client about the connection begin ready + * to queue more data. @a buf will be NULL and @a size zero if the + * connection was closed for writing in the meantime. + * + * @param cls closure + * @param size number of bytes available in @a buf + * @param buf where the callee should write the message + * @return number of bytes written to @a buf + */ +typedef size_t +(*GNUNET_CONNECTION_TransmitReadyNotify) (void *cls, + size_t size, + void *buf); + +/** + * Credentials for UNIX domain sockets. + */ +struct GNUNET_CONNECTION_Credentials +{ + /** + * UID of the other end of the connection. + */ + uid_t uid; + + /** + * GID of the other end of the connection. + */ + gid_t gid; +}; + + +/** + * Functions with this signature are called whenever a client + * is disconnected on the network level. + * + * @param cls closure + * @param client identification of the client; NULL + * for the last call when the server is destroyed + */ +typedef void +(*GNUNET_SERVER_DisconnectCallback) (void *cls, + struct GNUNET_SERVER_Client *client); + + +/** + * Functions with this signature are called whenever a client + * is connected on the network level. + * + * @param cls closure + * @param client identification of the client + */ +typedef void +(*GNUNET_SERVER_ConnectCallback) (void *cls, + struct GNUNET_SERVER_Client *client); + + +/** + * Function to call for access control checks. + * + * @param cls closure + * @param ucred credentials, if available, otherwise NULL + * @param addr address + * @param addrlen length of address + * @return GNUNET_YES to allow, GNUNET_NO to deny, GNUNET_SYSERR + * for unknown address family (will be denied). + */ +typedef int +(*GNUNET_CONNECTION_AccessCheck) (void *cls, + const struct + GNUNET_CONNECTION_Credentials * + ucred, + const struct sockaddr * addr, + socklen_t addrlen); + +/** + * Callback function for data received from the network. Note that + * both "available" and "err" would be 0 if the read simply timed out. + * + * @param cls closure + * @param buf pointer to received data + * @param available number of bytes availabe in "buf", + * possibly 0 (on errors) + * @param addr address of the sender + * @param addrlen size of addr + * @param errCode value of errno (on errors receiving) + */ +typedef void +(*GNUNET_CONNECTION_Receiver) (void *cls, const void *buf, + size_t available, + const struct sockaddr * addr, + socklen_t addrlen, int errCode); + + + +/** + * Close the connection and free associated resources. There must + * not be any pending requests for reading or writing to the + * connection at this time. + * + * @param connection connection to destroy + */ +void +GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *connection); + + +/** + * Signature of a function to create a custom tokenizer. + * + * @param cls closure from #GNUNET_SERVER_set_callbacks + * @param client handle to client the tokenzier will be used for + * @return handle to custom tokenizer ('mst') + */ +typedef void* +(*GNUNET_SERVER_MstCreateCallback) (void *cls, + struct GNUNET_SERVER_Client *client); + + +/** + * Signature of a function to destroy a custom tokenizer. + * + * @param cls closure from #GNUNET_SERVER_set_callbacks + * @param mst custom tokenizer handle + */ +typedef void +(*GNUNET_SERVER_MstDestroyCallback) (void *cls, + void *mst); + +/** + * Signature of a function to receive data for a custom tokenizer. + * + * @param cls closure from #GNUNET_SERVER_set_callbacks + * @param mst custom tokenizer handle + * @param client_identity ID of client for which this is a buffer, + * can be NULL (will be passed back to 'cb') + * @param buf input data to add + * @param size number of bytes in @a buf + * @param purge should any excess bytes in the buffer be discarded + * (i.e. for packet-based services like UDP) + * @param one_shot only call callback once, keep rest of message in buffer + * @return #GNUNET_OK if we are done processing (need more data) + * #GNUNET_NO if one_shot was set and we have another message ready + * #GNUNET_SYSERR if the data stream is corrupt + */ +typedef int +(*GNUNET_SERVER_MstReceiveCallback) (void *cls, void *mst, + struct GNUNET_SERVER_Client *client, + const char *buf, + size_t size, + int purge, + int one_shot); +/** + * Functions with this signature are called whenever a message is + * received. + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + */ +typedef void +(*GNUNET_SERVER_MessageCallback) (void *cls, + struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message); + +/** + * Message handler. Each struct specifies how to handle on particular + * type of message received. + */ +struct GNUNET_SERVER_MessageHandler +{ + /** + * Function to call for messages of "type". + */ + GNUNET_SERVER_MessageCallback callback; + + /** + * Closure argument for @e callback. + */ + void *callback_cls; + + /** + * Type of the message this handler covers. + */ + uint16_t type; + + /** + * Expected size of messages of this type. Use 0 for + * variable-size. If non-zero, messages of the given + * type will be discarded (and the connection closed) + * if they do not have the right size. + */ + uint16_t expected_size; + +}; + +/** + * Ask the server to disconnect from the given client. This is the + * same as passing #GNUNET_SYSERR to #GNUNET_SERVER_receive_done, + * except that it allows dropping of a client even when not handling a + * message from that client. + * + * @param client the client to disconnect from + */ +void +GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client); + +/** + * Return user context associated with the given client. + * Note: you should probably use the macro (call without the underscore). + * + * @param client client to query + * @param size number of bytes in user context struct (for verification only) + * @return pointer to user context + */ +void * +GNUNET_SERVER_client_get_user_context_ (struct GNUNET_SERVER_Client *client, + size_t size); + + +/** + * Functions with this signature are called whenever a + * complete message is received by the tokenizer. + * + * Do not call #GNUNET_SERVER_mst_destroy from within + * the scope of this callback. + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing + */ +typedef int +(*GNUNET_SERVER_MessageTokenizerCallback) (void *cls, + void *client, + const struct GNUNET_MessageHeader *message); + + +/** + * Create a message stream tokenizer. + * + * @param cb function to call on completed messages + * @param cb_cls closure for @a cb + * @return handle to tokenizer + */ +struct GNUNET_SERVER_MessageStreamTokenizer * +GNUNET_SERVER_mst_create (GNUNET_SERVER_MessageTokenizerCallback cb, + void *cb_cls); + +/** + * Add incoming data to the receive buffer and call the + * callback for all complete messages. + * + * @param mst tokenizer to use + * @param client_identity ID of client for which this is a buffer, + * can be NULL (will be passed back to 'cb') + * @param buf input data to add + * @param size number of bytes in @a buf + * @param purge should any excess bytes in the buffer be discarded + * (i.e. for packet-based services like UDP) + * @param one_shot only call callback once, keep rest of message in buffer + * @return #GNUNET_OK if we are done processing (need more data) + * #GNUNET_NO if one_shot was set and we have another message ready + * #GNUNET_SYSERR if the data stream is corrupt + */ +int +GNUNET_SERVER_mst_receive (struct GNUNET_SERVER_MessageStreamTokenizer *mst, + void *client_identity, + const char *buf, size_t size, + int purge, int one_shot); + + + +/** + * Destroys a tokenizer. + * + * @param mst tokenizer to destroy + */ +void +GNUNET_SERVER_mst_destroy (struct GNUNET_SERVER_MessageStreamTokenizer *mst); + + +/** + * Set user context to be associated with the given client. + * Note: you should probably use the macro (call without the underscore). + * + * @param client client to query + * @param ptr pointer to user context + * @param size number of bytes in user context struct (for verification only) + */ +void +GNUNET_SERVER_client_set_user_context_ (struct GNUNET_SERVER_Client *client, + void *ptr, + size_t size); +/** + * Return user context associated with the given client. + * + * @param client client to query + * @param type expected return type (i.e. 'struct Foo') + * @return pointer to user context of type 'type *'. + */ +#define GNUNET_SERVER_client_get_user_context(client,type) \ + (type *) GNUNET_SERVER_client_get_user_context_ (client, sizeof (type)) + +/** + * Set user context to be associated with the given client. + * + * @param client client to query + * @param value pointer to user context + */ +#define GNUNET_SERVER_client_set_user_context(client,value) \ + GNUNET_SERVER_client_set_user_context_ (client, value, sizeof (*value)) + + + +/** + * Notify us when the server has enough space to transmit + * a message of the given size to the given client. + * + * @param client client to transmit message to + * @param size requested amount of buffer space + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param callback function to call when space is available + * @param callback_cls closure for @a callback + * @return non-NULL if the notify callback was queued; can be used + * to cancel the request using + * #GNUNET_SERVER_notify_transmit_ready_cancel. + * NULL if we are already going to notify someone else (busy) + */ +struct GNUNET_SERVER_TransmitHandle * +GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, + size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_CONNECTION_TransmitReadyNotify callback, + void *callback_cls); + +/** + * Abort transmission request. + * + * @param th request to abort + */ +void +GNUNET_SERVER_notify_transmit_ready_cancel (struct GNUNET_SERVER_TransmitHandle *th); + + + + +/** + * Notify the server that the given client handle should + * be kept (keeps the connection up if possible, increments + * the internal reference counter). + * + * @param client the client to keep + */ +void +GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client); + + +/** + * Notify the server that the given client handle is no + * longer required. Decrements the reference counter. If + * that counter reaches zero an inactive connection maybe + * closed. + * + * @param client the client to drop + */ +void +GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client); + + +/** + * Function called by the service's run + * method to run service-specific setup code. + * + * @param cls closure + * @param server the initialized server + * @param cfg configuration to use + */ +typedef void +(*GNUNET_SERVICE_Main) (void *cls, + struct GNUNET_SERVER_Handle *server, + const struct GNUNET_CONFIGURATION_Handle *cfg); + + + +/** + * Suspend accepting connections from the listen socket temporarily. + * Resume activity using #GNUNET_SERVER_resume. + * + * @param server server to stop accepting connections. + */ +void +GNUNET_SERVER_suspend (struct GNUNET_SERVER_Handle *server); + +/** + * Notify us when the server has enough space to transmit + * a message of the given size to the given client. + * + * @param client client to transmit message to + * @param size requested amount of buffer space + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param callback function to call when space is available + * @param callback_cls closure for @a callback + * @return non-NULL if the notify callback was queued; can be used + * to cancel the request using + * #GNUNET_SERVER_notify_transmit_ready_cancel. + * NULL if we are already going to notify someone else (busy) + */ +struct GNUNET_SERVER_TransmitHandle * +GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, + size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_CONNECTION_TransmitReadyNotify callback, + void *callback_cls); + + +/** + * Add a TCP socket-based connection to the set of handles managed by + * this server. Use this function for outgoing (P2P) connections that + * we initiated (and where this server should process incoming + * messages). + * + * @param server the server to use + * @param connection the connection to manage (client must + * stop using this connection from now on) + * @return the client handle + */ +struct GNUNET_SERVER_Client * +GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server, + struct GNUNET_CONNECTION_Handle *connection); + + +/** + * Resume accepting connections from the listen socket. + * + * @param server server to resume accepting connections. + */ +void +GNUNET_SERVER_resume (struct GNUNET_SERVER_Handle *server); + +/** + * Free resources held by this server. + * + * @param server server to destroy + */ +void +GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *server); + + + + +#include "tcp_connection_legacy.c" +#include "tcp_server_mst_legacy.c" +#include "tcp_server_legacy.c" +#include "tcp_service_legacy.c" + +GNUNET_NETWORK_STRUCT_BEGIN + /** * Initial handshake message for a session. */ @@ -521,47 +1006,6 @@ struct Plugin }; -/* begin of ancient copy-and-pasted code that should be - specialized for TCP ...*/ -/** - * Add the given UNIX domain path as an address to the - * list (as the first entry). - * - * @param saddrs array to update - * @param saddrlens where to store the address length - * @param unixpath path to add - * @param abstract #GNUNET_YES to add an abstract UNIX domain socket. This - * parameter is ignore on systems other than LINUX - */ -static void -add_unixpath (struct sockaddr **saddrs, - socklen_t *saddrlens, - const char *unixpath, - int abstract) -{ -#ifdef AF_UNIX - struct sockaddr_un *un; - - un = GNUNET_new (struct sockaddr_un); - un->sun_family = AF_UNIX; - strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1); -#ifdef LINUX - if (GNUNET_YES == abstract) - un->sun_path[0] = '\0'; -#endif -#if HAVE_SOCKADDR_UN_SUN_LEN - un->sun_len = (u_char) sizeof (struct sockaddr_un); -#endif - *saddrs = (struct sockaddr *) un; - *saddrlens = sizeof (struct sockaddr_un); -#else - /* this function should never be called - * unless AF_UNIX is defined! */ - GNUNET_assert (0); -#endif -} - - /** * Get the list of addresses that a server for the given service * should bind to. diff --git a/src/transport/plugin_transport_udp.c b/src/transport/plugin_transport_udp.c index eb48341b7..3a9013a5a 100644 --- a/src/transport/plugin_transport_udp.c +++ b/src/transport/plugin_transport_udp.c @@ -158,6 +158,11 @@ struct GNUNET_ATS_Session */ struct GNUNET_PeerIdentity target; + /** + * Tokenizer for inbound messages. + */ + struct GNUNET_MessageStreamTokenizer *mst; + /** * Plugin this session belongs to. */ @@ -626,6 +631,11 @@ free_session (struct GNUNET_ATS_Session *s) GNUNET_free (s->frag_ctx); s->frag_ctx = NULL; } + if (NULL != s->mst) + { + GNUNET_MST_destroy (s->mst); + s->mst = NULL; + } GNUNET_free (s); } @@ -2499,18 +2509,16 @@ read_process_ack (struct Plugin *plugin, * Message tokenizer has broken up an incomming message. Pass it on * to the service. * - * @param cls the `struct Plugin *` - * @param client the `struct GNUNET_ATS_Session *` + * @param cls the `struct GNUNET_ATS_Session *` * @param hdr the actual message * @return #GNUNET_OK (always) */ static int process_inbound_tokenized_messages (void *cls, - void *client, const struct GNUNET_MessageHeader *hdr) { - struct Plugin *plugin = cls; - struct GNUNET_ATS_Session *session = client; + struct GNUNET_ATS_Session *session = cls; + struct Plugin *plugin = session->plugin; if (GNUNET_YES == session->in_destroy) return GNUNET_OK; @@ -2626,6 +2634,8 @@ udp_plugin_create_session (void *cls, struct GNUNET_ATS_Session *s; s = GNUNET_new (struct GNUNET_ATS_Session); + s->mst = GNUNET_MST_create (&process_inbound_tokenized_messages, + s); s->plugin = plugin; s->address = GNUNET_HELLO_address_copy (address); s->target = address->peer; @@ -2792,12 +2802,11 @@ process_udp_message (struct Plugin *plugin, GNUNET_free (address); s->rc++; - GNUNET_SERVER_mst_receive (plugin->mst, - s, - (const char *) &msg[1], - ntohs (msg->header.size) - sizeof(struct UDPMessage), - GNUNET_YES, - GNUNET_NO); + GNUNET_MST_from_buffer (s->mst, + (const char *) &msg[1], + ntohs (msg->header.size) - sizeof(struct UDPMessage), + GNUNET_YES, + GNUNET_NO); s->rc--; if ( (0 == s->rc) && (GNUNET_YES == s->in_destroy) ) @@ -3990,8 +3999,6 @@ libgnunet_plugin_transport_udp_init (void *cls) p->sessions = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_NO); p->defrag_ctxs = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); - p->mst = GNUNET_SERVER_mst_create (&process_inbound_tokenized_messages, - p); GNUNET_BANDWIDTH_tracker_init (&p->tracker, NULL, NULL, @@ -4008,7 +4015,6 @@ libgnunet_plugin_transport_udp_init (void *cls) _("Failed to create UDP network sockets\n")); GNUNET_CONTAINER_multipeermap_destroy (p->sessions); GNUNET_CONTAINER_heap_destroy (p->defrag_ctxs); - GNUNET_SERVER_mst_destroy (p->mst); if (NULL != p->nat) GNUNET_NAT_unregister (p->nat); GNUNET_free (p); @@ -4120,11 +4126,6 @@ libgnunet_plugin_transport_udp_done (void *cls) GNUNET_CONTAINER_heap_destroy (plugin->defrag_ctxs); plugin->defrag_ctxs = NULL; } - if (NULL != plugin->mst) - { - GNUNET_SERVER_mst_destroy (plugin->mst); - plugin->mst = NULL; - } while (NULL != (udpw = plugin->ipv4_queue_head)) { dequeue (plugin, diff --git a/src/transport/plugin_transport_udp.h b/src/transport/plugin_transport_udp.h index 152b16099..48c7365c7 100644 --- a/src/transport/plugin_transport_udp.h +++ b/src/transport/plugin_transport_udp.h @@ -163,11 +163,6 @@ struct Plugin */ struct GNUNET_SCHEDULER_Task *select_task_v6; - /** - * Tokenizer for inbound messages. - */ - struct GNUNET_SERVER_MessageStreamTokenizer *mst; - /** * Bandwidth tracker to limit global UDP traffic. */ @@ -192,7 +187,7 @@ struct Plugin * Handle to NAT traversal support. */ struct GNUNET_NAT_STUN_Handle *stun; - + /** * The read socket for IPv4 */ @@ -203,11 +198,6 @@ struct Plugin */ struct GNUNET_NETWORK_Handle *sockv6; - /** - * Tokenizer for inbound messages. - */ - struct GNUNET_SERVER_MessageStreamTokenizer *broadcast_mst; - /** * Head of DLL of broadcast addresses */ diff --git a/src/transport/plugin_transport_udp_broadcasting.c b/src/transport/plugin_transport_udp_broadcasting.c index a440830fd..c6ddbce9b 100644 --- a/src/transport/plugin_transport_udp_broadcasting.c +++ b/src/transport/plugin_transport_udp_broadcasting.c @@ -133,11 +133,10 @@ struct MstContext */ static int broadcast_mst_cb (void *cls, - void *client, const struct GNUNET_MessageHeader *message) { - struct Plugin *plugin = cls; - struct MstContext *mc = client; + struct MstContext *mc = cls; + struct Plugin *plugin = mc->plugin; struct GNUNET_HELLO_Address *address; const struct GNUNET_MessageHeader *hello; const struct UDP_Beacon_Message *msg; @@ -191,16 +190,20 @@ udp_broadcast_receive (struct Plugin *plugin, size_t udp_addr_len, enum GNUNET_ATS_Network_Type network_type) { + struct GNUNET_MessageStreamTokenizer *broadcast_mst; struct MstContext mc; + broadcast_mst = GNUNET_MST_create (&broadcast_mst_cb, + &mc); + mc.plugin = plugin; mc.udp_addr = udp_addr; mc.udp_addr_len = udp_addr_len; mc.ats_address_network_type = network_type; - GNUNET_SERVER_mst_receive (plugin->broadcast_mst, - &mc, - buf, size, - GNUNET_NO, - GNUNET_NO); + GNUNET_MST_from_buffer (broadcast_mst, + buf, size, + GNUNET_NO, + GNUNET_NO); + GNUNET_MST_destroy (broadcast_mst); } @@ -546,10 +549,6 @@ setup_broadcast (struct Plugin *plugin, return; } - /* always create tokenizers */ - plugin->broadcast_mst = - GNUNET_SERVER_mst_create (&broadcast_mst_cb, plugin); - if (GNUNET_YES != plugin->enable_broadcasting) return; /* We do not send, just receive */ @@ -636,13 +635,6 @@ stop_broadcast (struct Plugin *plugin) GNUNET_free (p); } } - - /* Destroy MSTs */ - if (NULL != plugin->broadcast_mst) - { - GNUNET_SERVER_mst_destroy (plugin->broadcast_mst); - plugin->broadcast_mst = NULL; - } } /* end of plugin_transport_udp_broadcasting.c */ diff --git a/src/transport/plugin_transport_wlan.c b/src/transport/plugin_transport_wlan.c index 376065d24..3f5ada10b 100644 --- a/src/transport/plugin_transport_wlan.c +++ b/src/transport/plugin_transport_wlan.c @@ -38,6 +38,7 @@ #include "gnunet_fragmentation_lib.h" #include "gnunet_constants.h" + #if BUILD_WLAN /* begin case wlan */ #define PLUGIN_NAME "wlan" @@ -48,6 +49,7 @@ #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_wlan_done #define LOG(kind,...) GNUNET_log_from (kind, "transport-wlan",__VA_ARGS__) + /** * time out of a mac endpoint */ @@ -92,6 +94,30 @@ #error need to build wlan or bluetooth #endif + + +/** + * Functions with this signature are called whenever a + * complete message is received by the tokenizer. + * + * Do not call #GNUNET_SERVER_mst_destroy from within + * the scope of this callback. + * + * @param cls closure + * @param client identification of the client + * @param message the actual message + * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing + */ +typedef int +(*GNUNET_SERVER_MessageTokenizerCallback) (void *cls, + void *client, + const struct GNUNET_MessageHeader *message); + + +/* Include legacy message stream tokenizer that was removed from util (for now) */ +#include "tcp_server_mst_legacy.c" + + /** * Max size of packet (that we give to the WLAN driver for transmission) */ @@ -1728,11 +1754,10 @@ send_hello_beacon (void *cls) * Function used for to process the data from the suid process * * @param cls the plugin handle - * @param client client that send the data (not used) * @param hdr header of the GNUNET_MessageHeader */ static int -handle_helper_message (void *cls, void *client, +handle_helper_message (void *cls, const struct GNUNET_MessageHeader *hdr) { struct Plugin *plugin = cls; diff --git a/src/transport/tcp_connection_legacy.c b/src/transport/tcp_connection_legacy.c new file mode 100644 index 000000000..f5253445d --- /dev/null +++ b/src/transport/tcp_connection_legacy.c @@ -0,0 +1,1647 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009-2013 GNUnet e.V. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file util/connection.c + * @brief TCP connection management + * @author Christian Grothoff + * + * This code is rather complex. Only modify it if you + * 1) Have a NEW testcase showing that the new code + * is needed and correct + * 2) All EXISTING testcases pass with the new code + * These rules should apply in general, but for this + * module they are VERY, VERY important. + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_resolver_service.h" + + + +#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-connection", syscall) + + +/** + * Transmission handle. There can only be one for each connection. + */ +struct GNUNET_CONNECTION_TransmitHandle +{ + + /** + * Function to call if the send buffer has notify_size + * bytes available. + */ + GNUNET_CONNECTION_TransmitReadyNotify notify_ready; + + /** + * Closure for notify_ready. + */ + void *notify_ready_cls; + + /** + * Our connection handle. + */ + struct GNUNET_CONNECTION_Handle *connection; + + /** + * Timeout for receiving (in absolute time). + */ + struct GNUNET_TIME_Absolute transmit_timeout; + + /** + * Task called on timeout. + */ + struct GNUNET_SCHEDULER_Task * timeout_task; + + /** + * At what number of bytes available in the + * write buffer should the notify method be called? + */ + size_t notify_size; + +}; + + +/** + * During connect, we try multiple possible IP addresses + * to find out which one might work. + */ +struct AddressProbe +{ + + /** + * This is a linked list. + */ + struct AddressProbe *next; + + /** + * This is a doubly-linked list. + */ + struct AddressProbe *prev; + + /** + * The address; do not free (allocated at the end of this struct). + */ + const struct sockaddr *addr; + + /** + * Underlying OS's socket. + */ + struct GNUNET_NETWORK_Handle *sock; + + /** + * Connection for which we are probing. + */ + struct GNUNET_CONNECTION_Handle *connection; + + /** + * Lenth of addr. + */ + socklen_t addrlen; + + /** + * Task waiting for the connection to finish connecting. + */ + struct GNUNET_SCHEDULER_Task * task; +}; + + +/** + * @brief handle for a network connection + */ +struct GNUNET_CONNECTION_Handle +{ + + /** + * Configuration to use. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Linked list of sockets we are currently trying out + * (during connect). + */ + struct AddressProbe *ap_head; + + /** + * Linked list of sockets we are currently trying out + * (during connect). + */ + struct AddressProbe *ap_tail; + + /** + * Network address of the other end-point, may be NULL. + */ + struct sockaddr *addr; + + /** + * Pointer to the hostname if connection was + * created using DNS lookup, otherwise NULL. + */ + char *hostname; + + /** + * Underlying OS's socket, set to NULL after fatal errors. + */ + struct GNUNET_NETWORK_Handle *sock; + + /** + * Function to call on data received, NULL if no receive is pending. + */ + GNUNET_CONNECTION_Receiver receiver; + + /** + * Closure for @e receiver. + */ + void *receiver_cls; + + /** + * Pointer to our write buffer. + */ + char *write_buffer; + + /** + * Current size of our @e write_buffer. + */ + size_t write_buffer_size; + + /** + * Current write-offset in @e write_buffer (where + * would we write next). + */ + size_t write_buffer_off; + + /** + * Current read-offset in @e write_buffer (how many + * bytes have already been sent). + */ + size_t write_buffer_pos; + + /** + * Length of @e addr. + */ + socklen_t addrlen; + + /** + * Read task that we may need to wait for. + */ + struct GNUNET_SCHEDULER_Task *read_task; + + /** + * Write task that we may need to wait for. + */ + struct GNUNET_SCHEDULER_Task *write_task; + + /** + * Handle to a pending DNS lookup request. + */ + struct GNUNET_RESOLVER_RequestHandle *dns_active; + + /** + * The handle we return for #GNUNET_CONNECTION_notify_transmit_ready(). + */ + struct GNUNET_CONNECTION_TransmitHandle nth; + + /** + * Timeout for receiving (in absolute time). + */ + struct GNUNET_TIME_Absolute receive_timeout; + + /** + * Maximum number of bytes to read (for receiving). + */ + size_t max; + + /** + * Port to connect to. + */ + uint16_t port; + + /** + * When shutdown, do not ever actually close the socket, but + * free resources. Only should ever be set if using program + * termination as a signal (because only then will the leaked + * socket be freed!) + */ + int8_t persist; + + /** + * Usually 0. Set to 1 if this handle is in use, and should + * #GNUNET_CONNECTION_destroy() be called right now, the action needs + * to be deferred by setting it to -1. + */ + int8_t destroy_later; + + /** + * Handle to subsequent connection after proxy handshake completes, + */ + struct GNUNET_CONNECTION_Handle *proxy_handshake; + +}; + + +/** + * Set the persist option on this connection handle. Indicates + * that the underlying socket or fd should never really be closed. + * Used for indicating process death. + * + * @param connection the connection to set persistent + */ +void +GNUNET_CONNECTION_persist_ (struct GNUNET_CONNECTION_Handle *connection) +{ + connection->persist = GNUNET_YES; +} + + +/** + * Disable the "CORK" feature for communication with the given connection, + * forcing the OS to immediately flush the buffer on transmission + * instead of potentially buffering multiple messages. Essentially + * reduces the OS send buffers to zero. + * Used to make sure that the last messages sent through the connection + * reach the other side before the process is terminated. + * + * @param connection the connection to make flushing and blocking + * @return #GNUNET_OK on success + */ +int +GNUNET_CONNECTION_disable_corking (struct GNUNET_CONNECTION_Handle *connection) +{ + return GNUNET_NETWORK_socket_disable_corking (connection->sock); +} + + +/** + * Create a connection handle by boxing an existing OS socket. The OS + * socket should henceforth be no longer used directly. + * #GNUNET_connection_destroy() will close it. + * + * @param osSocket existing socket to box + * @return the boxed connection handle + */ +struct GNUNET_CONNECTION_Handle * +GNUNET_CONNECTION_create_from_existing (struct GNUNET_NETWORK_Handle *osSocket) +{ + struct GNUNET_CONNECTION_Handle *connection; + + connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); + connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; + connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); + connection->sock = osSocket; + return connection; +} + + +/** + * Create a connection handle by accepting on a listen socket. This + * function may block if the listen socket has no connection ready. + * + * @param access_cb function to use to check if access is allowed + * @param access_cb_cls closure for @a access_cb + * @param lsock listen socket + * @return the connection handle, NULL on error + */ +struct GNUNET_CONNECTION_Handle * +GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access_cb, + void *access_cb_cls, + struct GNUNET_NETWORK_Handle *lsock) +{ + struct GNUNET_CONNECTION_Handle *connection; + char addr[128]; + socklen_t addrlen; + struct GNUNET_NETWORK_Handle *sock; + int aret; + struct sockaddr_in *v4; + struct sockaddr_in6 *v6; + struct sockaddr *sa; + void *uaddr; +#ifdef SO_PEERCRED + struct ucred uc; + socklen_t olen; +#endif + struct GNUNET_CONNECTION_Credentials *gcp; +#if HAVE_GETPEEREID || defined(SO_PEERCRED) || HAVE_GETPEERUCRED + struct GNUNET_CONNECTION_Credentials gc; + + gc.uid = 0; + gc.gid = 0; +#endif + + addrlen = sizeof (addr); + sock = + GNUNET_NETWORK_socket_accept (lsock, + (struct sockaddr *) &addr, + &addrlen); + if (NULL == sock) + { + if (EAGAIN != errno) + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "accept"); + return NULL; + } + if ((addrlen > sizeof (addr)) || (addrlen < sizeof (sa_family_t))) + { + GNUNET_break (0); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); + return NULL; + } + + sa = (struct sockaddr *) addr; + v6 = (struct sockaddr_in6 *) addr; + if ( (AF_INET6 == sa->sa_family) && + (IN6_IS_ADDR_V4MAPPED (&v6->sin6_addr)) ) + { + /* convert to V4 address */ + v4 = GNUNET_new (struct sockaddr_in); + memset (v4, 0, sizeof (struct sockaddr_in)); + v4->sin_family = AF_INET; +#if HAVE_SOCKADDR_IN_SIN_LEN + v4->sin_len = (u_char) sizeof (struct sockaddr_in); +#endif + GNUNET_memcpy (&v4->sin_addr, + &((char *) &v6->sin6_addr)[sizeof (struct in6_addr) - + sizeof (struct in_addr)], + sizeof (struct in_addr)); + v4->sin_port = v6->sin6_port; + uaddr = v4; + addrlen = sizeof (struct sockaddr_in); + } + else + { + uaddr = GNUNET_malloc (addrlen); + GNUNET_memcpy (uaddr, addr, addrlen); + } + gcp = NULL; + if (AF_UNIX == sa->sa_family) + { +#if HAVE_GETPEEREID + /* most BSDs */ + if (0 == getpeereid (GNUNET_NETWORK_get_fd (sock), + &gc.uid, + &gc.gid)) + gcp = &gc; +#else +#ifdef SO_PEERCRED + /* largely traditional GNU/Linux */ + olen = sizeof (uc); + if ( (0 == + getsockopt (GNUNET_NETWORK_get_fd (sock), + SOL_SOCKET, + SO_PEERCRED, + &uc, + &olen)) && + (olen == sizeof (uc)) ) + { + gc.uid = uc.uid; + gc.gid = uc.gid; + gcp = &gc; + } +#else +#if HAVE_GETPEERUCRED + /* this is for Solaris 10 */ + ucred_t *uc; + + uc = NULL; + if (0 == getpeerucred (GNUNET_NETWORK_get_fd (sock), &uc)) + { + gc.uid = ucred_geteuid (uc); + gc.gid = ucred_getegid (uc); + gcp = &gc; + } + ucred_free (uc); +#endif +#endif +#endif + } + + if ( (NULL != access_cb) && + (GNUNET_YES != (aret = access_cb (access_cb_cls, + gcp, + uaddr, + addrlen))) ) + { + if (GNUNET_NO == aret) + LOG (GNUNET_ERROR_TYPE_INFO, + _("Access denied to `%s'\n"), + GNUNET_a2s (uaddr, + addrlen)); + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_shutdown (sock, + SHUT_RDWR)); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); + GNUNET_free (uaddr); + return NULL; + } + connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); + connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; + connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); + connection->addr = uaddr; + connection->addrlen = addrlen; + connection->sock = sock; + LOG (GNUNET_ERROR_TYPE_INFO, + _("Accepting connection from `%s': %p\n"), + GNUNET_a2s (uaddr, + addrlen), + connection); + return connection; +} + + +/** + * Obtain the network address of the other party. + * + * @param connection the client to get the address for + * @param addr where to store the address + * @param addrlen where to store the length of the @a addr + * @return #GNUNET_OK on success + */ +int +GNUNET_CONNECTION_get_address (struct GNUNET_CONNECTION_Handle *connection, + void **addr, + size_t *addrlen) +{ + if ((NULL == connection->addr) || (0 == connection->addrlen)) + return GNUNET_NO; + *addr = GNUNET_malloc (connection->addrlen); + GNUNET_memcpy (*addr, connection->addr, connection->addrlen); + *addrlen = connection->addrlen; + return GNUNET_OK; +} + + +/** + * Tell the receiver callback that we had an IO error. + * + * @param connection connection to signal error + * @param errcode error code to send + */ +static void +signal_receive_error (struct GNUNET_CONNECTION_Handle *connection, + int errcode) +{ + GNUNET_CONNECTION_Receiver receiver; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Receive encounters error (%s), connection closed (%p)\n", + STRERROR (errcode), + connection); + GNUNET_assert (NULL != (receiver = connection->receiver)); + connection->receiver = NULL; + receiver (connection->receiver_cls, + NULL, + 0, + connection->addr, + connection->addrlen, + errcode); +} + + +/** + * Tell the receiver callback that a timeout was reached. + * + * @param connection connection to signal for + */ +static void +signal_receive_timeout (struct GNUNET_CONNECTION_Handle *connection) +{ + GNUNET_CONNECTION_Receiver receiver; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Connection signals timeout to receiver (%p)!\n", + connection); + GNUNET_assert (NULL != (receiver = connection->receiver)); + connection->receiver = NULL; + receiver (connection->receiver_cls, NULL, 0, NULL, 0, 0); +} + + +/** + * We failed to transmit data to the service, signal the error. + * + * @param connection handle that had trouble + * @param ecode error code (errno) + */ +static void +signal_transmit_error (struct GNUNET_CONNECTION_Handle *connection, + int ecode) +{ + GNUNET_CONNECTION_TransmitReadyNotify notify; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmission encounterd error (%s), connection closed (%p)\n", + STRERROR (ecode), + connection); + if (NULL != connection->sock) + { + (void) GNUNET_NETWORK_socket_shutdown (connection->sock, + SHUT_RDWR); + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (connection->sock)); + connection->sock = NULL; + GNUNET_assert (NULL == connection->write_task); + } + if (NULL != connection->read_task) + { + /* send errors trigger read errors... */ + GNUNET_SCHEDULER_cancel (connection->read_task); + connection->read_task = NULL; + signal_receive_timeout (connection); + return; + } + if (NULL == connection->nth.notify_ready) + return; /* nobody to tell about it */ + notify = connection->nth.notify_ready; + connection->nth.notify_ready = NULL; + notify (connection->nth.notify_ready_cls, + 0, + NULL); +} + + +/** + * We've failed for good to establish a connection (timeout or + * no more addresses to try). + * + * @param connection the connection we tried to establish + */ +static void +connect_fail_continuation (struct GNUNET_CONNECTION_Handle *connection) +{ + LOG (GNUNET_ERROR_TYPE_INFO, + "Failed to establish TCP connection to `%s:%u', no further addresses to try.\n", + connection->hostname, + connection->port); + GNUNET_break (NULL == connection->ap_head); + GNUNET_break (NULL == connection->ap_tail); + GNUNET_break (GNUNET_NO == connection->dns_active); + GNUNET_break (NULL == connection->sock); + GNUNET_assert (NULL == connection->write_task); + GNUNET_assert (NULL == connection->proxy_handshake); + + /* signal errors for jobs that used to wait on the connection */ + connection->destroy_later = 1; + if (NULL != connection->receiver) + signal_receive_error (connection, + ECONNREFUSED); + if (NULL != connection->nth.notify_ready) + { + GNUNET_assert (NULL != connection->nth.timeout_task); + GNUNET_SCHEDULER_cancel (connection->nth.timeout_task); + connection->nth.timeout_task = NULL; + signal_transmit_error (connection, + ECONNREFUSED); + } + if (-1 == connection->destroy_later) + { + /* do it now */ + connection->destroy_later = 0; + GNUNET_CONNECTION_destroy (connection); + return; + } + connection->destroy_later = 0; +} + + +/** + * We are ready to transmit (or got a timeout). + * + * @param cls our connection handle + */ +static void +transmit_ready (void *cls); + + +/** + * This function is called once we either timeout or have data ready + * to read. + * + * @param cls connection to read from + */ +static void +receive_ready (void *cls); + + +/** + * We've succeeded in establishing a connection. + * + * @param connection the connection we tried to establish + */ +static void +connect_success_continuation (struct GNUNET_CONNECTION_Handle *connection) +{ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Connection to `%s' succeeded! (%p)\n", + GNUNET_a2s (connection->addr, + connection->addrlen), + connection); + /* trigger jobs that waited for the connection */ + if (NULL != connection->receiver) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Connection succeeded, starting with receiving data (%p)\n", + connection); + GNUNET_assert (NULL == connection->read_task); + connection->read_task = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining + (connection->receive_timeout), + connection->sock, + &receive_ready, connection); + } + if (NULL != connection->nth.notify_ready) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Connection succeeded, starting with sending data (%p)\n", + connection); + GNUNET_assert (connection->nth.timeout_task != NULL); + GNUNET_SCHEDULER_cancel (connection->nth.timeout_task); + connection->nth.timeout_task = NULL; + GNUNET_assert (connection->write_task == NULL); + connection->write_task = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining + (connection->nth.transmit_timeout), connection->sock, + &transmit_ready, connection); + } +} + + +/** + * Scheduler let us know that we're either ready to write on the + * socket OR connect timed out. Do the right thing. + * + * @param cls the `struct AddressProbe *` with the address that we are probing + */ +static void +connect_probe_continuation (void *cls) +{ + struct AddressProbe *ap = cls; + struct GNUNET_CONNECTION_Handle *connection = ap->connection; + const struct GNUNET_SCHEDULER_TaskContext *tc; + struct AddressProbe *pos; + int error; + socklen_t len; + + GNUNET_assert (NULL != ap->sock); + GNUNET_CONTAINER_DLL_remove (connection->ap_head, + connection->ap_tail, + ap); + len = sizeof (error); + errno = 0; + error = 0; + tc = GNUNET_SCHEDULER_get_task_context (); + if ( (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) || + (GNUNET_OK != + GNUNET_NETWORK_socket_getsockopt (ap->sock, + SOL_SOCKET, + SO_ERROR, + &error, + &len)) || + (0 != error) ) + { + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (ap->sock)); + GNUNET_free (ap); + if ( (NULL == connection->ap_head) && + (GNUNET_NO == connection->dns_active) && + (NULL == connection->proxy_handshake) ) + connect_fail_continuation (connection); + return; + } + GNUNET_assert (NULL == connection->sock); + connection->sock = ap->sock; + GNUNET_assert (NULL == connection->addr); + connection->addr = GNUNET_malloc (ap->addrlen); + GNUNET_memcpy (connection->addr, ap->addr, ap->addrlen); + connection->addrlen = ap->addrlen; + GNUNET_free (ap); + /* cancel all other attempts */ + while (NULL != (pos = connection->ap_head)) + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock)); + GNUNET_SCHEDULER_cancel (pos->task); + GNUNET_CONTAINER_DLL_remove (connection->ap_head, + connection->ap_tail, + pos); + GNUNET_free (pos); + } + connect_success_continuation (connection); +} + + +/** + * Try to establish a connection given the specified address. + * This function is called by the resolver once we have a DNS reply. + * + * @param cls our `struct GNUNET_CONNECTION_Handle *` + * @param addr address to try, NULL for "last call" + * @param addrlen length of @a addr + */ +static void +try_connect_using_address (void *cls, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct GNUNET_CONNECTION_Handle *connection = cls; + struct AddressProbe *ap; + struct GNUNET_TIME_Relative delay; + + if (NULL == addr) + { + connection->dns_active = NULL; + if ((NULL == connection->ap_head) && + (NULL == connection->sock) && + (NULL == connection->proxy_handshake)) + connect_fail_continuation (connection); + return; + } + if (NULL != connection->sock) + return; /* already connected */ + GNUNET_assert (NULL == connection->addr); + /* try to connect */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Trying to connect using address `%s:%u/%s:%u'\n", + connection->hostname, + connection->port, + GNUNET_a2s (addr, addrlen), + connection->port); + ap = GNUNET_malloc (sizeof (struct AddressProbe) + addrlen); + ap->addr = (const struct sockaddr *) &ap[1]; + GNUNET_memcpy (&ap[1], addr, addrlen); + ap->addrlen = addrlen; + ap->connection = connection; + + switch (ap->addr->sa_family) + { + case AF_INET: + ((struct sockaddr_in *) ap->addr)->sin_port = htons (connection->port); + break; + case AF_INET6: + ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (connection->port); + break; + default: + GNUNET_break (0); + GNUNET_free (ap); + return; /* not supported by us */ + } + ap->sock = GNUNET_NETWORK_socket_create (ap->addr->sa_family, + SOCK_STREAM, 0); + if (NULL == ap->sock) + { + GNUNET_free (ap); + return; /* not supported by OS */ + } + LOG (GNUNET_ERROR_TYPE_INFO, + "Trying to connect to `%s' (%p)\n", + GNUNET_a2s (ap->addr, ap->addrlen), + connection); + if ((GNUNET_OK != + GNUNET_NETWORK_socket_connect (ap->sock, + ap->addr, + ap->addrlen)) && + (EINPROGRESS != errno)) + { + /* maybe refused / unsupported address, try next */ + LOG_STRERROR (GNUNET_ERROR_TYPE_INFO, "connect"); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ap->sock)); + GNUNET_free (ap); + return; + } + GNUNET_CONTAINER_DLL_insert (connection->ap_head, connection->ap_tail, ap); + delay = GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT; + if (NULL != connection->nth.notify_ready) + delay = GNUNET_TIME_relative_min (delay, + GNUNET_TIME_absolute_get_remaining (connection->nth.transmit_timeout)); + if (NULL != connection->receiver) + delay = GNUNET_TIME_relative_min (delay, + GNUNET_TIME_absolute_get_remaining (connection->receive_timeout)); + ap->task = GNUNET_SCHEDULER_add_write_net (delay, + ap->sock, + &connect_probe_continuation, + ap); +} + + +/** + * Create a connection handle by (asynchronously) connecting to a host. + * This function returns immediately, even if the connection has not + * yet been established. This function only creates TCP connections. + * + * @param cfg configuration to use + * @param hostname name of the host to connect to + * @param port port to connect to + * @return the connection handle + */ +struct GNUNET_CONNECTION_Handle * +GNUNET_CONNECTION_create_from_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *hostname, + uint16_t port) +{ + struct GNUNET_CONNECTION_Handle *connection; + + GNUNET_assert (0 < strlen (hostname)); /* sanity check */ + connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); + connection->cfg = cfg; + connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; + connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); + connection->port = port; + connection->hostname = GNUNET_strdup (hostname); + connection->dns_active = + GNUNET_RESOLVER_ip_get (connection->hostname, + AF_UNSPEC, + GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT, + &try_connect_using_address, + connection); + return connection; +} + + +/** + * Create a connection handle by connecting to a UNIX domain service. + * This function returns immediately, even if the connection has not + * yet been established. This function only creates UNIX connections. + * + * @param cfg configuration to use + * @param unixpath path to connect to + * @return the connection handle, NULL on systems without UNIX support + */ +struct GNUNET_CONNECTION_Handle * +GNUNET_CONNECTION_create_from_connect_to_unixpath (const struct GNUNET_CONFIGURATION_Handle *cfg, + const char *unixpath) +{ +#ifdef AF_UNIX + struct GNUNET_CONNECTION_Handle *connection; + struct sockaddr_un *un; + + GNUNET_assert (0 < strlen (unixpath)); /* sanity check */ + un = GNUNET_new (struct sockaddr_un); + un->sun_family = AF_UNIX; + strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1); +#ifdef LINUX + { + int abstract; + + abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "TESTING", + "USE_ABSTRACT_SOCKETS"); + if (GNUNET_YES == abstract) + un->sun_path[0] = '\0'; + } +#endif +#if HAVE_SOCKADDR_UN_SUN_LEN + un->sun_len = (u_char) sizeof (struct sockaddr_un); +#endif + connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); + connection->cfg = cfg; + connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; + connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); + connection->port = 0; + connection->hostname = NULL; + connection->addr = (struct sockaddr *) un; + connection->addrlen = sizeof (struct sockaddr_un); + connection->sock = GNUNET_NETWORK_socket_create (AF_UNIX, + SOCK_STREAM, + 0); + if (NULL == connection->sock) + { + GNUNET_free (connection->addr); + GNUNET_free (connection->write_buffer); + GNUNET_free (connection); + return NULL; + } + if ( (GNUNET_OK != + GNUNET_NETWORK_socket_connect (connection->sock, + connection->addr, + connection->addrlen)) && + (EINPROGRESS != errno) ) + { + /* Just return; we expect everything to work eventually so don't fail HARD */ + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (connection->sock)); + connection->sock = NULL; + return connection; + } + connect_success_continuation (connection); + return connection; +#else + return NULL; +#endif +} + + +/** + * Create a connection handle by (asynchronously) connecting to a host. + * This function returns immediately, even if the connection has not + * yet been established. This function only creates TCP connections. + * + * @param s socket to connect + * @param serv_addr server address + * @param addrlen length of @a serv_addr + * @return the connection handle + */ +struct GNUNET_CONNECTION_Handle * +GNUNET_CONNECTION_connect_socket (struct GNUNET_NETWORK_Handle *s, + const struct sockaddr *serv_addr, + socklen_t addrlen) +{ + struct GNUNET_CONNECTION_Handle *connection; + + if ( (GNUNET_OK != + GNUNET_NETWORK_socket_connect (s, serv_addr, addrlen)) && + (EINPROGRESS != errno) ) + { + /* maybe refused / unsupported address, try next */ + LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG, + "connect"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Attempt to connect to `%s' failed\n", + GNUNET_a2s (serv_addr, + addrlen)); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (s)); + return NULL; + } + connection = GNUNET_CONNECTION_create_from_existing (s); + connection->addr = GNUNET_malloc (addrlen); + GNUNET_memcpy (connection->addr, serv_addr, addrlen); + connection->addrlen = addrlen; + LOG (GNUNET_ERROR_TYPE_INFO, + "Trying to connect to `%s' (%p)\n", + GNUNET_a2s (serv_addr, addrlen), + connection); + return connection; +} + + +/** + * Create a connection handle by creating a socket and + * (asynchronously) connecting to a host. This function returns + * immediately, even if the connection has not yet been established. + * This function only creates TCP connections. + * + * @param af_family address family to use + * @param serv_addr server address + * @param addrlen length of @a serv_addr + * @return the connection handle + */ +struct GNUNET_CONNECTION_Handle * +GNUNET_CONNECTION_create_from_sockaddr (int af_family, + const struct sockaddr *serv_addr, + socklen_t addrlen) +{ + struct GNUNET_NETWORK_Handle *s; + + s = GNUNET_NETWORK_socket_create (af_family, SOCK_STREAM, 0); + if (NULL == s) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, + "socket"); + return NULL; + } + return GNUNET_CONNECTION_connect_socket (s, + serv_addr, + addrlen); +} + + +/** + * Check if connection is valid (no fatal errors have happened so far). + * Note that a connection that is still trying to connect is considered + * valid. + * + * @param connection connection to check + * @return #GNUNET_YES if valid, #GNUNET_NO otherwise + */ +int +GNUNET_CONNECTION_check (struct GNUNET_CONNECTION_Handle *connection) +{ + if ((NULL != connection->ap_head) || + (NULL != connection->dns_active) || + (NULL != connection->proxy_handshake)) + return GNUNET_YES; /* still trying to connect */ + if ( (0 != connection->destroy_later) || + (NULL == connection->sock) ) + return GNUNET_NO; + return GNUNET_YES; +} + + +/** + * Close the connection and free associated resources. There must + * not be any pending requests for reading or writing to the + * connection at this time. + * + * @param connection connection to destroy + */ +void +GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *connection) +{ + struct AddressProbe *pos; + + if (0 != connection->destroy_later) + { + connection->destroy_later = -1; + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Shutting down connection (%p)\n", + connection); + GNUNET_assert (NULL == connection->nth.notify_ready); + GNUNET_assert (NULL == connection->receiver); + if (NULL != connection->write_task) + { + GNUNET_SCHEDULER_cancel (connection->write_task); + connection->write_task = NULL; + connection->write_buffer_off = 0; + } + if (NULL != connection->read_task) + { + GNUNET_SCHEDULER_cancel (connection->read_task); + connection->read_task = NULL; + } + if (NULL != connection->nth.timeout_task) + { + GNUNET_SCHEDULER_cancel (connection->nth.timeout_task); + connection->nth.timeout_task = NULL; + } + connection->nth.notify_ready = NULL; + if (NULL != connection->dns_active) + { + GNUNET_RESOLVER_request_cancel (connection->dns_active); + connection->dns_active = NULL; + } + if (NULL != connection->proxy_handshake) + { + /* GNUNET_CONNECTION_destroy (connection->proxy_handshake); */ + connection->proxy_handshake->destroy_later = -1; + connection->proxy_handshake = NULL; /* Not leaked ??? */ + } + while (NULL != (pos = connection->ap_head)) + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock)); + GNUNET_SCHEDULER_cancel (pos->task); + GNUNET_CONTAINER_DLL_remove (connection->ap_head, + connection->ap_tail, + pos); + GNUNET_free (pos); + } + if ( (NULL != connection->sock) && + (GNUNET_YES != connection->persist) ) + { + if ((GNUNET_OK != + GNUNET_NETWORK_socket_shutdown (connection->sock, + SHUT_RDWR)) && + (ENOTCONN != errno) && + (ECONNRESET != errno) ) + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, + "shutdown"); + } + if (NULL != connection->sock) + { + if (GNUNET_YES != connection->persist) + { + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (connection->sock)); + } + else + { + GNUNET_NETWORK_socket_free_memory_only_ (connection->sock); /* at least no memory leak (we deliberately + * leak the socket in this special case) ... */ + } + } + GNUNET_free_non_null (connection->addr); + GNUNET_free_non_null (connection->hostname); + GNUNET_free (connection->write_buffer); + GNUNET_free (connection); +} + + +/** + * This function is called once we either timeout + * or have data ready to read. + * + * @param cls connection to read from + */ +static void +receive_ready (void *cls) +{ + struct GNUNET_CONNECTION_Handle *connection = cls; + const struct GNUNET_SCHEDULER_TaskContext *tc; + char buffer[connection->max]; + ssize_t ret; + GNUNET_CONNECTION_Receiver receiver; + + connection->read_task = NULL; + tc = GNUNET_SCHEDULER_get_task_context (); + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Receive from `%s' encounters error: timeout (%s, %p)\n", + GNUNET_a2s (connection->addr, + connection->addrlen), + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (connection->receive_timeout), + GNUNET_YES), + connection); + signal_receive_timeout (connection); + return; + } + if (NULL == connection->sock) + { + /* connect failed for good */ + signal_receive_error (connection, ECONNREFUSED); + return; + } + GNUNET_assert (GNUNET_NETWORK_fdset_isset (tc->read_ready, + connection->sock)); +RETRY: + ret = GNUNET_NETWORK_socket_recv (connection->sock, + buffer, + connection->max); + if (-1 == ret) + { + if (EINTR == errno) + goto RETRY; + signal_receive_error (connection, errno); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "receive_ready read %u/%u bytes from `%s' (%p)!\n", + (unsigned int) ret, + connection->max, + GNUNET_a2s (connection->addr, + connection->addrlen), + connection); + GNUNET_assert (NULL != (receiver = connection->receiver)); + connection->receiver = NULL; + receiver (connection->receiver_cls, + buffer, + ret, + connection->addr, + connection->addrlen, + 0); +} + + +/** + * Receive data from the given connection. Note that this function + * will call @a receiver asynchronously using the scheduler. It will + * "immediately" return. Note that there MUST only be one active + * receive call per connection at any given point in time (so do not + * call receive again until the receiver callback has been invoked). + * + * @param connection connection handle + * @param max maximum number of bytes to read + * @param timeout maximum amount of time to wait + * @param receiver function to call with received data + * @param receiver_cls closure for @a receiver + */ +void +GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *connection, + size_t max, + struct GNUNET_TIME_Relative timeout, + GNUNET_CONNECTION_Receiver receiver, + void *receiver_cls) +{ + GNUNET_assert ((NULL == connection->read_task) && + (NULL == connection->receiver)); + GNUNET_assert (NULL != receiver); + connection->receiver = receiver; + connection->receiver_cls = receiver_cls; + connection->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout); + connection->max = max; + if (NULL != connection->sock) + { + connection->read_task = + GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining + (connection->receive_timeout), + connection->sock, + &receive_ready, + connection); + return; + } + if ((NULL == connection->dns_active) && + (NULL == connection->ap_head) && + (NULL == connection->proxy_handshake)) + { + connection->receiver = NULL; + receiver (receiver_cls, + NULL, 0, + NULL, 0, + ETIMEDOUT); + return; + } +} + + +/** + * Cancel receive job on the given connection. Note that the + * receiver callback must not have been called yet in order + * for the cancellation to be valid. + * + * @param connection connection handle + * @return closure of the original receiver callback closure + */ +void * +GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *connection) +{ + if (NULL != connection->read_task) + { + GNUNET_assert (connection == + GNUNET_SCHEDULER_cancel (connection->read_task)); + connection->read_task = NULL; + } + connection->receiver = NULL; + return connection->receiver_cls; +} + + +/** + * Try to call the transmit notify method (check if we do + * have enough space available first)! + * + * @param connection connection for which we should do this processing + * @return #GNUNET_YES if we were able to call notify + */ +static int +process_notify (struct GNUNET_CONNECTION_Handle *connection) +{ + size_t used; + size_t avail; + size_t size; + GNUNET_CONNECTION_TransmitReadyNotify notify; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "process_notify is running\n"); + GNUNET_assert (NULL == connection->write_task); + if (NULL == (notify = connection->nth.notify_ready)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "No one to notify\n"); + return GNUNET_NO; + } + used = connection->write_buffer_off - connection->write_buffer_pos; + avail = connection->write_buffer_size - used; + size = connection->nth.notify_size; + if (size > avail) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Not enough buffer\n"); + return GNUNET_NO; + } + connection->nth.notify_ready = NULL; + if (connection->write_buffer_size - connection->write_buffer_off < size) + { + /* need to compact */ + memmove (connection->write_buffer, + &connection->write_buffer[connection->write_buffer_pos], + used); + connection->write_buffer_off -= connection->write_buffer_pos; + connection->write_buffer_pos = 0; + } + avail = connection->write_buffer_size - connection->write_buffer_off; + GNUNET_assert (avail >= size); + size = + notify (connection->nth.notify_ready_cls, avail, + &connection->write_buffer[connection->write_buffer_off]); + GNUNET_assert (size <= avail); + if (0 != size) + connection->write_buffer_off += size; + return GNUNET_YES; +} + + +/** + * Task invoked by the scheduler when a call to transmit + * is timing out (we never got enough buffer space to call + * the callback function before the specified timeout + * expired). + * + * This task notifies the client about the timeout. + * + * @param cls the `struct GNUNET_CONNECTION_Handle` + */ +static void +transmit_timeout (void *cls) +{ + struct GNUNET_CONNECTION_Handle *connection = cls; + GNUNET_CONNECTION_TransmitReadyNotify notify; + + connection->nth.timeout_task = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmit to `%s:%u/%s' fails, time out reached (%p).\n", + connection->hostname, + connection->port, + GNUNET_a2s (connection->addr, + connection->addrlen), + connection); + notify = connection->nth.notify_ready; + GNUNET_assert (NULL != notify); + connection->nth.notify_ready = NULL; + notify (connection->nth.notify_ready_cls, + 0, + NULL); +} + + +/** + * Task invoked by the scheduler when we failed to connect + * at the time of being asked to transmit. + * + * This task notifies the client about the error. + * + * @param cls the `struct GNUNET_CONNECTION_Handle` + */ +static void +connect_error (void *cls) +{ + struct GNUNET_CONNECTION_Handle *connection = cls; + GNUNET_CONNECTION_TransmitReadyNotify notify; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmission request of size %u fails (%s/%u), connection failed (%p).\n", + connection->nth.notify_size, + connection->hostname, + connection->port, + connection); + connection->write_task = NULL; + notify = connection->nth.notify_ready; + connection->nth.notify_ready = NULL; + notify (connection->nth.notify_ready_cls, + 0, + NULL); +} + + +/** + * We are ready to transmit (or got a timeout). + * + * @param cls our connection handle + */ +static void +transmit_ready (void *cls) +{ + struct GNUNET_CONNECTION_Handle *connection = cls; + GNUNET_CONNECTION_TransmitReadyNotify notify; + const struct GNUNET_SCHEDULER_TaskContext *tc; + ssize_t ret; + size_t have; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "transmit_ready running (%p).\n", + connection); + GNUNET_assert (NULL != connection->write_task); + connection->write_task = NULL; + GNUNET_assert (NULL == connection->nth.timeout_task); + tc = GNUNET_SCHEDULER_get_task_context (); + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transmit to `%s' fails, time out reached (%p).\n", + GNUNET_a2s (connection->addr, + connection->addrlen), + connection); + notify = connection->nth.notify_ready; + GNUNET_assert (NULL != notify); + connection->nth.notify_ready = NULL; + notify (connection->nth.notify_ready_cls, 0, NULL); + return; + } + GNUNET_assert (NULL != connection->sock); + if (NULL == tc->write_ready) + { + /* special circumstances (in particular, PREREQ_DONE after + * connect): not yet ready to write, but no "fatal" error either. + * Hence retry. */ + goto SCHEDULE_WRITE; + } + if (! GNUNET_NETWORK_fdset_isset (tc->write_ready, + connection->sock)) + { + GNUNET_assert (NULL == connection->write_task); + /* special circumstances (in particular, shutdown): not yet ready + * to write, but no "fatal" error either. Hence retry. */ + goto SCHEDULE_WRITE; + } + GNUNET_assert (connection->write_buffer_off >= connection->write_buffer_pos); + if ((NULL != connection->nth.notify_ready) && + (connection->write_buffer_size < connection->nth.notify_size)) + { + connection->write_buffer = + GNUNET_realloc (connection->write_buffer, connection->nth.notify_size); + connection->write_buffer_size = connection->nth.notify_size; + } + process_notify (connection); + have = connection->write_buffer_off - connection->write_buffer_pos; + if (0 == have) + { + /* no data ready for writing, terminate write loop */ + return; + } + GNUNET_assert (have <= connection->write_buffer_size); + GNUNET_assert (have + connection->write_buffer_pos <= connection->write_buffer_size); + GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size); +RETRY: + ret = + GNUNET_NETWORK_socket_send (connection->sock, + &connection->write_buffer[connection->write_buffer_pos], + have); + if (-1 == ret) + { + if (EINTR == errno) + goto RETRY; + if (NULL != connection->write_task) + { + GNUNET_SCHEDULER_cancel (connection->write_task); + connection->write_task = NULL; + } + signal_transmit_error (connection, errno); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Connection transmitted %u/%u bytes to `%s' (%p)\n", + (unsigned int) ret, + have, + GNUNET_a2s (connection->addr, + connection->addrlen), + connection); + connection->write_buffer_pos += ret; + if (connection->write_buffer_pos == connection->write_buffer_off) + { + /* transmitted all pending data */ + connection->write_buffer_pos = 0; + connection->write_buffer_off = 0; + } + if ( (0 == connection->write_buffer_off) && + (NULL == connection->nth.notify_ready) ) + return; /* all data sent! */ + /* not done writing, schedule more */ +SCHEDULE_WRITE: + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Re-scheduling transmit_ready (more to do) (%p).\n", + connection); + have = connection->write_buffer_off - connection->write_buffer_pos; + GNUNET_assert ( (NULL != connection->nth.notify_ready) || + (have > 0) ); + if (NULL == connection->write_task) + connection->write_task = + GNUNET_SCHEDULER_add_write_net ((connection->nth.notify_ready == + NULL) ? GNUNET_TIME_UNIT_FOREVER_REL : + GNUNET_TIME_absolute_get_remaining + (connection->nth.transmit_timeout), + connection->sock, + &transmit_ready, connection); +} + + +/** + * Ask the connection to call us once the specified number of bytes + * are free in the transmission buffer. Will never call the @a notify + * callback in this task, but always first go into the scheduler. + * + * @param connection connection + * @param size number of bytes to send + * @param timeout after how long should we give up (and call + * @a notify with buf NULL and size 0)? + * @param notify function to call + * @param notify_cls closure for @a notify + * @return non-NULL if the notify callback was queued, + * NULL if we are already going to notify someone else (busy) + */ +struct GNUNET_CONNECTION_TransmitHandle * +GNUNET_CONNECTION_notify_transmit_ready (struct GNUNET_CONNECTION_Handle *connection, + size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_CONNECTION_TransmitReadyNotify notify, + void *notify_cls) +{ + if (NULL != connection->nth.notify_ready) + { + GNUNET_assert (0); + return NULL; + } + GNUNET_assert (NULL != notify); + GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE); + GNUNET_assert (connection->write_buffer_off <= connection->write_buffer_size); + GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size); + GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_off); + connection->nth.notify_ready = notify; + connection->nth.notify_ready_cls = notify_cls; + connection->nth.connection = connection; + connection->nth.notify_size = size; + connection->nth.transmit_timeout = GNUNET_TIME_relative_to_absolute (timeout); + GNUNET_assert (NULL == connection->nth.timeout_task); + if ((NULL == connection->sock) && + (NULL == connection->ap_head) && + (NULL == connection->dns_active) && + (NULL == connection->proxy_handshake)) + { + if (NULL != connection->write_task) + GNUNET_SCHEDULER_cancel (connection->write_task); + connection->write_task = GNUNET_SCHEDULER_add_now (&connect_error, + connection); + return &connection->nth; + } + if (NULL != connection->write_task) + return &connection->nth; /* previous transmission still in progress */ + if (NULL != connection->sock) + { + /* connected, try to transmit now */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling transmission (%p).\n", + connection); + connection->write_task = + GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining + (connection->nth.transmit_timeout), + connection->sock, + &transmit_ready, connection); + return &connection->nth; + } + /* not yet connected, wait for connection */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Need to wait to schedule transmission for connection, adding timeout task (%p).\n", + connection); + connection->nth.timeout_task = + GNUNET_SCHEDULER_add_delayed (timeout, + &transmit_timeout, + connection); + return &connection->nth; +} + + +/** + * Cancel the specified transmission-ready notification. + * + * @param th notification to cancel + */ +void +GNUNET_CONNECTION_notify_transmit_ready_cancel (struct GNUNET_CONNECTION_TransmitHandle *th) +{ + GNUNET_assert (NULL != th->notify_ready); + th->notify_ready = NULL; + if (NULL != th->timeout_task) + { + GNUNET_SCHEDULER_cancel (th->timeout_task); + th->timeout_task = NULL; + } + if (NULL != th->connection->write_task) + { + GNUNET_SCHEDULER_cancel (th->connection->write_task); + th->connection->write_task = NULL; + } +} + + +/** + * Create a connection to be proxied using a given connection. + * + * @param cph connection to proxy server + * @return connection to be proxied + */ +struct GNUNET_CONNECTION_Handle * +GNUNET_CONNECTION_create_proxied_from_handshake (struct GNUNET_CONNECTION_Handle *cph) +{ + struct GNUNET_CONNECTION_Handle *proxied = GNUNET_CONNECTION_create_from_existing (NULL); + + proxied->proxy_handshake = cph; + return proxied; +} + + +/** + * Activate proxied connection and destroy initial proxy handshake connection. + * There must not be any pending requests for reading or writing to the + * proxy hadshake connection at this time. + * + * @param proxied connection connection to proxy server + */ +void +GNUNET_CONNECTION_acivate_proxied (struct GNUNET_CONNECTION_Handle *proxied) +{ + struct GNUNET_CONNECTION_Handle *cph = proxied->proxy_handshake; + + GNUNET_assert (NULL != cph); + GNUNET_assert (NULL == proxied->sock); + GNUNET_assert (NULL != cph->sock); + proxied->sock = cph->sock; + cph->sock = NULL; + GNUNET_CONNECTION_destroy (cph); + connect_success_continuation (proxied); +} + + +/* end of connection.c */ diff --git a/src/transport/tcp_server_legacy.c b/src/transport/tcp_server_legacy.c new file mode 100644 index 000000000..c055285b1 --- /dev/null +++ b/src/transport/tcp_server_legacy.c @@ -0,0 +1,1748 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009-2013 GNUnet e.V. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file util/server.c + * @brief library for building GNUnet network servers + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" + +#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-server", syscall, filename) + + +/** + * List of arrays of message handlers. + */ +struct HandlerList +{ + /** + * This is a linked list. + */ + struct HandlerList *next; + + /** + * NULL-terminated array of handlers. + */ + const struct GNUNET_SERVER_MessageHandler *handlers; +}; + + +/** + * List of arrays of message handlers. + */ +struct NotifyList +{ + /** + * This is a doubly linked list. + */ + struct NotifyList *next; + + /** + * This is a doubly linked list. + */ + struct NotifyList *prev; + + /** + * Function to call. + */ + GNUNET_SERVER_DisconnectCallback callback; + + /** + * Closure for callback. + */ + void *callback_cls; +}; + + +/** + * @brief handle for a server + */ +struct GNUNET_SERVER_Handle +{ + /** + * List of handlers for incoming messages. + */ + struct HandlerList *handlers; + + /** + * Head of list of our current clients. + */ + struct GNUNET_SERVER_Client *clients_head; + + /** + * Head of list of our current clients. + */ + struct GNUNET_SERVER_Client *clients_tail; + + /** + * Head of linked list of functions to call on disconnects by clients. + */ + struct NotifyList *disconnect_notify_list_head; + + /** + * Tail of linked list of functions to call on disconnects by clients. + */ + struct NotifyList *disconnect_notify_list_tail; + + /** + * Head of linked list of functions to call on connects by clients. + */ + struct NotifyList *connect_notify_list_head; + + /** + * Tail of linked list of functions to call on connects by clients. + */ + struct NotifyList *connect_notify_list_tail; + + /** + * Function to call for access control. + */ + GNUNET_CONNECTION_AccessCheck access_cb; + + /** + * Closure for @e access_cb. + */ + void *access_cb_cls; + + /** + * NULL-terminated array of sockets used to listen for new + * connections. + */ + struct GNUNET_NETWORK_Handle **listen_sockets; + + /** + * After how long should an idle connection time + * out (on write). + */ + struct GNUNET_TIME_Relative idle_timeout; + + /** + * Task scheduled to do the listening. + */ + struct GNUNET_SCHEDULER_Task * listen_task; + + /** + * Alternative function to create a MST instance. + */ + GNUNET_SERVER_MstCreateCallback mst_create; + + /** + * Alternative function to destroy a MST instance. + */ + GNUNET_SERVER_MstDestroyCallback mst_destroy; + + /** + * Alternative function to give data to a MST instance. + */ + GNUNET_SERVER_MstReceiveCallback mst_receive; + + /** + * Closure for 'mst_'-callbacks. + */ + void *mst_cls; + + /** + * Do we ignore messages of types that we do not understand or do we + * require that a handler is found (and if not kill the connection)? + */ + int require_found; + + /** + * Set to #GNUNET_YES once we are in 'soft' shutdown where we wait for + * all non-monitor clients to disconnect before we call + * #GNUNET_SERVER_destroy. See test_monitor_clients(). Set to + * #GNUNET_SYSERR once the final destroy task has been scheduled + * (we cannot run it in the same task). + */ + int in_soft_shutdown; +}; + + +/** + * Handle server returns for aborting transmission to a client. + */ +struct GNUNET_SERVER_TransmitHandle +{ + /** + * Function to call to get the message. + */ + GNUNET_CONNECTION_TransmitReadyNotify callback; + + /** + * Closure for @e callback + */ + void *callback_cls; + + /** + * Active connection transmission handle. + */ + struct GNUNET_CONNECTION_TransmitHandle *cth; + +}; + + +/** + * @brief handle for a client of the server + */ +struct GNUNET_SERVER_Client +{ + + /** + * This is a doubly linked list. + */ + struct GNUNET_SERVER_Client *next; + + /** + * This is a doubly linked list. + */ + struct GNUNET_SERVER_Client *prev; + + /** + * Processing of incoming data. + */ + void *mst; + + /** + * Server that this client belongs to. + */ + struct GNUNET_SERVER_Handle *server; + + /** + * Client closure for callbacks. + */ + struct GNUNET_CONNECTION_Handle *connection; + + /** + * User context value, manipulated using + * 'GNUNET_SERVER_client_{get/set}_user_context' functions. + */ + void *user_context; + + /** + * ID of task used to restart processing. + */ + struct GNUNET_SCHEDULER_Task * restart_task; + + /** + * Task that warns about missing calls to #GNUNET_SERVER_receive_done. + */ + struct GNUNET_SCHEDULER_Task * warn_task; + + /** + * Time when the warn task was started. + */ + struct GNUNET_TIME_Absolute warn_start; + + /** + * Last activity on this socket (used to time it out + * if reference_count == 0). + */ + struct GNUNET_TIME_Absolute last_activity; + + /** + * Transmission handle we return for this client from + * #GNUNET_SERVER_notify_transmit_ready. + */ + struct GNUNET_SERVER_TransmitHandle th; + + /** + * After how long should an idle connection time + * out (on write). + */ + struct GNUNET_TIME_Relative idle_timeout; + + /** + * Number of external entities with a reference to + * this client object. + */ + unsigned int reference_count; + + /** + * Was processing if incoming messages suspended while + * we were still processing data already received? + * This is a counter saying how often processing was + * suspended (once per handler invoked). + */ + unsigned int suspended; + + /** + * Last size given when user context was initialized; used for + * sanity check. + */ + size_t user_context_size; + + /** + * Are we currently in the "process_client_buffer" function (and + * will hence restart the receive job on exit if suspended == 0 once + * we are done?). If this is set, then "receive_done" will + * essentially only decrement suspended; if this is not set, then + * "receive_done" may need to restart the receive process (either + * from the side-buffer or via select/recv). + */ + int in_process_client_buffer; + + /** + * We're about to close down this client. + */ + int shutdown_now; + + /** + * Are we currently trying to receive? (#GNUNET_YES if we are, + * #GNUNET_NO if we are not, #GNUNET_SYSERR if data is already + * available in MST). + */ + int receive_pending; + + /** + * Persist the file handle for this client no matter what happens, + * force the OS to close once the process actually dies. Should only + * be used in special cases! + */ + int persist; + + /** + * Is this client a 'monitor' client that should not be counted + * when deciding on destroying the server during soft shutdown? + * (see also #GNUNET_SERVICE_start) + */ + int is_monitor; + + /** + * Type of last message processed (for warn_no_receive_done). + */ + uint16_t warn_type; +}; + + + +/** + * Return user context associated with the given client. + * Note: you should probably use the macro (call without the underscore). + * + * @param client client to query + * @param size number of bytes in user context struct (for verification only) + * @return pointer to user context + */ +void * +GNUNET_SERVER_client_get_user_context_ (struct GNUNET_SERVER_Client *client, + size_t size) +{ + if ((0 == client->user_context_size) && + (NULL == client->user_context)) + return NULL; /* never set */ + GNUNET_assert (size == client->user_context_size); + return client->user_context; +} + + +/** + * Set user context to be associated with the given client. + * Note: you should probably use the macro (call without the underscore). + * + * @param client client to query + * @param ptr pointer to user context + * @param size number of bytes in user context struct (for verification only) + */ +void +GNUNET_SERVER_client_set_user_context_ (struct GNUNET_SERVER_Client *client, + void *ptr, + size_t size) +{ + if (NULL == ptr) + { + client->user_context_size = 0; + client->user_context = ptr; + return; + } + client->user_context_size = size; + client->user_context = ptr; +} + + +/** + * Scheduler says our listen socket is ready. Process it! + * + * @param cls handle to our server for which we are processing the listen + * socket + */ +static void +process_listen_socket (void *cls) +{ + struct GNUNET_SERVER_Handle *server = cls; + const struct GNUNET_SCHEDULER_TaskContext *tc; + struct GNUNET_CONNECTION_Handle *sock; + unsigned int i; + + server->listen_task = NULL; + tc = GNUNET_SCHEDULER_get_task_context (); + for (i = 0; NULL != server->listen_sockets[i]; i++) + { + if (GNUNET_NETWORK_fdset_isset (tc->read_ready, + server->listen_sockets[i])) + { + sock = + GNUNET_CONNECTION_create_from_accept (server->access_cb, + server->access_cb_cls, + server->listen_sockets[i]); + if (NULL != sock) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server accepted incoming connection.\n"); + (void) GNUNET_SERVER_connect_socket (server, + sock); + } + } + } + /* listen for more! */ + GNUNET_SERVER_resume (server); +} + + +/** + * Create and initialize a listen socket for the server. + * + * @param server_addr address to listen on + * @param socklen length of @a server_addr + * @return NULL on error, otherwise the listen socket + */ +static struct GNUNET_NETWORK_Handle * +open_listen_socket (const struct sockaddr *server_addr, + socklen_t socklen) +{ + struct GNUNET_NETWORK_Handle *sock; + uint16_t port; + int eno; + + switch (server_addr->sa_family) + { + case AF_INET: + port = ntohs (((const struct sockaddr_in *) server_addr)->sin_port); + break; + case AF_INET6: + port = ntohs (((const struct sockaddr_in6 *) server_addr)->sin6_port); + break; + case AF_UNIX: + port = 0; + break; + default: + GNUNET_break (0); + port = 0; + break; + } + sock = GNUNET_NETWORK_socket_create (server_addr->sa_family, SOCK_STREAM, 0); + if (NULL == sock) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); + errno = 0; + return NULL; + } + /* bind the socket */ + if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, server_addr, socklen)) + { + eno = errno; + if (EADDRINUSE != errno) + { + /* we don't log 'EADDRINUSE' here since an IPv4 bind may + * fail if we already took the port on IPv6; if both IPv4 and + * IPv6 binds fail, then our caller will log using the + * errno preserved in 'eno' */ + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, + "bind"); + if (0 != port) + LOG (GNUNET_ERROR_TYPE_ERROR, + _("`%s' failed for port %d (%s).\n"), + "bind", + port, + (AF_INET == server_addr->sa_family) ? "IPv4" : "IPv6"); + eno = 0; + } + else + { + if (0 != port) + LOG (GNUNET_ERROR_TYPE_WARNING, + _("`%s' failed for port %d (%s): address already in use\n"), + "bind", port, + (AF_INET == server_addr->sa_family) ? "IPv4" : "IPv6"); + else if (AF_UNIX == server_addr->sa_family) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("`%s' failed for `%s': address already in use\n"), + "bind", + GNUNET_a2s (server_addr, socklen)); + } + } + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); + errno = eno; + return NULL; + } + if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5)) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, + "listen"); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); + errno = 0; + return NULL; + } + if (0 != port) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server starts to listen on port %u.\n", + port); + return sock; +} + + +/** + * Create a new server. + * + * @param access_cb function for access control + * @param access_cb_cls closure for @a access_cb + * @param lsocks NULL-terminated array of listen sockets + * @param idle_timeout after how long should we timeout idle connections? + * @param require_found if #GNUNET_YES, connections sending messages of unknown type + * will be closed + * @return handle for the new server, NULL on error + * (typically, "port" already in use) + */ +struct GNUNET_SERVER_Handle * +GNUNET_SERVER_create_with_sockets (GNUNET_CONNECTION_AccessCheck access_cb, + void *access_cb_cls, + struct GNUNET_NETWORK_Handle **lsocks, + struct GNUNET_TIME_Relative idle_timeout, + int require_found) +{ + struct GNUNET_SERVER_Handle *server; + + server = GNUNET_new (struct GNUNET_SERVER_Handle); + server->idle_timeout = idle_timeout; + server->listen_sockets = lsocks; + server->access_cb = access_cb; + server->access_cb_cls = access_cb_cls; + server->require_found = require_found; + if (NULL != lsocks) + GNUNET_SERVER_resume (server); + return server; +} + + +/** + * Create a new server. + * + * @param access_cb function for access control + * @param access_cb_cls closure for @a access_cb + * @param server_addr address to listen on (including port), NULL terminated array + * @param socklen length of server_addr + * @param idle_timeout after how long should we timeout idle connections? + * @param require_found if YES, connections sending messages of unknown type + * will be closed + * @return handle for the new server, NULL on error + * (typically, "port" already in use) + */ +struct GNUNET_SERVER_Handle * +GNUNET_SERVER_create (GNUNET_CONNECTION_AccessCheck access_cb, + void *access_cb_cls, + struct sockaddr *const *server_addr, + const socklen_t * socklen, + struct GNUNET_TIME_Relative idle_timeout, + int require_found) +{ + struct GNUNET_NETWORK_Handle **lsocks; + unsigned int i; + unsigned int j; + unsigned int k; + int seen; + + i = 0; + while (NULL != server_addr[i]) + i++; + if (i > 0) + { + lsocks = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (i + 1)); + i = 0; + j = 0; + while (NULL != server_addr[i]) + { + seen = 0; + for (k=0;kis_monitor = GNUNET_YES; +} + + +/** + * Helper function for #test_monitor_clients() to trigger + * #GNUNET_SERVER_destroy() after the stack has unwound. + * + * @param cls the `struct GNUNET_SERVER_Handle *` to destroy + */ +static void +do_destroy (void *cls) +{ + struct GNUNET_SERVER_Handle *server = cls; + + GNUNET_SERVER_destroy (server); +} + + +/** + * Check if only 'monitor' clients are left. If so, destroy the + * server completely. + * + * @param server server to test for full shutdown + */ +static void +test_monitor_clients (struct GNUNET_SERVER_Handle *server) +{ + struct GNUNET_SERVER_Client *client; + + if (GNUNET_YES != server->in_soft_shutdown) + return; + for (client = server->clients_head; NULL != client; client = client->next) + if (GNUNET_NO == client->is_monitor) + return; /* not done yet */ + server->in_soft_shutdown = GNUNET_SYSERR; + (void) GNUNET_SCHEDULER_add_now (&do_destroy, server); +} + + +/** + * Suspend accepting connections from the listen socket temporarily. + * + * @param server server to stop accepting connections. + */ +void +GNUNET_SERVER_suspend (struct GNUNET_SERVER_Handle *server) +{ + if (NULL != server->listen_task) + { + GNUNET_SCHEDULER_cancel (server->listen_task); + server->listen_task = NULL; + } +} + + +/** + * Resume accepting connections from the listen socket. + * + * @param server server to stop accepting connections. + */ +void +GNUNET_SERVER_resume (struct GNUNET_SERVER_Handle *server) +{ + struct GNUNET_NETWORK_FDSet *r; + unsigned int i; + + if (NULL == server->listen_sockets) + return; + if (NULL == server->listen_sockets[0]) + return; /* nothing to do, no listen sockets! */ + if (NULL == server->listen_sockets[1]) + { + /* simplified method: no fd set needed; this is then much simpler + and much more efficient */ + server->listen_task = + GNUNET_SCHEDULER_add_read_net_with_priority (GNUNET_TIME_UNIT_FOREVER_REL, + GNUNET_SCHEDULER_PRIORITY_HIGH, + server->listen_sockets[0], + &process_listen_socket, server); + return; + } + r = GNUNET_NETWORK_fdset_create (); + i = 0; + while (NULL != server->listen_sockets[i]) + GNUNET_NETWORK_fdset_set (r, server->listen_sockets[i++]); + server->listen_task = + GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, + GNUNET_TIME_UNIT_FOREVER_REL, r, NULL, + &process_listen_socket, server); + GNUNET_NETWORK_fdset_destroy (r); +} + + +/** + * Stop the listen socket and get ready to shutdown the server + * once only 'monitor' clients are left. + * + * @param server server to stop listening on + */ +void +GNUNET_SERVER_stop_listening (struct GNUNET_SERVER_Handle *server) +{ + unsigned int i; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server in soft shutdown\n"); + if (NULL != server->listen_task) + { + GNUNET_SCHEDULER_cancel (server->listen_task); + server->listen_task = NULL; + } + if (NULL != server->listen_sockets) + { + i = 0; + while (NULL != server->listen_sockets[i]) + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (server->listen_sockets[i++])); + GNUNET_free (server->listen_sockets); + server->listen_sockets = NULL; + } + if (GNUNET_NO == server->in_soft_shutdown) + server->in_soft_shutdown = GNUNET_YES; + test_monitor_clients (server); +} + + +/** + * Free resources held by this server. + * + * @param server server to destroy + */ +void +GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *server) +{ + struct HandlerList *hpos; + struct NotifyList *npos; + unsigned int i; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server shutting down.\n"); + if (NULL != server->listen_task) + { + GNUNET_SCHEDULER_cancel (server->listen_task); + server->listen_task = NULL; + } + if (NULL != server->listen_sockets) + { + i = 0; + while (NULL != server->listen_sockets[i]) + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (server->listen_sockets[i++])); + GNUNET_free (server->listen_sockets); + server->listen_sockets = NULL; + } + while (NULL != server->clients_head) + GNUNET_SERVER_client_disconnect (server->clients_head); + while (NULL != (hpos = server->handlers)) + { + server->handlers = hpos->next; + GNUNET_free (hpos); + } + while (NULL != (npos = server->disconnect_notify_list_head)) + { + npos->callback (npos->callback_cls, + NULL); + GNUNET_CONTAINER_DLL_remove (server->disconnect_notify_list_head, + server->disconnect_notify_list_tail, + npos); + GNUNET_free (npos); + } + while (NULL != (npos = server->connect_notify_list_head)) + { + npos->callback (npos->callback_cls, + NULL); + GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head, + server->connect_notify_list_tail, + npos); + GNUNET_free (npos); + } + GNUNET_free (server); +} + + +/** + * Add additional handlers to an existing server. + * + * @param server the server to add handlers to + * @param handlers array of message handlers for + * incoming messages; the last entry must + * have "NULL" for the "callback"; multiple + * entries for the same type are allowed, + * they will be called in order of occurence. + * These handlers can be removed later; + * the handlers array must exist until removed + * (or server is destroyed). + */ +void +GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server, + const struct GNUNET_SERVER_MessageHandler *handlers) +{ + struct HandlerList *p; + + p = GNUNET_new (struct HandlerList); + p->handlers = handlers; + p->next = server->handlers; + server->handlers = p; +} + + +/** + * Change functions used by the server to tokenize the message stream. + * (very rarely used). + * + * @param server server to modify + * @param create new tokenizer initialization function + * @param destroy new tokenizer destruction function + * @param receive new tokenizer receive function + * @param cls closure for @a create, @a receive, @a destroy + */ +void +GNUNET_SERVER_set_callbacks (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_MstCreateCallback create, + GNUNET_SERVER_MstDestroyCallback destroy, + GNUNET_SERVER_MstReceiveCallback receive, + void *cls) +{ + server->mst_create = create; + server->mst_destroy = destroy; + server->mst_receive = receive; + server->mst_cls = cls; +} + + +/** + * Task run to warn about missing calls to #GNUNET_SERVER_receive_done. + * + * @param cls our `struct GNUNET_SERVER_Client *` to process more requests from + */ +static void +warn_no_receive_done (void *cls) +{ + struct GNUNET_SERVER_Client *client = cls; + + GNUNET_break (0 != client->warn_type); /* type should never be 0 here, as we don't use 0 */ + client->warn_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, + &warn_no_receive_done, client); + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Processing code for message of type %u did not call `GNUNET_SERVER_receive_done' after %s\n"), + (unsigned int) client->warn_type, + GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (client->warn_start), + GNUNET_YES)); +} + + +/** + * Disable the warning the server issues if a message is not acknowledged + * in a timely fashion. Use this call if a client is intentionally delayed + * for a while. Only applies to the current message. + * + * @param client client for which to disable the warning + */ +void +GNUNET_SERVER_disable_receive_done_warning (struct GNUNET_SERVER_Client *client) +{ + if (NULL != client->warn_task) + { + GNUNET_SCHEDULER_cancel (client->warn_task); + client->warn_task = NULL; + } +} + + +/** + * Inject a message into the server, pretend it came + * from the specified client. Delivery of the message + * will happen instantly (if a handler is installed; + * otherwise the call does nothing). + * + * @param server the server receiving the message + * @param sender the "pretended" sender of the message + * can be NULL! + * @param message message to transmit + * @return #GNUNET_OK if the message was OK and the + * connection can stay open + * #GNUNET_SYSERR if the connection to the + * client should be shut down + */ +int +GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server, + struct GNUNET_SERVER_Client *sender, + const struct GNUNET_MessageHeader *message) +{ + struct HandlerList *pos; + const struct GNUNET_SERVER_MessageHandler *mh; + unsigned int i; + uint16_t type; + uint16_t size; + int found; + + type = ntohs (message->type); + size = ntohs (message->size); + LOG (GNUNET_ERROR_TYPE_INFO, + "Received message of type %u and size %u from client\n", + type, size); + found = GNUNET_NO; + for (pos = server->handlers; NULL != pos; pos = pos->next) + { + i = 0; + while (pos->handlers[i].callback != NULL) + { + mh = &pos->handlers[i]; + if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL)) + { + if ((0 != mh->expected_size) && (mh->expected_size != size)) + { +#if GNUNET8_NETWORK_IS_DEAD + LOG (GNUNET_ERROR_TYPE_WARNING, + "Expected %u bytes for message of type %u, got %u\n", + mh->expected_size, mh->type, size); + GNUNET_break_op (0); +#else + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Expected %u bytes for message of type %u, got %u\n", + mh->expected_size, mh->type, size); +#endif + return GNUNET_SYSERR; + } + if (NULL != sender) + { + if ( (0 == sender->suspended) && + (NULL == sender->warn_task) ) + { + GNUNET_break (0 != type); /* type should never be 0 here, as we don't use 0 */ + sender->warn_start = GNUNET_TIME_absolute_get (); + sender->warn_task = + GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, + &warn_no_receive_done, + sender); + sender->warn_type = type; + } + sender->suspended++; + } + mh->callback (mh->callback_cls, sender, message); + found = GNUNET_YES; + } + i++; + } + } + if (GNUNET_NO == found) + { + LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, + "Received message of unknown type %d\n", type); + if (GNUNET_YES == server->require_found) + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * We are receiving an incoming message. Process it. + * + * @param cls our closure (handle for the client) + * @param buf buffer with data received from network + * @param available number of bytes available in buf + * @param addr address of the sender + * @param addrlen length of @a addr + * @param errCode code indicating errors receiving, 0 for success + */ +static void +process_incoming (void *cls, + const void *buf, + size_t available, + const struct sockaddr *addr, + socklen_t addrlen, + int errCode); + + +/** + * Process messages from the client's message tokenizer until either + * the tokenizer is empty (and then schedule receiving more), or + * until some handler is not immediately done (then wait for restart_processing) + * or shutdown. + * + * @param client the client to process, RC must have already been increased + * using #GNUNET_SERVER_client_keep and will be decreased by one in this + * function + * @param ret #GNUNET_NO to start processing from the buffer, + * #GNUNET_OK if the mst buffer is drained and we should instantly go back to receiving + * #GNUNET_SYSERR if we should instantly abort due to error in a previous step + */ +static void +process_mst (struct GNUNET_SERVER_Client *client, + int ret) +{ + while ((GNUNET_SYSERR != ret) && (NULL != client->server) && + (GNUNET_YES != client->shutdown_now) && (0 == client->suspended)) + { + if (GNUNET_OK == ret) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server re-enters receive loop, timeout: %s.\n", + GNUNET_STRINGS_relative_time_to_string (client->idle_timeout, GNUNET_YES)); + client->receive_pending = GNUNET_YES; + GNUNET_CONNECTION_receive (client->connection, + GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, + client->idle_timeout, + &process_incoming, + client); + break; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server processes additional messages instantly.\n"); + if (NULL != client->server->mst_receive) + ret = + client->server->mst_receive (client->server->mst_cls, client->mst, + client, NULL, 0, GNUNET_NO, GNUNET_YES); + else + ret = + GNUNET_SERVER_mst_receive (client->mst, client, NULL, 0, GNUNET_NO, + GNUNET_YES); + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server leaves instant processing loop: ret = %d, server = %p, shutdown = %d, suspended = %u\n", + ret, client->server, + client->shutdown_now, + client->suspended); + if (GNUNET_NO == ret) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server has more data pending but is suspended.\n"); + client->receive_pending = GNUNET_SYSERR; /* data pending */ + } + if ( (GNUNET_SYSERR == ret) || + (GNUNET_YES == client->shutdown_now) ) + GNUNET_SERVER_client_disconnect (client); +} + + +/** + * We are receiving an incoming message. Process it. + * + * @param cls our closure (handle for the client) + * @param buf buffer with data received from network + * @param available number of bytes available in buf + * @param addr address of the sender + * @param addrlen length of @a addr + * @param errCode code indicating errors receiving, 0 for success + */ +static void +process_incoming (void *cls, + const void *buf, + size_t available, + const struct sockaddr *addr, + socklen_t addrlen, + int errCode) +{ + struct GNUNET_SERVER_Client *client = cls; + struct GNUNET_SERVER_Handle *server = client->server; + struct GNUNET_TIME_Absolute end; + struct GNUNET_TIME_Absolute now; + int ret; + + GNUNET_assert (GNUNET_YES == client->receive_pending); + client->receive_pending = GNUNET_NO; + now = GNUNET_TIME_absolute_get (); + end = GNUNET_TIME_absolute_add (client->last_activity, + client->idle_timeout); + + if ( (NULL == buf) && + (0 == available) && + (NULL == addr) && + (0 == errCode) && + (GNUNET_YES != client->shutdown_now) && + (NULL != server) && + (GNUNET_YES == GNUNET_CONNECTION_check (client->connection)) && + (end.abs_value_us > now.abs_value_us) ) + { + /* wait longer, timeout changed (i.e. due to us sending) */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Receive time out, but no disconnect due to sending (%p)\n", + client); + client->receive_pending = GNUNET_YES; + GNUNET_CONNECTION_receive (client->connection, + GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, + GNUNET_TIME_absolute_get_remaining (end), + &process_incoming, + client); + return; + } + if ( (NULL == buf) || + (0 == available) || + (0 != errCode) || + (NULL == server) || + (GNUNET_YES == client->shutdown_now) || + (GNUNET_YES != GNUNET_CONNECTION_check (client->connection)) ) + { + /* other side closed connection, error connecting, etc. */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Failed to connect or other side closed connection (%p)\n", + client); + GNUNET_SERVER_client_disconnect (client); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server receives %u bytes from `%s'.\n", + (unsigned int) available, + GNUNET_a2s (addr, addrlen)); + GNUNET_SERVER_client_keep (client); + client->last_activity = now; + + if (NULL != server->mst_receive) + { + ret = client->server->mst_receive (client->server->mst_cls, + client->mst, + client, + buf, + available, + GNUNET_NO, + GNUNET_YES); + } + else if (NULL != client->mst) + { + ret = + GNUNET_SERVER_mst_receive (client->mst, + client, + buf, + available, + GNUNET_NO, + GNUNET_YES); + } + else + { + GNUNET_break (0); + return; + } + process_mst (client, + ret); + GNUNET_SERVER_client_drop (client); +} + + +/** + * Task run to start again receiving from the network + * and process requests. + * + * @param cls our `struct GNUNET_SERVER_Client *` to process more requests from + */ +static void +restart_processing (void *cls) +{ + struct GNUNET_SERVER_Client *client = cls; + + GNUNET_assert (GNUNET_YES != client->shutdown_now); + client->restart_task = NULL; + if (GNUNET_NO == client->receive_pending) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "Server begins to read again from client.\n"); + client->receive_pending = GNUNET_YES; + GNUNET_CONNECTION_receive (client->connection, + GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, + client->idle_timeout, + &process_incoming, + client); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server continues processing messages still in the buffer.\n"); + GNUNET_SERVER_client_keep (client); + client->receive_pending = GNUNET_NO; + process_mst (client, + GNUNET_NO); + GNUNET_SERVER_client_drop (client); +} + + +/** + * This function is called whenever our inbound message tokenizer has + * received a complete message. + * + * @param cls closure (struct GNUNET_SERVER_Handle) + * @param client identification of the client (`struct GNUNET_SERVER_Client *`) + * @param message the actual message + * + * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing + */ +static int +client_message_tokenizer_callback (void *cls, + void *client, + const struct GNUNET_MessageHeader *message) +{ + struct GNUNET_SERVER_Handle *server = cls; + struct GNUNET_SERVER_Client *sender = client; + int ret; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Tokenizer gives server message of type %u and size %u from client\n", + ntohs (message->type), ntohs (message->size)); + sender->in_process_client_buffer = GNUNET_YES; + ret = GNUNET_SERVER_inject (server, sender, message); + sender->in_process_client_buffer = GNUNET_NO; + if ( (GNUNET_OK != ret) || (GNUNET_YES == sender->shutdown_now) ) + { + GNUNET_SERVER_client_disconnect (sender); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Add a TCP socket-based connection to the set of handles managed by + * this server. Use this function for outgoing (P2P) connections that + * we initiated (and where this server should process incoming + * messages). + * + * @param server the server to use + * @param connection the connection to manage (client must + * stop using this connection from now on) + * @return the client handle + */ +struct GNUNET_SERVER_Client * +GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server, + struct GNUNET_CONNECTION_Handle *connection) +{ + struct GNUNET_SERVER_Client *client; + struct NotifyList *n; + + client = GNUNET_new (struct GNUNET_SERVER_Client); + client->connection = connection; + client->server = server; + client->last_activity = GNUNET_TIME_absolute_get (); + client->idle_timeout = server->idle_timeout; + GNUNET_CONTAINER_DLL_insert (server->clients_head, + server->clients_tail, + client); + if (NULL != server->mst_create) + client->mst = + server->mst_create (server->mst_cls, client); + else + client->mst = + GNUNET_SERVER_mst_create (&client_message_tokenizer_callback, + server); + GNUNET_assert (NULL != client->mst); + for (n = server->connect_notify_list_head; NULL != n; n = n->next) + n->callback (n->callback_cls, client); + client->receive_pending = GNUNET_YES; + GNUNET_CONNECTION_receive (client->connection, + GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, + client->idle_timeout, + &process_incoming, + client); + return client; +} + + +/** + * Change the timeout for a particular client. Decreasing the timeout + * may not go into effect immediately (only after the previous timeout + * times out or activity happens on the socket). + * + * @param client the client to update + * @param timeout new timeout for activities on the socket + */ +void +GNUNET_SERVER_client_set_timeout (struct GNUNET_SERVER_Client *client, + struct GNUNET_TIME_Relative timeout) +{ + client->idle_timeout = timeout; +} + + +/** + * Notify the server that the given client handle should + * be kept (keeps the connection up if possible, increments + * the internal reference counter). + * + * @param client the client to keep + */ +void +GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client) +{ + client->reference_count++; +} + + +/** + * Notify the server that the given client handle is no + * longer required. Decrements the reference counter. If + * that counter reaches zero an inactive connection maybe + * closed. + * + * @param client the client to drop + */ +void +GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client) +{ + GNUNET_assert (client->reference_count > 0); + client->reference_count--; + if ((GNUNET_YES == client->shutdown_now) && (0 == client->reference_count)) + GNUNET_SERVER_client_disconnect (client); +} + + +/** + * Obtain the network address of the other party. + * + * @param client the client to get the address for + * @param addr where to store the address + * @param addrlen where to store the length of the @a addr + * @return #GNUNET_OK on success + */ +int +GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client, + void **addr, size_t * addrlen) +{ + return GNUNET_CONNECTION_get_address (client->connection, addr, addrlen); +} + + +/** + * Ask the server to notify us whenever a client disconnects. + * This function is called whenever the actual network connection + * is closed; the reference count may be zero or larger than zero + * at this point. + * + * @param server the server manageing the clients + * @param callback function to call on disconnect + * @param callback_cls closure for @a callback + */ +void +GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_DisconnectCallback callback, + void *callback_cls) +{ + struct NotifyList *n; + + n = GNUNET_new (struct NotifyList); + n->callback = callback; + n->callback_cls = callback_cls; + GNUNET_CONTAINER_DLL_insert (server->disconnect_notify_list_head, + server->disconnect_notify_list_tail, + n); +} + + +/** + * Ask the server to notify us whenever a client connects. + * This function is called whenever the actual network connection + * is opened. If the server is destroyed before this + * notification is explicitly cancelled, the 'callback' will + * once be called with a 'client' argument of NULL to indicate + * that the server itself is now gone (and that the callback + * won't be called anymore and also can no longer be cancelled). + * + * @param server the server manageing the clients + * @param callback function to call on sconnect + * @param callback_cls closure for @a callback + */ +void +GNUNET_SERVER_connect_notify (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_ConnectCallback callback, + void *callback_cls) +{ + struct NotifyList *n; + struct GNUNET_SERVER_Client *client; + + n = GNUNET_new (struct NotifyList); + n->callback = callback; + n->callback_cls = callback_cls; + GNUNET_CONTAINER_DLL_insert (server->connect_notify_list_head, + server->connect_notify_list_tail, + n); + for (client = server->clients_head; NULL != client; client = client->next) + callback (callback_cls, client); +} + + +/** + * Ask the server to stop notifying us whenever a client connects. + * + * @param server the server manageing the clients + * @param callback function to call on connect + * @param callback_cls closure for @a callback + */ +void +GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_DisconnectCallback callback, + void *callback_cls) +{ + struct NotifyList *pos; + + for (pos = server->disconnect_notify_list_head; NULL != pos; pos = pos->next) + if ((pos->callback == callback) && (pos->callback_cls == callback_cls)) + break; + if (NULL == pos) + { + GNUNET_break (0); + return; + } + GNUNET_CONTAINER_DLL_remove (server->disconnect_notify_list_head, + server->disconnect_notify_list_tail, + pos); + GNUNET_free (pos); +} + + +/** + * Ask the server to stop notifying us whenever a client disconnects. + * + * @param server the server manageing the clients + * @param callback function to call on disconnect + * @param callback_cls closure for @a callback + */ +void +GNUNET_SERVER_connect_notify_cancel (struct GNUNET_SERVER_Handle *server, + GNUNET_SERVER_ConnectCallback callback, + void *callback_cls) +{ + struct NotifyList *pos; + + for (pos = server->connect_notify_list_head; NULL != pos; pos = pos->next) + if ((pos->callback == callback) && (pos->callback_cls == callback_cls)) + break; + if (NULL == pos) + { + GNUNET_break (0); + return; + } + GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head, + server->connect_notify_list_tail, + pos); + GNUNET_free (pos); +} + + +/** + * Destroy the connection that is passed in via @a cls. Used + * as calling #GNUNET_CONNECTION_destroy from within a function + * that was itself called from within process_notify() of + * 'connection.c' is not allowed (see #2329). + * + * @param cls connection to destroy + */ +static void +destroy_connection (void *cls) +{ + struct GNUNET_CONNECTION_Handle *connection = cls; + + GNUNET_CONNECTION_destroy (connection); +} + + +/** + * Ask the server to disconnect from the given client. + * This is the same as returning #GNUNET_SYSERR from a message + * handler, except that it allows dropping of a client even + * when not handling a message from that client. + * + * @param client the client to disconnect from + */ +void +GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client) +{ + struct GNUNET_SERVER_Handle *server = client->server; + struct NotifyList *n; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Client is being disconnected from the server.\n"); + if (NULL != client->restart_task) + { + GNUNET_SCHEDULER_cancel (client->restart_task); + client->restart_task = NULL; + } + if (NULL != client->warn_task) + { + GNUNET_SCHEDULER_cancel (client->warn_task); + client->warn_task = NULL; + } + if (GNUNET_YES == client->receive_pending) + { + GNUNET_CONNECTION_receive_cancel (client->connection); + client->receive_pending = GNUNET_NO; + } + client->shutdown_now = GNUNET_YES; + client->reference_count++; /* make sure nobody else clean up client... */ + if ( (NULL != client->mst) && + (NULL != server) ) + { + GNUNET_CONTAINER_DLL_remove (server->clients_head, + server->clients_tail, + client); + if (NULL != server->mst_destroy) + server->mst_destroy (server->mst_cls, + client->mst); + else + GNUNET_SERVER_mst_destroy (client->mst); + client->mst = NULL; + for (n = server->disconnect_notify_list_head; NULL != n; n = n->next) + n->callback (n->callback_cls, + client); + } + client->reference_count--; + if (client->reference_count > 0) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "RC of %p still positive, not destroying everything.\n", + client); + client->server = NULL; + return; + } + if (GNUNET_YES == client->in_process_client_buffer) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Still processing inputs of %p, not destroying everything.\n", + client); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "RC of %p now zero, destroying everything.\n", + client); + if (GNUNET_YES == client->persist) + GNUNET_CONNECTION_persist_ (client->connection); + if (NULL != client->th.cth) + GNUNET_SERVER_notify_transmit_ready_cancel (&client->th); + (void) GNUNET_SCHEDULER_add_now (&destroy_connection, + client->connection); + /* need to cancel again, as it might have been re-added + in the meantime (i.e. during callbacks) */ + if (NULL != client->warn_task) + { + GNUNET_SCHEDULER_cancel (client->warn_task); + client->warn_task = NULL; + } + if (GNUNET_YES == client->receive_pending) + { + GNUNET_CONNECTION_receive_cancel (client->connection); + client->receive_pending = GNUNET_NO; + } + GNUNET_free (client); + /* we might be in soft-shutdown, test if we're done */ + if (NULL != server) + test_monitor_clients (server); +} + + +/** + * Disable the "CORK" feature for communication with the given client, + * forcing the OS to immediately flush the buffer on transmission + * instead of potentially buffering multiple messages. + * + * @param client handle to the client + * @return #GNUNET_OK on success + */ +int +GNUNET_SERVER_client_disable_corking (struct GNUNET_SERVER_Client *client) +{ + return GNUNET_CONNECTION_disable_corking (client->connection); +} + + +/** + * Wrapper for transmission notification that calls the original + * callback and update the last activity time for our connection. + * + * @param cls the `struct GNUNET_SERVER_Client *` + * @param size number of bytes we can transmit + * @param buf where to copy the message + * @return number of bytes actually transmitted + */ +static size_t +transmit_ready_callback_wrapper (void *cls, size_t size, void *buf) +{ + struct GNUNET_SERVER_Client *client = cls; + GNUNET_CONNECTION_TransmitReadyNotify callback; + + client->th.cth = NULL; + callback = client->th.callback; + client->th.callback = NULL; + client->last_activity = GNUNET_TIME_absolute_get (); + return callback (client->th.callback_cls, size, buf); +} + + +/** + * Notify us when the server has enough space to transmit + * a message of the given size to the given client. + * + * @param client client to transmit message to + * @param size requested amount of buffer space + * @param timeout after how long should we give up (and call + * notify with buf NULL and size 0)? + * @param callback function to call when space is available + * @param callback_cls closure for @a callback + * @return non-NULL if the notify callback was queued; can be used + * to cancel the request using + * #GNUNET_SERVER_notify_transmit_ready_cancel(). + * NULL if we are already going to notify someone else (busy) + */ +struct GNUNET_SERVER_TransmitHandle * +GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, + size_t size, + struct GNUNET_TIME_Relative timeout, + GNUNET_CONNECTION_TransmitReadyNotify callback, + void *callback_cls) +{ + if (NULL != client->th.callback) + return NULL; + client->th.callback_cls = callback_cls; + client->th.callback = callback; + client->th.cth = GNUNET_CONNECTION_notify_transmit_ready (client->connection, size, + timeout, + &transmit_ready_callback_wrapper, + client); + return &client->th; +} + + +/** + * Abort transmission request. + * + * @param th request to abort + */ +void +GNUNET_SERVER_notify_transmit_ready_cancel (struct GNUNET_SERVER_TransmitHandle *th) +{ + GNUNET_CONNECTION_notify_transmit_ready_cancel (th->cth); + th->cth = NULL; + th->callback = NULL; +} + + +/** + * Set the persistent flag on this client, used to setup client connection + * to only be killed when the service it's connected to is actually dead. + * + * @param client the client to set the persistent flag on + */ +void +GNUNET_SERVER_client_persist_ (struct GNUNET_SERVER_Client *client) +{ + client->persist = GNUNET_YES; +} + + +/** + * Resume receiving from this client, we are done processing the + * current request. This function must be called from within each + * GNUNET_SERVER_MessageCallback (or its respective continuations). + * + * @param client client we were processing a message of + * @param success #GNUNET_OK to keep the connection open and + * continue to receive + * #GNUNET_NO to close the connection (normal behavior) + * #GNUNET_SYSERR to close the connection (signal + * serious error) + */ +void +GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, + int success) +{ + if (NULL == client) + return; + GNUNET_assert (client->suspended > 0); + client->suspended--; + if (GNUNET_OK != success) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "GNUNET_SERVER_receive_done called with failure indication\n"); + if ( (client->reference_count > 0) || (client->suspended > 0) ) + client->shutdown_now = GNUNET_YES; + else + GNUNET_SERVER_client_disconnect (client); + return; + } + if (client->suspended > 0) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "GNUNET_SERVER_receive_done called, but more clients pending\n"); + return; + } + if (NULL != client->warn_task) + { + GNUNET_SCHEDULER_cancel (client->warn_task); + client->warn_task = NULL; + } + if (GNUNET_YES == client->in_process_client_buffer) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "GNUNET_SERVER_receive_done called while still in processing loop\n"); + return; + } + if ((NULL == client->server) || (GNUNET_YES == client->shutdown_now)) + { + GNUNET_SERVER_client_disconnect (client); + return; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "GNUNET_SERVER_receive_done causes restart in reading from the socket\n"); + GNUNET_assert (NULL == client->restart_task); + client->restart_task = GNUNET_SCHEDULER_add_now (&restart_processing, + client); +} + + +/* end of server.c */ diff --git a/src/transport/tcp_server_mst_legacy.c b/src/transport/tcp_server_mst_legacy.c new file mode 100644 index 000000000..ba42b1837 --- /dev/null +++ b/src/transport/tcp_server_mst_legacy.c @@ -0,0 +1,311 @@ +/* + This file is part of GNUnet. + Copyright (C) 2010 GNUnet e.V. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file util/server_mst.c + * @brief convenience functions for handling inbound message buffers + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_util_lib.h" + + +#if HAVE_UNALIGNED_64_ACCESS +#define ALIGN_FACTOR 4 +#else +#define ALIGN_FACTOR 8 +#endif + + +/** + * Handle to a message stream tokenizer. + */ +struct GNUNET_SERVER_MessageStreamTokenizer +{ + + /** + * Function to call on completed messages. + */ + GNUNET_SERVER_MessageTokenizerCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Size of the buffer (starting at @e hdr). + */ + size_t curr_buf; + + /** + * How many bytes in buffer have we already processed? + */ + size_t off; + + /** + * How many bytes in buffer are valid right now? + */ + size_t pos; + + /** + * Beginning of the buffer. Typed like this to force alignment. + */ + struct GNUNET_MessageHeader *hdr; + +}; + + + +/** + * Create a message stream tokenizer. + * + * @param cb function to call on completed messages + * @param cb_cls closure for @a cb + * @return handle to tokenizer + */ +struct GNUNET_SERVER_MessageStreamTokenizer * +GNUNET_SERVER_mst_create (GNUNET_SERVER_MessageTokenizerCallback cb, + void *cb_cls) +{ + struct GNUNET_SERVER_MessageStreamTokenizer *ret; + + ret = GNUNET_new (struct GNUNET_SERVER_MessageStreamTokenizer); + ret->hdr = GNUNET_malloc (GNUNET_SERVER_MIN_BUFFER_SIZE); + ret->curr_buf = GNUNET_SERVER_MIN_BUFFER_SIZE; + ret->cb = cb; + ret->cb_cls = cb_cls; + return ret; +} + + +/** + * Add incoming data to the receive buffer and call the + * callback for all complete messages. + * + * @param mst tokenizer to use + * @param client_identity ID of client for which this is a buffer + * @param buf input data to add + * @param size number of bytes in @a buf + * @param purge should any excess bytes in the buffer be discarded + * (i.e. for packet-based services like UDP) + * @param one_shot only call callback once, keep rest of message in buffer + * @return #GNUNET_OK if we are done processing (need more data) + * #GNUNET_NO if @a one_shot was set and we have another message ready + * #GNUNET_SYSERR if the data stream is corrupt + */ +int +GNUNET_SERVER_mst_receive (struct GNUNET_SERVER_MessageStreamTokenizer *mst, + void *client_identity, + const char *buf, size_t size, + int purge, int one_shot) +{ + const struct GNUNET_MessageHeader *hdr; + size_t delta; + uint16_t want; + char *ibuf; + int need_align; + unsigned long offset; + int ret; + + GNUNET_assert (mst->off <= mst->pos); + GNUNET_assert (mst->pos <= mst->curr_buf); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server-mst receives %u bytes with %u bytes already in private buffer\n", + (unsigned int) size, (unsigned int) (mst->pos - mst->off)); + ret = GNUNET_OK; + ibuf = (char *) mst->hdr; + while (mst->pos > 0) + { +do_align: + GNUNET_assert (mst->pos >= mst->off); + if ((mst->curr_buf - mst->off < sizeof (struct GNUNET_MessageHeader)) || + (0 != (mst->off % ALIGN_FACTOR))) + { + /* need to align or need more space */ + mst->pos -= mst->off; + memmove (ibuf, &ibuf[mst->off], mst->pos); + mst->off = 0; + } + if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader)) + { + delta = + GNUNET_MIN (sizeof (struct GNUNET_MessageHeader) - + (mst->pos - mst->off), size); + GNUNET_memcpy (&ibuf[mst->pos], buf, delta); + mst->pos += delta; + buf += delta; + size -= delta; + } + if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader)) + { + if (purge) + { + mst->off = 0; + mst->pos = 0; + } + return GNUNET_OK; + } + hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off]; + want = ntohs (hdr->size); + if (want < sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ( (mst->curr_buf - mst->off < want) && + (mst->off > 0) ) + { + /* can get more space by moving */ + mst->pos -= mst->off; + memmove (ibuf, &ibuf[mst->off], mst->pos); + mst->off = 0; + } + if (mst->curr_buf < want) + { + /* need to get more space by growing buffer */ + GNUNET_assert (0 == mst->off); + mst->hdr = GNUNET_realloc (mst->hdr, want); + ibuf = (char *) mst->hdr; + mst->curr_buf = want; + } + hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off]; + if (mst->pos - mst->off < want) + { + delta = GNUNET_MIN (want - (mst->pos - mst->off), size); + GNUNET_assert (mst->pos + delta <= mst->curr_buf); + GNUNET_memcpy (&ibuf[mst->pos], buf, delta); + mst->pos += delta; + buf += delta; + size -= delta; + } + if (mst->pos - mst->off < want) + { + if (purge) + { + mst->off = 0; + mst->pos = 0; + } + return GNUNET_OK; + } + if (one_shot == GNUNET_SYSERR) + { + /* cannot call callback again, but return value saying that + * we have another full message in the buffer */ + ret = GNUNET_NO; + goto copy; + } + if (one_shot == GNUNET_YES) + one_shot = GNUNET_SYSERR; + mst->off += want; + if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr)) + return GNUNET_SYSERR; + if (mst->off == mst->pos) + { + /* reset to beginning of buffer, it's free right now! */ + mst->off = 0; + mst->pos = 0; + } + } + GNUNET_assert (0 == mst->pos); + while (size > 0) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server-mst has %u bytes left in inbound buffer\n", + (unsigned int) size); + if (size < sizeof (struct GNUNET_MessageHeader)) + break; + offset = (unsigned long) buf; + need_align = (0 != (offset % ALIGN_FACTOR)) ? GNUNET_YES : GNUNET_NO; + if (GNUNET_NO == need_align) + { + /* can try to do zero-copy and process directly from original buffer */ + hdr = (const struct GNUNET_MessageHeader *) buf; + want = ntohs (hdr->size); + if (want < sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_break_op (0); + mst->off = 0; + return GNUNET_SYSERR; + } + if (size < want) + break; /* or not: buffer incomplete, so copy to private buffer... */ + if (one_shot == GNUNET_SYSERR) + { + /* cannot call callback again, but return value saying that + * we have another full message in the buffer */ + ret = GNUNET_NO; + goto copy; + } + if (one_shot == GNUNET_YES) + one_shot = GNUNET_SYSERR; + if (GNUNET_SYSERR == mst->cb (mst->cb_cls, client_identity, hdr)) + return GNUNET_SYSERR; + buf += want; + size -= want; + } + else + { + /* need to copy to private buffer to align; + * yes, we go a bit more spagetti than usual here */ + goto do_align; + } + } +copy: + if ((size > 0) && (!purge)) + { + if (size + mst->pos > mst->curr_buf) + { + mst->hdr = GNUNET_realloc (mst->hdr, size + mst->pos); + ibuf = (char *) mst->hdr; + mst->curr_buf = size + mst->pos; + } + GNUNET_assert (size + mst->pos <= mst->curr_buf); + GNUNET_memcpy (&ibuf[mst->pos], buf, size); + mst->pos += size; + } + if (purge) + { + mst->off = 0; + mst->pos = 0; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server-mst leaves %u bytes in private buffer\n", + (unsigned int) (mst->pos - mst->off)); + return ret; +} + + +/** + * Destroys a tokenizer. + * + * @param mst tokenizer to destroy + */ +void +GNUNET_SERVER_mst_destroy (struct GNUNET_SERVER_MessageStreamTokenizer *mst) +{ + GNUNET_free (mst->hdr); + GNUNET_free (mst); +} + + + +/* end of server_mst.c */ diff --git a/src/transport/tcp_service_legacy.c b/src/transport/tcp_service_legacy.c new file mode 100644 index 000000000..c55d586f3 --- /dev/null +++ b/src/transport/tcp_service_legacy.c @@ -0,0 +1,1687 @@ +/* + This file is part of GNUnet. + Copyright (C) 2009, 2012 GNUnet e.V. + + 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., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +/** + * @file util/service.c + * @brief functions related to starting services + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_constants.h" +#include "gnunet_resolver_service.h" + +#if HAVE_MALLINFO +#include +#include "gauger.h" +#endif + + +/* ******************* access control ******************** */ + +/** + * Check if the given IP address is in the list of IP addresses. + * + * @param list a list of networks + * @param add the IP to check (in network byte order) + * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is + */ +static int +check_ipv4_listed (const struct GNUNET_STRINGS_IPv4NetworkPolicy *list, + const struct in_addr *add) +{ + unsigned int i; + + if (NULL == list) + return GNUNET_NO; + i = 0; + while ((list[i].network.s_addr != 0) || (list[i].netmask.s_addr != 0)) + { + if ((add->s_addr & list[i].netmask.s_addr) == + (list[i].network.s_addr & list[i].netmask.s_addr)) + return GNUNET_YES; + i++; + } + return GNUNET_NO; +} + + +/** + * Check if the given IP address is in the list of IP addresses. + * + * @param list a list of networks + * @param ip the IP to check (in network byte order) + * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is + */ +static int +check_ipv6_listed (const struct GNUNET_STRINGS_IPv6NetworkPolicy *list, + const struct in6_addr *ip) +{ + unsigned int i; + unsigned int j; + struct in6_addr zero; + + if (NULL == list) + return GNUNET_NO; + memset (&zero, 0, sizeof (struct in6_addr)); + i = 0; +NEXT: + while (0 != memcmp (&zero, &list[i].network, sizeof (struct in6_addr))) + { + for (j = 0; j < sizeof (struct in6_addr) / sizeof (int); j++) + if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) != + (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j])) + { + i++; + goto NEXT; + } + return GNUNET_YES; + } + return GNUNET_NO; +} + + +/* ****************** service struct ****************** */ + + +/** + * Context for "service_task". + */ +struct GNUNET_SERVICE_Context +{ + /** + * Our configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Handle for the server. + */ + struct GNUNET_SERVER_Handle *server; + + /** + * NULL-terminated array of addresses to bind to, NULL if we got pre-bound + * listen sockets. + */ + struct sockaddr **addrs; + + /** + * Name of our service. + */ + const char *service_name; + + /** + * Main service-specific task to run. + */ + GNUNET_SERVICE_Main task; + + /** + * Closure for @e task. + */ + void *task_cls; + + /** + * IPv4 addresses that are not allowed to connect. + */ + struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_denied; + + /** + * IPv6 addresses that are not allowed to connect. + */ + struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_denied; + + /** + * IPv4 addresses that are allowed to connect (if not + * set, all are allowed). + */ + struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_allowed; + + /** + * IPv6 addresses that are allowed to connect (if not + * set, all are allowed). + */ + struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_allowed; + + /** + * My (default) message handlers. Adjusted copy + * of "defhandlers". + */ + struct GNUNET_SERVER_MessageHandler *my_handlers; + + /** + * Array of the lengths of the entries in addrs. + */ + socklen_t *addrlens; + + /** + * NULL-terminated array of listen sockets we should take over. + */ + struct GNUNET_NETWORK_Handle **lsocks; + + /** + * Task ID of the shutdown task. + */ + struct GNUNET_SCHEDULER_Task *shutdown_task; + + /** + * Idle timeout for server. + */ + struct GNUNET_TIME_Relative timeout; + + /** + * Overall success/failure of the service start. + */ + int ret; + + /** + * If we are daemonizing, this FD is set to the + * pipe to the parent. Send '.' if we started + * ok, '!' if not. -1 if we are not daemonizing. + */ + int ready_confirm_fd; + + /** + * Do we close connections if we receive messages + * for which we have no handler? + */ + int require_found; + + /** + * Do we require a matching UID for UNIX domain socket connections? + * #GNUNET_NO means that the UID does not have to match (however, + * @e match_gid may still impose other access control checks). + */ + int match_uid; + + /** + * Do we require a matching GID for UNIX domain socket connections? + * Ignored if @e match_uid is #GNUNET_YES. Note that this is about + * checking that the client's UID is in our group OR that the + * client's GID is our GID. If both "match_gid" and @e match_uid are + * #GNUNET_NO, all users on the local system have access. + */ + int match_gid; + + /** + * Our options. + */ + enum GNUNET_SERVICE_Options options; + +}; + + +/* ****************** message handlers ****************** */ + +/** + * Send a 'TEST' message back to the client. + * + * @param cls the 'struct GNUNET_SERVER_Client' to send TEST to + * @param size number of bytes available in 'buf' + * @param buf where to copy the message + * @return number of bytes written to 'buf' + */ +static size_t +write_test (void *cls, size_t size, void *buf) +{ + struct GNUNET_SERVER_Client *client = cls; + struct GNUNET_MessageHeader *msg; + + if (size < sizeof (struct GNUNET_MessageHeader)) + { + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return 0; /* client disconnected */ + } + msg = (struct GNUNET_MessageHeader *) buf; + msg->type = htons (GNUNET_MESSAGE_TYPE_TEST); + msg->size = htons (sizeof (struct GNUNET_MessageHeader)); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return sizeof (struct GNUNET_MessageHeader); +} + + +/** + * Handler for TEST message. + * + * @param cls closure (refers to service) + * @param client identification of the client + * @param message the actual message + */ +static void +handle_test (void *cls, struct GNUNET_SERVER_Client *client, + const struct GNUNET_MessageHeader *message) +{ + /* simply bounce message back to acknowledge */ + if (NULL == + GNUNET_SERVER_notify_transmit_ready (client, + sizeof (struct GNUNET_MessageHeader), + GNUNET_TIME_UNIT_FOREVER_REL, + &write_test, client)) + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); +} + + +/** + * Default handlers for all services. Will be copied and the + * "callback_cls" fields will be replaced with the specific service + * struct. + */ +static const struct GNUNET_SERVER_MessageHandler defhandlers[] = { + {&handle_test, NULL, GNUNET_MESSAGE_TYPE_TEST, + sizeof (struct GNUNET_MessageHeader)}, + {NULL, NULL, 0, 0} +}; + + +/* ****************** service core routines ************** */ + + +/** + * Check if access to the service is allowed from the given address. + * + * @param cls closure + * @param uc credentials, if available, otherwise NULL + * @param addr address + * @param addrlen length of address + * @return #GNUNET_YES to allow, #GNUNET_NO to deny, #GNUNET_SYSERR + * for unknown address family (will be denied). + */ +static int +check_access (void *cls, const struct GNUNET_CONNECTION_Credentials *uc, + const struct sockaddr *addr, socklen_t addrlen) +{ + struct GNUNET_SERVICE_Context *sctx = cls; + const struct sockaddr_in *i4; + const struct sockaddr_in6 *i6; + int ret; + + switch (addr->sa_family) + { + case AF_INET: + GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); + i4 = (const struct sockaddr_in *) addr; + ret = ((NULL == sctx->v4_allowed) || + (check_ipv4_listed (sctx->v4_allowed, &i4->sin_addr))) && + ((NULL == sctx->v4_denied) || + (!check_ipv4_listed (sctx->v4_denied, &i4->sin_addr))); + break; + case AF_INET6: + GNUNET_assert (addrlen == sizeof (struct sockaddr_in6)); + i6 = (const struct sockaddr_in6 *) addr; + ret = ((NULL == sctx->v6_allowed) || + (check_ipv6_listed (sctx->v6_allowed, &i6->sin6_addr))) && + ((NULL == sctx->v6_denied) || + (!check_ipv6_listed (sctx->v6_denied, &i6->sin6_addr))); + break; +#ifndef WINDOWS + case AF_UNIX: + ret = GNUNET_OK; /* controlled using file-system ACL now */ + break; +#endif + default: + LOG (GNUNET_ERROR_TYPE_WARNING, _("Unknown address family %d\n"), + addr->sa_family); + return GNUNET_SYSERR; + } + if (GNUNET_OK != ret) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Access from `%s' denied to service `%s'\n"), + GNUNET_a2s (addr, addrlen), + sctx->service_name); + } + return ret; +} + + +/** + * Get the name of the file where we will + * write the PID of the service. + * + * @param sctx service context + * @return name of the file for the process ID + */ +static char * +get_pid_file_name (struct GNUNET_SERVICE_Context *sctx) +{ + char *pif; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, sctx->service_name, + "PIDFILE", &pif)) + return NULL; + return pif; +} + + +/** + * Parse an IPv4 access control list. + * + * @param ret location where to write the ACL (set) + * @param sctx service context to use to get the configuration + * @param option name of the ACL option to parse + * @return #GNUNET_SYSERR on parse error, #GNUNET_OK on success (including + * no ACL configured) + */ +static int +process_acl4 (struct GNUNET_STRINGS_IPv4NetworkPolicy **ret, + struct GNUNET_SERVICE_Context *sctx, + const char *option) +{ + char *opt; + + if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, option)) + { + *ret = NULL; + return GNUNET_OK; + } + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (sctx->cfg, + sctx->service_name, + option, &opt)); + if (NULL == (*ret = GNUNET_STRINGS_parse_ipv4_policy (opt))) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Could not parse IPv4 network specification `%s' for `%s:%s'\n"), + opt, sctx->service_name, option); + GNUNET_free (opt); + return GNUNET_SYSERR; + } + GNUNET_free (opt); + return GNUNET_OK; +} + + +/** + * Parse an IPv6 access control list. + * + * @param ret location where to write the ACL (set) + * @param sctx service context to use to get the configuration + * @param option name of the ACL option to parse + * @return #GNUNET_SYSERR on parse error, #GNUNET_OK on success (including + * no ACL configured) + */ +static int +process_acl6 (struct GNUNET_STRINGS_IPv6NetworkPolicy **ret, + struct GNUNET_SERVICE_Context *sctx, + const char *option) +{ + char *opt; + + if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, option)) + { + *ret = NULL; + return GNUNET_OK; + } + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (sctx->cfg, + sctx->service_name, + option, &opt)); + if (NULL == (*ret = GNUNET_STRINGS_parse_ipv6_policy (opt))) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Could not parse IPv6 network specification `%s' for `%s:%s'\n"), + opt, sctx->service_name, option); + GNUNET_free (opt); + return GNUNET_SYSERR; + } + GNUNET_free (opt); + return GNUNET_OK; +} + + +/** + * Add the given UNIX domain path as an address to the + * list (as the first entry). + * + * @param saddrs array to update + * @param saddrlens where to store the address length + * @param unixpath path to add + * @param abstract #GNUNET_YES to add an abstract UNIX domain socket. This + * parameter is ignore on systems other than LINUX + */ +static void +add_unixpath (struct sockaddr **saddrs, + socklen_t *saddrlens, + const char *unixpath, + int abstract) +{ +#ifdef AF_UNIX + struct sockaddr_un *un; + + un = GNUNET_new (struct sockaddr_un); + un->sun_family = AF_UNIX; + strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1); +#ifdef LINUX + if (GNUNET_YES == abstract) + un->sun_path[0] = '\0'; +#endif +#if HAVE_SOCKADDR_UN_SUN_LEN + un->sun_len = (u_char) sizeof (struct sockaddr_un); +#endif + *saddrs = (struct sockaddr *) un; + *saddrlens = sizeof (struct sockaddr_un); +#else + /* this function should never be called + * unless AF_UNIX is defined! */ + GNUNET_assert (0); +#endif +} + + +/** + * Get the list of addresses that a server for the given service + * should bind to. + * + * @param service_name name of the service + * @param cfg configuration (which specifies the addresses) + * @param addrs set (call by reference) to an array of pointers to the + * addresses the server should bind to and listen on; the + * array will be NULL-terminated (on success) + * @param addr_lens set (call by reference) to an array of the lengths + * of the respective `struct sockaddr` struct in the @a addrs + * array (on success) + * @return number of addresses found on success, + * #GNUNET_SYSERR if the configuration + * did not specify reasonable finding information or + * if it specified a hostname that could not be resolved; + * #GNUNET_NO if the number of addresses configured is + * zero (in this case, `*addrs` and `*addr_lens` will be + * set to NULL). + */ +int +GNUNET_SERVICE_get_server_addresses (const char *service_name, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct sockaddr ***addrs, + socklen_t ** addr_lens) +{ + int disablev6; + struct GNUNET_NETWORK_Handle *desc; + unsigned long long port; + char *unixpath; + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *pos; + struct addrinfo *next; + unsigned int i; + int resi; + int ret; + int abstract; + struct sockaddr **saddrs; + socklen_t *saddrlens; + char *hostname; + + *addrs = NULL; + *addr_lens = NULL; + desc = NULL; + if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "DISABLEV6")) + { + if (GNUNET_SYSERR == + (disablev6 = + GNUNET_CONFIGURATION_get_value_yesno (cfg, service_name, "DISABLEV6"))) + return GNUNET_SYSERR; + } + else + disablev6 = GNUNET_NO; + + if (! disablev6) + { + /* probe IPv6 support */ + desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); + if (NULL == desc) + { + if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) || + (EACCES == errno)) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); + return GNUNET_SYSERR; + } + LOG (GNUNET_ERROR_TYPE_INFO, + _("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"), + service_name, STRERROR (errno)); + disablev6 = GNUNET_YES; + } + else + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); + desc = NULL; + } + } + + port = 0; + if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT")) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, service_name, + "PORT", &port)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Require valid port number for service `%s' in configuration!\n"), + service_name); + } + if (port > 65535) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Require valid port number for service `%s' in configuration!\n"), + service_name); + return GNUNET_SYSERR; + } + } + + if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "BINDTO")) + { + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, service_name, + "BINDTO", &hostname)); + } + else + hostname = NULL; + + unixpath = NULL; + abstract = GNUNET_NO; +#ifdef AF_UNIX + if ((GNUNET_YES == + GNUNET_CONFIGURATION_have_value (cfg, service_name, "UNIXPATH")) && + (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_filename (cfg, service_name, "UNIXPATH", + &unixpath)) && + (0 < strlen (unixpath))) + { + /* probe UNIX support */ + struct sockaddr_un s_un; + + if (strlen (unixpath) >= sizeof (s_un.sun_path)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath, + (unsigned long long) sizeof (s_un.sun_path)); + unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath); + LOG (GNUNET_ERROR_TYPE_INFO, + _("Using `%s' instead\n"), + unixpath); + } +#ifdef LINUX + abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg, + "TESTING", + "USE_ABSTRACT_SOCKETS"); + if (GNUNET_SYSERR == abstract) + abstract = GNUNET_NO; +#endif + if ((GNUNET_YES != abstract) + && (GNUNET_OK != + GNUNET_DISK_directory_create_for_file (unixpath))) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "mkdir", + unixpath); + } + if (NULL != unixpath) + { + desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0); + if (NULL == desc) + { + if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) || + (EACCES == errno)) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); + GNUNET_free_non_null (hostname); + GNUNET_free (unixpath); + return GNUNET_SYSERR; + } + LOG (GNUNET_ERROR_TYPE_INFO, + _("Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"), + service_name, + STRERROR (errno)); + GNUNET_free (unixpath); + unixpath = NULL; + } + else + { + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); + desc = NULL; + } + } +#endif + + if ((0 == port) && (NULL == unixpath)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Have neither PORT nor UNIXPATH for service `%s', but one is required\n"), + service_name); + GNUNET_free_non_null (hostname); + return GNUNET_SYSERR; + } + if (0 == port) + { + saddrs = GNUNET_malloc (2 * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc (2 * sizeof (socklen_t)); + add_unixpath (saddrs, saddrlens, unixpath, abstract); + GNUNET_free_non_null (unixpath); + GNUNET_free_non_null (hostname); + *addrs = saddrs; + *addr_lens = saddrlens; + return 1; + } + + if (NULL != hostname) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Resolving `%s' since that is where `%s' will bind to.\n", + hostname, + service_name); + memset (&hints, 0, sizeof (struct addrinfo)); + if (disablev6) + hints.ai_family = AF_INET; + hints.ai_protocol = IPPROTO_TCP; + if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) || + (NULL == res)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Failed to resolve `%s': %s\n"), + hostname, + gai_strerror (ret)); + GNUNET_free (hostname); + GNUNET_free_non_null (unixpath); + return GNUNET_SYSERR; + } + next = res; + i = 0; + while (NULL != (pos = next)) + { + next = pos->ai_next; + if ((disablev6) && (pos->ai_family == AF_INET6)) + continue; + i++; + } + if (0 == i) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Failed to find %saddress for `%s'.\n"), + disablev6 ? "IPv4 " : "", + hostname); + freeaddrinfo (res); + GNUNET_free (hostname); + GNUNET_free_non_null (unixpath); + return GNUNET_SYSERR; + } + resi = i; + if (NULL != unixpath) + resi++; + saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); + i = 0; + if (NULL != unixpath) + { + add_unixpath (saddrs, saddrlens, unixpath, abstract); + i++; + } + next = res; + while (NULL != (pos = next)) + { + next = pos->ai_next; + if ((disablev6) && (AF_INET6 == pos->ai_family)) + continue; + if ((IPPROTO_TCP != pos->ai_protocol) && (0 != pos->ai_protocol)) + continue; /* not TCP */ + if ((SOCK_STREAM != pos->ai_socktype) && (0 != pos->ai_socktype)) + continue; /* huh? */ + LOG (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' will bind to `%s'\n", + service_name, GNUNET_a2s (pos->ai_addr, pos->ai_addrlen)); + if (AF_INET == pos->ai_family) + { + GNUNET_assert (sizeof (struct sockaddr_in) == pos->ai_addrlen); + saddrlens[i] = pos->ai_addrlen; + saddrs[i] = GNUNET_malloc (saddrlens[i]); + GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); + } + else + { + GNUNET_assert (AF_INET6 == pos->ai_family); + GNUNET_assert (sizeof (struct sockaddr_in6) == pos->ai_addrlen); + saddrlens[i] = pos->ai_addrlen; + saddrs[i] = GNUNET_malloc (saddrlens[i]); + GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); + ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); + } + i++; + } + GNUNET_free (hostname); + freeaddrinfo (res); + resi = i; + } + else + { + /* will bind against everything, just set port */ + if (disablev6) + { + /* V4-only */ + resi = 1; + if (NULL != unixpath) + resi++; + i = 0; + saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); + if (NULL != unixpath) + { + add_unixpath (saddrs, saddrlens, unixpath, abstract); + i++; + } + saddrlens[i] = sizeof (struct sockaddr_in); + saddrs[i] = GNUNET_malloc (saddrlens[i]); +#if HAVE_SOCKADDR_IN_SIN_LEN + ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i]; +#endif + ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); + } + else + { + /* dual stack */ + resi = 2; + if (NULL != unixpath) + resi++; + saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); + i = 0; + if (NULL != unixpath) + { + add_unixpath (saddrs, saddrlens, unixpath, abstract); + i++; + } + saddrlens[i] = sizeof (struct sockaddr_in6); + saddrs[i] = GNUNET_malloc (saddrlens[i]); +#if HAVE_SOCKADDR_IN_SIN_LEN + ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0]; +#endif + ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6; + ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); + i++; + saddrlens[i] = sizeof (struct sockaddr_in); + saddrs[i] = GNUNET_malloc (saddrlens[i]); +#if HAVE_SOCKADDR_IN_SIN_LEN + ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1]; +#endif + ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); + } + } + GNUNET_free_non_null (unixpath); + *addrs = saddrs; + *addr_lens = saddrlens; + return resi; +} + + +#ifdef MINGW +/** + * Read listen sockets from the parent process (ARM). + * + * @param sctx service context to initialize + * @return #GNUNET_YES if ok, #GNUNET_NO if not ok (must bind yourself), + * and #GNUNET_SYSERR on error. + */ +static int +receive_sockets_from_parent (struct GNUNET_SERVICE_Context *sctx) +{ + const char *env_buf; + int fail; + uint64_t count; + uint64_t i; + HANDLE lsocks_pipe; + + env_buf = getenv ("GNUNET_OS_READ_LSOCKS"); + if ((NULL == env_buf) || (strlen (env_buf) <= 0)) + return GNUNET_NO; + /* Using W32 API directly here, because this pipe will + * never be used outside of this function, and it's just too much of a bother + * to create a GNUnet API that boxes a HANDLE (the way it is done with socks) + */ + lsocks_pipe = (HANDLE) strtoul (env_buf, NULL, 10); + if ( (0 == lsocks_pipe) || (INVALID_HANDLE_VALUE == lsocks_pipe)) + return GNUNET_NO; + fail = 1; + do + { + int ret; + int fail2; + DWORD rd; + + ret = ReadFile (lsocks_pipe, &count, sizeof (count), &rd, NULL); + if ((0 == ret) || (sizeof (count) != rd) || (0 == count)) + break; + sctx->lsocks = + GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (count + 1)); + + fail2 = 1; + for (i = 0; i < count; i++) + { + WSAPROTOCOL_INFOA pi; + uint64_t size; + SOCKET s; + + ret = ReadFile (lsocks_pipe, &size, sizeof (size), &rd, NULL); + if ( (0 == ret) || (sizeof (size) != rd) || (sizeof (pi) != size) ) + break; + ret = ReadFile (lsocks_pipe, &pi, sizeof (pi), &rd, NULL); + if ( (0 == ret) || (sizeof (pi) != rd)) + break; + s = WSASocketA (pi.iAddressFamily, pi.iSocketType, pi.iProtocol, &pi, 0, WSA_FLAG_OVERLAPPED); + sctx->lsocks[i] = GNUNET_NETWORK_socket_box_native (s); + if (NULL == sctx->lsocks[i]) + break; + else if (i == count - 1) + fail2 = 0; + } + if (fail2) + break; + sctx->lsocks[count] = NULL; + fail = 0; + } + while (fail); + + CloseHandle (lsocks_pipe); + + if (fail) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Could not access a pre-bound socket, will try to bind myself\n")); + for (i = 0; (i < count) && (NULL != sctx->lsocks[i]); i++) + GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[i])); + GNUNET_free_non_null (sctx->lsocks); + sctx->lsocks = NULL; + return GNUNET_NO; + } + return GNUNET_YES; +} +#endif + + +/** + * Setup addr, addrlen, idle_timeout + * based on configuration! + * + * Configuration may specify: + * - PORT (where to bind to for TCP) + * - UNIXPATH (where to bind to for UNIX domain sockets) + * - TIMEOUT (after how many ms does an inactive service timeout); + * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack) + * - BINDTO (hostname or IP address to bind to, otherwise we take everything) + * - ACCEPT_FROM (only allow connections from specified IPv4 subnets) + * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets) + * - REJECT_FROM (disallow allow connections from specified IPv4 subnets) + * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets) + * + * @param sctx service context to initialize + * @return #GNUNET_OK if configuration succeeded + */ +static int +setup_service (struct GNUNET_SERVICE_Context *sctx) +{ + struct GNUNET_TIME_Relative idleout; + int tolerant; + +#ifndef MINGW + const char *nfds; + unsigned int cnt; + int flags; +#endif + + if (GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, "TIMEOUT")) + { + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (sctx->cfg, sctx->service_name, + "TIMEOUT", &idleout)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Specified value for `%s' of service `%s' is invalid\n"), + "TIMEOUT", sctx->service_name); + return GNUNET_SYSERR; + } + sctx->timeout = idleout; + } + else + sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; + + if (GNUNET_CONFIGURATION_have_value + (sctx->cfg, sctx->service_name, "TOLERANT")) + { + if (GNUNET_SYSERR == + (tolerant = + GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name, + "TOLERANT"))) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Specified value for `%s' of service `%s' is invalid\n"), + "TOLERANT", sctx->service_name); + return GNUNET_SYSERR; + } + } + else + tolerant = GNUNET_NO; + +#ifndef MINGW + errno = 0; + if ((NULL != (nfds = getenv ("LISTEN_FDS"))) && + (1 == SSCANF (nfds, "%u", &cnt)) && (cnt > 0) && (cnt < FD_SETSIZE) && + (cnt + 4 < FD_SETSIZE)) + { + sctx->lsocks = + GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (cnt + 1)); + while (0 < cnt--) + { + flags = fcntl (3 + cnt, F_GETFD); + if ((flags < 0) || (0 != (flags & FD_CLOEXEC)) || + (NULL == + (sctx->lsocks[cnt] = GNUNET_NETWORK_socket_box_native (3 + cnt)))) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _ + ("Could not access pre-bound socket %u, will try to bind myself\n"), + (unsigned int) 3 + cnt); + cnt++; + while (sctx->lsocks[cnt] != NULL) + GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[cnt++])); + GNUNET_free (sctx->lsocks); + sctx->lsocks = NULL; + break; + } + } + unsetenv ("LISTEN_FDS"); + } +#else + if (getenv ("GNUNET_OS_READ_LSOCKS") != NULL) + { + receive_sockets_from_parent (sctx); + putenv ("GNUNET_OS_READ_LSOCKS="); + } +#endif + + if ((NULL == sctx->lsocks) && + (GNUNET_SYSERR == + GNUNET_SERVICE_get_server_addresses (sctx->service_name, sctx->cfg, + &sctx->addrs, &sctx->addrlens))) + return GNUNET_SYSERR; + sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES; + sctx->match_uid = + GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name, + "UNIX_MATCH_UID"); + sctx->match_gid = + GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name, + "UNIX_MATCH_GID"); + process_acl4 (&sctx->v4_denied, sctx, "REJECT_FROM"); + process_acl4 (&sctx->v4_allowed, sctx, "ACCEPT_FROM"); + process_acl6 (&sctx->v6_denied, sctx, "REJECT_FROM6"); + process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6"); + + return GNUNET_OK; +} + + +/** + * Get the name of the user that'll be used + * to provide the service. + * + * @param sctx service context + * @return value of the 'USERNAME' option + */ +static char * +get_user_name (struct GNUNET_SERVICE_Context *sctx) +{ + char *un; + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, sctx->service_name, + "USERNAME", &un)) + return NULL; + return un; +} + + +/** + * Write PID file. + * + * @param sctx service context + * @param pid PID to write (should be equal to 'getpid()' + * @return #GNUNET_OK on success (including no work to be done) + */ +static int +write_pid_file (struct GNUNET_SERVICE_Context *sctx, pid_t pid) +{ + FILE *pidfd; + char *pif; + char *user; + char *rdir; + int len; + + if (NULL == (pif = get_pid_file_name (sctx))) + return GNUNET_OK; /* no file desired */ + user = get_user_name (sctx); + rdir = GNUNET_strdup (pif); + len = strlen (rdir); + while ((len > 0) && (rdir[len] != DIR_SEPARATOR)) + len--; + rdir[len] = '\0'; + if (0 != ACCESS (rdir, F_OK)) + { + /* we get to create a directory -- and claim it + * as ours! */ + (void) GNUNET_DISK_directory_create (rdir); + if ((NULL != user) && (0 < strlen (user))) + GNUNET_DISK_file_change_owner (rdir, user); + } + if (0 != ACCESS (rdir, W_OK | X_OK)) + { + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", rdir); + GNUNET_free (rdir); + GNUNET_free_non_null (user); + GNUNET_free (pif); + return GNUNET_SYSERR; + } + GNUNET_free (rdir); + pidfd = FOPEN (pif, "w"); + if (NULL == pidfd) + { + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "fopen", pif); + GNUNET_free (pif); + GNUNET_free_non_null (user); + return GNUNET_SYSERR; + } + if (0 > FPRINTF (pidfd, "%u", pid)) + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fprintf", pif); + GNUNET_break (0 == FCLOSE (pidfd)); + if ((NULL != user) && (0 < strlen (user))) + GNUNET_DISK_file_change_owner (pif, user); + GNUNET_free_non_null (user); + GNUNET_free (pif); + return GNUNET_OK; +} + + +/** + * Task run during shutdown. Stops the server/service. + * + * @param cls the `struct GNUNET_SERVICE_Context` + */ +static void +shutdown_task (void *cls) +{ + struct GNUNET_SERVICE_Context *service = cls; + struct GNUNET_SERVER_Handle *server = service->server; + + service->shutdown_task = NULL; + if (0 != (service->options & GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN)) + GNUNET_SERVER_stop_listening (server); + else + GNUNET_SERVER_destroy (server); +} + + +/** + * Initial task for the service. + * + * @param cls service context + */ +static void +service_task (void *cls) +{ + struct GNUNET_SERVICE_Context *sctx = cls; + unsigned int i; + + GNUNET_RESOLVER_connect (sctx->cfg); + if (NULL != sctx->lsocks) + sctx->server + = GNUNET_SERVER_create_with_sockets (&check_access, sctx, sctx->lsocks, + sctx->timeout, sctx->require_found); + else + sctx->server + = GNUNET_SERVER_create (&check_access, sctx, sctx->addrs, sctx->addrlens, + sctx->timeout, sctx->require_found); + if (NULL == sctx->server) + { + if (NULL != sctx->addrs) + for (i = 0; NULL != sctx->addrs[i]; i++) + LOG (GNUNET_ERROR_TYPE_INFO, + _("Failed to start `%s' at `%s'\n"), + sctx->service_name, GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); + sctx->ret = GNUNET_SYSERR; + return; + } +#ifndef WINDOWS + if (NULL != sctx->addrs) + for (i = 0; NULL != sctx->addrs[i]; i++) + if ((AF_UNIX == sctx->addrs[i]->sa_family) + && ('\0' != ((const struct sockaddr_un *)sctx->addrs[i])->sun_path[0])) + GNUNET_DISK_fix_permissions (((const struct sockaddr_un *)sctx->addrs[i])->sun_path, + sctx->match_uid, + sctx->match_gid); +#endif + + + if (0 == (sctx->options & GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN)) + { + /* install a task that will kill the server + * process if the scheduler ever gets a shutdown signal */ + sctx->shutdown_task = GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + sctx); + } + sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); + GNUNET_memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); + i = 0; + while (NULL != sctx->my_handlers[i].callback) + sctx->my_handlers[i++].callback_cls = sctx; + GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); + if (-1 != sctx->ready_confirm_fd) + { + GNUNET_break (1 == WRITE (sctx->ready_confirm_fd, ".", 1)); + GNUNET_break (0 == CLOSE (sctx->ready_confirm_fd)); + sctx->ready_confirm_fd = -1; + write_pid_file (sctx, getpid ()); + } + if (NULL != sctx->addrs) + { + i = 0; + while (NULL != sctx->addrs[i]) + { + LOG (GNUNET_ERROR_TYPE_INFO, _("Service `%s' runs at %s\n"), + sctx->service_name, GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); + i++; + } + } + sctx->task (sctx->task_cls, sctx->server, sctx->cfg); +} + + +/** + * Detach from terminal. + * + * @param sctx service context + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +detach_terminal (struct GNUNET_SERVICE_Context *sctx) +{ +#ifndef MINGW + pid_t pid; + int nullfd; + int filedes[2]; + + if (0 != PIPE (filedes)) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe"); + return GNUNET_SYSERR; + } + pid = fork (); + if (pid < 0) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork"); + return GNUNET_SYSERR; + } + if (0 != pid) + { + /* Parent */ + char c; + + GNUNET_break (0 == CLOSE (filedes[1])); + c = 'X'; + if (1 != READ (filedes[0], &c, sizeof (char))) + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "read"); + fflush (stdout); + switch (c) + { + case '.': + exit (0); + case 'I': + LOG (GNUNET_ERROR_TYPE_INFO, _("Service process failed to initialize\n")); + break; + case 'S': + LOG (GNUNET_ERROR_TYPE_INFO, + _("Service process could not initialize server function\n")); + break; + case 'X': + LOG (GNUNET_ERROR_TYPE_INFO, + _("Service process failed to report status\n")); + break; + } + exit (1); /* child reported error */ + } + GNUNET_break (0 == CLOSE (0)); + GNUNET_break (0 == CLOSE (1)); + GNUNET_break (0 == CLOSE (filedes[0])); + nullfd = OPEN ("/dev/null", O_RDWR | O_APPEND); + if (nullfd < 0) + return GNUNET_SYSERR; + /* set stdin/stdout to /dev/null */ + if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0)) + { + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2"); + (void) CLOSE (nullfd); + return GNUNET_SYSERR; + } + (void) CLOSE (nullfd); + /* Detach from controlling terminal */ + pid = setsid (); + if (-1 == pid) + LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "setsid"); + sctx->ready_confirm_fd = filedes[1]; +#else + /* FIXME: we probably need to do something else + * elsewhere in order to fork the process itself... */ + FreeConsole (); +#endif + return GNUNET_OK; +} + + +/** + * Set user ID. + * + * @param sctx service context + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +static int +set_user_id (struct GNUNET_SERVICE_Context *sctx) +{ + char *user; + + if (NULL == (user = get_user_name (sctx))) + return GNUNET_OK; /* keep */ +#ifndef MINGW + struct passwd *pws; + + errno = 0; + pws = getpwnam (user); + if (NULL == pws) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Cannot obtain information about user `%s': %s\n"), user, + errno == 0 ? _("No such user") : STRERROR (errno)); + GNUNET_free (user); + return GNUNET_SYSERR; + } + if ((0 != setgid (pws->pw_gid)) || (0 != setegid (pws->pw_gid)) || +#if HAVE_INITGROUPS + (0 != initgroups (user, pws->pw_gid)) || +#endif + (0 != setuid (pws->pw_uid)) || (0 != seteuid (pws->pw_uid))) + { + if ((0 != setregid (pws->pw_gid, pws->pw_gid)) || + (0 != setreuid (pws->pw_uid, pws->pw_uid))) + { + LOG (GNUNET_ERROR_TYPE_ERROR, _("Cannot change user/group to `%s': %s\n"), + user, STRERROR (errno)); + GNUNET_free (user); + return GNUNET_SYSERR; + } + } +#endif + GNUNET_free (user); + return GNUNET_OK; +} + + +/** + * Delete the PID file that was created by our parent. + * + * @param sctx service context + */ +static void +pid_file_delete (struct GNUNET_SERVICE_Context *sctx) +{ + char *pif = get_pid_file_name (sctx); + + if (NULL == pif) + return; /* no PID file */ + if (0 != UNLINK (pif)) + LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", pif); + GNUNET_free (pif); +} + + +/** + * Run a standard GNUnet service startup sequence (initialize loggers + * and configuration, parse options). + * + * @param argc number of command line arguments + * @param argv command line arguments + * @param service_name our service name + * @param options service options + * @param task main task of the service + * @param task_cls closure for @a task + * @return #GNUNET_SYSERR on error, #GNUNET_OK + * if we shutdown nicely + */ +int +GNUNET_SERVICE_run (int argc, char *const *argv, + const char *service_name, + enum GNUNET_SERVICE_Options options, + GNUNET_SERVICE_Main task, + void *task_cls) +{ +#define HANDLE_ERROR do { GNUNET_break (0); goto shutdown; } while (0) + + int err; + int ret; + char *cfg_fn; + char *opt_cfg_fn; + char *loglev; + char *logfile; + int do_daemonize; + unsigned int i; + unsigned long long skew_offset; + unsigned long long skew_variance; + long long clock_offset; + struct GNUNET_SERVICE_Context sctx; + struct GNUNET_CONFIGURATION_Handle *cfg; + const char *xdg; + + struct GNUNET_GETOPT_CommandLineOption service_options[] = { + GNUNET_GETOPT_OPTION_CFG_FILE (&opt_cfg_fn), + {'d', "daemonize", NULL, + gettext_noop ("do daemonize (detach from terminal)"), 0, + GNUNET_GETOPT_set_one, &do_daemonize}, + GNUNET_GETOPT_OPTION_HELP (NULL), + GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev), + GNUNET_GETOPT_OPTION_LOGFILE (&logfile), + GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION " " VCS_VERSION), + GNUNET_GETOPT_OPTION_END + }; + err = 1; + do_daemonize = 0; + logfile = NULL; + loglev = NULL; + opt_cfg_fn = NULL; + xdg = getenv ("XDG_CONFIG_HOME"); + if (NULL != xdg) + GNUNET_asprintf (&cfg_fn, + "%s%s%s", + xdg, + DIR_SEPARATOR_STR, + GNUNET_OS_project_data_get ()->config_file); + else + cfg_fn = GNUNET_strdup (GNUNET_OS_project_data_get ()->user_config_file); + memset (&sctx, 0, sizeof (sctx)); + sctx.options = options; + sctx.ready_confirm_fd = -1; + sctx.ret = GNUNET_OK; + sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL; + sctx.task = task; + sctx.task_cls = task_cls; + sctx.service_name = service_name; + sctx.cfg = cfg = GNUNET_CONFIGURATION_create (); + + /* setup subsystems */ + ret = GNUNET_GETOPT_run (service_name, service_options, argc, argv); + if (GNUNET_SYSERR == ret) + goto shutdown; + if (GNUNET_NO == ret) + { + err = 0; + goto shutdown; + } + if (GNUNET_OK != GNUNET_log_setup (service_name, loglev, logfile)) + HANDLE_ERROR; + if (NULL == opt_cfg_fn) + opt_cfg_fn = GNUNET_strdup (cfg_fn); + if (GNUNET_YES == GNUNET_DISK_file_test (opt_cfg_fn)) + { + if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, opt_cfg_fn)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Malformed configuration file `%s', exit ...\n"), + opt_cfg_fn); + goto shutdown; + } + } + else + { + if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Malformed configuration, exit ...\n")); + goto shutdown; + } + if (0 != strcmp (opt_cfg_fn, cfg_fn)) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Could not access configuration file `%s'\n"), + opt_cfg_fn); + } + if (GNUNET_OK != setup_service (&sctx)) + goto shutdown; + if ((1 == do_daemonize) && (GNUNET_OK != detach_terminal (&sctx))) + HANDLE_ERROR; + if (GNUNET_OK != set_user_id (&sctx)) + goto shutdown; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Service `%s' runs with configuration from `%s'\n", + service_name, + opt_cfg_fn); + if ((GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (sctx.cfg, "TESTING", + "SKEW_OFFSET", &skew_offset)) && + (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (sctx.cfg, "TESTING", + "SKEW_VARIANCE", &skew_variance))) + { + clock_offset = skew_offset - skew_variance; + GNUNET_TIME_set_offset (clock_offset); + LOG (GNUNET_ERROR_TYPE_DEBUG, "Skewing clock by %dll ms\n", clock_offset); + } + /* actually run service */ + err = 0; + GNUNET_SCHEDULER_run (&service_task, &sctx); + /* shutdown */ + if ((1 == do_daemonize) && (NULL != sctx.server)) + pid_file_delete (&sctx); + GNUNET_free_non_null (sctx.my_handlers); + +shutdown: + if (-1 != sctx.ready_confirm_fd) + { + if (1 != WRITE (sctx.ready_confirm_fd, err ? "I" : "S", 1)) + LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "write"); + GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd)); + } +#if HAVE_MALLINFO + { + char *counter; + + if ( (GNUNET_YES == + GNUNET_CONFIGURATION_have_value (sctx.cfg, service_name, + "GAUGER_HEAP")) && + (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (sctx.cfg, service_name, + "GAUGER_HEAP", + &counter)) ) + { + struct mallinfo mi; + + mi = mallinfo (); + GAUGER (service_name, counter, mi.usmblks, "blocks"); + GNUNET_free (counter); + } + } +#endif + GNUNET_CONFIGURATION_destroy (cfg); + i = 0; + if (NULL != sctx.addrs) + while (NULL != sctx.addrs[i]) + GNUNET_free (sctx.addrs[i++]); + GNUNET_free_non_null (sctx.addrs); + GNUNET_free_non_null (sctx.addrlens); + GNUNET_free_non_null (logfile); + GNUNET_free_non_null (loglev); + GNUNET_free (cfg_fn); + GNUNET_free_non_null (opt_cfg_fn); + GNUNET_free_non_null (sctx.v4_denied); + GNUNET_free_non_null (sctx.v6_denied); + GNUNET_free_non_null (sctx.v4_allowed); + GNUNET_free_non_null (sctx.v6_allowed); + + return err ? GNUNET_SYSERR : sctx.ret; +} + + +/** + * Run a service startup sequence within an existing + * initialized system. + * + * @param service_name our service name + * @param cfg configuration to use + * @param options service options + * @return NULL on error, service handle + */ +struct GNUNET_SERVICE_Context * +GNUNET_SERVICE_start (const char *service_name, + const struct GNUNET_CONFIGURATION_Handle *cfg, + enum GNUNET_SERVICE_Options options) +{ + int i; + struct GNUNET_SERVICE_Context *sctx; + + sctx = GNUNET_new (struct GNUNET_SERVICE_Context); + sctx->ready_confirm_fd = -1; /* no daemonizing */ + sctx->ret = GNUNET_OK; + sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; + sctx->service_name = service_name; + sctx->cfg = cfg; + sctx->options = options; + + /* setup subsystems */ + if (GNUNET_OK != setup_service (sctx)) + { + GNUNET_SERVICE_stop (sctx); + return NULL; + } + if (NULL != sctx->lsocks) + sctx->server = + GNUNET_SERVER_create_with_sockets (&check_access, sctx, sctx->lsocks, + sctx->timeout, sctx->require_found); + else + sctx->server = + GNUNET_SERVER_create (&check_access, sctx, sctx->addrs, sctx->addrlens, + sctx->timeout, sctx->require_found); + + if (NULL == sctx->server) + { + GNUNET_SERVICE_stop (sctx); + return NULL; + } +#ifndef WINDOWS + if (NULL != sctx->addrs) + for (i = 0; NULL != sctx->addrs[i]; i++) + if ((AF_UNIX == sctx->addrs[i]->sa_family) + && ('\0' != ((const struct sockaddr_un *)sctx->addrs[i])->sun_path[0])) + GNUNET_DISK_fix_permissions (((const struct sockaddr_un *)sctx->addrs[i])->sun_path, + sctx->match_uid, + sctx->match_gid); +#endif + sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); + GNUNET_memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); + i = 0; + while ((sctx->my_handlers[i].callback != NULL)) + sctx->my_handlers[i++].callback_cls = sctx; + GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); + return sctx; +} + + +/** + * Obtain the server used by a service. Note that the server must NOT + * be destroyed by the caller. + * + * @param ctx the service context returned from the start function + * @return handle to the server for this service, NULL if there is none + */ +struct GNUNET_SERVER_Handle * +GNUNET_SERVICE_get_server (struct GNUNET_SERVICE_Context *ctx) +{ + return ctx->server; +} + + +/** + * Get the NULL-terminated array of listen sockets for this service. + * + * @param ctx service context to query + * @return NULL if there are no listen sockets, otherwise NULL-terminated + * array of listen sockets. + */ +struct GNUNET_NETWORK_Handle *const* +GNUNET_SERVICE_get_listen_sockets (struct GNUNET_SERVICE_Context *ctx) +{ + return ctx->lsocks; +} + + +/** + * Stop a service that was started with "GNUNET_SERVICE_start". + * + * @param sctx the service context returned from the start function + */ +void +GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx) +{ + unsigned int i; + +#if HAVE_MALLINFO + { + char *counter; + + if ( (GNUNET_YES == + GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, + "GAUGER_HEAP")) && + (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (sctx->cfg, sctx->service_name, + "GAUGER_HEAP", + &counter)) ) + { + struct mallinfo mi; + + mi = mallinfo (); + GAUGER (sctx->service_name, counter, mi.usmblks, "blocks"); + GNUNET_free (counter); + } + } +#endif + if (NULL != sctx->shutdown_task) + { + GNUNET_SCHEDULER_cancel (sctx->shutdown_task); + sctx->shutdown_task = NULL; + } + if (NULL != sctx->server) + GNUNET_SERVER_destroy (sctx->server); + GNUNET_free_non_null (sctx->my_handlers); + if (NULL != sctx->addrs) + { + i = 0; + while (NULL != sctx->addrs[i]) + GNUNET_free (sctx->addrs[i++]); + GNUNET_free (sctx->addrs); + } + GNUNET_free_non_null (sctx->addrlens); + GNUNET_free_non_null (sctx->v4_denied); + GNUNET_free_non_null (sctx->v6_denied); + GNUNET_free_non_null (sctx->v4_allowed); + GNUNET_free_non_null (sctx->v6_allowed); + GNUNET_free (sctx); +} + + +/* end of service.c */ diff --git a/src/transport/test_plugin_transport.c b/src/transport/test_plugin_transport.c index be79d5499..1d92588ea 100644 --- a/src/transport/test_plugin_transport.c +++ b/src/transport/test_plugin_transport.c @@ -552,7 +552,7 @@ setup_plugin_environment () static int -handle_helper_message (void *cls, void *client, +handle_helper_message (void *cls, const struct GNUNET_MessageHeader *hdr) { return GNUNET_OK; diff --git a/src/util/Makefile.am b/src/util/Makefile.am index df319fe77..4418fcfee 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -68,7 +68,6 @@ libgnunetutil_la_SOURCES = \ common_logging.c \ configuration.c \ configuration_loader.c \ - connection.c \ container_bloomfilter.c \ container_heap.c \ container_meta_data.c \ @@ -108,16 +107,10 @@ libgnunetutil_la_SOURCES = \ program.c \ resolver_api.c resolver.h \ scheduler.c \ - server.c \ - server_mst.c \ - server_nc.c \ - server_tc.c \ - service.c \ service_new.c \ signal.c \ strings.c \ time.c \ - socks.c \ speedup.c speedup.h libgnunetutil_la_LIBADD = \ @@ -263,14 +256,13 @@ if HAVE_BENCHMARKS endif if HAVE_SSH_KEY - SSH_USING_TESTS = test_socks.nc +# SSH_USING_TESTS = test_socks.nc endif check_PROGRAMS = \ test_bio \ test_client.nc \ $(TEST_CLIENT_UNIX_NC) \ - $(SSH_USING_TESTS) \ test_common_allocation \ test_common_endian \ test_common_logging \ @@ -298,12 +290,6 @@ check_PROGRAMS = \ test_crypto_rsa \ test_disk \ test_getopt \ - test_connection.nc \ - test_connection_addressing.nc \ - test_connection_receive_cancel.nc \ - test_connection_timeout.nc \ - test_connection_timeout_no_connect.nc \ - test_connection_transmit_cancel.nc \ test_mq \ test_os_network \ test_peer \ @@ -312,11 +298,6 @@ check_PROGRAMS = \ test_resolver_api.nc \ test_scheduler \ test_scheduler_delay \ - test_server.nc \ - test_server_disconnect.nc \ - test_server_with_client.nc \ - test_server_mst_interrupt.nc \ - $(SERVER_CLIENT_UNIX) \ test_service \ test_strings \ test_strings_to_data \ @@ -337,11 +318,6 @@ test_connection_transmit_cancel.log: test_connection_timeout_no_connect.log test_connection_receive_cancel.log: test_connection_transmit_cancel.log test_connection_timeout.log: test_connection_receive_cancel.log test_resolver_api.log: test_connection_timeout.log -test_server.log: test_resolver_api.log -test_server_disconnect.log: test_server.log -test_server_with_client.log: test_server_disconnect.log -test_server_mst_interrupt.log: test_server_with_client.log -test_client_unix.log: test_server_mst_interrupt.log test_bio_SOURCES = \ test_bio.c @@ -518,36 +494,6 @@ test_getopt_SOURCES = \ test_getopt_LDADD = \ libgnunetutil.la -test_connection_nc_SOURCES = \ - test_connection.c -test_connection_nc_LDADD = \ - libgnunetutil.la - -test_connection_addressing_nc_SOURCES = \ - test_connection_addressing.c -test_connection_addressing_nc_LDADD = \ - libgnunetutil.la - -test_connection_receive_cancel_nc_SOURCES = \ - test_connection_receive_cancel.c -test_connection_receive_cancel_nc_LDADD = \ - libgnunetutil.la - -test_connection_timeout_nc_SOURCES = \ - test_connection_timeout.c -test_connection_timeout_nc_LDADD = \ - libgnunetutil.la - -test_connection_timeout_no_connect_nc_SOURCES = \ - test_connection_timeout_no_connect.c -test_connection_timeout_no_connect_nc_LDADD = \ - libgnunetutil.la - -test_connection_transmit_cancel_nc_SOURCES = \ - test_connection_transmit_cancel.c -test_connection_transmit_cancel_nc_LDADD = \ - libgnunetutil.la - test_mq_SOURCES = \ test_mq.c test_mq_LDADD = \ @@ -588,32 +534,6 @@ test_scheduler_delay_SOURCES = \ test_scheduler_delay_LDADD = \ libgnunetutil.la -test_server_mst_interrupt_nc_SOURCES = \ - test_server_mst_interrupt.c -test_server_mst_interrupt_nc_LDADD = \ - libgnunetutil.la - -test_server_nc_SOURCES = \ - test_server.c -test_server_nc_LDADD = \ - libgnunetutil.la - -test_server_disconnect_nc_SOURCES = \ - test_server_disconnect.c -test_server_disconnect_nc_LDADD = \ - libgnunetutil.la - -test_server_with_client_nc_SOURCES = \ - test_server_with_client.c -test_server_with_client_nc_LDADD = \ - libgnunetutil.la - -test_server_with_client_unix_SOURCES = \ - test_server_with_client_unix.c -test_server_with_client_unix_LDADD = \ - libgnunetutil.la - - test_service_SOURCES = \ test_service.c test_service_LDADD = \ @@ -624,7 +544,6 @@ test_strings_SOURCES = \ test_strings_LDADD = \ libgnunetutil.la - test_strings_to_data_SOURCES = \ test_strings_to_data.c test_strings_to_data_LDADD = \ diff --git a/src/util/connection.c b/src/util/connection.c deleted file mode 100644 index e822b264f..000000000 --- a/src/util/connection.c +++ /dev/null @@ -1,1648 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009-2013 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file util/connection.c - * @brief TCP connection management - * @author Christian Grothoff - * - * This code is rather complex. Only modify it if you - * 1) Have a NEW testcase showing that the new code - * is needed and correct - * 2) All EXISTING testcases pass with the new code - * These rules should apply in general, but for this - * module they are VERY, VERY important. - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_resolver_service.h" - - -#define LOG(kind,...) GNUNET_log_from (kind, "util-connection", __VA_ARGS__) - -#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-connection", syscall) - - -/** - * Transmission handle. There can only be one for each connection. - */ -struct GNUNET_CONNECTION_TransmitHandle -{ - - /** - * Function to call if the send buffer has notify_size - * bytes available. - */ - GNUNET_CONNECTION_TransmitReadyNotify notify_ready; - - /** - * Closure for notify_ready. - */ - void *notify_ready_cls; - - /** - * Our connection handle. - */ - struct GNUNET_CONNECTION_Handle *connection; - - /** - * Timeout for receiving (in absolute time). - */ - struct GNUNET_TIME_Absolute transmit_timeout; - - /** - * Task called on timeout. - */ - struct GNUNET_SCHEDULER_Task * timeout_task; - - /** - * At what number of bytes available in the - * write buffer should the notify method be called? - */ - size_t notify_size; - -}; - - -/** - * During connect, we try multiple possible IP addresses - * to find out which one might work. - */ -struct AddressProbe -{ - - /** - * This is a linked list. - */ - struct AddressProbe *next; - - /** - * This is a doubly-linked list. - */ - struct AddressProbe *prev; - - /** - * The address; do not free (allocated at the end of this struct). - */ - const struct sockaddr *addr; - - /** - * Underlying OS's socket. - */ - struct GNUNET_NETWORK_Handle *sock; - - /** - * Connection for which we are probing. - */ - struct GNUNET_CONNECTION_Handle *connection; - - /** - * Lenth of addr. - */ - socklen_t addrlen; - - /** - * Task waiting for the connection to finish connecting. - */ - struct GNUNET_SCHEDULER_Task * task; -}; - - -/** - * @brief handle for a network connection - */ -struct GNUNET_CONNECTION_Handle -{ - - /** - * Configuration to use. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Linked list of sockets we are currently trying out - * (during connect). - */ - struct AddressProbe *ap_head; - - /** - * Linked list of sockets we are currently trying out - * (during connect). - */ - struct AddressProbe *ap_tail; - - /** - * Network address of the other end-point, may be NULL. - */ - struct sockaddr *addr; - - /** - * Pointer to the hostname if connection was - * created using DNS lookup, otherwise NULL. - */ - char *hostname; - - /** - * Underlying OS's socket, set to NULL after fatal errors. - */ - struct GNUNET_NETWORK_Handle *sock; - - /** - * Function to call on data received, NULL if no receive is pending. - */ - GNUNET_CONNECTION_Receiver receiver; - - /** - * Closure for @e receiver. - */ - void *receiver_cls; - - /** - * Pointer to our write buffer. - */ - char *write_buffer; - - /** - * Current size of our @e write_buffer. - */ - size_t write_buffer_size; - - /** - * Current write-offset in @e write_buffer (where - * would we write next). - */ - size_t write_buffer_off; - - /** - * Current read-offset in @e write_buffer (how many - * bytes have already been sent). - */ - size_t write_buffer_pos; - - /** - * Length of @e addr. - */ - socklen_t addrlen; - - /** - * Read task that we may need to wait for. - */ - struct GNUNET_SCHEDULER_Task *read_task; - - /** - * Write task that we may need to wait for. - */ - struct GNUNET_SCHEDULER_Task *write_task; - - /** - * Handle to a pending DNS lookup request. - */ - struct GNUNET_RESOLVER_RequestHandle *dns_active; - - /** - * The handle we return for #GNUNET_CONNECTION_notify_transmit_ready(). - */ - struct GNUNET_CONNECTION_TransmitHandle nth; - - /** - * Timeout for receiving (in absolute time). - */ - struct GNUNET_TIME_Absolute receive_timeout; - - /** - * Maximum number of bytes to read (for receiving). - */ - size_t max; - - /** - * Port to connect to. - */ - uint16_t port; - - /** - * When shutdown, do not ever actually close the socket, but - * free resources. Only should ever be set if using program - * termination as a signal (because only then will the leaked - * socket be freed!) - */ - int8_t persist; - - /** - * Usually 0. Set to 1 if this handle is in use, and should - * #GNUNET_CONNECTION_destroy() be called right now, the action needs - * to be deferred by setting it to -1. - */ - int8_t destroy_later; - - /** - * Handle to subsequent connection after proxy handshake completes, - */ - struct GNUNET_CONNECTION_Handle *proxy_handshake; - -}; - - -/** - * Set the persist option on this connection handle. Indicates - * that the underlying socket or fd should never really be closed. - * Used for indicating process death. - * - * @param connection the connection to set persistent - */ -void -GNUNET_CONNECTION_persist_ (struct GNUNET_CONNECTION_Handle *connection) -{ - connection->persist = GNUNET_YES; -} - - -/** - * Disable the "CORK" feature for communication with the given connection, - * forcing the OS to immediately flush the buffer on transmission - * instead of potentially buffering multiple messages. Essentially - * reduces the OS send buffers to zero. - * Used to make sure that the last messages sent through the connection - * reach the other side before the process is terminated. - * - * @param connection the connection to make flushing and blocking - * @return #GNUNET_OK on success - */ -int -GNUNET_CONNECTION_disable_corking (struct GNUNET_CONNECTION_Handle *connection) -{ - return GNUNET_NETWORK_socket_disable_corking (connection->sock); -} - - -/** - * Create a connection handle by boxing an existing OS socket. The OS - * socket should henceforth be no longer used directly. - * #GNUNET_connection_destroy() will close it. - * - * @param osSocket existing socket to box - * @return the boxed connection handle - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_existing (struct GNUNET_NETWORK_Handle *osSocket) -{ - struct GNUNET_CONNECTION_Handle *connection; - - connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); - connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; - connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); - connection->sock = osSocket; - return connection; -} - - -/** - * Create a connection handle by accepting on a listen socket. This - * function may block if the listen socket has no connection ready. - * - * @param access_cb function to use to check if access is allowed - * @param access_cb_cls closure for @a access_cb - * @param lsock listen socket - * @return the connection handle, NULL on error - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access_cb, - void *access_cb_cls, - struct GNUNET_NETWORK_Handle *lsock) -{ - struct GNUNET_CONNECTION_Handle *connection; - char addr[128]; - socklen_t addrlen; - struct GNUNET_NETWORK_Handle *sock; - int aret; - struct sockaddr_in *v4; - struct sockaddr_in6 *v6; - struct sockaddr *sa; - void *uaddr; -#ifdef SO_PEERCRED - struct ucred uc; - socklen_t olen; -#endif - struct GNUNET_CONNECTION_Credentials *gcp; -#if HAVE_GETPEEREID || defined(SO_PEERCRED) || HAVE_GETPEERUCRED - struct GNUNET_CONNECTION_Credentials gc; - - gc.uid = 0; - gc.gid = 0; -#endif - - addrlen = sizeof (addr); - sock = - GNUNET_NETWORK_socket_accept (lsock, - (struct sockaddr *) &addr, - &addrlen); - if (NULL == sock) - { - if (EAGAIN != errno) - LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "accept"); - return NULL; - } - if ((addrlen > sizeof (addr)) || (addrlen < sizeof (sa_family_t))) - { - GNUNET_break (0); - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); - return NULL; - } - - sa = (struct sockaddr *) addr; - v6 = (struct sockaddr_in6 *) addr; - if ( (AF_INET6 == sa->sa_family) && - (IN6_IS_ADDR_V4MAPPED (&v6->sin6_addr)) ) - { - /* convert to V4 address */ - v4 = GNUNET_new (struct sockaddr_in); - memset (v4, 0, sizeof (struct sockaddr_in)); - v4->sin_family = AF_INET; -#if HAVE_SOCKADDR_IN_SIN_LEN - v4->sin_len = (u_char) sizeof (struct sockaddr_in); -#endif - GNUNET_memcpy (&v4->sin_addr, - &((char *) &v6->sin6_addr)[sizeof (struct in6_addr) - - sizeof (struct in_addr)], - sizeof (struct in_addr)); - v4->sin_port = v6->sin6_port; - uaddr = v4; - addrlen = sizeof (struct sockaddr_in); - } - else - { - uaddr = GNUNET_malloc (addrlen); - GNUNET_memcpy (uaddr, addr, addrlen); - } - gcp = NULL; - if (AF_UNIX == sa->sa_family) - { -#if HAVE_GETPEEREID - /* most BSDs */ - if (0 == getpeereid (GNUNET_NETWORK_get_fd (sock), - &gc.uid, - &gc.gid)) - gcp = &gc; -#else -#ifdef SO_PEERCRED - /* largely traditional GNU/Linux */ - olen = sizeof (uc); - if ( (0 == - getsockopt (GNUNET_NETWORK_get_fd (sock), - SOL_SOCKET, - SO_PEERCRED, - &uc, - &olen)) && - (olen == sizeof (uc)) ) - { - gc.uid = uc.uid; - gc.gid = uc.gid; - gcp = &gc; - } -#else -#if HAVE_GETPEERUCRED - /* this is for Solaris 10 */ - ucred_t *uc; - - uc = NULL; - if (0 == getpeerucred (GNUNET_NETWORK_get_fd (sock), &uc)) - { - gc.uid = ucred_geteuid (uc); - gc.gid = ucred_getegid (uc); - gcp = &gc; - } - ucred_free (uc); -#endif -#endif -#endif - } - - if ( (NULL != access_cb) && - (GNUNET_YES != (aret = access_cb (access_cb_cls, - gcp, - uaddr, - addrlen))) ) - { - if (GNUNET_NO == aret) - LOG (GNUNET_ERROR_TYPE_INFO, - _("Access denied to `%s'\n"), - GNUNET_a2s (uaddr, - addrlen)); - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_shutdown (sock, - SHUT_RDWR)); - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); - GNUNET_free (uaddr); - return NULL; - } - connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); - connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; - connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); - connection->addr = uaddr; - connection->addrlen = addrlen; - connection->sock = sock; - LOG (GNUNET_ERROR_TYPE_INFO, - _("Accepting connection from `%s': %p\n"), - GNUNET_a2s (uaddr, - addrlen), - connection); - return connection; -} - - -/** - * Obtain the network address of the other party. - * - * @param connection the client to get the address for - * @param addr where to store the address - * @param addrlen where to store the length of the @a addr - * @return #GNUNET_OK on success - */ -int -GNUNET_CONNECTION_get_address (struct GNUNET_CONNECTION_Handle *connection, - void **addr, - size_t *addrlen) -{ - if ((NULL == connection->addr) || (0 == connection->addrlen)) - return GNUNET_NO; - *addr = GNUNET_malloc (connection->addrlen); - GNUNET_memcpy (*addr, connection->addr, connection->addrlen); - *addrlen = connection->addrlen; - return GNUNET_OK; -} - - -/** - * Tell the receiver callback that we had an IO error. - * - * @param connection connection to signal error - * @param errcode error code to send - */ -static void -signal_receive_error (struct GNUNET_CONNECTION_Handle *connection, - int errcode) -{ - GNUNET_CONNECTION_Receiver receiver; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Receive encounters error (%s), connection closed (%p)\n", - STRERROR (errcode), - connection); - GNUNET_assert (NULL != (receiver = connection->receiver)); - connection->receiver = NULL; - receiver (connection->receiver_cls, - NULL, - 0, - connection->addr, - connection->addrlen, - errcode); -} - - -/** - * Tell the receiver callback that a timeout was reached. - * - * @param connection connection to signal for - */ -static void -signal_receive_timeout (struct GNUNET_CONNECTION_Handle *connection) -{ - GNUNET_CONNECTION_Receiver receiver; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Connection signals timeout to receiver (%p)!\n", - connection); - GNUNET_assert (NULL != (receiver = connection->receiver)); - connection->receiver = NULL; - receiver (connection->receiver_cls, NULL, 0, NULL, 0, 0); -} - - -/** - * We failed to transmit data to the service, signal the error. - * - * @param connection handle that had trouble - * @param ecode error code (errno) - */ -static void -signal_transmit_error (struct GNUNET_CONNECTION_Handle *connection, - int ecode) -{ - GNUNET_CONNECTION_TransmitReadyNotify notify; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Transmission encounterd error (%s), connection closed (%p)\n", - STRERROR (ecode), - connection); - if (NULL != connection->sock) - { - (void) GNUNET_NETWORK_socket_shutdown (connection->sock, - SHUT_RDWR); - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (connection->sock)); - connection->sock = NULL; - GNUNET_assert (NULL == connection->write_task); - } - if (NULL != connection->read_task) - { - /* send errors trigger read errors... */ - GNUNET_SCHEDULER_cancel (connection->read_task); - connection->read_task = NULL; - signal_receive_timeout (connection); - return; - } - if (NULL == connection->nth.notify_ready) - return; /* nobody to tell about it */ - notify = connection->nth.notify_ready; - connection->nth.notify_ready = NULL; - notify (connection->nth.notify_ready_cls, - 0, - NULL); -} - - -/** - * We've failed for good to establish a connection (timeout or - * no more addresses to try). - * - * @param connection the connection we tried to establish - */ -static void -connect_fail_continuation (struct GNUNET_CONNECTION_Handle *connection) -{ - LOG (GNUNET_ERROR_TYPE_INFO, - "Failed to establish TCP connection to `%s:%u', no further addresses to try.\n", - connection->hostname, - connection->port); - GNUNET_break (NULL == connection->ap_head); - GNUNET_break (NULL == connection->ap_tail); - GNUNET_break (GNUNET_NO == connection->dns_active); - GNUNET_break (NULL == connection->sock); - GNUNET_assert (NULL == connection->write_task); - GNUNET_assert (NULL == connection->proxy_handshake); - - /* signal errors for jobs that used to wait on the connection */ - connection->destroy_later = 1; - if (NULL != connection->receiver) - signal_receive_error (connection, - ECONNREFUSED); - if (NULL != connection->nth.notify_ready) - { - GNUNET_assert (NULL != connection->nth.timeout_task); - GNUNET_SCHEDULER_cancel (connection->nth.timeout_task); - connection->nth.timeout_task = NULL; - signal_transmit_error (connection, - ECONNREFUSED); - } - if (-1 == connection->destroy_later) - { - /* do it now */ - connection->destroy_later = 0; - GNUNET_CONNECTION_destroy (connection); - return; - } - connection->destroy_later = 0; -} - - -/** - * We are ready to transmit (or got a timeout). - * - * @param cls our connection handle - */ -static void -transmit_ready (void *cls); - - -/** - * This function is called once we either timeout or have data ready - * to read. - * - * @param cls connection to read from - */ -static void -receive_ready (void *cls); - - -/** - * We've succeeded in establishing a connection. - * - * @param connection the connection we tried to establish - */ -static void -connect_success_continuation (struct GNUNET_CONNECTION_Handle *connection) -{ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Connection to `%s' succeeded! (%p)\n", - GNUNET_a2s (connection->addr, - connection->addrlen), - connection); - /* trigger jobs that waited for the connection */ - if (NULL != connection->receiver) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Connection succeeded, starting with receiving data (%p)\n", - connection); - GNUNET_assert (NULL == connection->read_task); - connection->read_task = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining - (connection->receive_timeout), - connection->sock, - &receive_ready, connection); - } - if (NULL != connection->nth.notify_ready) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Connection succeeded, starting with sending data (%p)\n", - connection); - GNUNET_assert (connection->nth.timeout_task != NULL); - GNUNET_SCHEDULER_cancel (connection->nth.timeout_task); - connection->nth.timeout_task = NULL; - GNUNET_assert (connection->write_task == NULL); - connection->write_task = - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining - (connection->nth.transmit_timeout), connection->sock, - &transmit_ready, connection); - } -} - - -/** - * Scheduler let us know that we're either ready to write on the - * socket OR connect timed out. Do the right thing. - * - * @param cls the `struct AddressProbe *` with the address that we are probing - */ -static void -connect_probe_continuation (void *cls) -{ - struct AddressProbe *ap = cls; - struct GNUNET_CONNECTION_Handle *connection = ap->connection; - const struct GNUNET_SCHEDULER_TaskContext *tc; - struct AddressProbe *pos; - int error; - socklen_t len; - - GNUNET_assert (NULL != ap->sock); - GNUNET_CONTAINER_DLL_remove (connection->ap_head, - connection->ap_tail, - ap); - len = sizeof (error); - errno = 0; - error = 0; - tc = GNUNET_SCHEDULER_get_task_context (); - if ( (0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) || - (GNUNET_OK != - GNUNET_NETWORK_socket_getsockopt (ap->sock, - SOL_SOCKET, - SO_ERROR, - &error, - &len)) || - (0 != error) ) - { - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (ap->sock)); - GNUNET_free (ap); - if ( (NULL == connection->ap_head) && - (GNUNET_NO == connection->dns_active) && - (NULL == connection->proxy_handshake) ) - connect_fail_continuation (connection); - return; - } - GNUNET_assert (NULL == connection->sock); - connection->sock = ap->sock; - GNUNET_assert (NULL == connection->addr); - connection->addr = GNUNET_malloc (ap->addrlen); - GNUNET_memcpy (connection->addr, ap->addr, ap->addrlen); - connection->addrlen = ap->addrlen; - GNUNET_free (ap); - /* cancel all other attempts */ - while (NULL != (pos = connection->ap_head)) - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock)); - GNUNET_SCHEDULER_cancel (pos->task); - GNUNET_CONTAINER_DLL_remove (connection->ap_head, - connection->ap_tail, - pos); - GNUNET_free (pos); - } - connect_success_continuation (connection); -} - - -/** - * Try to establish a connection given the specified address. - * This function is called by the resolver once we have a DNS reply. - * - * @param cls our `struct GNUNET_CONNECTION_Handle *` - * @param addr address to try, NULL for "last call" - * @param addrlen length of @a addr - */ -static void -try_connect_using_address (void *cls, - const struct sockaddr *addr, - socklen_t addrlen) -{ - struct GNUNET_CONNECTION_Handle *connection = cls; - struct AddressProbe *ap; - struct GNUNET_TIME_Relative delay; - - if (NULL == addr) - { - connection->dns_active = NULL; - if ((NULL == connection->ap_head) && - (NULL == connection->sock) && - (NULL == connection->proxy_handshake)) - connect_fail_continuation (connection); - return; - } - if (NULL != connection->sock) - return; /* already connected */ - GNUNET_assert (NULL == connection->addr); - /* try to connect */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Trying to connect using address `%s:%u/%s:%u'\n", - connection->hostname, - connection->port, - GNUNET_a2s (addr, addrlen), - connection->port); - ap = GNUNET_malloc (sizeof (struct AddressProbe) + addrlen); - ap->addr = (const struct sockaddr *) &ap[1]; - GNUNET_memcpy (&ap[1], addr, addrlen); - ap->addrlen = addrlen; - ap->connection = connection; - - switch (ap->addr->sa_family) - { - case AF_INET: - ((struct sockaddr_in *) ap->addr)->sin_port = htons (connection->port); - break; - case AF_INET6: - ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (connection->port); - break; - default: - GNUNET_break (0); - GNUNET_free (ap); - return; /* not supported by us */ - } - ap->sock = GNUNET_NETWORK_socket_create (ap->addr->sa_family, - SOCK_STREAM, 0); - if (NULL == ap->sock) - { - GNUNET_free (ap); - return; /* not supported by OS */ - } - LOG (GNUNET_ERROR_TYPE_INFO, - "Trying to connect to `%s' (%p)\n", - GNUNET_a2s (ap->addr, ap->addrlen), - connection); - if ((GNUNET_OK != - GNUNET_NETWORK_socket_connect (ap->sock, - ap->addr, - ap->addrlen)) && - (EINPROGRESS != errno)) - { - /* maybe refused / unsupported address, try next */ - LOG_STRERROR (GNUNET_ERROR_TYPE_INFO, "connect"); - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ap->sock)); - GNUNET_free (ap); - return; - } - GNUNET_CONTAINER_DLL_insert (connection->ap_head, connection->ap_tail, ap); - delay = GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT; - if (NULL != connection->nth.notify_ready) - delay = GNUNET_TIME_relative_min (delay, - GNUNET_TIME_absolute_get_remaining (connection->nth.transmit_timeout)); - if (NULL != connection->receiver) - delay = GNUNET_TIME_relative_min (delay, - GNUNET_TIME_absolute_get_remaining (connection->receive_timeout)); - ap->task = GNUNET_SCHEDULER_add_write_net (delay, - ap->sock, - &connect_probe_continuation, - ap); -} - - -/** - * Create a connection handle by (asynchronously) connecting to a host. - * This function returns immediately, even if the connection has not - * yet been established. This function only creates TCP connections. - * - * @param cfg configuration to use - * @param hostname name of the host to connect to - * @param port port to connect to - * @return the connection handle - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *hostname, - uint16_t port) -{ - struct GNUNET_CONNECTION_Handle *connection; - - GNUNET_assert (0 < strlen (hostname)); /* sanity check */ - connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); - connection->cfg = cfg; - connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; - connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); - connection->port = port; - connection->hostname = GNUNET_strdup (hostname); - connection->dns_active = - GNUNET_RESOLVER_ip_get (connection->hostname, - AF_UNSPEC, - GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT, - &try_connect_using_address, - connection); - return connection; -} - - -/** - * Create a connection handle by connecting to a UNIX domain service. - * This function returns immediately, even if the connection has not - * yet been established. This function only creates UNIX connections. - * - * @param cfg configuration to use - * @param unixpath path to connect to - * @return the connection handle, NULL on systems without UNIX support - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_connect_to_unixpath (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *unixpath) -{ -#ifdef AF_UNIX - struct GNUNET_CONNECTION_Handle *connection; - struct sockaddr_un *un; - - GNUNET_assert (0 < strlen (unixpath)); /* sanity check */ - un = GNUNET_new (struct sockaddr_un); - un->sun_family = AF_UNIX; - strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1); -#ifdef LINUX - { - int abstract; - - abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg, - "TESTING", - "USE_ABSTRACT_SOCKETS"); - if (GNUNET_YES == abstract) - un->sun_path[0] = '\0'; - } -#endif -#if HAVE_SOCKADDR_UN_SUN_LEN - un->sun_len = (u_char) sizeof (struct sockaddr_un); -#endif - connection = GNUNET_new (struct GNUNET_CONNECTION_Handle); - connection->cfg = cfg; - connection->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE; - connection->write_buffer = GNUNET_malloc (connection->write_buffer_size); - connection->port = 0; - connection->hostname = NULL; - connection->addr = (struct sockaddr *) un; - connection->addrlen = sizeof (struct sockaddr_un); - connection->sock = GNUNET_NETWORK_socket_create (AF_UNIX, - SOCK_STREAM, - 0); - if (NULL == connection->sock) - { - GNUNET_free (connection->addr); - GNUNET_free (connection->write_buffer); - GNUNET_free (connection); - return NULL; - } - if ( (GNUNET_OK != - GNUNET_NETWORK_socket_connect (connection->sock, - connection->addr, - connection->addrlen)) && - (EINPROGRESS != errno) ) - { - /* Just return; we expect everything to work eventually so don't fail HARD */ - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (connection->sock)); - connection->sock = NULL; - return connection; - } - connect_success_continuation (connection); - return connection; -#else - return NULL; -#endif -} - - -/** - * Create a connection handle by (asynchronously) connecting to a host. - * This function returns immediately, even if the connection has not - * yet been established. This function only creates TCP connections. - * - * @param s socket to connect - * @param serv_addr server address - * @param addrlen length of @a serv_addr - * @return the connection handle - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_connect_socket (struct GNUNET_NETWORK_Handle *s, - const struct sockaddr *serv_addr, - socklen_t addrlen) -{ - struct GNUNET_CONNECTION_Handle *connection; - - if ( (GNUNET_OK != - GNUNET_NETWORK_socket_connect (s, serv_addr, addrlen)) && - (EINPROGRESS != errno) ) - { - /* maybe refused / unsupported address, try next */ - LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG, - "connect"); - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Attempt to connect to `%s' failed\n", - GNUNET_a2s (serv_addr, - addrlen)); - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (s)); - return NULL; - } - connection = GNUNET_CONNECTION_create_from_existing (s); - connection->addr = GNUNET_malloc (addrlen); - GNUNET_memcpy (connection->addr, serv_addr, addrlen); - connection->addrlen = addrlen; - LOG (GNUNET_ERROR_TYPE_INFO, - "Trying to connect to `%s' (%p)\n", - GNUNET_a2s (serv_addr, addrlen), - connection); - return connection; -} - - -/** - * Create a connection handle by creating a socket and - * (asynchronously) connecting to a host. This function returns - * immediately, even if the connection has not yet been established. - * This function only creates TCP connections. - * - * @param af_family address family to use - * @param serv_addr server address - * @param addrlen length of @a serv_addr - * @return the connection handle - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_from_sockaddr (int af_family, - const struct sockaddr *serv_addr, - socklen_t addrlen) -{ - struct GNUNET_NETWORK_Handle *s; - - s = GNUNET_NETWORK_socket_create (af_family, SOCK_STREAM, 0); - if (NULL == s) - { - LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, - "socket"); - return NULL; - } - return GNUNET_CONNECTION_connect_socket (s, - serv_addr, - addrlen); -} - - -/** - * Check if connection is valid (no fatal errors have happened so far). - * Note that a connection that is still trying to connect is considered - * valid. - * - * @param connection connection to check - * @return #GNUNET_YES if valid, #GNUNET_NO otherwise - */ -int -GNUNET_CONNECTION_check (struct GNUNET_CONNECTION_Handle *connection) -{ - if ((NULL != connection->ap_head) || - (NULL != connection->dns_active) || - (NULL != connection->proxy_handshake)) - return GNUNET_YES; /* still trying to connect */ - if ( (0 != connection->destroy_later) || - (NULL == connection->sock) ) - return GNUNET_NO; - return GNUNET_YES; -} - - -/** - * Close the connection and free associated resources. There must - * not be any pending requests for reading or writing to the - * connection at this time. - * - * @param connection connection to destroy - */ -void -GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *connection) -{ - struct AddressProbe *pos; - - if (0 != connection->destroy_later) - { - connection->destroy_later = -1; - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Shutting down connection (%p)\n", - connection); - GNUNET_assert (NULL == connection->nth.notify_ready); - GNUNET_assert (NULL == connection->receiver); - if (NULL != connection->write_task) - { - GNUNET_SCHEDULER_cancel (connection->write_task); - connection->write_task = NULL; - connection->write_buffer_off = 0; - } - if (NULL != connection->read_task) - { - GNUNET_SCHEDULER_cancel (connection->read_task); - connection->read_task = NULL; - } - if (NULL != connection->nth.timeout_task) - { - GNUNET_SCHEDULER_cancel (connection->nth.timeout_task); - connection->nth.timeout_task = NULL; - } - connection->nth.notify_ready = NULL; - if (NULL != connection->dns_active) - { - GNUNET_RESOLVER_request_cancel (connection->dns_active); - connection->dns_active = NULL; - } - if (NULL != connection->proxy_handshake) - { - /* GNUNET_CONNECTION_destroy (connection->proxy_handshake); */ - connection->proxy_handshake->destroy_later = -1; - connection->proxy_handshake = NULL; /* Not leaked ??? */ - } - while (NULL != (pos = connection->ap_head)) - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock)); - GNUNET_SCHEDULER_cancel (pos->task); - GNUNET_CONTAINER_DLL_remove (connection->ap_head, - connection->ap_tail, - pos); - GNUNET_free (pos); - } - if ( (NULL != connection->sock) && - (GNUNET_YES != connection->persist) ) - { - if ((GNUNET_OK != - GNUNET_NETWORK_socket_shutdown (connection->sock, - SHUT_RDWR)) && - (ENOTCONN != errno) && - (ECONNRESET != errno) ) - LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, - "shutdown"); - } - if (NULL != connection->sock) - { - if (GNUNET_YES != connection->persist) - { - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (connection->sock)); - } - else - { - GNUNET_NETWORK_socket_free_memory_only_ (connection->sock); /* at least no memory leak (we deliberately - * leak the socket in this special case) ... */ - } - } - GNUNET_free_non_null (connection->addr); - GNUNET_free_non_null (connection->hostname); - GNUNET_free (connection->write_buffer); - GNUNET_free (connection); -} - - -/** - * This function is called once we either timeout - * or have data ready to read. - * - * @param cls connection to read from - */ -static void -receive_ready (void *cls) -{ - struct GNUNET_CONNECTION_Handle *connection = cls; - const struct GNUNET_SCHEDULER_TaskContext *tc; - char buffer[connection->max]; - ssize_t ret; - GNUNET_CONNECTION_Receiver receiver; - - connection->read_task = NULL; - tc = GNUNET_SCHEDULER_get_task_context (); - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Receive from `%s' encounters error: timeout (%s, %p)\n", - GNUNET_a2s (connection->addr, - connection->addrlen), - GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (connection->receive_timeout), - GNUNET_YES), - connection); - signal_receive_timeout (connection); - return; - } - if (NULL == connection->sock) - { - /* connect failed for good */ - signal_receive_error (connection, ECONNREFUSED); - return; - } - GNUNET_assert (GNUNET_NETWORK_fdset_isset (tc->read_ready, - connection->sock)); -RETRY: - ret = GNUNET_NETWORK_socket_recv (connection->sock, - buffer, - connection->max); - if (-1 == ret) - { - if (EINTR == errno) - goto RETRY; - signal_receive_error (connection, errno); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "receive_ready read %u/%u bytes from `%s' (%p)!\n", - (unsigned int) ret, - connection->max, - GNUNET_a2s (connection->addr, - connection->addrlen), - connection); - GNUNET_assert (NULL != (receiver = connection->receiver)); - connection->receiver = NULL; - receiver (connection->receiver_cls, - buffer, - ret, - connection->addr, - connection->addrlen, - 0); -} - - -/** - * Receive data from the given connection. Note that this function - * will call @a receiver asynchronously using the scheduler. It will - * "immediately" return. Note that there MUST only be one active - * receive call per connection at any given point in time (so do not - * call receive again until the receiver callback has been invoked). - * - * @param connection connection handle - * @param max maximum number of bytes to read - * @param timeout maximum amount of time to wait - * @param receiver function to call with received data - * @param receiver_cls closure for @a receiver - */ -void -GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *connection, - size_t max, - struct GNUNET_TIME_Relative timeout, - GNUNET_CONNECTION_Receiver receiver, - void *receiver_cls) -{ - GNUNET_assert ((NULL == connection->read_task) && - (NULL == connection->receiver)); - GNUNET_assert (NULL != receiver); - connection->receiver = receiver; - connection->receiver_cls = receiver_cls; - connection->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout); - connection->max = max; - if (NULL != connection->sock) - { - connection->read_task = - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining - (connection->receive_timeout), - connection->sock, - &receive_ready, - connection); - return; - } - if ((NULL == connection->dns_active) && - (NULL == connection->ap_head) && - (NULL == connection->proxy_handshake)) - { - connection->receiver = NULL; - receiver (receiver_cls, - NULL, 0, - NULL, 0, - ETIMEDOUT); - return; - } -} - - -/** - * Cancel receive job on the given connection. Note that the - * receiver callback must not have been called yet in order - * for the cancellation to be valid. - * - * @param connection connection handle - * @return closure of the original receiver callback closure - */ -void * -GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *connection) -{ - if (NULL != connection->read_task) - { - GNUNET_assert (connection == - GNUNET_SCHEDULER_cancel (connection->read_task)); - connection->read_task = NULL; - } - connection->receiver = NULL; - return connection->receiver_cls; -} - - -/** - * Try to call the transmit notify method (check if we do - * have enough space available first)! - * - * @param connection connection for which we should do this processing - * @return #GNUNET_YES if we were able to call notify - */ -static int -process_notify (struct GNUNET_CONNECTION_Handle *connection) -{ - size_t used; - size_t avail; - size_t size; - GNUNET_CONNECTION_TransmitReadyNotify notify; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "process_notify is running\n"); - GNUNET_assert (NULL == connection->write_task); - if (NULL == (notify = connection->nth.notify_ready)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "No one to notify\n"); - return GNUNET_NO; - } - used = connection->write_buffer_off - connection->write_buffer_pos; - avail = connection->write_buffer_size - used; - size = connection->nth.notify_size; - if (size > avail) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Not enough buffer\n"); - return GNUNET_NO; - } - connection->nth.notify_ready = NULL; - if (connection->write_buffer_size - connection->write_buffer_off < size) - { - /* need to compact */ - memmove (connection->write_buffer, - &connection->write_buffer[connection->write_buffer_pos], - used); - connection->write_buffer_off -= connection->write_buffer_pos; - connection->write_buffer_pos = 0; - } - avail = connection->write_buffer_size - connection->write_buffer_off; - GNUNET_assert (avail >= size); - size = - notify (connection->nth.notify_ready_cls, avail, - &connection->write_buffer[connection->write_buffer_off]); - GNUNET_assert (size <= avail); - if (0 != size) - connection->write_buffer_off += size; - return GNUNET_YES; -} - - -/** - * Task invoked by the scheduler when a call to transmit - * is timing out (we never got enough buffer space to call - * the callback function before the specified timeout - * expired). - * - * This task notifies the client about the timeout. - * - * @param cls the `struct GNUNET_CONNECTION_Handle` - */ -static void -transmit_timeout (void *cls) -{ - struct GNUNET_CONNECTION_Handle *connection = cls; - GNUNET_CONNECTION_TransmitReadyNotify notify; - - connection->nth.timeout_task = NULL; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Transmit to `%s:%u/%s' fails, time out reached (%p).\n", - connection->hostname, - connection->port, - GNUNET_a2s (connection->addr, - connection->addrlen), - connection); - notify = connection->nth.notify_ready; - GNUNET_assert (NULL != notify); - connection->nth.notify_ready = NULL; - notify (connection->nth.notify_ready_cls, - 0, - NULL); -} - - -/** - * Task invoked by the scheduler when we failed to connect - * at the time of being asked to transmit. - * - * This task notifies the client about the error. - * - * @param cls the `struct GNUNET_CONNECTION_Handle` - */ -static void -connect_error (void *cls) -{ - struct GNUNET_CONNECTION_Handle *connection = cls; - GNUNET_CONNECTION_TransmitReadyNotify notify; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Transmission request of size %u fails (%s/%u), connection failed (%p).\n", - connection->nth.notify_size, - connection->hostname, - connection->port, - connection); - connection->write_task = NULL; - notify = connection->nth.notify_ready; - connection->nth.notify_ready = NULL; - notify (connection->nth.notify_ready_cls, - 0, - NULL); -} - - -/** - * We are ready to transmit (or got a timeout). - * - * @param cls our connection handle - */ -static void -transmit_ready (void *cls) -{ - struct GNUNET_CONNECTION_Handle *connection = cls; - GNUNET_CONNECTION_TransmitReadyNotify notify; - const struct GNUNET_SCHEDULER_TaskContext *tc; - ssize_t ret; - size_t have; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "transmit_ready running (%p).\n", - connection); - GNUNET_assert (NULL != connection->write_task); - connection->write_task = NULL; - GNUNET_assert (NULL == connection->nth.timeout_task); - tc = GNUNET_SCHEDULER_get_task_context (); - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Transmit to `%s' fails, time out reached (%p).\n", - GNUNET_a2s (connection->addr, - connection->addrlen), - connection); - notify = connection->nth.notify_ready; - GNUNET_assert (NULL != notify); - connection->nth.notify_ready = NULL; - notify (connection->nth.notify_ready_cls, 0, NULL); - return; - } - GNUNET_assert (NULL != connection->sock); - if (NULL == tc->write_ready) - { - /* special circumstances (in particular, PREREQ_DONE after - * connect): not yet ready to write, but no "fatal" error either. - * Hence retry. */ - goto SCHEDULE_WRITE; - } - if (! GNUNET_NETWORK_fdset_isset (tc->write_ready, - connection->sock)) - { - GNUNET_assert (NULL == connection->write_task); - /* special circumstances (in particular, shutdown): not yet ready - * to write, but no "fatal" error either. Hence retry. */ - goto SCHEDULE_WRITE; - } - GNUNET_assert (connection->write_buffer_off >= connection->write_buffer_pos); - if ((NULL != connection->nth.notify_ready) && - (connection->write_buffer_size < connection->nth.notify_size)) - { - connection->write_buffer = - GNUNET_realloc (connection->write_buffer, connection->nth.notify_size); - connection->write_buffer_size = connection->nth.notify_size; - } - process_notify (connection); - have = connection->write_buffer_off - connection->write_buffer_pos; - if (0 == have) - { - /* no data ready for writing, terminate write loop */ - return; - } - GNUNET_assert (have <= connection->write_buffer_size); - GNUNET_assert (have + connection->write_buffer_pos <= connection->write_buffer_size); - GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size); -RETRY: - ret = - GNUNET_NETWORK_socket_send (connection->sock, - &connection->write_buffer[connection->write_buffer_pos], - have); - if (-1 == ret) - { - if (EINTR == errno) - goto RETRY; - if (NULL != connection->write_task) - { - GNUNET_SCHEDULER_cancel (connection->write_task); - connection->write_task = NULL; - } - signal_transmit_error (connection, errno); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Connection transmitted %u/%u bytes to `%s' (%p)\n", - (unsigned int) ret, - have, - GNUNET_a2s (connection->addr, - connection->addrlen), - connection); - connection->write_buffer_pos += ret; - if (connection->write_buffer_pos == connection->write_buffer_off) - { - /* transmitted all pending data */ - connection->write_buffer_pos = 0; - connection->write_buffer_off = 0; - } - if ( (0 == connection->write_buffer_off) && - (NULL == connection->nth.notify_ready) ) - return; /* all data sent! */ - /* not done writing, schedule more */ -SCHEDULE_WRITE: - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Re-scheduling transmit_ready (more to do) (%p).\n", - connection); - have = connection->write_buffer_off - connection->write_buffer_pos; - GNUNET_assert ( (NULL != connection->nth.notify_ready) || - (have > 0) ); - if (NULL == connection->write_task) - connection->write_task = - GNUNET_SCHEDULER_add_write_net ((connection->nth.notify_ready == - NULL) ? GNUNET_TIME_UNIT_FOREVER_REL : - GNUNET_TIME_absolute_get_remaining - (connection->nth.transmit_timeout), - connection->sock, - &transmit_ready, connection); -} - - -/** - * Ask the connection to call us once the specified number of bytes - * are free in the transmission buffer. Will never call the @a notify - * callback in this task, but always first go into the scheduler. - * - * @param connection connection - * @param size number of bytes to send - * @param timeout after how long should we give up (and call - * @a notify with buf NULL and size 0)? - * @param notify function to call - * @param notify_cls closure for @a notify - * @return non-NULL if the notify callback was queued, - * NULL if we are already going to notify someone else (busy) - */ -struct GNUNET_CONNECTION_TransmitHandle * -GNUNET_CONNECTION_notify_transmit_ready (struct GNUNET_CONNECTION_Handle *connection, - size_t size, - struct GNUNET_TIME_Relative timeout, - GNUNET_CONNECTION_TransmitReadyNotify notify, - void *notify_cls) -{ - if (NULL != connection->nth.notify_ready) - { - GNUNET_assert (0); - return NULL; - } - GNUNET_assert (NULL != notify); - GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE); - GNUNET_assert (connection->write_buffer_off <= connection->write_buffer_size); - GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_size); - GNUNET_assert (connection->write_buffer_pos <= connection->write_buffer_off); - connection->nth.notify_ready = notify; - connection->nth.notify_ready_cls = notify_cls; - connection->nth.connection = connection; - connection->nth.notify_size = size; - connection->nth.transmit_timeout = GNUNET_TIME_relative_to_absolute (timeout); - GNUNET_assert (NULL == connection->nth.timeout_task); - if ((NULL == connection->sock) && - (NULL == connection->ap_head) && - (NULL == connection->dns_active) && - (NULL == connection->proxy_handshake)) - { - if (NULL != connection->write_task) - GNUNET_SCHEDULER_cancel (connection->write_task); - connection->write_task = GNUNET_SCHEDULER_add_now (&connect_error, - connection); - return &connection->nth; - } - if (NULL != connection->write_task) - return &connection->nth; /* previous transmission still in progress */ - if (NULL != connection->sock) - { - /* connected, try to transmit now */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Scheduling transmission (%p).\n", - connection); - connection->write_task = - GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining - (connection->nth.transmit_timeout), - connection->sock, - &transmit_ready, connection); - return &connection->nth; - } - /* not yet connected, wait for connection */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Need to wait to schedule transmission for connection, adding timeout task (%p).\n", - connection); - connection->nth.timeout_task = - GNUNET_SCHEDULER_add_delayed (timeout, - &transmit_timeout, - connection); - return &connection->nth; -} - - -/** - * Cancel the specified transmission-ready notification. - * - * @param th notification to cancel - */ -void -GNUNET_CONNECTION_notify_transmit_ready_cancel (struct GNUNET_CONNECTION_TransmitHandle *th) -{ - GNUNET_assert (NULL != th->notify_ready); - th->notify_ready = NULL; - if (NULL != th->timeout_task) - { - GNUNET_SCHEDULER_cancel (th->timeout_task); - th->timeout_task = NULL; - } - if (NULL != th->connection->write_task) - { - GNUNET_SCHEDULER_cancel (th->connection->write_task); - th->connection->write_task = NULL; - } -} - - -/** - * Create a connection to be proxied using a given connection. - * - * @param cph connection to proxy server - * @return connection to be proxied - */ -struct GNUNET_CONNECTION_Handle * -GNUNET_CONNECTION_create_proxied_from_handshake (struct GNUNET_CONNECTION_Handle *cph) -{ - struct GNUNET_CONNECTION_Handle *proxied = GNUNET_CONNECTION_create_from_existing (NULL); - - proxied->proxy_handshake = cph; - return proxied; -} - - -/** - * Activate proxied connection and destroy initial proxy handshake connection. - * There must not be any pending requests for reading or writing to the - * proxy hadshake connection at this time. - * - * @param proxied connection connection to proxy server - */ -void -GNUNET_CONNECTION_acivate_proxied (struct GNUNET_CONNECTION_Handle *proxied) -{ - struct GNUNET_CONNECTION_Handle *cph = proxied->proxy_handshake; - - GNUNET_assert (NULL != cph); - GNUNET_assert (NULL == proxied->sock); - GNUNET_assert (NULL != cph->sock); - proxied->sock = cph->sock; - cph->sock = NULL; - GNUNET_CONNECTION_destroy (cph); - connect_success_continuation (proxied); -} - - -/* end of connection.c */ diff --git a/src/util/helper.c b/src/util/helper.c index cdb1b01d4..1a79c477a 100644 --- a/src/util/helper.c +++ b/src/util/helper.c @@ -27,6 +27,7 @@ */ #include "platform.h" #include "gnunet_util_lib.h" +#include "gnunet_mst_lib.h" /** @@ -107,7 +108,7 @@ struct GNUNET_HELPER_Handle /** * The Message-Tokenizer that tokenizes the messages comming from the helper */ - struct GNUNET_SERVER_MessageStreamTokenizer *mst; + struct GNUNET_MessageStreamTokenizer *mst; /** * The exception callback @@ -272,7 +273,10 @@ GNUNET_HELPER_wait (struct GNUNET_HELPER_Handle *h) } /* purge MST buffer */ if (NULL != h->mst) - (void) GNUNET_SERVER_mst_receive (h->mst, NULL, NULL, 0, GNUNET_YES, GNUNET_NO); + (void) GNUNET_MST_from_buffer (h->mst, + NULL, 0, + GNUNET_YES, + GNUNET_NO); return ret; } @@ -373,10 +377,10 @@ helper_read (void *cls) h->fh_from_helper, &helper_read, h); if (GNUNET_SYSERR == - GNUNET_SERVER_mst_receive (h->mst, - NULL, - buf, t, - GNUNET_NO, GNUNET_NO)) + GNUNET_MST_from_buffer (h->mst, + buf, t, + GNUNET_NO, + GNUNET_NO)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to parse inbound message from helper `%s'\n"), @@ -487,7 +491,7 @@ struct GNUNET_HELPER_Handle * GNUNET_HELPER_start (int with_control_pipe, const char *binary_name, char *const binary_argv[], - GNUNET_SERVER_MessageTokenizerCallback cb, + GNUNET_MessageTokenizerCallback cb, GNUNET_HELPER_ExceptionCallback exp_cb, void *cb_cls) { @@ -508,7 +512,8 @@ GNUNET_HELPER_start (int with_control_pipe, h->binary_argv[c] = NULL; h->cb_cls = cb_cls; if (NULL != cb) - h->mst = GNUNET_SERVER_mst_create (cb, h->cb_cls); + h->mst = GNUNET_MST_create (cb, + h->cb_cls); h->exp_cb = exp_cb; h->retry_back_off = 0; start_helper (h); @@ -544,7 +549,7 @@ GNUNET_HELPER_destroy (struct GNUNET_HELPER_Handle *h) GNUNET_free (sh); } if (NULL != h->mst) - GNUNET_SERVER_mst_destroy (h->mst); + GNUNET_MST_destroy (h->mst); GNUNET_free (h->binary_name); for (c = 0; h->binary_argv[c] != NULL; c++) GNUNET_free (h->binary_argv[c]); diff --git a/src/util/mq.c b/src/util/mq.c index 25cf24e11..e644994e8 100644 --- a/src/util/mq.c +++ b/src/util/mq.c @@ -707,92 +707,6 @@ GNUNET_MQ_msg_nested_mh_ (struct GNUNET_MessageHeader **mhp, } -/** - * Transmit a queued message to the session's client. - * - * @param cls consensus session - * @param size number of bytes available in @a buf - * @param buf where the callee should write the message - * @return number of bytes written to @a buf - */ -static size_t -transmit_queued (void *cls, - size_t size, - void *buf) -{ - struct GNUNET_MQ_Handle *mq = cls; - struct ServerClientSocketState *state = GNUNET_MQ_impl_state (mq); - const struct GNUNET_MessageHeader *msg = GNUNET_MQ_impl_current (mq); - size_t msg_size; - - GNUNET_assert (NULL != buf); - msg_size = ntohs (msg->size); - GNUNET_assert (size >= msg_size); - GNUNET_memcpy (buf, msg, msg_size); - state->th = NULL; - - GNUNET_MQ_impl_send_continue (mq); - - return msg_size; -} - - -static void -server_client_destroy_impl (struct GNUNET_MQ_Handle *mq, - void *impl_state) -{ - struct ServerClientSocketState *state = impl_state; - - if (NULL != state->th) - { - GNUNET_SERVER_notify_transmit_ready_cancel (state->th); - state->th = NULL; - } - - GNUNET_assert (NULL != mq); - GNUNET_assert (NULL != state); - GNUNET_SERVER_client_drop (state->client); - GNUNET_free (state); -} - - -static void -server_client_send_impl (struct GNUNET_MQ_Handle *mq, - const struct GNUNET_MessageHeader *msg, - void *impl_state) -{ - GNUNET_assert (NULL != mq); - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending message of type %u and size %u\n", - ntohs (msg->type), ntohs (msg->size)); - - struct ServerClientSocketState *state = impl_state; - state->th = GNUNET_SERVER_notify_transmit_ready (state->client, - ntohs (msg->size), - GNUNET_TIME_UNIT_FOREVER_REL, - &transmit_queued, - mq); -} - - -struct GNUNET_MQ_Handle * -GNUNET_MQ_queue_for_server_client (struct GNUNET_SERVER_Client *client) -{ - struct GNUNET_MQ_Handle *mq; - struct ServerClientSocketState *scss; - - mq = GNUNET_new (struct GNUNET_MQ_Handle); - scss = GNUNET_new (struct ServerClientSocketState); - mq->impl_state = scss; - scss->client = client; - GNUNET_SERVER_client_keep (client); - mq->send_impl = &server_client_send_impl; - mq->destroy_impl = &server_client_destroy_impl; - return mq; -} - - /** * Associate the assoc_data in mq with a unique request id. * diff --git a/src/util/server.c b/src/util/server.c deleted file mode 100644 index 83c30e328..000000000 --- a/src/util/server.c +++ /dev/null @@ -1,1752 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009-2013 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file util/server.c - * @brief library for building GNUnet network servers - * @author Christian Grothoff - */ - -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" - -#define LOG(kind,...) GNUNET_log_from (kind, "util-server", __VA_ARGS__) - -#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-server", syscall) - -#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-server", syscall, filename) - - -/** - * List of arrays of message handlers. - */ -struct HandlerList -{ - /** - * This is a linked list. - */ - struct HandlerList *next; - - /** - * NULL-terminated array of handlers. - */ - const struct GNUNET_SERVER_MessageHandler *handlers; -}; - - -/** - * List of arrays of message handlers. - */ -struct NotifyList -{ - /** - * This is a doubly linked list. - */ - struct NotifyList *next; - - /** - * This is a doubly linked list. - */ - struct NotifyList *prev; - - /** - * Function to call. - */ - GNUNET_SERVER_DisconnectCallback callback; - - /** - * Closure for callback. - */ - void *callback_cls; -}; - - -/** - * @brief handle for a server - */ -struct GNUNET_SERVER_Handle -{ - /** - * List of handlers for incoming messages. - */ - struct HandlerList *handlers; - - /** - * Head of list of our current clients. - */ - struct GNUNET_SERVER_Client *clients_head; - - /** - * Head of list of our current clients. - */ - struct GNUNET_SERVER_Client *clients_tail; - - /** - * Head of linked list of functions to call on disconnects by clients. - */ - struct NotifyList *disconnect_notify_list_head; - - /** - * Tail of linked list of functions to call on disconnects by clients. - */ - struct NotifyList *disconnect_notify_list_tail; - - /** - * Head of linked list of functions to call on connects by clients. - */ - struct NotifyList *connect_notify_list_head; - - /** - * Tail of linked list of functions to call on connects by clients. - */ - struct NotifyList *connect_notify_list_tail; - - /** - * Function to call for access control. - */ - GNUNET_CONNECTION_AccessCheck access_cb; - - /** - * Closure for @e access_cb. - */ - void *access_cb_cls; - - /** - * NULL-terminated array of sockets used to listen for new - * connections. - */ - struct GNUNET_NETWORK_Handle **listen_sockets; - - /** - * After how long should an idle connection time - * out (on write). - */ - struct GNUNET_TIME_Relative idle_timeout; - - /** - * Task scheduled to do the listening. - */ - struct GNUNET_SCHEDULER_Task * listen_task; - - /** - * Alternative function to create a MST instance. - */ - GNUNET_SERVER_MstCreateCallback mst_create; - - /** - * Alternative function to destroy a MST instance. - */ - GNUNET_SERVER_MstDestroyCallback mst_destroy; - - /** - * Alternative function to give data to a MST instance. - */ - GNUNET_SERVER_MstReceiveCallback mst_receive; - - /** - * Closure for 'mst_'-callbacks. - */ - void *mst_cls; - - /** - * Do we ignore messages of types that we do not understand or do we - * require that a handler is found (and if not kill the connection)? - */ - int require_found; - - /** - * Set to #GNUNET_YES once we are in 'soft' shutdown where we wait for - * all non-monitor clients to disconnect before we call - * #GNUNET_SERVER_destroy. See test_monitor_clients(). Set to - * #GNUNET_SYSERR once the final destroy task has been scheduled - * (we cannot run it in the same task). - */ - int in_soft_shutdown; -}; - - -/** - * Handle server returns for aborting transmission to a client. - */ -struct GNUNET_SERVER_TransmitHandle -{ - /** - * Function to call to get the message. - */ - GNUNET_CONNECTION_TransmitReadyNotify callback; - - /** - * Closure for @e callback - */ - void *callback_cls; - - /** - * Active connection transmission handle. - */ - struct GNUNET_CONNECTION_TransmitHandle *cth; - -}; - - -/** - * @brief handle for a client of the server - */ -struct GNUNET_SERVER_Client -{ - - /** - * This is a doubly linked list. - */ - struct GNUNET_SERVER_Client *next; - - /** - * This is a doubly linked list. - */ - struct GNUNET_SERVER_Client *prev; - - /** - * Processing of incoming data. - */ - void *mst; - - /** - * Server that this client belongs to. - */ - struct GNUNET_SERVER_Handle *server; - - /** - * Client closure for callbacks. - */ - struct GNUNET_CONNECTION_Handle *connection; - - /** - * User context value, manipulated using - * 'GNUNET_SERVER_client_{get/set}_user_context' functions. - */ - void *user_context; - - /** - * ID of task used to restart processing. - */ - struct GNUNET_SCHEDULER_Task * restart_task; - - /** - * Task that warns about missing calls to #GNUNET_SERVER_receive_done. - */ - struct GNUNET_SCHEDULER_Task * warn_task; - - /** - * Time when the warn task was started. - */ - struct GNUNET_TIME_Absolute warn_start; - - /** - * Last activity on this socket (used to time it out - * if reference_count == 0). - */ - struct GNUNET_TIME_Absolute last_activity; - - /** - * Transmission handle we return for this client from - * #GNUNET_SERVER_notify_transmit_ready. - */ - struct GNUNET_SERVER_TransmitHandle th; - - /** - * After how long should an idle connection time - * out (on write). - */ - struct GNUNET_TIME_Relative idle_timeout; - - /** - * Number of external entities with a reference to - * this client object. - */ - unsigned int reference_count; - - /** - * Was processing if incoming messages suspended while - * we were still processing data already received? - * This is a counter saying how often processing was - * suspended (once per handler invoked). - */ - unsigned int suspended; - - /** - * Last size given when user context was initialized; used for - * sanity check. - */ - size_t user_context_size; - - /** - * Are we currently in the "process_client_buffer" function (and - * will hence restart the receive job on exit if suspended == 0 once - * we are done?). If this is set, then "receive_done" will - * essentially only decrement suspended; if this is not set, then - * "receive_done" may need to restart the receive process (either - * from the side-buffer or via select/recv). - */ - int in_process_client_buffer; - - /** - * We're about to close down this client. - */ - int shutdown_now; - - /** - * Are we currently trying to receive? (#GNUNET_YES if we are, - * #GNUNET_NO if we are not, #GNUNET_SYSERR if data is already - * available in MST). - */ - int receive_pending; - - /** - * Persist the file handle for this client no matter what happens, - * force the OS to close once the process actually dies. Should only - * be used in special cases! - */ - int persist; - - /** - * Is this client a 'monitor' client that should not be counted - * when deciding on destroying the server during soft shutdown? - * (see also #GNUNET_SERVICE_start) - */ - int is_monitor; - - /** - * Type of last message processed (for warn_no_receive_done). - */ - uint16_t warn_type; -}; - - - -/** - * Return user context associated with the given client. - * Note: you should probably use the macro (call without the underscore). - * - * @param client client to query - * @param size number of bytes in user context struct (for verification only) - * @return pointer to user context - */ -void * -GNUNET_SERVER_client_get_user_context_ (struct GNUNET_SERVER_Client *client, - size_t size) -{ - if ((0 == client->user_context_size) && - (NULL == client->user_context)) - return NULL; /* never set */ - GNUNET_assert (size == client->user_context_size); - return client->user_context; -} - - -/** - * Set user context to be associated with the given client. - * Note: you should probably use the macro (call without the underscore). - * - * @param client client to query - * @param ptr pointer to user context - * @param size number of bytes in user context struct (for verification only) - */ -void -GNUNET_SERVER_client_set_user_context_ (struct GNUNET_SERVER_Client *client, - void *ptr, - size_t size) -{ - if (NULL == ptr) - { - client->user_context_size = 0; - client->user_context = ptr; - return; - } - client->user_context_size = size; - client->user_context = ptr; -} - - -/** - * Scheduler says our listen socket is ready. Process it! - * - * @param cls handle to our server for which we are processing the listen - * socket - */ -static void -process_listen_socket (void *cls) -{ - struct GNUNET_SERVER_Handle *server = cls; - const struct GNUNET_SCHEDULER_TaskContext *tc; - struct GNUNET_CONNECTION_Handle *sock; - unsigned int i; - - server->listen_task = NULL; - tc = GNUNET_SCHEDULER_get_task_context (); - for (i = 0; NULL != server->listen_sockets[i]; i++) - { - if (GNUNET_NETWORK_fdset_isset (tc->read_ready, - server->listen_sockets[i])) - { - sock = - GNUNET_CONNECTION_create_from_accept (server->access_cb, - server->access_cb_cls, - server->listen_sockets[i]); - if (NULL != sock) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Server accepted incoming connection.\n"); - (void) GNUNET_SERVER_connect_socket (server, - sock); - } - } - } - /* listen for more! */ - GNUNET_SERVER_resume (server); -} - - -/** - * Create and initialize a listen socket for the server. - * - * @param server_addr address to listen on - * @param socklen length of @a server_addr - * @return NULL on error, otherwise the listen socket - */ -static struct GNUNET_NETWORK_Handle * -open_listen_socket (const struct sockaddr *server_addr, - socklen_t socklen) -{ - struct GNUNET_NETWORK_Handle *sock; - uint16_t port; - int eno; - - switch (server_addr->sa_family) - { - case AF_INET: - port = ntohs (((const struct sockaddr_in *) server_addr)->sin_port); - break; - case AF_INET6: - port = ntohs (((const struct sockaddr_in6 *) server_addr)->sin6_port); - break; - case AF_UNIX: - port = 0; - break; - default: - GNUNET_break (0); - port = 0; - break; - } - sock = GNUNET_NETWORK_socket_create (server_addr->sa_family, SOCK_STREAM, 0); - if (NULL == sock) - { - LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); - errno = 0; - return NULL; - } - /* bind the socket */ - if (GNUNET_OK != GNUNET_NETWORK_socket_bind (sock, server_addr, socklen)) - { - eno = errno; - if (EADDRINUSE != errno) - { - /* we don't log 'EADDRINUSE' here since an IPv4 bind may - * fail if we already took the port on IPv6; if both IPv4 and - * IPv6 binds fail, then our caller will log using the - * errno preserved in 'eno' */ - LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, - "bind"); - if (0 != port) - LOG (GNUNET_ERROR_TYPE_ERROR, - _("`%s' failed for port %d (%s).\n"), - "bind", - port, - (AF_INET == server_addr->sa_family) ? "IPv4" : "IPv6"); - eno = 0; - } - else - { - if (0 != port) - LOG (GNUNET_ERROR_TYPE_WARNING, - _("`%s' failed for port %d (%s): address already in use\n"), - "bind", port, - (AF_INET == server_addr->sa_family) ? "IPv4" : "IPv6"); - else if (AF_UNIX == server_addr->sa_family) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - _("`%s' failed for `%s': address already in use\n"), - "bind", - GNUNET_a2s (server_addr, socklen)); - } - } - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); - errno = eno; - return NULL; - } - if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5)) - { - LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, - "listen"); - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); - errno = 0; - return NULL; - } - if (0 != port) - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Server starts to listen on port %u.\n", - port); - return sock; -} - - -/** - * Create a new server. - * - * @param access_cb function for access control - * @param access_cb_cls closure for @a access_cb - * @param lsocks NULL-terminated array of listen sockets - * @param idle_timeout after how long should we timeout idle connections? - * @param require_found if #GNUNET_YES, connections sending messages of unknown type - * will be closed - * @return handle for the new server, NULL on error - * (typically, "port" already in use) - */ -struct GNUNET_SERVER_Handle * -GNUNET_SERVER_create_with_sockets (GNUNET_CONNECTION_AccessCheck access_cb, - void *access_cb_cls, - struct GNUNET_NETWORK_Handle **lsocks, - struct GNUNET_TIME_Relative idle_timeout, - int require_found) -{ - struct GNUNET_SERVER_Handle *server; - - server = GNUNET_new (struct GNUNET_SERVER_Handle); - server->idle_timeout = idle_timeout; - server->listen_sockets = lsocks; - server->access_cb = access_cb; - server->access_cb_cls = access_cb_cls; - server->require_found = require_found; - if (NULL != lsocks) - GNUNET_SERVER_resume (server); - return server; -} - - -/** - * Create a new server. - * - * @param access_cb function for access control - * @param access_cb_cls closure for @a access_cb - * @param server_addr address to listen on (including port), NULL terminated array - * @param socklen length of server_addr - * @param idle_timeout after how long should we timeout idle connections? - * @param require_found if YES, connections sending messages of unknown type - * will be closed - * @return handle for the new server, NULL on error - * (typically, "port" already in use) - */ -struct GNUNET_SERVER_Handle * -GNUNET_SERVER_create (GNUNET_CONNECTION_AccessCheck access_cb, - void *access_cb_cls, - struct sockaddr *const *server_addr, - const socklen_t * socklen, - struct GNUNET_TIME_Relative idle_timeout, - int require_found) -{ - struct GNUNET_NETWORK_Handle **lsocks; - unsigned int i; - unsigned int j; - unsigned int k; - int seen; - - i = 0; - while (NULL != server_addr[i]) - i++; - if (i > 0) - { - lsocks = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (i + 1)); - i = 0; - j = 0; - while (NULL != server_addr[i]) - { - seen = 0; - for (k=0;kis_monitor = GNUNET_YES; -} - - -/** - * Helper function for #test_monitor_clients() to trigger - * #GNUNET_SERVER_destroy() after the stack has unwound. - * - * @param cls the `struct GNUNET_SERVER_Handle *` to destroy - */ -static void -do_destroy (void *cls) -{ - struct GNUNET_SERVER_Handle *server = cls; - - GNUNET_SERVER_destroy (server); -} - - -/** - * Check if only 'monitor' clients are left. If so, destroy the - * server completely. - * - * @param server server to test for full shutdown - */ -static void -test_monitor_clients (struct GNUNET_SERVER_Handle *server) -{ - struct GNUNET_SERVER_Client *client; - - if (GNUNET_YES != server->in_soft_shutdown) - return; - for (client = server->clients_head; NULL != client; client = client->next) - if (GNUNET_NO == client->is_monitor) - return; /* not done yet */ - server->in_soft_shutdown = GNUNET_SYSERR; - (void) GNUNET_SCHEDULER_add_now (&do_destroy, server); -} - - -/** - * Suspend accepting connections from the listen socket temporarily. - * - * @param server server to stop accepting connections. - */ -void -GNUNET_SERVER_suspend (struct GNUNET_SERVER_Handle *server) -{ - if (NULL != server->listen_task) - { - GNUNET_SCHEDULER_cancel (server->listen_task); - server->listen_task = NULL; - } -} - - -/** - * Resume accepting connections from the listen socket. - * - * @param server server to stop accepting connections. - */ -void -GNUNET_SERVER_resume (struct GNUNET_SERVER_Handle *server) -{ - struct GNUNET_NETWORK_FDSet *r; - unsigned int i; - - if (NULL == server->listen_sockets) - return; - if (NULL == server->listen_sockets[0]) - return; /* nothing to do, no listen sockets! */ - if (NULL == server->listen_sockets[1]) - { - /* simplified method: no fd set needed; this is then much simpler - and much more efficient */ - server->listen_task = - GNUNET_SCHEDULER_add_read_net_with_priority (GNUNET_TIME_UNIT_FOREVER_REL, - GNUNET_SCHEDULER_PRIORITY_HIGH, - server->listen_sockets[0], - &process_listen_socket, server); - return; - } - r = GNUNET_NETWORK_fdset_create (); - i = 0; - while (NULL != server->listen_sockets[i]) - GNUNET_NETWORK_fdset_set (r, server->listen_sockets[i++]); - server->listen_task = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, - GNUNET_TIME_UNIT_FOREVER_REL, r, NULL, - &process_listen_socket, server); - GNUNET_NETWORK_fdset_destroy (r); -} - - -/** - * Stop the listen socket and get ready to shutdown the server - * once only 'monitor' clients are left. - * - * @param server server to stop listening on - */ -void -GNUNET_SERVER_stop_listening (struct GNUNET_SERVER_Handle *server) -{ - unsigned int i; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Server in soft shutdown\n"); - if (NULL != server->listen_task) - { - GNUNET_SCHEDULER_cancel (server->listen_task); - server->listen_task = NULL; - } - if (NULL != server->listen_sockets) - { - i = 0; - while (NULL != server->listen_sockets[i]) - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (server->listen_sockets[i++])); - GNUNET_free (server->listen_sockets); - server->listen_sockets = NULL; - } - if (GNUNET_NO == server->in_soft_shutdown) - server->in_soft_shutdown = GNUNET_YES; - test_monitor_clients (server); -} - - -/** - * Free resources held by this server. - * - * @param server server to destroy - */ -void -GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *server) -{ - struct HandlerList *hpos; - struct NotifyList *npos; - unsigned int i; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Server shutting down.\n"); - if (NULL != server->listen_task) - { - GNUNET_SCHEDULER_cancel (server->listen_task); - server->listen_task = NULL; - } - if (NULL != server->listen_sockets) - { - i = 0; - while (NULL != server->listen_sockets[i]) - GNUNET_break (GNUNET_OK == - GNUNET_NETWORK_socket_close (server->listen_sockets[i++])); - GNUNET_free (server->listen_sockets); - server->listen_sockets = NULL; - } - while (NULL != server->clients_head) - GNUNET_SERVER_client_disconnect (server->clients_head); - while (NULL != (hpos = server->handlers)) - { - server->handlers = hpos->next; - GNUNET_free (hpos); - } - while (NULL != (npos = server->disconnect_notify_list_head)) - { - npos->callback (npos->callback_cls, - NULL); - GNUNET_CONTAINER_DLL_remove (server->disconnect_notify_list_head, - server->disconnect_notify_list_tail, - npos); - GNUNET_free (npos); - } - while (NULL != (npos = server->connect_notify_list_head)) - { - npos->callback (npos->callback_cls, - NULL); - GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head, - server->connect_notify_list_tail, - npos); - GNUNET_free (npos); - } - GNUNET_free (server); -} - - -/** - * Add additional handlers to an existing server. - * - * @param server the server to add handlers to - * @param handlers array of message handlers for - * incoming messages; the last entry must - * have "NULL" for the "callback"; multiple - * entries for the same type are allowed, - * they will be called in order of occurence. - * These handlers can be removed later; - * the handlers array must exist until removed - * (or server is destroyed). - */ -void -GNUNET_SERVER_add_handlers (struct GNUNET_SERVER_Handle *server, - const struct GNUNET_SERVER_MessageHandler *handlers) -{ - struct HandlerList *p; - - p = GNUNET_new (struct HandlerList); - p->handlers = handlers; - p->next = server->handlers; - server->handlers = p; -} - - -/** - * Change functions used by the server to tokenize the message stream. - * (very rarely used). - * - * @param server server to modify - * @param create new tokenizer initialization function - * @param destroy new tokenizer destruction function - * @param receive new tokenizer receive function - * @param cls closure for @a create, @a receive, @a destroy - */ -void -GNUNET_SERVER_set_callbacks (struct GNUNET_SERVER_Handle *server, - GNUNET_SERVER_MstCreateCallback create, - GNUNET_SERVER_MstDestroyCallback destroy, - GNUNET_SERVER_MstReceiveCallback receive, - void *cls) -{ - server->mst_create = create; - server->mst_destroy = destroy; - server->mst_receive = receive; - server->mst_cls = cls; -} - - -/** - * Task run to warn about missing calls to #GNUNET_SERVER_receive_done. - * - * @param cls our `struct GNUNET_SERVER_Client *` to process more requests from - */ -static void -warn_no_receive_done (void *cls) -{ - struct GNUNET_SERVER_Client *client = cls; - - GNUNET_break (0 != client->warn_type); /* type should never be 0 here, as we don't use 0 */ - client->warn_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, - &warn_no_receive_done, client); - LOG (GNUNET_ERROR_TYPE_WARNING, - _("Processing code for message of type %u did not call `GNUNET_SERVER_receive_done' after %s\n"), - (unsigned int) client->warn_type, - GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (client->warn_start), - GNUNET_YES)); -} - - -/** - * Disable the warning the server issues if a message is not acknowledged - * in a timely fashion. Use this call if a client is intentionally delayed - * for a while. Only applies to the current message. - * - * @param client client for which to disable the warning - */ -void -GNUNET_SERVER_disable_receive_done_warning (struct GNUNET_SERVER_Client *client) -{ - if (NULL != client->warn_task) - { - GNUNET_SCHEDULER_cancel (client->warn_task); - client->warn_task = NULL; - } -} - - -/** - * Inject a message into the server, pretend it came - * from the specified client. Delivery of the message - * will happen instantly (if a handler is installed; - * otherwise the call does nothing). - * - * @param server the server receiving the message - * @param sender the "pretended" sender of the message - * can be NULL! - * @param message message to transmit - * @return #GNUNET_OK if the message was OK and the - * connection can stay open - * #GNUNET_SYSERR if the connection to the - * client should be shut down - */ -int -GNUNET_SERVER_inject (struct GNUNET_SERVER_Handle *server, - struct GNUNET_SERVER_Client *sender, - const struct GNUNET_MessageHeader *message) -{ - struct HandlerList *pos; - const struct GNUNET_SERVER_MessageHandler *mh; - unsigned int i; - uint16_t type; - uint16_t size; - int found; - - type = ntohs (message->type); - size = ntohs (message->size); - LOG (GNUNET_ERROR_TYPE_INFO, - "Received message of type %u and size %u from client\n", - type, size); - found = GNUNET_NO; - for (pos = server->handlers; NULL != pos; pos = pos->next) - { - i = 0; - while (pos->handlers[i].callback != NULL) - { - mh = &pos->handlers[i]; - if ((mh->type == type) || (mh->type == GNUNET_MESSAGE_TYPE_ALL)) - { - if ((0 != mh->expected_size) && (mh->expected_size != size)) - { -#if GNUNET8_NETWORK_IS_DEAD - LOG (GNUNET_ERROR_TYPE_WARNING, - "Expected %u bytes for message of type %u, got %u\n", - mh->expected_size, mh->type, size); - GNUNET_break_op (0); -#else - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Expected %u bytes for message of type %u, got %u\n", - mh->expected_size, mh->type, size); -#endif - return GNUNET_SYSERR; - } - if (NULL != sender) - { - if ( (0 == sender->suspended) && - (NULL == sender->warn_task) ) - { - GNUNET_break (0 != type); /* type should never be 0 here, as we don't use 0 */ - sender->warn_start = GNUNET_TIME_absolute_get (); - sender->warn_task = - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, - &warn_no_receive_done, - sender); - sender->warn_type = type; - } - sender->suspended++; - } - mh->callback (mh->callback_cls, sender, message); - found = GNUNET_YES; - } - i++; - } - } - if (GNUNET_NO == found) - { - LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK, - "Received message of unknown type %d\n", type); - if (GNUNET_YES == server->require_found) - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * We are receiving an incoming message. Process it. - * - * @param cls our closure (handle for the client) - * @param buf buffer with data received from network - * @param available number of bytes available in buf - * @param addr address of the sender - * @param addrlen length of @a addr - * @param errCode code indicating errors receiving, 0 for success - */ -static void -process_incoming (void *cls, - const void *buf, - size_t available, - const struct sockaddr *addr, - socklen_t addrlen, - int errCode); - - -/** - * Process messages from the client's message tokenizer until either - * the tokenizer is empty (and then schedule receiving more), or - * until some handler is not immediately done (then wait for restart_processing) - * or shutdown. - * - * @param client the client to process, RC must have already been increased - * using #GNUNET_SERVER_client_keep and will be decreased by one in this - * function - * @param ret #GNUNET_NO to start processing from the buffer, - * #GNUNET_OK if the mst buffer is drained and we should instantly go back to receiving - * #GNUNET_SYSERR if we should instantly abort due to error in a previous step - */ -static void -process_mst (struct GNUNET_SERVER_Client *client, - int ret) -{ - while ((GNUNET_SYSERR != ret) && (NULL != client->server) && - (GNUNET_YES != client->shutdown_now) && (0 == client->suspended)) - { - if (GNUNET_OK == ret) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Server re-enters receive loop, timeout: %s.\n", - GNUNET_STRINGS_relative_time_to_string (client->idle_timeout, GNUNET_YES)); - client->receive_pending = GNUNET_YES; - GNUNET_CONNECTION_receive (client->connection, - GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, - client->idle_timeout, - &process_incoming, - client); - break; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Server processes additional messages instantly.\n"); - if (NULL != client->server->mst_receive) - ret = - client->server->mst_receive (client->server->mst_cls, client->mst, - client, NULL, 0, GNUNET_NO, GNUNET_YES); - else - ret = - GNUNET_SERVER_mst_receive (client->mst, client, NULL, 0, GNUNET_NO, - GNUNET_YES); - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Server leaves instant processing loop: ret = %d, server = %p, shutdown = %d, suspended = %u\n", - ret, client->server, - client->shutdown_now, - client->suspended); - if (GNUNET_NO == ret) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Server has more data pending but is suspended.\n"); - client->receive_pending = GNUNET_SYSERR; /* data pending */ - } - if ( (GNUNET_SYSERR == ret) || - (GNUNET_YES == client->shutdown_now) ) - GNUNET_SERVER_client_disconnect (client); -} - - -/** - * We are receiving an incoming message. Process it. - * - * @param cls our closure (handle for the client) - * @param buf buffer with data received from network - * @param available number of bytes available in buf - * @param addr address of the sender - * @param addrlen length of @a addr - * @param errCode code indicating errors receiving, 0 for success - */ -static void -process_incoming (void *cls, - const void *buf, - size_t available, - const struct sockaddr *addr, - socklen_t addrlen, - int errCode) -{ - struct GNUNET_SERVER_Client *client = cls; - struct GNUNET_SERVER_Handle *server = client->server; - struct GNUNET_TIME_Absolute end; - struct GNUNET_TIME_Absolute now; - int ret; - - GNUNET_assert (GNUNET_YES == client->receive_pending); - client->receive_pending = GNUNET_NO; - now = GNUNET_TIME_absolute_get (); - end = GNUNET_TIME_absolute_add (client->last_activity, - client->idle_timeout); - - if ( (NULL == buf) && - (0 == available) && - (NULL == addr) && - (0 == errCode) && - (GNUNET_YES != client->shutdown_now) && - (NULL != server) && - (GNUNET_YES == GNUNET_CONNECTION_check (client->connection)) && - (end.abs_value_us > now.abs_value_us) ) - { - /* wait longer, timeout changed (i.e. due to us sending) */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Receive time out, but no disconnect due to sending (%p)\n", - client); - client->receive_pending = GNUNET_YES; - GNUNET_CONNECTION_receive (client->connection, - GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, - GNUNET_TIME_absolute_get_remaining (end), - &process_incoming, - client); - return; - } - if ( (NULL == buf) || - (0 == available) || - (0 != errCode) || - (NULL == server) || - (GNUNET_YES == client->shutdown_now) || - (GNUNET_YES != GNUNET_CONNECTION_check (client->connection)) ) - { - /* other side closed connection, error connecting, etc. */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Failed to connect or other side closed connection (%p)\n", - client); - GNUNET_SERVER_client_disconnect (client); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Server receives %u bytes from `%s'.\n", - (unsigned int) available, - GNUNET_a2s (addr, addrlen)); - GNUNET_SERVER_client_keep (client); - client->last_activity = now; - - if (NULL != server->mst_receive) - { - ret = client->server->mst_receive (client->server->mst_cls, - client->mst, - client, - buf, - available, - GNUNET_NO, - GNUNET_YES); - } - else if (NULL != client->mst) - { - ret = - GNUNET_SERVER_mst_receive (client->mst, - client, - buf, - available, - GNUNET_NO, - GNUNET_YES); - } - else - { - GNUNET_break (0); - return; - } - process_mst (client, - ret); - GNUNET_SERVER_client_drop (client); -} - - -/** - * Task run to start again receiving from the network - * and process requests. - * - * @param cls our `struct GNUNET_SERVER_Client *` to process more requests from - */ -static void -restart_processing (void *cls) -{ - struct GNUNET_SERVER_Client *client = cls; - - GNUNET_assert (GNUNET_YES != client->shutdown_now); - client->restart_task = NULL; - if (GNUNET_NO == client->receive_pending) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "Server begins to read again from client.\n"); - client->receive_pending = GNUNET_YES; - GNUNET_CONNECTION_receive (client->connection, - GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, - client->idle_timeout, - &process_incoming, - client); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Server continues processing messages still in the buffer.\n"); - GNUNET_SERVER_client_keep (client); - client->receive_pending = GNUNET_NO; - process_mst (client, - GNUNET_NO); - GNUNET_SERVER_client_drop (client); -} - - -/** - * This function is called whenever our inbound message tokenizer has - * received a complete message. - * - * @param cls closure (struct GNUNET_SERVER_Handle) - * @param client identification of the client (`struct GNUNET_SERVER_Client *`) - * @param message the actual message - * - * @return #GNUNET_OK on success, #GNUNET_SYSERR to stop further processing - */ -static int -client_message_tokenizer_callback (void *cls, - void *client, - const struct GNUNET_MessageHeader *message) -{ - struct GNUNET_SERVER_Handle *server = cls; - struct GNUNET_SERVER_Client *sender = client; - int ret; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Tokenizer gives server message of type %u and size %u from client\n", - ntohs (message->type), ntohs (message->size)); - sender->in_process_client_buffer = GNUNET_YES; - ret = GNUNET_SERVER_inject (server, sender, message); - sender->in_process_client_buffer = GNUNET_NO; - if ( (GNUNET_OK != ret) || (GNUNET_YES == sender->shutdown_now) ) - { - GNUNET_SERVER_client_disconnect (sender); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Add a TCP socket-based connection to the set of handles managed by - * this server. Use this function for outgoing (P2P) connections that - * we initiated (and where this server should process incoming - * messages). - * - * @param server the server to use - * @param connection the connection to manage (client must - * stop using this connection from now on) - * @return the client handle - */ -struct GNUNET_SERVER_Client * -GNUNET_SERVER_connect_socket (struct GNUNET_SERVER_Handle *server, - struct GNUNET_CONNECTION_Handle *connection) -{ - struct GNUNET_SERVER_Client *client; - struct NotifyList *n; - - client = GNUNET_new (struct GNUNET_SERVER_Client); - client->connection = connection; - client->server = server; - client->last_activity = GNUNET_TIME_absolute_get (); - client->idle_timeout = server->idle_timeout; - GNUNET_CONTAINER_DLL_insert (server->clients_head, - server->clients_tail, - client); - if (NULL != server->mst_create) - client->mst = - server->mst_create (server->mst_cls, client); - else - client->mst = - GNUNET_SERVER_mst_create (&client_message_tokenizer_callback, - server); - GNUNET_assert (NULL != client->mst); - for (n = server->connect_notify_list_head; NULL != n; n = n->next) - n->callback (n->callback_cls, client); - client->receive_pending = GNUNET_YES; - GNUNET_CONNECTION_receive (client->connection, - GNUNET_SERVER_MAX_MESSAGE_SIZE - 1, - client->idle_timeout, - &process_incoming, - client); - return client; -} - - -/** - * Change the timeout for a particular client. Decreasing the timeout - * may not go into effect immediately (only after the previous timeout - * times out or activity happens on the socket). - * - * @param client the client to update - * @param timeout new timeout for activities on the socket - */ -void -GNUNET_SERVER_client_set_timeout (struct GNUNET_SERVER_Client *client, - struct GNUNET_TIME_Relative timeout) -{ - client->idle_timeout = timeout; -} - - -/** - * Notify the server that the given client handle should - * be kept (keeps the connection up if possible, increments - * the internal reference counter). - * - * @param client the client to keep - */ -void -GNUNET_SERVER_client_keep (struct GNUNET_SERVER_Client *client) -{ - client->reference_count++; -} - - -/** - * Notify the server that the given client handle is no - * longer required. Decrements the reference counter. If - * that counter reaches zero an inactive connection maybe - * closed. - * - * @param client the client to drop - */ -void -GNUNET_SERVER_client_drop (struct GNUNET_SERVER_Client *client) -{ - GNUNET_assert (client->reference_count > 0); - client->reference_count--; - if ((GNUNET_YES == client->shutdown_now) && (0 == client->reference_count)) - GNUNET_SERVER_client_disconnect (client); -} - - -/** - * Obtain the network address of the other party. - * - * @param client the client to get the address for - * @param addr where to store the address - * @param addrlen where to store the length of the @a addr - * @return #GNUNET_OK on success - */ -int -GNUNET_SERVER_client_get_address (struct GNUNET_SERVER_Client *client, - void **addr, size_t * addrlen) -{ - return GNUNET_CONNECTION_get_address (client->connection, addr, addrlen); -} - - -/** - * Ask the server to notify us whenever a client disconnects. - * This function is called whenever the actual network connection - * is closed; the reference count may be zero or larger than zero - * at this point. - * - * @param server the server manageing the clients - * @param callback function to call on disconnect - * @param callback_cls closure for @a callback - */ -void -GNUNET_SERVER_disconnect_notify (struct GNUNET_SERVER_Handle *server, - GNUNET_SERVER_DisconnectCallback callback, - void *callback_cls) -{ - struct NotifyList *n; - - n = GNUNET_new (struct NotifyList); - n->callback = callback; - n->callback_cls = callback_cls; - GNUNET_CONTAINER_DLL_insert (server->disconnect_notify_list_head, - server->disconnect_notify_list_tail, - n); -} - - -/** - * Ask the server to notify us whenever a client connects. - * This function is called whenever the actual network connection - * is opened. If the server is destroyed before this - * notification is explicitly cancelled, the 'callback' will - * once be called with a 'client' argument of NULL to indicate - * that the server itself is now gone (and that the callback - * won't be called anymore and also can no longer be cancelled). - * - * @param server the server manageing the clients - * @param callback function to call on sconnect - * @param callback_cls closure for @a callback - */ -void -GNUNET_SERVER_connect_notify (struct GNUNET_SERVER_Handle *server, - GNUNET_SERVER_ConnectCallback callback, - void *callback_cls) -{ - struct NotifyList *n; - struct GNUNET_SERVER_Client *client; - - n = GNUNET_new (struct NotifyList); - n->callback = callback; - n->callback_cls = callback_cls; - GNUNET_CONTAINER_DLL_insert (server->connect_notify_list_head, - server->connect_notify_list_tail, - n); - for (client = server->clients_head; NULL != client; client = client->next) - callback (callback_cls, client); -} - - -/** - * Ask the server to stop notifying us whenever a client connects. - * - * @param server the server manageing the clients - * @param callback function to call on connect - * @param callback_cls closure for @a callback - */ -void -GNUNET_SERVER_disconnect_notify_cancel (struct GNUNET_SERVER_Handle *server, - GNUNET_SERVER_DisconnectCallback callback, - void *callback_cls) -{ - struct NotifyList *pos; - - for (pos = server->disconnect_notify_list_head; NULL != pos; pos = pos->next) - if ((pos->callback == callback) && (pos->callback_cls == callback_cls)) - break; - if (NULL == pos) - { - GNUNET_break (0); - return; - } - GNUNET_CONTAINER_DLL_remove (server->disconnect_notify_list_head, - server->disconnect_notify_list_tail, - pos); - GNUNET_free (pos); -} - - -/** - * Ask the server to stop notifying us whenever a client disconnects. - * - * @param server the server manageing the clients - * @param callback function to call on disconnect - * @param callback_cls closure for @a callback - */ -void -GNUNET_SERVER_connect_notify_cancel (struct GNUNET_SERVER_Handle *server, - GNUNET_SERVER_ConnectCallback callback, - void *callback_cls) -{ - struct NotifyList *pos; - - for (pos = server->connect_notify_list_head; NULL != pos; pos = pos->next) - if ((pos->callback == callback) && (pos->callback_cls == callback_cls)) - break; - if (NULL == pos) - { - GNUNET_break (0); - return; - } - GNUNET_CONTAINER_DLL_remove (server->connect_notify_list_head, - server->connect_notify_list_tail, - pos); - GNUNET_free (pos); -} - - -/** - * Destroy the connection that is passed in via @a cls. Used - * as calling #GNUNET_CONNECTION_destroy from within a function - * that was itself called from within process_notify() of - * 'connection.c' is not allowed (see #2329). - * - * @param cls connection to destroy - */ -static void -destroy_connection (void *cls) -{ - struct GNUNET_CONNECTION_Handle *connection = cls; - - GNUNET_CONNECTION_destroy (connection); -} - - -/** - * Ask the server to disconnect from the given client. - * This is the same as returning #GNUNET_SYSERR from a message - * handler, except that it allows dropping of a client even - * when not handling a message from that client. - * - * @param client the client to disconnect from - */ -void -GNUNET_SERVER_client_disconnect (struct GNUNET_SERVER_Client *client) -{ - struct GNUNET_SERVER_Handle *server = client->server; - struct NotifyList *n; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Client is being disconnected from the server.\n"); - if (NULL != client->restart_task) - { - GNUNET_SCHEDULER_cancel (client->restart_task); - client->restart_task = NULL; - } - if (NULL != client->warn_task) - { - GNUNET_SCHEDULER_cancel (client->warn_task); - client->warn_task = NULL; - } - if (GNUNET_YES == client->receive_pending) - { - GNUNET_CONNECTION_receive_cancel (client->connection); - client->receive_pending = GNUNET_NO; - } - client->shutdown_now = GNUNET_YES; - client->reference_count++; /* make sure nobody else clean up client... */ - if ( (NULL != client->mst) && - (NULL != server) ) - { - GNUNET_CONTAINER_DLL_remove (server->clients_head, - server->clients_tail, - client); - if (NULL != server->mst_destroy) - server->mst_destroy (server->mst_cls, - client->mst); - else - GNUNET_SERVER_mst_destroy (client->mst); - client->mst = NULL; - for (n = server->disconnect_notify_list_head; NULL != n; n = n->next) - n->callback (n->callback_cls, - client); - } - client->reference_count--; - if (client->reference_count > 0) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "RC of %p still positive, not destroying everything.\n", - client); - client->server = NULL; - return; - } - if (GNUNET_YES == client->in_process_client_buffer) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Still processing inputs of %p, not destroying everything.\n", - client); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "RC of %p now zero, destroying everything.\n", - client); - if (GNUNET_YES == client->persist) - GNUNET_CONNECTION_persist_ (client->connection); - if (NULL != client->th.cth) - GNUNET_SERVER_notify_transmit_ready_cancel (&client->th); - (void) GNUNET_SCHEDULER_add_now (&destroy_connection, - client->connection); - /* need to cancel again, as it might have been re-added - in the meantime (i.e. during callbacks) */ - if (NULL != client->warn_task) - { - GNUNET_SCHEDULER_cancel (client->warn_task); - client->warn_task = NULL; - } - if (GNUNET_YES == client->receive_pending) - { - GNUNET_CONNECTION_receive_cancel (client->connection); - client->receive_pending = GNUNET_NO; - } - GNUNET_free (client); - /* we might be in soft-shutdown, test if we're done */ - if (NULL != server) - test_monitor_clients (server); -} - - -/** - * Disable the "CORK" feature for communication with the given client, - * forcing the OS to immediately flush the buffer on transmission - * instead of potentially buffering multiple messages. - * - * @param client handle to the client - * @return #GNUNET_OK on success - */ -int -GNUNET_SERVER_client_disable_corking (struct GNUNET_SERVER_Client *client) -{ - return GNUNET_CONNECTION_disable_corking (client->connection); -} - - -/** - * Wrapper for transmission notification that calls the original - * callback and update the last activity time for our connection. - * - * @param cls the `struct GNUNET_SERVER_Client *` - * @param size number of bytes we can transmit - * @param buf where to copy the message - * @return number of bytes actually transmitted - */ -static size_t -transmit_ready_callback_wrapper (void *cls, size_t size, void *buf) -{ - struct GNUNET_SERVER_Client *client = cls; - GNUNET_CONNECTION_TransmitReadyNotify callback; - - client->th.cth = NULL; - callback = client->th.callback; - client->th.callback = NULL; - client->last_activity = GNUNET_TIME_absolute_get (); - return callback (client->th.callback_cls, size, buf); -} - - -/** - * Notify us when the server has enough space to transmit - * a message of the given size to the given client. - * - * @param client client to transmit message to - * @param size requested amount of buffer space - * @param timeout after how long should we give up (and call - * notify with buf NULL and size 0)? - * @param callback function to call when space is available - * @param callback_cls closure for @a callback - * @return non-NULL if the notify callback was queued; can be used - * to cancel the request using - * #GNUNET_SERVER_notify_transmit_ready_cancel(). - * NULL if we are already going to notify someone else (busy) - */ -struct GNUNET_SERVER_TransmitHandle * -GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client, - size_t size, - struct GNUNET_TIME_Relative timeout, - GNUNET_CONNECTION_TransmitReadyNotify callback, - void *callback_cls) -{ - if (NULL != client->th.callback) - return NULL; - client->th.callback_cls = callback_cls; - client->th.callback = callback; - client->th.cth = GNUNET_CONNECTION_notify_transmit_ready (client->connection, size, - timeout, - &transmit_ready_callback_wrapper, - client); - return &client->th; -} - - -/** - * Abort transmission request. - * - * @param th request to abort - */ -void -GNUNET_SERVER_notify_transmit_ready_cancel (struct GNUNET_SERVER_TransmitHandle *th) -{ - GNUNET_CONNECTION_notify_transmit_ready_cancel (th->cth); - th->cth = NULL; - th->callback = NULL; -} - - -/** - * Set the persistent flag on this client, used to setup client connection - * to only be killed when the service it's connected to is actually dead. - * - * @param client the client to set the persistent flag on - */ -void -GNUNET_SERVER_client_persist_ (struct GNUNET_SERVER_Client *client) -{ - client->persist = GNUNET_YES; -} - - -/** - * Resume receiving from this client, we are done processing the - * current request. This function must be called from within each - * GNUNET_SERVER_MessageCallback (or its respective continuations). - * - * @param client client we were processing a message of - * @param success #GNUNET_OK to keep the connection open and - * continue to receive - * #GNUNET_NO to close the connection (normal behavior) - * #GNUNET_SYSERR to close the connection (signal - * serious error) - */ -void -GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, - int success) -{ - if (NULL == client) - return; - GNUNET_assert (client->suspended > 0); - client->suspended--; - if (GNUNET_OK != success) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "GNUNET_SERVER_receive_done called with failure indication\n"); - if ( (client->reference_count > 0) || (client->suspended > 0) ) - client->shutdown_now = GNUNET_YES; - else - GNUNET_SERVER_client_disconnect (client); - return; - } - if (client->suspended > 0) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "GNUNET_SERVER_receive_done called, but more clients pending\n"); - return; - } - if (NULL != client->warn_task) - { - GNUNET_SCHEDULER_cancel (client->warn_task); - client->warn_task = NULL; - } - if (GNUNET_YES == client->in_process_client_buffer) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "GNUNET_SERVER_receive_done called while still in processing loop\n"); - return; - } - if ((NULL == client->server) || (GNUNET_YES == client->shutdown_now)) - { - GNUNET_SERVER_client_disconnect (client); - return; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, - "GNUNET_SERVER_receive_done causes restart in reading from the socket\n"); - GNUNET_assert (NULL == client->restart_task); - client->restart_task = GNUNET_SCHEDULER_add_now (&restart_processing, - client); -} - - -/* end of server.c */ diff --git a/src/util/service.c b/src/util/service.c deleted file mode 100644 index 496904fb1..000000000 --- a/src/util/service.c +++ /dev/null @@ -1,1697 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2012 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -/** - * @file util/service.c - * @brief functions related to starting services - * @author Christian Grothoff - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_constants.h" -#include "gnunet_resolver_service.h" -#include "speedup.h" - -#if HAVE_MALLINFO -#include -#include "gauger.h" -#endif - - -#define LOG(kind,...) GNUNET_log_from (kind, "util-service", __VA_ARGS__) - -#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-service", syscall) - -#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-service", syscall, filename) - - -/* ******************* access control ******************** */ - -/** - * Check if the given IP address is in the list of IP addresses. - * - * @param list a list of networks - * @param add the IP to check (in network byte order) - * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is - */ -static int -check_ipv4_listed (const struct GNUNET_STRINGS_IPv4NetworkPolicy *list, - const struct in_addr *add) -{ - unsigned int i; - - if (NULL == list) - return GNUNET_NO; - i = 0; - while ((list[i].network.s_addr != 0) || (list[i].netmask.s_addr != 0)) - { - if ((add->s_addr & list[i].netmask.s_addr) == - (list[i].network.s_addr & list[i].netmask.s_addr)) - return GNUNET_YES; - i++; - } - return GNUNET_NO; -} - - -/** - * Check if the given IP address is in the list of IP addresses. - * - * @param list a list of networks - * @param ip the IP to check (in network byte order) - * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is - */ -static int -check_ipv6_listed (const struct GNUNET_STRINGS_IPv6NetworkPolicy *list, - const struct in6_addr *ip) -{ - unsigned int i; - unsigned int j; - struct in6_addr zero; - - if (NULL == list) - return GNUNET_NO; - memset (&zero, 0, sizeof (struct in6_addr)); - i = 0; -NEXT: - while (0 != memcmp (&zero, &list[i].network, sizeof (struct in6_addr))) - { - for (j = 0; j < sizeof (struct in6_addr) / sizeof (int); j++) - if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) != - (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j])) - { - i++; - goto NEXT; - } - return GNUNET_YES; - } - return GNUNET_NO; -} - - -/* ****************** service struct ****************** */ - - -/** - * Context for "service_task". - */ -struct GNUNET_SERVICE_Context -{ - /** - * Our configuration. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Handle for the server. - */ - struct GNUNET_SERVER_Handle *server; - - /** - * NULL-terminated array of addresses to bind to, NULL if we got pre-bound - * listen sockets. - */ - struct sockaddr **addrs; - - /** - * Name of our service. - */ - const char *service_name; - - /** - * Main service-specific task to run. - */ - GNUNET_SERVICE_Main task; - - /** - * Closure for @e task. - */ - void *task_cls; - - /** - * IPv4 addresses that are not allowed to connect. - */ - struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_denied; - - /** - * IPv6 addresses that are not allowed to connect. - */ - struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_denied; - - /** - * IPv4 addresses that are allowed to connect (if not - * set, all are allowed). - */ - struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_allowed; - - /** - * IPv6 addresses that are allowed to connect (if not - * set, all are allowed). - */ - struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_allowed; - - /** - * My (default) message handlers. Adjusted copy - * of "defhandlers". - */ - struct GNUNET_SERVER_MessageHandler *my_handlers; - - /** - * Array of the lengths of the entries in addrs. - */ - socklen_t *addrlens; - - /** - * NULL-terminated array of listen sockets we should take over. - */ - struct GNUNET_NETWORK_Handle **lsocks; - - /** - * Task ID of the shutdown task. - */ - struct GNUNET_SCHEDULER_Task *shutdown_task; - - /** - * Idle timeout for server. - */ - struct GNUNET_TIME_Relative timeout; - - /** - * Overall success/failure of the service start. - */ - int ret; - - /** - * If we are daemonizing, this FD is set to the - * pipe to the parent. Send '.' if we started - * ok, '!' if not. -1 if we are not daemonizing. - */ - int ready_confirm_fd; - - /** - * Do we close connections if we receive messages - * for which we have no handler? - */ - int require_found; - - /** - * Do we require a matching UID for UNIX domain socket connections? - * #GNUNET_NO means that the UID does not have to match (however, - * @e match_gid may still impose other access control checks). - */ - int match_uid; - - /** - * Do we require a matching GID for UNIX domain socket connections? - * Ignored if @e match_uid is #GNUNET_YES. Note that this is about - * checking that the client's UID is in our group OR that the - * client's GID is our GID. If both "match_gid" and @e match_uid are - * #GNUNET_NO, all users on the local system have access. - */ - int match_gid; - - /** - * Our options. - */ - enum GNUNET_SERVICE_Options options; - -}; - - -/* ****************** message handlers ****************** */ - -/** - * Send a 'TEST' message back to the client. - * - * @param cls the 'struct GNUNET_SERVER_Client' to send TEST to - * @param size number of bytes available in 'buf' - * @param buf where to copy the message - * @return number of bytes written to 'buf' - */ -static size_t -write_test (void *cls, size_t size, void *buf) -{ - struct GNUNET_SERVER_Client *client = cls; - struct GNUNET_MessageHeader *msg; - - if (size < sizeof (struct GNUNET_MessageHeader)) - { - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return 0; /* client disconnected */ - } - msg = (struct GNUNET_MessageHeader *) buf; - msg->type = htons (GNUNET_MESSAGE_TYPE_TEST); - msg->size = htons (sizeof (struct GNUNET_MessageHeader)); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return sizeof (struct GNUNET_MessageHeader); -} - - -/** - * Handler for TEST message. - * - * @param cls closure (refers to service) - * @param client identification of the client - * @param message the actual message - */ -static void -handle_test (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - /* simply bounce message back to acknowledge */ - if (NULL == - GNUNET_SERVER_notify_transmit_ready (client, - sizeof (struct GNUNET_MessageHeader), - GNUNET_TIME_UNIT_FOREVER_REL, - &write_test, client)) - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); -} - - -/** - * Default handlers for all services. Will be copied and the - * "callback_cls" fields will be replaced with the specific service - * struct. - */ -static const struct GNUNET_SERVER_MessageHandler defhandlers[] = { - {&handle_test, NULL, GNUNET_MESSAGE_TYPE_TEST, - sizeof (struct GNUNET_MessageHeader)}, - {NULL, NULL, 0, 0} -}; - - -/* ****************** service core routines ************** */ - - -/** - * Check if access to the service is allowed from the given address. - * - * @param cls closure - * @param uc credentials, if available, otherwise NULL - * @param addr address - * @param addrlen length of address - * @return #GNUNET_YES to allow, #GNUNET_NO to deny, #GNUNET_SYSERR - * for unknown address family (will be denied). - */ -static int -check_access (void *cls, const struct GNUNET_CONNECTION_Credentials *uc, - const struct sockaddr *addr, socklen_t addrlen) -{ - struct GNUNET_SERVICE_Context *sctx = cls; - const struct sockaddr_in *i4; - const struct sockaddr_in6 *i6; - int ret; - - switch (addr->sa_family) - { - case AF_INET: - GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); - i4 = (const struct sockaddr_in *) addr; - ret = ((NULL == sctx->v4_allowed) || - (check_ipv4_listed (sctx->v4_allowed, &i4->sin_addr))) && - ((NULL == sctx->v4_denied) || - (!check_ipv4_listed (sctx->v4_denied, &i4->sin_addr))); - break; - case AF_INET6: - GNUNET_assert (addrlen == sizeof (struct sockaddr_in6)); - i6 = (const struct sockaddr_in6 *) addr; - ret = ((NULL == sctx->v6_allowed) || - (check_ipv6_listed (sctx->v6_allowed, &i6->sin6_addr))) && - ((NULL == sctx->v6_denied) || - (!check_ipv6_listed (sctx->v6_denied, &i6->sin6_addr))); - break; -#ifndef WINDOWS - case AF_UNIX: - ret = GNUNET_OK; /* controlled using file-system ACL now */ - break; -#endif - default: - LOG (GNUNET_ERROR_TYPE_WARNING, _("Unknown address family %d\n"), - addr->sa_family); - return GNUNET_SYSERR; - } - if (GNUNET_OK != ret) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - _("Access from `%s' denied to service `%s'\n"), - GNUNET_a2s (addr, addrlen), - sctx->service_name); - } - return ret; -} - - -/** - * Get the name of the file where we will - * write the PID of the service. - * - * @param sctx service context - * @return name of the file for the process ID - */ -static char * -get_pid_file_name (struct GNUNET_SERVICE_Context *sctx) -{ - char *pif; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, sctx->service_name, - "PIDFILE", &pif)) - return NULL; - return pif; -} - - -/** - * Parse an IPv4 access control list. - * - * @param ret location where to write the ACL (set) - * @param sctx service context to use to get the configuration - * @param option name of the ACL option to parse - * @return #GNUNET_SYSERR on parse error, #GNUNET_OK on success (including - * no ACL configured) - */ -static int -process_acl4 (struct GNUNET_STRINGS_IPv4NetworkPolicy **ret, - struct GNUNET_SERVICE_Context *sctx, - const char *option) -{ - char *opt; - - if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, option)) - { - *ret = NULL; - return GNUNET_OK; - } - GNUNET_break (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (sctx->cfg, - sctx->service_name, - option, &opt)); - if (NULL == (*ret = GNUNET_STRINGS_parse_ipv4_policy (opt))) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - _("Could not parse IPv4 network specification `%s' for `%s:%s'\n"), - opt, sctx->service_name, option); - GNUNET_free (opt); - return GNUNET_SYSERR; - } - GNUNET_free (opt); - return GNUNET_OK; -} - - -/** - * Parse an IPv6 access control list. - * - * @param ret location where to write the ACL (set) - * @param sctx service context to use to get the configuration - * @param option name of the ACL option to parse - * @return #GNUNET_SYSERR on parse error, #GNUNET_OK on success (including - * no ACL configured) - */ -static int -process_acl6 (struct GNUNET_STRINGS_IPv6NetworkPolicy **ret, - struct GNUNET_SERVICE_Context *sctx, - const char *option) -{ - char *opt; - - if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, option)) - { - *ret = NULL; - return GNUNET_OK; - } - GNUNET_break (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (sctx->cfg, - sctx->service_name, - option, &opt)); - if (NULL == (*ret = GNUNET_STRINGS_parse_ipv6_policy (opt))) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - _("Could not parse IPv6 network specification `%s' for `%s:%s'\n"), - opt, sctx->service_name, option); - GNUNET_free (opt); - return GNUNET_SYSERR; - } - GNUNET_free (opt); - return GNUNET_OK; -} - - -/** - * Add the given UNIX domain path as an address to the - * list (as the first entry). - * - * @param saddrs array to update - * @param saddrlens where to store the address length - * @param unixpath path to add - * @param abstract #GNUNET_YES to add an abstract UNIX domain socket. This - * parameter is ignore on systems other than LINUX - */ -static void -add_unixpath (struct sockaddr **saddrs, - socklen_t *saddrlens, - const char *unixpath, - int abstract) -{ -#ifdef AF_UNIX - struct sockaddr_un *un; - - un = GNUNET_new (struct sockaddr_un); - un->sun_family = AF_UNIX; - strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1); -#ifdef LINUX - if (GNUNET_YES == abstract) - un->sun_path[0] = '\0'; -#endif -#if HAVE_SOCKADDR_UN_SUN_LEN - un->sun_len = (u_char) sizeof (struct sockaddr_un); -#endif - *saddrs = (struct sockaddr *) un; - *saddrlens = sizeof (struct sockaddr_un); -#else - /* this function should never be called - * unless AF_UNIX is defined! */ - GNUNET_assert (0); -#endif -} - - -/** - * Get the list of addresses that a server for the given service - * should bind to. - * - * @param service_name name of the service - * @param cfg configuration (which specifies the addresses) - * @param addrs set (call by reference) to an array of pointers to the - * addresses the server should bind to and listen on; the - * array will be NULL-terminated (on success) - * @param addr_lens set (call by reference) to an array of the lengths - * of the respective `struct sockaddr` struct in the @a addrs - * array (on success) - * @return number of addresses found on success, - * #GNUNET_SYSERR if the configuration - * did not specify reasonable finding information or - * if it specified a hostname that could not be resolved; - * #GNUNET_NO if the number of addresses configured is - * zero (in this case, `*addrs` and `*addr_lens` will be - * set to NULL). - */ -int -GNUNET_SERVICE_get_server_addresses (const char *service_name, - const struct GNUNET_CONFIGURATION_Handle *cfg, - struct sockaddr ***addrs, - socklen_t ** addr_lens) -{ - int disablev6; - struct GNUNET_NETWORK_Handle *desc; - unsigned long long port; - char *unixpath; - struct addrinfo hints; - struct addrinfo *res; - struct addrinfo *pos; - struct addrinfo *next; - unsigned int i; - int resi; - int ret; - int abstract; - struct sockaddr **saddrs; - socklen_t *saddrlens; - char *hostname; - - *addrs = NULL; - *addr_lens = NULL; - desc = NULL; - if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "DISABLEV6")) - { - if (GNUNET_SYSERR == - (disablev6 = - GNUNET_CONFIGURATION_get_value_yesno (cfg, service_name, "DISABLEV6"))) - return GNUNET_SYSERR; - } - else - disablev6 = GNUNET_NO; - - if (! disablev6) - { - /* probe IPv6 support */ - desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); - if (NULL == desc) - { - if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) || - (EACCES == errno)) - { - LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); - return GNUNET_SYSERR; - } - LOG (GNUNET_ERROR_TYPE_INFO, - _("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"), - service_name, STRERROR (errno)); - disablev6 = GNUNET_YES; - } - else - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); - desc = NULL; - } - } - - port = 0; - if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT")) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, service_name, - "PORT", &port)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Require valid port number for service `%s' in configuration!\n"), - service_name); - } - if (port > 65535) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Require valid port number for service `%s' in configuration!\n"), - service_name); - return GNUNET_SYSERR; - } - } - - if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "BINDTO")) - { - GNUNET_break (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (cfg, service_name, - "BINDTO", &hostname)); - } - else - hostname = NULL; - - unixpath = NULL; - abstract = GNUNET_NO; -#ifdef AF_UNIX - if ((GNUNET_YES == - GNUNET_CONFIGURATION_have_value (cfg, service_name, "UNIXPATH")) && - (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_filename (cfg, service_name, "UNIXPATH", - &unixpath)) && - (0 < strlen (unixpath))) - { - /* probe UNIX support */ - struct sockaddr_un s_un; - - if (strlen (unixpath) >= sizeof (s_un.sun_path)) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath, - (unsigned long long) sizeof (s_un.sun_path)); - unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath); - LOG (GNUNET_ERROR_TYPE_INFO, - _("Using `%s' instead\n"), - unixpath); - } -#ifdef LINUX - abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg, - "TESTING", - "USE_ABSTRACT_SOCKETS"); - if (GNUNET_SYSERR == abstract) - abstract = GNUNET_NO; -#endif - if ((GNUNET_YES != abstract) - && (GNUNET_OK != - GNUNET_DISK_directory_create_for_file (unixpath))) - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "mkdir", - unixpath); - } - if (NULL != unixpath) - { - desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0); - if (NULL == desc) - { - if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) || - (EACCES == errno)) - { - LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); - GNUNET_free_non_null (hostname); - GNUNET_free (unixpath); - return GNUNET_SYSERR; - } - LOG (GNUNET_ERROR_TYPE_INFO, - _("Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"), - service_name, - STRERROR (errno)); - GNUNET_free (unixpath); - unixpath = NULL; - } - else - { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); - desc = NULL; - } - } -#endif - - if ((0 == port) && (NULL == unixpath)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Have neither PORT nor UNIXPATH for service `%s', but one is required\n"), - service_name); - GNUNET_free_non_null (hostname); - return GNUNET_SYSERR; - } - if (0 == port) - { - saddrs = GNUNET_malloc (2 * sizeof (struct sockaddr *)); - saddrlens = GNUNET_malloc (2 * sizeof (socklen_t)); - add_unixpath (saddrs, saddrlens, unixpath, abstract); - GNUNET_free_non_null (unixpath); - GNUNET_free_non_null (hostname); - *addrs = saddrs; - *addr_lens = saddrlens; - return 1; - } - - if (NULL != hostname) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Resolving `%s' since that is where `%s' will bind to.\n", - hostname, - service_name); - memset (&hints, 0, sizeof (struct addrinfo)); - if (disablev6) - hints.ai_family = AF_INET; - hints.ai_protocol = IPPROTO_TCP; - if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) || - (NULL == res)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Failed to resolve `%s': %s\n"), - hostname, - gai_strerror (ret)); - GNUNET_free (hostname); - GNUNET_free_non_null (unixpath); - return GNUNET_SYSERR; - } - next = res; - i = 0; - while (NULL != (pos = next)) - { - next = pos->ai_next; - if ((disablev6) && (pos->ai_family == AF_INET6)) - continue; - i++; - } - if (0 == i) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Failed to find %saddress for `%s'.\n"), - disablev6 ? "IPv4 " : "", - hostname); - freeaddrinfo (res); - GNUNET_free (hostname); - GNUNET_free_non_null (unixpath); - return GNUNET_SYSERR; - } - resi = i; - if (NULL != unixpath) - resi++; - saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); - saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); - i = 0; - if (NULL != unixpath) - { - add_unixpath (saddrs, saddrlens, unixpath, abstract); - i++; - } - next = res; - while (NULL != (pos = next)) - { - next = pos->ai_next; - if ((disablev6) && (AF_INET6 == pos->ai_family)) - continue; - if ((IPPROTO_TCP != pos->ai_protocol) && (0 != pos->ai_protocol)) - continue; /* not TCP */ - if ((SOCK_STREAM != pos->ai_socktype) && (0 != pos->ai_socktype)) - continue; /* huh? */ - LOG (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' will bind to `%s'\n", - service_name, GNUNET_a2s (pos->ai_addr, pos->ai_addrlen)); - if (AF_INET == pos->ai_family) - { - GNUNET_assert (sizeof (struct sockaddr_in) == pos->ai_addrlen); - saddrlens[i] = pos->ai_addrlen; - saddrs[i] = GNUNET_malloc (saddrlens[i]); - GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); - ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); - } - else - { - GNUNET_assert (AF_INET6 == pos->ai_family); - GNUNET_assert (sizeof (struct sockaddr_in6) == pos->ai_addrlen); - saddrlens[i] = pos->ai_addrlen; - saddrs[i] = GNUNET_malloc (saddrlens[i]); - GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); - ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); - } - i++; - } - GNUNET_free (hostname); - freeaddrinfo (res); - resi = i; - } - else - { - /* will bind against everything, just set port */ - if (disablev6) - { - /* V4-only */ - resi = 1; - if (NULL != unixpath) - resi++; - i = 0; - saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); - saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); - if (NULL != unixpath) - { - add_unixpath (saddrs, saddrlens, unixpath, abstract); - i++; - } - saddrlens[i] = sizeof (struct sockaddr_in); - saddrs[i] = GNUNET_malloc (saddrlens[i]); -#if HAVE_SOCKADDR_IN_SIN_LEN - ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i]; -#endif - ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; - ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); - } - else - { - /* dual stack */ - resi = 2; - if (NULL != unixpath) - resi++; - saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); - saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); - i = 0; - if (NULL != unixpath) - { - add_unixpath (saddrs, saddrlens, unixpath, abstract); - i++; - } - saddrlens[i] = sizeof (struct sockaddr_in6); - saddrs[i] = GNUNET_malloc (saddrlens[i]); -#if HAVE_SOCKADDR_IN_SIN_LEN - ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0]; -#endif - ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6; - ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); - i++; - saddrlens[i] = sizeof (struct sockaddr_in); - saddrs[i] = GNUNET_malloc (saddrlens[i]); -#if HAVE_SOCKADDR_IN_SIN_LEN - ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1]; -#endif - ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; - ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); - } - } - GNUNET_free_non_null (unixpath); - *addrs = saddrs; - *addr_lens = saddrlens; - return resi; -} - - -#ifdef MINGW -/** - * Read listen sockets from the parent process (ARM). - * - * @param sctx service context to initialize - * @return #GNUNET_YES if ok, #GNUNET_NO if not ok (must bind yourself), - * and #GNUNET_SYSERR on error. - */ -static int -receive_sockets_from_parent (struct GNUNET_SERVICE_Context *sctx) -{ - const char *env_buf; - int fail; - uint64_t count; - uint64_t i; - HANDLE lsocks_pipe; - - env_buf = getenv ("GNUNET_OS_READ_LSOCKS"); - if ((NULL == env_buf) || (strlen (env_buf) <= 0)) - return GNUNET_NO; - /* Using W32 API directly here, because this pipe will - * never be used outside of this function, and it's just too much of a bother - * to create a GNUnet API that boxes a HANDLE (the way it is done with socks) - */ - lsocks_pipe = (HANDLE) strtoul (env_buf, NULL, 10); - if ( (0 == lsocks_pipe) || (INVALID_HANDLE_VALUE == lsocks_pipe)) - return GNUNET_NO; - fail = 1; - do - { - int ret; - int fail2; - DWORD rd; - - ret = ReadFile (lsocks_pipe, &count, sizeof (count), &rd, NULL); - if ((0 == ret) || (sizeof (count) != rd) || (0 == count)) - break; - sctx->lsocks = - GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (count + 1)); - - fail2 = 1; - for (i = 0; i < count; i++) - { - WSAPROTOCOL_INFOA pi; - uint64_t size; - SOCKET s; - - ret = ReadFile (lsocks_pipe, &size, sizeof (size), &rd, NULL); - if ( (0 == ret) || (sizeof (size) != rd) || (sizeof (pi) != size) ) - break; - ret = ReadFile (lsocks_pipe, &pi, sizeof (pi), &rd, NULL); - if ( (0 == ret) || (sizeof (pi) != rd)) - break; - s = WSASocketA (pi.iAddressFamily, pi.iSocketType, pi.iProtocol, &pi, 0, WSA_FLAG_OVERLAPPED); - sctx->lsocks[i] = GNUNET_NETWORK_socket_box_native (s); - if (NULL == sctx->lsocks[i]) - break; - else if (i == count - 1) - fail2 = 0; - } - if (fail2) - break; - sctx->lsocks[count] = NULL; - fail = 0; - } - while (fail); - - CloseHandle (lsocks_pipe); - - if (fail) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Could not access a pre-bound socket, will try to bind myself\n")); - for (i = 0; (i < count) && (NULL != sctx->lsocks[i]); i++) - GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[i])); - GNUNET_free_non_null (sctx->lsocks); - sctx->lsocks = NULL; - return GNUNET_NO; - } - return GNUNET_YES; -} -#endif - - -/** - * Setup addr, addrlen, idle_timeout - * based on configuration! - * - * Configuration may specify: - * - PORT (where to bind to for TCP) - * - UNIXPATH (where to bind to for UNIX domain sockets) - * - TIMEOUT (after how many ms does an inactive service timeout); - * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack) - * - BINDTO (hostname or IP address to bind to, otherwise we take everything) - * - ACCEPT_FROM (only allow connections from specified IPv4 subnets) - * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets) - * - REJECT_FROM (disallow allow connections from specified IPv4 subnets) - * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets) - * - * @param sctx service context to initialize - * @return #GNUNET_OK if configuration succeeded - */ -static int -setup_service (struct GNUNET_SERVICE_Context *sctx) -{ - struct GNUNET_TIME_Relative idleout; - int tolerant; - -#ifndef MINGW - const char *nfds; - unsigned int cnt; - int flags; -#endif - - if (GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, "TIMEOUT")) - { - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_time (sctx->cfg, sctx->service_name, - "TIMEOUT", &idleout)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Specified value for `%s' of service `%s' is invalid\n"), - "TIMEOUT", sctx->service_name); - return GNUNET_SYSERR; - } - sctx->timeout = idleout; - } - else - sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; - - if (GNUNET_CONFIGURATION_have_value - (sctx->cfg, sctx->service_name, "TOLERANT")) - { - if (GNUNET_SYSERR == - (tolerant = - GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name, - "TOLERANT"))) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Specified value for `%s' of service `%s' is invalid\n"), - "TOLERANT", sctx->service_name); - return GNUNET_SYSERR; - } - } - else - tolerant = GNUNET_NO; - -#ifndef MINGW - errno = 0; - if ((NULL != (nfds = getenv ("LISTEN_FDS"))) && - (1 == SSCANF (nfds, "%u", &cnt)) && (cnt > 0) && (cnt < FD_SETSIZE) && - (cnt + 4 < FD_SETSIZE)) - { - sctx->lsocks = - GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (cnt + 1)); - while (0 < cnt--) - { - flags = fcntl (3 + cnt, F_GETFD); - if ((flags < 0) || (0 != (flags & FD_CLOEXEC)) || - (NULL == - (sctx->lsocks[cnt] = GNUNET_NETWORK_socket_box_native (3 + cnt)))) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _ - ("Could not access pre-bound socket %u, will try to bind myself\n"), - (unsigned int) 3 + cnt); - cnt++; - while (sctx->lsocks[cnt] != NULL) - GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[cnt++])); - GNUNET_free (sctx->lsocks); - sctx->lsocks = NULL; - break; - } - } - unsetenv ("LISTEN_FDS"); - } -#else - if (getenv ("GNUNET_OS_READ_LSOCKS") != NULL) - { - receive_sockets_from_parent (sctx); - putenv ("GNUNET_OS_READ_LSOCKS="); - } -#endif - - if ((NULL == sctx->lsocks) && - (GNUNET_SYSERR == - GNUNET_SERVICE_get_server_addresses (sctx->service_name, sctx->cfg, - &sctx->addrs, &sctx->addrlens))) - return GNUNET_SYSERR; - sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES; - sctx->match_uid = - GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name, - "UNIX_MATCH_UID"); - sctx->match_gid = - GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name, - "UNIX_MATCH_GID"); - process_acl4 (&sctx->v4_denied, sctx, "REJECT_FROM"); - process_acl4 (&sctx->v4_allowed, sctx, "ACCEPT_FROM"); - process_acl6 (&sctx->v6_denied, sctx, "REJECT_FROM6"); - process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6"); - - return GNUNET_OK; -} - - -/** - * Get the name of the user that'll be used - * to provide the service. - * - * @param sctx service context - * @return value of the 'USERNAME' option - */ -static char * -get_user_name (struct GNUNET_SERVICE_Context *sctx) -{ - char *un; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, sctx->service_name, - "USERNAME", &un)) - return NULL; - return un; -} - - -/** - * Write PID file. - * - * @param sctx service context - * @param pid PID to write (should be equal to 'getpid()' - * @return #GNUNET_OK on success (including no work to be done) - */ -static int -write_pid_file (struct GNUNET_SERVICE_Context *sctx, pid_t pid) -{ - FILE *pidfd; - char *pif; - char *user; - char *rdir; - int len; - - if (NULL == (pif = get_pid_file_name (sctx))) - return GNUNET_OK; /* no file desired */ - user = get_user_name (sctx); - rdir = GNUNET_strdup (pif); - len = strlen (rdir); - while ((len > 0) && (rdir[len] != DIR_SEPARATOR)) - len--; - rdir[len] = '\0'; - if (0 != ACCESS (rdir, F_OK)) - { - /* we get to create a directory -- and claim it - * as ours! */ - (void) GNUNET_DISK_directory_create (rdir); - if ((NULL != user) && (0 < strlen (user))) - GNUNET_DISK_file_change_owner (rdir, user); - } - if (0 != ACCESS (rdir, W_OK | X_OK)) - { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", rdir); - GNUNET_free (rdir); - GNUNET_free_non_null (user); - GNUNET_free (pif); - return GNUNET_SYSERR; - } - GNUNET_free (rdir); - pidfd = FOPEN (pif, "w"); - if (NULL == pidfd) - { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "fopen", pif); - GNUNET_free (pif); - GNUNET_free_non_null (user); - return GNUNET_SYSERR; - } - if (0 > FPRINTF (pidfd, "%u", pid)) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fprintf", pif); - GNUNET_break (0 == FCLOSE (pidfd)); - if ((NULL != user) && (0 < strlen (user))) - GNUNET_DISK_file_change_owner (pif, user); - GNUNET_free_non_null (user); - GNUNET_free (pif); - return GNUNET_OK; -} - - -/** - * Task run during shutdown. Stops the server/service. - * - * @param cls the `struct GNUNET_SERVICE_Context` - */ -static void -shutdown_task (void *cls) -{ - struct GNUNET_SERVICE_Context *service = cls; - struct GNUNET_SERVER_Handle *server = service->server; - - service->shutdown_task = NULL; - if (0 != (service->options & GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN)) - GNUNET_SERVER_stop_listening (server); - else - GNUNET_SERVER_destroy (server); -} - - -/** - * Initial task for the service. - * - * @param cls service context - */ -static void -service_task (void *cls) -{ - struct GNUNET_SERVICE_Context *sctx = cls; - unsigned int i; - - (void) GNUNET_SPEEDUP_start_ (sctx->cfg); - GNUNET_RESOLVER_connect (sctx->cfg); - if (NULL != sctx->lsocks) - sctx->server - = GNUNET_SERVER_create_with_sockets (&check_access, sctx, sctx->lsocks, - sctx->timeout, sctx->require_found); - else - sctx->server - = GNUNET_SERVER_create (&check_access, sctx, sctx->addrs, sctx->addrlens, - sctx->timeout, sctx->require_found); - if (NULL == sctx->server) - { - if (NULL != sctx->addrs) - for (i = 0; NULL != sctx->addrs[i]; i++) - LOG (GNUNET_ERROR_TYPE_INFO, - _("Failed to start `%s' at `%s'\n"), - sctx->service_name, GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); - sctx->ret = GNUNET_SYSERR; - return; - } -#ifndef WINDOWS - if (NULL != sctx->addrs) - for (i = 0; NULL != sctx->addrs[i]; i++) - if ((AF_UNIX == sctx->addrs[i]->sa_family) - && ('\0' != ((const struct sockaddr_un *)sctx->addrs[i])->sun_path[0])) - GNUNET_DISK_fix_permissions (((const struct sockaddr_un *)sctx->addrs[i])->sun_path, - sctx->match_uid, - sctx->match_gid); -#endif - - - if (0 == (sctx->options & GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN)) - { - /* install a task that will kill the server - * process if the scheduler ever gets a shutdown signal */ - sctx->shutdown_task = GNUNET_SCHEDULER_add_shutdown (&shutdown_task, - sctx); - } - sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); - GNUNET_memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); - i = 0; - while (NULL != sctx->my_handlers[i].callback) - sctx->my_handlers[i++].callback_cls = sctx; - GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); - if (-1 != sctx->ready_confirm_fd) - { - GNUNET_break (1 == WRITE (sctx->ready_confirm_fd, ".", 1)); - GNUNET_break (0 == CLOSE (sctx->ready_confirm_fd)); - sctx->ready_confirm_fd = -1; - write_pid_file (sctx, getpid ()); - } - if (NULL != sctx->addrs) - { - i = 0; - while (NULL != sctx->addrs[i]) - { - LOG (GNUNET_ERROR_TYPE_INFO, _("Service `%s' runs at %s\n"), - sctx->service_name, GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); - i++; - } - } - sctx->task (sctx->task_cls, sctx->server, sctx->cfg); -} - - -/** - * Detach from terminal. - * - * @param sctx service context - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static int -detach_terminal (struct GNUNET_SERVICE_Context *sctx) -{ -#ifndef MINGW - pid_t pid; - int nullfd; - int filedes[2]; - - if (0 != PIPE (filedes)) - { - LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe"); - return GNUNET_SYSERR; - } - pid = fork (); - if (pid < 0) - { - LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork"); - return GNUNET_SYSERR; - } - if (0 != pid) - { - /* Parent */ - char c; - - GNUNET_break (0 == CLOSE (filedes[1])); - c = 'X'; - if (1 != READ (filedes[0], &c, sizeof (char))) - LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "read"); - fflush (stdout); - switch (c) - { - case '.': - exit (0); - case 'I': - LOG (GNUNET_ERROR_TYPE_INFO, _("Service process failed to initialize\n")); - break; - case 'S': - LOG (GNUNET_ERROR_TYPE_INFO, - _("Service process could not initialize server function\n")); - break; - case 'X': - LOG (GNUNET_ERROR_TYPE_INFO, - _("Service process failed to report status\n")); - break; - } - exit (1); /* child reported error */ - } - GNUNET_break (0 == CLOSE (0)); - GNUNET_break (0 == CLOSE (1)); - GNUNET_break (0 == CLOSE (filedes[0])); - nullfd = OPEN ("/dev/null", O_RDWR | O_APPEND); - if (nullfd < 0) - return GNUNET_SYSERR; - /* set stdin/stdout to /dev/null */ - if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0)) - { - LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2"); - (void) CLOSE (nullfd); - return GNUNET_SYSERR; - } - (void) CLOSE (nullfd); - /* Detach from controlling terminal */ - pid = setsid (); - if (-1 == pid) - LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "setsid"); - sctx->ready_confirm_fd = filedes[1]; -#else - /* FIXME: we probably need to do something else - * elsewhere in order to fork the process itself... */ - FreeConsole (); -#endif - return GNUNET_OK; -} - - -/** - * Set user ID. - * - * @param sctx service context - * @return #GNUNET_OK on success, #GNUNET_SYSERR on error - */ -static int -set_user_id (struct GNUNET_SERVICE_Context *sctx) -{ - char *user; - - if (NULL == (user = get_user_name (sctx))) - return GNUNET_OK; /* keep */ -#ifndef MINGW - struct passwd *pws; - - errno = 0; - pws = getpwnam (user); - if (NULL == pws) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Cannot obtain information about user `%s': %s\n"), user, - errno == 0 ? _("No such user") : STRERROR (errno)); - GNUNET_free (user); - return GNUNET_SYSERR; - } - if ((0 != setgid (pws->pw_gid)) || (0 != setegid (pws->pw_gid)) || -#if HAVE_INITGROUPS - (0 != initgroups (user, pws->pw_gid)) || -#endif - (0 != setuid (pws->pw_uid)) || (0 != seteuid (pws->pw_uid))) - { - if ((0 != setregid (pws->pw_gid, pws->pw_gid)) || - (0 != setreuid (pws->pw_uid, pws->pw_uid))) - { - LOG (GNUNET_ERROR_TYPE_ERROR, _("Cannot change user/group to `%s': %s\n"), - user, STRERROR (errno)); - GNUNET_free (user); - return GNUNET_SYSERR; - } - } -#endif - GNUNET_free (user); - return GNUNET_OK; -} - - -/** - * Delete the PID file that was created by our parent. - * - * @param sctx service context - */ -static void -pid_file_delete (struct GNUNET_SERVICE_Context *sctx) -{ - char *pif = get_pid_file_name (sctx); - - if (NULL == pif) - return; /* no PID file */ - if (0 != UNLINK (pif)) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", pif); - GNUNET_free (pif); -} - - -/** - * Run a standard GNUnet service startup sequence (initialize loggers - * and configuration, parse options). - * - * @param argc number of command line arguments - * @param argv command line arguments - * @param service_name our service name - * @param options service options - * @param task main task of the service - * @param task_cls closure for @a task - * @return #GNUNET_SYSERR on error, #GNUNET_OK - * if we shutdown nicely - */ -int -GNUNET_SERVICE_run (int argc, char *const *argv, - const char *service_name, - enum GNUNET_SERVICE_Options options, - GNUNET_SERVICE_Main task, - void *task_cls) -{ -#define HANDLE_ERROR do { GNUNET_break (0); goto shutdown; } while (0) - - int err; - int ret; - char *cfg_fn; - char *opt_cfg_fn; - char *loglev; - char *logfile; - int do_daemonize; - unsigned int i; - unsigned long long skew_offset; - unsigned long long skew_variance; - long long clock_offset; - struct GNUNET_SERVICE_Context sctx; - struct GNUNET_CONFIGURATION_Handle *cfg; - const char *xdg; - - struct GNUNET_GETOPT_CommandLineOption service_options[] = { - GNUNET_GETOPT_OPTION_CFG_FILE (&opt_cfg_fn), - {'d', "daemonize", NULL, - gettext_noop ("do daemonize (detach from terminal)"), 0, - GNUNET_GETOPT_set_one, &do_daemonize}, - GNUNET_GETOPT_OPTION_HELP (NULL), - GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev), - GNUNET_GETOPT_OPTION_LOGFILE (&logfile), - GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION " " VCS_VERSION), - GNUNET_GETOPT_OPTION_END - }; - err = 1; - do_daemonize = 0; - logfile = NULL; - loglev = NULL; - opt_cfg_fn = NULL; - xdg = getenv ("XDG_CONFIG_HOME"); - if (NULL != xdg) - GNUNET_asprintf (&cfg_fn, - "%s%s%s", - xdg, - DIR_SEPARATOR_STR, - GNUNET_OS_project_data_get ()->config_file); - else - cfg_fn = GNUNET_strdup (GNUNET_OS_project_data_get ()->user_config_file); - memset (&sctx, 0, sizeof (sctx)); - sctx.options = options; - sctx.ready_confirm_fd = -1; - sctx.ret = GNUNET_OK; - sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL; - sctx.task = task; - sctx.task_cls = task_cls; - sctx.service_name = service_name; - sctx.cfg = cfg = GNUNET_CONFIGURATION_create (); - - /* setup subsystems */ - ret = GNUNET_GETOPT_run (service_name, service_options, argc, argv); - if (GNUNET_SYSERR == ret) - goto shutdown; - if (GNUNET_NO == ret) - { - err = 0; - goto shutdown; - } - if (GNUNET_OK != GNUNET_log_setup (service_name, loglev, logfile)) - HANDLE_ERROR; - if (NULL == opt_cfg_fn) - opt_cfg_fn = GNUNET_strdup (cfg_fn); - if (GNUNET_YES == GNUNET_DISK_file_test (opt_cfg_fn)) - { - if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, opt_cfg_fn)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Malformed configuration file `%s', exit ...\n"), - opt_cfg_fn); - goto shutdown; - } - } - else - { - if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, NULL)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Malformed configuration, exit ...\n")); - goto shutdown; - } - if (0 != strcmp (opt_cfg_fn, cfg_fn)) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Could not access configuration file `%s'\n"), - opt_cfg_fn); - } - if (GNUNET_OK != setup_service (&sctx)) - goto shutdown; - if ((1 == do_daemonize) && (GNUNET_OK != detach_terminal (&sctx))) - HANDLE_ERROR; - if (GNUNET_OK != set_user_id (&sctx)) - goto shutdown; - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Service `%s' runs with configuration from `%s'\n", - service_name, - opt_cfg_fn); - if ((GNUNET_OK == - GNUNET_CONFIGURATION_get_value_number (sctx.cfg, "TESTING", - "SKEW_OFFSET", &skew_offset)) && - (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_number (sctx.cfg, "TESTING", - "SKEW_VARIANCE", &skew_variance))) - { - clock_offset = skew_offset - skew_variance; - GNUNET_TIME_set_offset (clock_offset); - LOG (GNUNET_ERROR_TYPE_DEBUG, "Skewing clock by %dll ms\n", clock_offset); - } - /* actually run service */ - err = 0; - GNUNET_SCHEDULER_run (&service_task, &sctx); - /* shutdown */ - if ((1 == do_daemonize) && (NULL != sctx.server)) - pid_file_delete (&sctx); - GNUNET_free_non_null (sctx.my_handlers); - -shutdown: - if (-1 != sctx.ready_confirm_fd) - { - if (1 != WRITE (sctx.ready_confirm_fd, err ? "I" : "S", 1)) - LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "write"); - GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd)); - } -#if HAVE_MALLINFO - { - char *counter; - - if ( (GNUNET_YES == - GNUNET_CONFIGURATION_have_value (sctx.cfg, service_name, - "GAUGER_HEAP")) && - (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (sctx.cfg, service_name, - "GAUGER_HEAP", - &counter)) ) - { - struct mallinfo mi; - - mi = mallinfo (); - GAUGER (service_name, counter, mi.usmblks, "blocks"); - GNUNET_free (counter); - } - } -#endif - GNUNET_SPEEDUP_stop_ (); - GNUNET_CONFIGURATION_destroy (cfg); - i = 0; - if (NULL != sctx.addrs) - while (NULL != sctx.addrs[i]) - GNUNET_free (sctx.addrs[i++]); - GNUNET_free_non_null (sctx.addrs); - GNUNET_free_non_null (sctx.addrlens); - GNUNET_free_non_null (logfile); - GNUNET_free_non_null (loglev); - GNUNET_free (cfg_fn); - GNUNET_free_non_null (opt_cfg_fn); - GNUNET_free_non_null (sctx.v4_denied); - GNUNET_free_non_null (sctx.v6_denied); - GNUNET_free_non_null (sctx.v4_allowed); - GNUNET_free_non_null (sctx.v6_allowed); - - return err ? GNUNET_SYSERR : sctx.ret; -} - - -/** - * Run a service startup sequence within an existing - * initialized system. - * - * @param service_name our service name - * @param cfg configuration to use - * @param options service options - * @return NULL on error, service handle - */ -struct GNUNET_SERVICE_Context * -GNUNET_SERVICE_start (const char *service_name, - const struct GNUNET_CONFIGURATION_Handle *cfg, - enum GNUNET_SERVICE_Options options) -{ - int i; - struct GNUNET_SERVICE_Context *sctx; - - sctx = GNUNET_new (struct GNUNET_SERVICE_Context); - sctx->ready_confirm_fd = -1; /* no daemonizing */ - sctx->ret = GNUNET_OK; - sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; - sctx->service_name = service_name; - sctx->cfg = cfg; - sctx->options = options; - - /* setup subsystems */ - if (GNUNET_OK != setup_service (sctx)) - { - GNUNET_SERVICE_stop (sctx); - return NULL; - } - if (NULL != sctx->lsocks) - sctx->server = - GNUNET_SERVER_create_with_sockets (&check_access, sctx, sctx->lsocks, - sctx->timeout, sctx->require_found); - else - sctx->server = - GNUNET_SERVER_create (&check_access, sctx, sctx->addrs, sctx->addrlens, - sctx->timeout, sctx->require_found); - - if (NULL == sctx->server) - { - GNUNET_SERVICE_stop (sctx); - return NULL; - } -#ifndef WINDOWS - if (NULL != sctx->addrs) - for (i = 0; NULL != sctx->addrs[i]; i++) - if ((AF_UNIX == sctx->addrs[i]->sa_family) - && ('\0' != ((const struct sockaddr_un *)sctx->addrs[i])->sun_path[0])) - GNUNET_DISK_fix_permissions (((const struct sockaddr_un *)sctx->addrs[i])->sun_path, - sctx->match_uid, - sctx->match_gid); -#endif - sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); - GNUNET_memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); - i = 0; - while ((sctx->my_handlers[i].callback != NULL)) - sctx->my_handlers[i++].callback_cls = sctx; - GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); - return sctx; -} - - -/** - * Obtain the server used by a service. Note that the server must NOT - * be destroyed by the caller. - * - * @param ctx the service context returned from the start function - * @return handle to the server for this service, NULL if there is none - */ -struct GNUNET_SERVER_Handle * -GNUNET_SERVICE_get_server (struct GNUNET_SERVICE_Context *ctx) -{ - return ctx->server; -} - - -/** - * Get the NULL-terminated array of listen sockets for this service. - * - * @param ctx service context to query - * @return NULL if there are no listen sockets, otherwise NULL-terminated - * array of listen sockets. - */ -struct GNUNET_NETWORK_Handle *const* -GNUNET_SERVICE_get_listen_sockets (struct GNUNET_SERVICE_Context *ctx) -{ - return ctx->lsocks; -} - - -/** - * Stop a service that was started with "GNUNET_SERVICE_start". - * - * @param sctx the service context returned from the start function - */ -void -GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx) -{ - unsigned int i; - -#if HAVE_MALLINFO - { - char *counter; - - if ( (GNUNET_YES == - GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, - "GAUGER_HEAP")) && - (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (sctx->cfg, sctx->service_name, - "GAUGER_HEAP", - &counter)) ) - { - struct mallinfo mi; - - mi = mallinfo (); - GAUGER (sctx->service_name, counter, mi.usmblks, "blocks"); - GNUNET_free (counter); - } - } -#endif - if (NULL != sctx->shutdown_task) - { - GNUNET_SCHEDULER_cancel (sctx->shutdown_task); - sctx->shutdown_task = NULL; - } - if (NULL != sctx->server) - GNUNET_SERVER_destroy (sctx->server); - GNUNET_free_non_null (sctx->my_handlers); - if (NULL != sctx->addrs) - { - i = 0; - while (NULL != sctx->addrs[i]) - GNUNET_free (sctx->addrs[i++]); - GNUNET_free (sctx->addrs); - } - GNUNET_free_non_null (sctx->addrlens); - GNUNET_free_non_null (sctx->v4_denied); - GNUNET_free_non_null (sctx->v6_denied); - GNUNET_free_non_null (sctx->v4_allowed); - GNUNET_free_non_null (sctx->v6_allowed); - GNUNET_free (sctx); -} - - -/* end of service.c */ diff --git a/src/util/test_connection.c b/src/util/test_connection.c deleted file mode 100644 index eaca75c2e..000000000 --- a/src/util/test_connection.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_connection.c - * @brief tests for connection.c - */ -#include "platform.h" -#include "gnunet_util_lib.h" - -#define PORT 12435 - - -static struct GNUNET_CONNECTION_Handle *csock; - -static struct GNUNET_CONNECTION_Handle *asock; - -static struct GNUNET_CONNECTION_Handle *lsock; - -static size_t sofar; - -static struct GNUNET_NETWORK_Handle *ls; - -static struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Create and initialize a listen socket for the server. - * - * @return -1 on error, otherwise the listen socket - */ -static struct GNUNET_NETWORK_Handle * -open_listen_socket () -{ - const static int on = 1; - struct sockaddr_in sa; - struct GNUNET_NETWORK_Handle *desc; - - memset (&sa, 0, sizeof (sa)); -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_port = htons (PORT); - sa.sin_family = AF_INET; - desc = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); - GNUNET_assert (desc != NULL); - if (GNUNET_NETWORK_socket_setsockopt - (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt"); - GNUNET_assert (GNUNET_OK == - GNUNET_NETWORK_socket_bind (desc, (const struct sockaddr *) &sa, - sizeof (sa))); - GNUNET_NETWORK_socket_listen (desc, 5); - return desc; -} - -static void -receive_check (void *cls, const void *buf, size_t available, - const struct sockaddr *addr, socklen_t addrlen, int errCode) -{ - int *ok = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receive validates incoming data\n"); - GNUNET_assert (buf != NULL); /* no timeout */ - if (0 == memcmp (&"Hello World"[sofar], buf, available)) - sofar += available; - if (sofar < 12) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receive needs more data\n"); - GNUNET_CONNECTION_receive (asock, 1024, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, - cls); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receive closes accepted socket\n"); - *ok = 0; - GNUNET_CONNECTION_destroy (asock); - GNUNET_CONNECTION_destroy (csock); - } -} - - -static void -run_accept (void *cls) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test accepts connection\n"); - asock = GNUNET_CONNECTION_create_from_accept (NULL, NULL, ls); - GNUNET_assert (asock != NULL); - GNUNET_assert (GNUNET_YES == GNUNET_CONNECTION_check (asock)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test destroys listen socket\n"); - GNUNET_CONNECTION_destroy (lsock); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Test asks to receive on accepted socket\n"); - GNUNET_CONNECTION_receive (asock, 1024, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, - cls); -} - - -static size_t -make_hello (void *cls, size_t size, void *buf) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Test prepares to transmit on connect socket\n"); - GNUNET_assert (size >= 12); - strcpy ((char *) buf, "Hello World"); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test destroys client socket\n"); - return 12; -} - - -static void -task (void *cls) -{ - ls = open_listen_socket (); - lsock = GNUNET_CONNECTION_create_from_existing (ls); - GNUNET_assert (lsock != NULL); - csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT); - GNUNET_assert (csock != NULL); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test asks for write notification\n"); - GNUNET_assert (NULL != - GNUNET_CONNECTION_notify_transmit_ready (csock, 12, - GNUNET_TIME_UNIT_SECONDS, - &make_hello, NULL)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test prepares to accept\n"); - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, ls, &run_accept, - cls); -} - - -int -main (int argc, char *argv[]) -{ - int ok; - - GNUNET_log_setup ("test_connection", - "WARNING", - NULL); - - ok = 1; - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME", - "localhost"); - GNUNET_SCHEDULER_run (&task, &ok); - GNUNET_CONFIGURATION_destroy (cfg); - return ok; -} - -/* end of test_connection.c */ diff --git a/src/util/test_connection_addressing.c b/src/util/test_connection_addressing.c deleted file mode 100644 index a6345b10a..000000000 --- a/src/util/test_connection_addressing.c +++ /dev/null @@ -1,186 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_connection_addressing.c - * @brief tests for connection.c - */ -#include "platform.h" -#include "gnunet_util_lib.h" - - -#define PORT 12435 - - -static struct GNUNET_CONNECTION_Handle *csock; - -static struct GNUNET_CONNECTION_Handle *asock; - -static struct GNUNET_CONNECTION_Handle *lsock; - -static size_t sofar; - -static struct GNUNET_NETWORK_Handle *ls; - - - -/** - * Create and initialize a listen socket for the server. - * - * @return NULL on error, otherwise the listen socket - */ -static struct GNUNET_NETWORK_Handle * -open_listen_socket () -{ - const static int on = 1; - struct sockaddr_in sa; - struct GNUNET_NETWORK_Handle *desc; - - memset (&sa, 0, sizeof (sa)); -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_family = AF_INET; - sa.sin_port = htons (PORT); - desc = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); - GNUNET_assert (desc != 0); - if (GNUNET_NETWORK_socket_setsockopt - (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt"); - if (GNUNET_OK != - GNUNET_NETWORK_socket_bind (desc, (const struct sockaddr *) &sa, - sizeof (sa))) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "bind"); - GNUNET_assert (0); - } - GNUNET_NETWORK_socket_listen (desc, 5); - return desc; -} - - -static void -receive_check (void *cls, const void *buf, size_t available, - const struct sockaddr *addr, socklen_t addrlen, int errCode) -{ - int *ok = cls; - - GNUNET_assert (buf != NULL); /* no timeout */ - if (0 == memcmp (&"Hello World"[sofar], buf, available)) - sofar += available; - if (sofar < 12) - { - GNUNET_CONNECTION_receive (asock, 1024, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, - cls); - } - else - { - *ok = 0; - GNUNET_CONNECTION_destroy (csock); - GNUNET_CONNECTION_destroy (asock); - } -} - - -static void -run_accept (void *cls) -{ - void *addr; - size_t alen; - struct sockaddr_in *v4; - struct sockaddr_in expect; - - asock = GNUNET_CONNECTION_create_from_accept (NULL, NULL, ls); - GNUNET_assert (asock != NULL); - GNUNET_assert (GNUNET_YES == GNUNET_CONNECTION_check (asock)); - GNUNET_assert (GNUNET_OK == - GNUNET_CONNECTION_get_address (asock, &addr, &alen)); - GNUNET_assert (alen == sizeof (struct sockaddr_in)); - v4 = addr; - memset (&expect, 0, sizeof (expect)); -#if HAVE_SOCKADDR_IN_SIN_LEN - expect.sin_len = sizeof (expect); -#endif - expect.sin_family = AF_INET; - expect.sin_port = v4->sin_port; - expect.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - GNUNET_assert (0 == memcmp (&expect, v4, alen)); - GNUNET_free (addr); - GNUNET_CONNECTION_destroy (lsock); - GNUNET_CONNECTION_receive (asock, 1024, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check, - cls); -} - -static size_t -make_hello (void *cls, size_t size, void *buf) -{ - GNUNET_assert (size >= 12); - strcpy ((char *) buf, "Hello World"); - return 12; -} - - -static void -task (void *cls) -{ - struct sockaddr_in v4; - - ls = open_listen_socket (); - lsock = GNUNET_CONNECTION_create_from_existing (ls); - GNUNET_assert (lsock != NULL); - -#if HAVE_SOCKADDR_IN_SIN_LEN - v4.sin_len = sizeof (v4); -#endif - v4.sin_family = AF_INET; - v4.sin_port = htons (PORT); - v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - csock = - GNUNET_CONNECTION_create_from_sockaddr (AF_INET, - (const struct sockaddr *) &v4, - sizeof (v4)); - GNUNET_assert (csock != NULL); - GNUNET_assert (NULL != - GNUNET_CONNECTION_notify_transmit_ready (csock, 12, - GNUNET_TIME_UNIT_SECONDS, - &make_hello, NULL)); - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, ls, &run_accept, - cls); -} - - -int -main (int argc, char *argv[]) -{ - int ok; - - GNUNET_log_setup ("test_connection_addressing", - "WARNING", - NULL); - ok = 1; - GNUNET_SCHEDULER_run (&task, &ok); - return ok; -} - -/* end of test_connection_addressing.c */ diff --git a/src/util/test_connection_receive_cancel.c b/src/util/test_connection_receive_cancel.c deleted file mode 100644 index 9c0ab699e..000000000 --- a/src/util/test_connection_receive_cancel.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_connection_receive_cancel.c - * @brief tests for connection.c - */ -#include "platform.h" -#include "gnunet_util_lib.h" - -#define PORT 12435 - - -static struct GNUNET_CONNECTION_Handle *csock; - -static struct GNUNET_CONNECTION_Handle *asock; - -static struct GNUNET_CONNECTION_Handle *lsock; - -static struct GNUNET_NETWORK_Handle *ls; - -static struct GNUNET_CONFIGURATION_Handle *cfg; - - -/** - * Create and initialize a listen socket for the server. - * - * @return NULL on error, otherwise the listen socket - */ -static struct GNUNET_NETWORK_Handle * -open_listen_socket () -{ - const static int on = 1; - struct sockaddr_in sa; - struct GNUNET_NETWORK_Handle *desc; - - memset (&sa, 0, sizeof (sa)); -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_family = AF_INET; - sa.sin_port = htons (PORT); - desc = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); - GNUNET_assert (desc != NULL); - if (GNUNET_NETWORK_socket_setsockopt - (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, - "setsockopt"); - GNUNET_assert (GNUNET_OK == - GNUNET_NETWORK_socket_bind (desc, (const struct sockaddr *) &sa, - sizeof (sa))); - GNUNET_NETWORK_socket_listen (desc, 5); - return desc; -} - - -static void -dead_receive (void *cls, - const void *buf, - size_t available, - const struct sockaddr *addr, - socklen_t addrlen, - int errCode) -{ - GNUNET_assert (0); -} - - -static void -run_accept_cancel (void *cls) -{ - asock = GNUNET_CONNECTION_create_from_accept (NULL, NULL, ls); - GNUNET_assert (asock != NULL); - GNUNET_assert (GNUNET_YES == GNUNET_CONNECTION_check (asock)); - GNUNET_CONNECTION_destroy (lsock); - GNUNET_CONNECTION_receive (asock, 1024, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_SECONDS, 5), - &dead_receive, cls); -} - - -static void -receive_cancel_task (void *cls) -{ - int *ok = cls; - - GNUNET_CONNECTION_receive_cancel (asock); - GNUNET_CONNECTION_destroy (csock); - GNUNET_CONNECTION_destroy (asock); - *ok = 0; -} - - -static void -task_receive_cancel (void *cls) -{ - ls = open_listen_socket (); - lsock = GNUNET_CONNECTION_create_from_existing (ls); - GNUNET_assert (lsock != NULL); - csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT); - GNUNET_assert (csock != NULL); - GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - ls, - &run_accept_cancel, - cls); - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, - &receive_cancel_task, - cls); -} - - -/** - * Main method, starts scheduler with task_timeout. - */ -static int -check_receive_cancel () -{ - int ok; - - ok = 1; - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_CONFIGURATION_set_value_string (cfg, - "resolver", - "HOSTNAME", - "localhost"); - GNUNET_SCHEDULER_run (&task_receive_cancel, &ok); - GNUNET_CONFIGURATION_destroy (cfg); - return ok; -} - - -int -main (int argc, char *argv[]) -{ - int ret = 0; - - GNUNET_log_setup ("test_connection_receive_cancel", "WARNING", NULL); - ret += check_receive_cancel (); - - return ret; -} - -/* end of test_connection_receive_cancel.c */ diff --git a/src/util/test_connection_timeout.c b/src/util/test_connection_timeout.c deleted file mode 100644 index e78cec669..000000000 --- a/src/util/test_connection_timeout.c +++ /dev/null @@ -1,129 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_connection_timeout.c - * @brief tests for connection.c - */ -#include "platform.h" -#include "gnunet_util_lib.h" - -#define PORT 12435 - -static struct GNUNET_CONNECTION_Handle *csock; - -static struct GNUNET_CONNECTION_Handle *lsock; - -static struct GNUNET_NETWORK_Handle *ls; - -static struct GNUNET_CONFIGURATION_Handle *cfg; - - -/** - * Create and initialize a listen socket for the server. - * - * @return NULL on error, otherwise the listen socket - */ -static struct GNUNET_NETWORK_Handle * -open_listen_socket () -{ - const static int on = 1; - struct sockaddr_in sa; - struct GNUNET_NETWORK_Handle *desc; - - memset (&sa, 0, sizeof (sa)); -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_family = AF_INET; - sa.sin_port = htons (PORT); - desc = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0); - GNUNET_assert (desc != NULL); - if (GNUNET_NETWORK_socket_setsockopt - (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt"); - GNUNET_assert (GNUNET_OK == - GNUNET_NETWORK_socket_bind (desc, (const struct sockaddr *) &sa, - sizeof (sa))); - GNUNET_NETWORK_socket_listen (desc, 5); - return desc; -} - - -static size_t -send_kilo (void *cls, size_t size, void *buf) -{ - int *ok = cls; - - if (size == 0) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got the desired timeout!\n"); - GNUNET_assert (buf == NULL); - *ok = 0; - GNUNET_CONNECTION_destroy (lsock); - GNUNET_CONNECTION_destroy (csock); - return 0; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending kilo to fill buffer.\n"); - GNUNET_assert (size >= 1024); - memset (buf, 42, 1024); - - GNUNET_assert (NULL != - GNUNET_CONNECTION_notify_transmit_ready (csock, 1024, - GNUNET_TIME_UNIT_SECONDS, - &send_kilo, cls)); - return 1024; -} - - -static void -task_timeout (void *cls) -{ - - ls = open_listen_socket (); - lsock = GNUNET_CONNECTION_create_from_existing (ls); - GNUNET_assert (lsock != NULL); - csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT); - GNUNET_assert (csock != NULL); - GNUNET_assert (NULL != - GNUNET_CONNECTION_notify_transmit_ready (csock, 1024, - GNUNET_TIME_UNIT_SECONDS, - &send_kilo, cls)); -} - - -int -main (int argc, char *argv[]) -{ - int ok; - - GNUNET_log_setup ("test_connection_timeout", - "WARNING", - NULL); - - ok = 1; - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME", - "localhost"); - GNUNET_SCHEDULER_run (&task_timeout, &ok); - GNUNET_CONFIGURATION_destroy (cfg); - return ok; -} - -/* end of test_connection_timeout.c */ diff --git a/src/util/test_connection_timeout_no_connect.c b/src/util/test_connection_timeout_no_connect.c deleted file mode 100644 index ebcd4b71e..000000000 --- a/src/util/test_connection_timeout_no_connect.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_connection_timeout_no_connect.c - * @brief tests for connection.c, doing timeout which connect failure - */ -#include "platform.h" -#include "gnunet_util_lib.h" - -#define PORT 13425 - -static struct GNUNET_CONNECTION_Handle *csock; - -static struct GNUNET_CONFIGURATION_Handle *cfg; - -static size_t -handle_timeout (void *cls, size_t size, void *buf) -{ - int *ok = cls; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received timeout signal.\n"); - GNUNET_assert (size == 0); - GNUNET_assert (buf == NULL); - *ok = 0; - return 0; -} - - -static void -task_timeout (void *cls) -{ - csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT); - GNUNET_assert (csock != NULL); - GNUNET_assert (NULL != - GNUNET_CONNECTION_notify_transmit_ready (csock, 1024, - GNUNET_TIME_UNIT_SECONDS, - &handle_timeout, - cls)); -} - - -int -main (int argc, char *argv[]) -{ - int ok; - - GNUNET_log_setup ("test_connection_timeout_no_connect", - "WARNING", - NULL); - ok = 1; - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME", - "localhost"); - GNUNET_SCHEDULER_run (&task_timeout, &ok); - GNUNET_CONFIGURATION_destroy (cfg); - return ok; -} - -/* end of test_connection_timeout_no_connect.c */ diff --git a/src/util/test_connection_transmit_cancel.c b/src/util/test_connection_transmit_cancel.c deleted file mode 100644 index 9ef0720ed..000000000 --- a/src/util/test_connection_transmit_cancel.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_connection_transmit_cancel.c - * @brief tests for connection.c - */ -#include "platform.h" -#include "gnunet_util_lib.h" - -#define PORT 12435 - -static struct GNUNET_CONFIGURATION_Handle *cfg; - - -static size_t -not_run (void *cls, size_t size, void *buf) -{ - GNUNET_assert (0); - return 0; -} - - -static void -task_transmit_cancel (void *cls) -{ - int *ok = cls; - struct GNUNET_CONNECTION_TransmitHandle *th; - struct GNUNET_CONNECTION_Handle *csock; - - csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT); - GNUNET_assert (csock != NULL); - th = GNUNET_CONNECTION_notify_transmit_ready (csock, 12, - GNUNET_TIME_UNIT_MINUTES, - ¬_run, cls); - GNUNET_assert (NULL != th); - GNUNET_CONNECTION_notify_transmit_ready_cancel (th); - GNUNET_CONNECTION_destroy (csock); - *ok = 0; -} - - -int -main (int argc, char *argv[]) -{ - int ok; - - GNUNET_log_setup ("test_connection_transmit_cancel", - "WARNING", - NULL); - ok = 1; - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME", - "localhost"); - GNUNET_SCHEDULER_run (&task_transmit_cancel, &ok); - GNUNET_CONFIGURATION_destroy (cfg); - return ok; -} - -/* end of test_connection_transmit_cancel.c */ diff --git a/src/util/test_server.c b/src/util/test_server.c deleted file mode 100644 index 8003adbf4..000000000 --- a/src/util/test_server.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 2014, 2016 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_server.c - * @brief tests for server.c - */ -#include "platform.h" -#include "gnunet_util_lib.h" - -/** - * TCP port to use for the server. - */ -#define PORT 12435 - -/** - * Timeout to use for operations. - */ -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2) - -/** - * Test message type. - */ -#define MY_TYPE 128 - -/** - * Test message type. - */ -#define MY_TYPE2 129 - -/** - * Handle for the server. - */ -static struct GNUNET_SERVER_Handle *server; - -/** - * Handle for the client. - */ -static struct GNUNET_MQ_Handle *mq; - -/** - * Handle of the server for the client. - */ -static struct GNUNET_SERVER_Client *argclient; - -/** - * Our configuration. - */ -static struct GNUNET_CONFIGURATION_Handle *cfg; - -/** - * Number indiciating in which phase of the test we are. - */ -static int ok; - - -/** - * Final task invoked to clean up. - * - * @param cls NULL - */ -static void -finish_up (void *cls) -{ - GNUNET_assert (7 == ok); - ok = 0; - GNUNET_SERVER_destroy (server); - GNUNET_MQ_destroy (mq); - GNUNET_CONFIGURATION_destroy (cfg); -} - - -/** - * The server has received the second message, initiate clean up. - * - * @param cls NULL - * @param client client we got the message from - * @param message the message - */ -static void -recv_fin_cb (void *cls, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - GNUNET_assert (6 == ok); - ok = 7; - GNUNET_SERVER_receive_done (client, GNUNET_OK); - GNUNET_SCHEDULER_add_now (&finish_up, NULL); -} - - -/** - * We have received the reply from the server, check that we are at - * the right stage and queue the next message to the server. Cleans - * up #argclient. - * - * @param cls NULL - * @param msg message we got from the server - */ -static void -handle_reply (void *cls, - const struct GNUNET_MessageHeader *msg) -{ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *m; - - GNUNET_assert (4 == ok); - ok = 6; - env = GNUNET_MQ_msg (m, - MY_TYPE2); - GNUNET_MQ_send (mq, - env); -} - - -/** - * Send a reply of type #MY_TYPE from the server to the client. - * Checks that we are in the right phase and transmits the - * reply. Cleans up #argclient state. - * - * @param cls NULL - * @param size number of bytes we are allowed to send - * @param buf where to copy the reply - * @return number of bytes written to @a buf - */ -static size_t -reply_msg (void *cls, - size_t size, - void *buf) -{ - struct GNUNET_MessageHeader msg; - - GNUNET_assert (3 == ok); - ok = 4; - GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); - msg.type = htons (MY_TYPE); - msg.size = htons (sizeof (struct GNUNET_MessageHeader)); - GNUNET_memcpy (buf, &msg, sizeof (struct GNUNET_MessageHeader)); - GNUNET_assert (NULL != argclient); - GNUNET_SERVER_receive_done (argclient, GNUNET_OK); - GNUNET_SERVER_client_drop (argclient); - argclient = NULL; - return sizeof (struct GNUNET_MessageHeader); -} - - -/** - * Function called whenever the server receives a message of - * type #MY_TYPE. Checks that we are at the stage where - * we expect the first message, then sends a reply. Stores - * the handle to the client in #argclient. - * - * @param cls NULL - * @param client client that sent the message - * @param message the message we received - */ -static void -recv_cb (void *cls, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - GNUNET_assert (2 == ok); - ok = 3; - argclient = client; - GNUNET_SERVER_client_keep (argclient); - GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == ntohs (message->size)); - GNUNET_assert (MY_TYPE == ntohs (message->type)); - GNUNET_assert (NULL != - GNUNET_SERVER_notify_transmit_ready (client, - ntohs (message->size), - TIMEOUT, - &reply_msg, - NULL)); -} - - -/** - * Message handlers for the server. - */ -static struct GNUNET_SERVER_MessageHandler handlers[] = { - {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, - {&recv_fin_cb, NULL, MY_TYPE2, sizeof (struct GNUNET_MessageHeader)}, - {NULL, NULL, 0, 0} -}; - - -/** - * Generic error handler, called with the appropriate error code and - * the same closure specified at the creation of the message queue. - * Not every message queue implementation supports an error handler. - * - * @param cls closure with the `struct GNUNET_STATISTICS_Handle *` - * @param error error code - */ -static void -mq_error_handler (void *cls, - enum GNUNET_MQ_Error error) -{ - GNUNET_assert (0); /* should never happen */ -} - - -/** - * First task run by the scheduler. Initializes the server and - * a client and asks for a transmission from the client to the - * server. - * - * @param cls NULL - */ -static void -task (void *cls) -{ - struct sockaddr_in sa; - struct sockaddr *sap[2]; - socklen_t slens[2]; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *msg; - struct GNUNET_MQ_MessageHandler chandlers[] = { - GNUNET_MQ_hd_fixed_size (reply, - MY_TYPE, - struct GNUNET_MessageHeader, - cls), - GNUNET_MQ_handler_end () - }; - - sap[0] = (struct sockaddr *) &sa; - slens[0] = sizeof (sa); - sap[1] = NULL; - slens[1] = 0; - memset (&sa, 0, sizeof (sa)); -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_family = AF_INET; - sa.sin_port = htons (PORT); - server = GNUNET_SERVER_create (NULL, NULL, - sap, slens, - TIMEOUT, GNUNET_NO); - GNUNET_assert (server != NULL); - GNUNET_SERVER_add_handlers (server, handlers); - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_CONFIGURATION_set_value_number (cfg, - "test-server", - "PORT", - PORT); - GNUNET_CONFIGURATION_set_value_string (cfg, - "test-server", - "HOSTNAME", - "localhost"); - GNUNET_CONFIGURATION_set_value_string (cfg, - "resolver", - "HOSTNAME", - "localhost"); - mq = GNUNET_CLIENT_connect (cfg, - "test-server", - chandlers, - &mq_error_handler, - NULL); - GNUNET_assert (NULL != mq); - ok = 2; - env = GNUNET_MQ_msg (msg, - MY_TYPE); - GNUNET_MQ_send (mq, - env); -} - - -/** - * Runs the test. - * - * @param argc length of @a argv - * @param argv command line arguments (ignored) - * @return 0 on success, otherwise phase of failure - */ -int -main (int argc, char *argv[]) -{ - GNUNET_log_setup ("test_server", - "WARNING", - NULL); - ok = 1; - GNUNET_SCHEDULER_run (&task, &ok); - return ok; -} - -/* end of test_server.c */ diff --git a/src/util/test_server_disconnect.c b/src/util/test_server_disconnect.c deleted file mode 100644 index c3d003e90..000000000 --- a/src/util/test_server_disconnect.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010, 2016 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_server_disconnect.c - * @brief tests for server.c, specifically GNUNET_SERVER_client_disconnect - */ -#include "platform.h" -#include "gnunet_util_lib.h" - - -#define PORT 12435 - -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250) - -#define MY_TYPE 128 - -static struct GNUNET_SERVER_Handle *server; - -static struct GNUNET_MQ_Handle *mq; - -static struct GNUNET_CONFIGURATION_Handle *cfg; - -static int ok; - - -static void -finish_up (void *cls) -{ - GNUNET_assert (ok == 5); - ok = 0; - GNUNET_SERVER_destroy (server); - GNUNET_MQ_destroy (mq); - GNUNET_CONFIGURATION_destroy (cfg); -} - - -static void -notify_disconnect (void *cls, - struct GNUNET_SERVER_Client *clientarg) -{ - if (NULL == clientarg) - return; - GNUNET_assert (ok == 4); - ok = 5; - GNUNET_SCHEDULER_add_now (&finish_up, NULL); -} - - -static void -server_disconnect (void *cls) -{ - struct GNUNET_SERVER_Client *argclient = cls; - - GNUNET_assert (ok == 3); - ok = 4; - GNUNET_SERVER_client_disconnect (argclient); - GNUNET_SERVER_client_drop (argclient); -} - - -static void -recv_cb (void *cls, - struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) -{ - GNUNET_assert (ok == 2); - ok = 3; - GNUNET_SERVER_client_keep (client); - GNUNET_SCHEDULER_add_now (&server_disconnect, client); - GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == ntohs (message->size)); - GNUNET_assert (MY_TYPE == ntohs (message->type)); - GNUNET_SERVER_receive_done (client, GNUNET_OK); -} - - -static struct GNUNET_SERVER_MessageHandler handlers[] = { - {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, - {NULL, NULL, 0, 0} -}; - - -static void -task (void *cls) -{ - struct sockaddr_in sa; - struct sockaddr *sap[2]; - socklen_t slens[2]; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *msg; - - sap[0] = (struct sockaddr *) &sa; - slens[0] = sizeof (sa); - sap[1] = NULL; - slens[1] = 0; - memset (&sa, 0, sizeof (sa)); -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_family = AF_INET; - sa.sin_port = htons (PORT); - server = GNUNET_SERVER_create (NULL, NULL, sap, slens, TIMEOUT, GNUNET_NO); - GNUNET_assert (server != NULL); - GNUNET_SERVER_add_handlers (server, handlers); - GNUNET_SERVER_disconnect_notify (server, ¬ify_disconnect, NULL); - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_CONFIGURATION_set_value_number (cfg, "test-server", "PORT", PORT); - GNUNET_CONFIGURATION_set_value_string (cfg, "test-server", "HOSTNAME", - "localhost"); - GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME", - "localhost"); - mq = GNUNET_CLIENT_connect (cfg, - "test-server", - NULL, - NULL, - NULL); - GNUNET_assert (NULL != mq); - ok = 2; - env = GNUNET_MQ_msg (msg, - MY_TYPE); - GNUNET_MQ_send (mq, - env); -} - - -/** - * Main method, starts scheduler with task1, - * checks that "ok" is correct at the end. - */ -static int -check () -{ - ok = 1; - GNUNET_SCHEDULER_run (&task, &ok); - return ok; -} - - -int -main (int argc, char *argv[]) -{ - int ret = 0; - - GNUNET_log_setup ("test_server_disconnect", "WARNING", NULL); - ret += check (); - - return ret; -} - -/* end of test_server_disconnect.c */ diff --git a/src/util/test_server_mst_interrupt.c b/src/util/test_server_mst_interrupt.c deleted file mode 100644 index 3141a75bd..000000000 --- a/src/util/test_server_mst_interrupt.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2010 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_server_mst_interrupt.c - * @brief test for interrupt message processing in server_mst.c - */ -#include "platform.h" -#include "gnunet_protocols.h" -#include "gnunet_util_lib.h" - -static struct GNUNET_SERVER_MessageStreamTokenizer * mst; - - -/* Callback destroying mst with data in buffer */ -static int -mst_cb (void *cls, void *client, - const struct GNUNET_MessageHeader * message) -{ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MST gave me message, destroying\n"); - GNUNET_SERVER_mst_destroy (mst); - return GNUNET_SYSERR; -} - - -int -main (int argc, char *argv[]) -{ - struct GNUNET_PeerIdentity id; - struct GNUNET_MessageHeader msg[2]; - - GNUNET_log_setup ("test_server_mst_interrupt", "WARNING", NULL); - memset (&id, 0, sizeof (id)); - msg[0].size = htons (sizeof (msg)); - msg[0].type = htons (sizeof (GNUNET_MESSAGE_TYPE_DUMMY)); - mst = GNUNET_SERVER_mst_create(mst_cb, NULL); - GNUNET_SERVER_mst_receive (mst, &id, - (const char *) &msg, 2 * sizeof (msg), - GNUNET_NO, GNUNET_NO); - /* If we reach this line, it did not crash */ - return 0; -} - -/* end of test_server_mst_interrupt.c */ diff --git a/src/util/test_server_with_client.c b/src/util/test_server_with_client.c deleted file mode 100644 index 63bfda00c..000000000 --- a/src/util/test_server_with_client.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2016 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_server_with_client.c - * @brief tests for server.c and client.c, - * specifically disconnect_notify, - * client_get_address and receive_done (resume processing) - */ -#include "platform.h" -#include "gnunet_util_lib.h" - -#define PORT 22335 - -#define MY_TYPE 128 - - -static struct GNUNET_SERVER_Handle *server; - -static struct GNUNET_MQ_Handle *mq; - -static struct GNUNET_CONFIGURATION_Handle *cfg; - -static int ok; - - -static void -send_done (void *cls) -{ - struct GNUNET_SERVER_Client *argclient = cls; - - GNUNET_assert (ok == 3); - ok++; - GNUNET_SERVER_receive_done (argclient, GNUNET_OK); -} - - -static void -recv_cb (void *cls, - struct GNUNET_SERVER_Client *argclient, - const struct GNUNET_MessageHeader *message) -{ - void *addr; - size_t addrlen; - struct sockaddr_in sa; - struct sockaddr_in *have; - - GNUNET_assert (GNUNET_OK == - GNUNET_SERVER_client_get_address (argclient, - &addr, - &addrlen)); - - GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); - have = addr; - memset (&sa, 0, sizeof (sa)); -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_family = AF_INET; - sa.sin_port = have->sin_port; - sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - GNUNET_assert (0 == memcmp (&sa, addr, addrlen)); - GNUNET_free (addr); - switch (ok) - { - case 2: - ok++; - GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_MILLISECONDS, 50), - &send_done, - argclient); - break; - case 4: - ok++; - GNUNET_MQ_destroy (mq); - GNUNET_SERVER_receive_done (argclient, - GNUNET_OK); - break; - default: - GNUNET_assert (0); - } - -} - - -static void -clean_up (void *cls) -{ - GNUNET_SERVER_destroy (server); - server = NULL; - GNUNET_CONFIGURATION_destroy (cfg); - cfg = NULL; -} - - -/** - * Functions with this signature are called whenever a client - * is disconnected on the network level. - * - * @param cls closure - * @param client identification of the client - */ -static void -notify_disconnect (void *cls, - struct GNUNET_SERVER_Client *client) -{ - if (client == NULL) - return; - GNUNET_assert (ok == 5); - ok = 0; - GNUNET_SCHEDULER_add_now (&clean_up, NULL); -} - - -static struct GNUNET_SERVER_MessageHandler handlers[] = { - {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, - {NULL, NULL, 0, 0} -}; - - -static void -task (void *cls) -{ - struct sockaddr_in sa; - struct sockaddr *sap[2]; - socklen_t slens[2]; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *msg; - - sap[0] = (struct sockaddr *) &sa; - slens[0] = sizeof (sa); - sap[1] = NULL; - slens[1] = 0; - memset (&sa, 0, sizeof (sa)); -#if HAVE_SOCKADDR_IN_SIN_LEN - sa.sin_len = sizeof (sa); -#endif - sa.sin_family = AF_INET; - sa.sin_port = htons (PORT); - server = - GNUNET_SERVER_create (NULL, NULL, sap, slens, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_MILLISECONDS, 250), GNUNET_NO); - GNUNET_assert (server != NULL); - handlers[0].callback_cls = cls; - GNUNET_SERVER_add_handlers (server, handlers); - GNUNET_SERVER_disconnect_notify (server, ¬ify_disconnect, cls); - cfg = GNUNET_CONFIGURATION_create (); - GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT); - GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME", "localhost"); - GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME", - "localhost"); - mq = GNUNET_CLIENT_connect (cfg, - "test", - NULL, - NULL, - NULL); - GNUNET_assert (NULL != mq); - ok = 2; - env = GNUNET_MQ_msg (msg, - MY_TYPE); - GNUNET_MQ_send (mq, - env); - env = GNUNET_MQ_msg (msg, - MY_TYPE); - GNUNET_MQ_send (mq, - env); -} - - -int -main (int argc, char *argv[]) -{ - GNUNET_log_setup ("test_server_with_client", - "WARNING", - NULL); - ok = 1; - GNUNET_SCHEDULER_run (&task, NULL); - return ok; -} - -/* end of test_server_with_client.c */ diff --git a/src/util/test_server_with_client_unix.c b/src/util/test_server_with_client_unix.c deleted file mode 100644 index 8fabbe210..000000000 --- a/src/util/test_server_with_client_unix.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 2009, 2016 GNUnet e.V. - - 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., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ -/** - * @file util/test_server_with_client_unix.c - * @brief tests for server.c and client.c, - * specifically disconnect_notify, - * client_get_address and receive_done (resume processing) - */ -#include "platform.h" -#include "gnunet_util_lib.h" - -#define MY_TYPE 128 - - -static struct GNUNET_SERVER_Handle *server; - -static struct GNUNET_MQ_Handle *mq; - -static struct GNUNET_CONFIGURATION_Handle *cfg; - -static int ok; - - -static void -send_done (void *cls) -{ - struct GNUNET_SERVER_Client *argclient = cls; - - GNUNET_assert (ok == 3); - ok++; - GNUNET_SERVER_receive_done (argclient, GNUNET_OK); -} - - -static void -recv_cb (void *cls, - struct GNUNET_SERVER_Client *argclient, - const struct GNUNET_MessageHeader *message) -{ - switch (ok) - { - case 2: - ok++; - (void) GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_MILLISECONDS, 50), - &send_done, - argclient); - break; - case 4: - ok++; - GNUNET_MQ_destroy (mq); - GNUNET_SERVER_receive_done (argclient, GNUNET_OK); - break; - default: - GNUNET_assert (0); - } - -} - - -static void -clean_up (void *cls) -{ - GNUNET_SERVER_destroy (server); - server = NULL; - GNUNET_CONFIGURATION_destroy (cfg); - cfg = NULL; -} - - -/** - * Functions with this signature are called whenever a client - * is disconnected on the network level. - * - * @param cls closure - * @param client identification of the client - */ -static void -notify_disconnect (void *cls, - struct GNUNET_SERVER_Client *client) -{ - if (client == NULL) - return; - GNUNET_assert (ok == 5); - ok = 0; - (void) GNUNET_SCHEDULER_add_now (&clean_up, NULL); -} - - -static struct GNUNET_SERVER_MessageHandler handlers[] = { - {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)}, - {NULL, NULL, 0, 0} -}; - - -static void -task (void *cls) -{ - struct sockaddr_un un; - const char *unixpath = "/tmp/testsock"; - struct sockaddr *sap[2]; - socklen_t slens[2]; - struct GNUNET_MQ_Envelope *env; - struct GNUNET_MessageHeader *msg; - - memset (&un, 0, sizeof (un)); - un.sun_family = AF_UNIX; - strncpy(un.sun_path, unixpath, sizeof (un.sun_path) - 1); -#if HAVE_SOCKADDR_UN_SUN_LEN - un.sun_len = (u_char) sizeof (un); -#endif - - sap[0] = (struct sockaddr *) &un; - slens[0] = sizeof (un); - sap[1] = NULL; - slens[1] = 0; - server = - GNUNET_SERVER_create (NULL, NULL, sap, slens, - GNUNET_TIME_relative_multiply - (GNUNET_TIME_UNIT_MILLISECONDS, 250), GNUNET_NO); - GNUNET_assert (server != NULL); - handlers[0].callback_cls = cls; - GNUNET_SERVER_add_handlers (server, handlers); - GNUNET_SERVER_disconnect_notify (server, ¬ify_disconnect, cls); - cfg = GNUNET_CONFIGURATION_create (); - - GNUNET_CONFIGURATION_set_value_string (cfg, "test", "UNIXPATH", unixpath); - GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME", - "localhost"); - mq = GNUNET_CLIENT_connect (cfg, - "test", - NULL, - NULL, - NULL); - GNUNET_assert (NULL != mq); - ok = 2; - env = GNUNET_MQ_msg (msg, - MY_TYPE); - GNUNET_MQ_send (mq, - env); - env = GNUNET_MQ_msg (msg, - MY_TYPE); - GNUNET_MQ_send (mq, - env); -} - - -int -main (int argc, char *argv[]) -{ - GNUNET_log_setup ("test_server_with_client_unix", - "WARNING", - NULL); - ok = 1; - GNUNET_SCHEDULER_run (&task, NULL); - return ok; -} - -/* end of test_server_with_client_unix.c */ diff --git a/src/vpn/gnunet-service-vpn.c b/src/vpn/gnunet-service-vpn.c index ab0b00d76..4759f3746 100644 --- a/src/vpn/gnunet-service-vpn.c +++ b/src/vpn/gnunet-service-vpn.c @@ -2217,12 +2217,10 @@ route_packet (struct DestinationEntry *destination, * and forward the packet. * * @param cls closure, NULL - * @param client NULL * @param message message we got from the client (VPN channel interface) */ static int message_token (void *cls, - void *client, const struct GNUNET_MessageHeader *message) { const struct GNUNET_TUN_Layer2PacketHeader *tun; -- cgit v1.2.3