X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fplugin_transport_http_server.c;h=6a9c1b0baba318e9002aede5072a3ebdc1264ee8;hb=4bd01d03e323377bbbd7609e29d8179bcb682deb;hp=9e4e54e676f9c0d9be5a436c8e115d5d95c4b07b;hpb=9351b1e9bdf2b067b6db06562c26ba658cff42b8;p=oweals%2Fgnunet.git diff --git a/src/transport/plugin_transport_http_server.c b/src/transport/plugin_transport_http_server.c index 9e4e54e67..6a9c1b0ba 100644 --- a/src/transport/plugin_transport_http_server.c +++ b/src/transport/plugin_transport_http_server.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - (C) 2002-2013 Christian Grothoff (and other contributing authors) + Copyright (C) 2002-2014, 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 @@ -14,23 +14,25 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** * @file transport/plugin_transport_http_server.c * @brief HTTP/S server transport plugin * @author Matthias Wachs + * @author David Barksdale + * @author Christian Grothoff */ - #include "platform.h" #include "gnunet_util_lib.h" #include "gnunet_statistics_service.h" #include "gnunet_transport_plugin.h" -#include "gnunet_nat_lib.h" +#include "gnunet_nat_service.h" #include "plugin_transport_http_common.h" #include +#include @@ -49,289 +51,297 @@ #define _SEND 1 -/* Enable output for debbuging URL's of incoming requests */ -#define DEBUG_URL_PARSE GNUNET_NO - +#define LOG(kind,...) GNUNET_log_from (kind, "transport-" PLUGIN_NAME,__VA_ARGS__) -/** - * Encapsulation of all of the state of the plugin. - */ -struct Plugin; /** - * Session handle for connections. + * Information we keep with MHD for an HTTP request. */ -struct Session +struct ServerRequest { /** - * Stored in a linked list. + * The session this server request belongs to + * Can be NULL, when session was disconnected and freed */ - struct Session *next; + struct GNUNET_ATS_Session *session; /** - * Stored in a linked list. + * The MHD connection */ - struct Session *prev; + struct MHD_Connection *mhd_conn; /** - * To whom are we talking to (set to our identity - * if we are still waiting for the welcome message) + * The MHD daemon */ - struct GNUNET_PeerIdentity target; + struct MHD_Daemon *mhd_daemon; /** - * Pointer to the global plugin struct. + * Options requested by peer */ - struct HTTP_Server_Plugin *plugin; + uint32_t options; +#define OPTION_LONG_POLL 1 /* GET request wants long-poll semantics */ /** - * next pointer for double linked list + * _RECV or _SEND */ - struct HTTP_Message *msg_head; + int direction; /** - * previous pointer for double linked list + * For PUT requests: Is this the first or last callback with size 0 + * For GET requests: Have we sent a message */ - struct HTTP_Message *msg_tail; + int connected; /** - * Message stream tokenizer for incoming data + * Currently suspended */ - struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk; + bool suspended; +}; + + +/** + * Wrapper to manage addresses + */ +struct HttpAddressWrapper +{ /** - * Client send handle + * Linked list next */ - struct ServerConnection *server_recv; + struct HttpAddressWrapper *next; /** - * Client send handle + * Linked list previous */ - struct ServerConnection *server_send; + struct HttpAddressWrapper *prev; /** - * Address + * An address we are using. */ - void *addr; + struct HttpAddress *address; /** - * Address length + * Length of the address. */ size_t addrlen; +}; + +/** + * Message to send using http + */ +struct HTTP_Message +{ /** - * Unique HTTP/S connection tag for this connection + * next pointer for double linked list */ - uint32_t tag; + struct HTTP_Message *next; /** - * ATS network type in NBO + * previous pointer for double linked list */ - uint32_t ats_address_network_type; + struct HTTP_Message *prev; /** - * Was session given to transport service? + * buffer containing data to send */ - int session_passed; + char *buf; /** - * Did we immediately end the session in disconnect_cb + * amount of data already sent */ - int session_ended; + size_t pos; /** - * Are incoming connection established at the moment + * buffer length */ - int connect_in_progress; + size_t size; /** - * Absolute time when to receive data again - * Used for receive throttling + * HTTP/S specific overhead */ - struct GNUNET_TIME_Absolute next_receive; + size_t overhead; /** - * Session timeout task + * Continuation function to call once the transmission buffer + * has again space available. NULL if there is no + * continuation to call. */ - GNUNET_SCHEDULER_TaskIdentifier timeout_task; -}; - - -struct ServerConnection -{ - /* _RECV or _SEND */ - int direction; - - /* Should this connection get disconnected? GNUNET_YES/NO */ - int disconnect; - - /* For PUT connections: Is this the first or last callback with size 0 */ - int connected; - - /* The session this server connection belongs to */ - struct Session *session; - - /* The MHD connection */ - struct MHD_Connection *mhd_conn; + GNUNET_TRANSPORT_TransmitContinuation transmit_cont; - /* The MHD daemon */ - struct MHD_Daemon *mhd_daemon; + /** + * Closure for transmit_cont. + */ + void *transmit_cont_cls; }; /** - * Encapsulation of all of the state of the plugin. + * Session handle for connections. */ -struct HTTP_Server_Plugin +struct GNUNET_ATS_Session { + /** - * Our environment. + * To whom are we talking to (set to our identity + * if we are still waiting for the welcome message) */ - struct GNUNET_TRANSPORT_PluginEnvironment *env; + struct GNUNET_PeerIdentity target; /** - * Linked list head of open sessions. + * Pointer to the global plugin struct. */ - - struct Session *head; + struct HTTP_Server_Plugin *plugin; /** - * Linked list tail of open sessions. + * next pointer for double linked list */ - struct Session *tail; + struct HTTP_Message *msg_head; /** - * Plugin name + * previous pointer for double linked list */ - char *name; + struct HTTP_Message *msg_tail; /** - * Protocol + * Message stream tokenizer for incoming data */ - char *protocol; + struct GNUNET_MessageStreamTokenizer *msg_tk; /** - * My options to be included in the address + * Client recv handle */ - uint32_t options; + struct ServerRequest *server_recv; /** - * External address + * Client send handle */ - char *external_hostname; + struct ServerRequest *server_send; /** - * Verify external address + * Address */ - int verify_external_hostname; + struct GNUNET_HELLO_Address *address; + /** + * Absolute time when to receive data again + * Used for receive throttling + */ + struct GNUNET_TIME_Absolute next_receive; /** - * Maximum number of sockets the plugin can use - * Each http inbound /outbound connections are two connections + * Absolute time when this connection will time out. */ - unsigned int max_connections; + struct GNUNET_TIME_Absolute timeout; /** - * Current number of sockets the plugin can use - * Each http inbound /outbound connections are two connections + * Session timeout task */ - unsigned int cur_connections; + struct GNUNET_SCHEDULER_Task * timeout_task; /** - * Did we immediately end the session in disconnect_cb + * Task to resume MHD handling when receiving is allowed again */ - int in_shutdown; + struct GNUNET_SCHEDULER_Task * recv_wakeup_task; /** - * Length of peer id + * Number of bytes waiting for transmission to this peer. */ - int peer_id_length; + unsigned long long bytes_in_queue; /** - * External hostname the plugin can be connected to, can be different to - * the host's FQDN, used e.g. for reverse proxying + * Number of messages waiting for transmission to this peer. */ - struct HttpAddress *ext_addr; + unsigned int msgs_in_queue; /** - * Notify transport only about external address + * Unique HTTP/S connection tag for this connection */ - unsigned int external_only; + uint32_t tag; /** - * External address length + * ATS network type. */ - size_t ext_addr_len; + enum GNUNET_ATS_Network_Type scope; /** - * use IPv6 + * #GNUNET_YES if this session is known to the service. */ - uint16_t use_ipv6; + int known_to_service; +}; + + +/** + * Encapsulation of all of the state of the plugin. + */ +struct HTTP_Server_Plugin +{ /** - * use IPv4 + * Our environment. */ - uint16_t use_ipv4; + struct GNUNET_TRANSPORT_PluginEnvironment *env; /** - * Port used + * Hash map of open sessions. */ - uint16_t port; + struct GNUNET_CONTAINER_MultiPeerMap *sessions; /** - * Task calling transport service about external address + * Function to call about session status changes. */ - GNUNET_SCHEDULER_TaskIdentifier notify_ext_task; + GNUNET_TRANSPORT_SessionInfoCallback sic; /** - * NAT handle & address management + * Closure for @e sic. */ - struct GNUNET_NAT_Handle *nat; + void *sic_cls; /** - * List of own addresses + * Plugin name */ + char *name; /** - * IPv4 addresses DLL head + * Protocol */ - struct HttpAddressWrapper *addr_head; + char *protocol; /** - * IPv4 addresses DLL tail + * External address */ - struct HttpAddressWrapper *addr_tail; + char *external_hostname; /** - * IPv4 server socket to bind to + * External hostname the plugin can be connected to, can be different to + * the host's FQDN, used e.g. for reverse proxying */ - struct sockaddr_in *server_addr_v4; + struct GNUNET_HELLO_Address *ext_addr; /** - * IPv6 server socket to bind to + * NAT handle & address management */ - struct sockaddr_in6 *server_addr_v6; + struct GNUNET_NAT_Handle *nat; /** - * MHD IPv4 task + * IPv4 addresses DLL head */ - GNUNET_SCHEDULER_TaskIdentifier server_v4_task; + struct HttpAddressWrapper *addr_head; /** - * MHD IPv6 task + * IPv4 addresses DLL tail */ - GNUNET_SCHEDULER_TaskIdentifier server_v6_task; + struct HttpAddressWrapper *addr_tail; /** - * The IPv4 server is scheduled to run asap + * IPv4 server socket to bind to */ - int server_v4_immediately; + struct sockaddr_in *server_addr_v4; /** - * The IPv6 server is scheduled to run asap + * IPv6 server socket to bind to */ - int server_v6_immediately; + struct sockaddr_in6 *server_addr_v6; /** * MHD IPv4 daemon @@ -366,138 +376,327 @@ struct HTTP_Server_Plugin char *cert; #endif -}; + /** + * MHD IPv4 task + */ + struct GNUNET_SCHEDULER_Task *server_v4_task; + /** + * MHD IPv6 task + */ + struct GNUNET_SCHEDULER_Task *server_v6_task; -/** - * Wrapper to manage addresses - */ -struct HttpAddressWrapper -{ /** - * Linked list next + * Task calling transport service about external address */ - struct HttpAddressWrapper *next; + struct GNUNET_SCHEDULER_Task *notify_ext_task; /** - * Linked list previous + * Notify transport only about external address */ - struct HttpAddressWrapper *prev; + unsigned int external_only; - struct HttpAddress *address; + /** + * The IPv4 server is scheduled to run asap + */ + int server_v4_immediately; - size_t addrlen; -}; + /** + * The IPv6 server is scheduled to run asap + */ + int server_v6_immediately; + /** + * Verify external address + */ + int verify_external_hostname; -/** - * Message to send using http - */ -struct HTTP_Message -{ /** - * next pointer for double linked list + * Maximum number of sockets the plugin can use + * Each http request /request connections are two connections */ - struct HTTP_Message *next; + unsigned int max_request; /** - * previous pointer for double linked list + * Current number of sockets the plugin can use + * Each http connection are two requests */ - struct HTTP_Message *prev; + unsigned int cur_request; /** - * buffer containing data to send + * Did we immediately end the session in disconnect_cb */ - char *buf; + int in_shutdown; /** - * amount of data already sent + * Length of peer id */ - size_t pos; + int peer_id_length; /** - * buffer length + * My options to be included in the address */ - size_t size; + uint32_t options; /** - * HTTP/S specific overhead + * use IPv6 */ - size_t overhead; + uint16_t use_ipv6; /** - * Continuation function to call once the transmission buffer - * has again space available. NULL if there is no - * continuation to call. + * use IPv4 */ - GNUNET_TRANSPORT_TransmitContinuation transmit_cont; + uint16_t use_ipv4; /** - * Closure for transmit_cont. + * Port used */ - void *transmit_cont_cls; + uint16_t port; + + /** + * Regex for parsing URLs. FIXME: this seems overkill. + */ + regex_t url_regex; + }; /** - * The http_server plugin handle + * If a session monitor is attached, notify it about the new + * session state. + * + * @param plugin our plugin + * @param session session that changed state + * @param state new state of the session */ -static struct HTTP_Server_Plugin * p; +static void +notify_session_monitor (struct HTTP_Server_Plugin *plugin, + struct GNUNET_ATS_Session *session, + enum GNUNET_TRANSPORT_SessionState state) +{ + struct GNUNET_TRANSPORT_SessionInfo info; + + if (NULL == plugin->sic) + return; + memset (&info, 0, sizeof (info)); + info.state = state; + info.is_inbound = GNUNET_YES; + info.num_msg_pending = session->msgs_in_queue; + info.num_bytes_pending = session->bytes_in_queue; + info.receive_delay = session->next_receive; + info.session_timeout = session->timeout; + info.address = session->address; + plugin->sic (plugin->sic_cls, + session, + &info); +} /** - * Start session timeout for session s - * @param s the session + * Wake up an MHD connection which was suspended + * + * @param cls the session */ static void -server_start_session_timeout (struct Session *s); +server_wake_up (void *cls) +{ + struct GNUNET_ATS_Session *s = cls; + + s->recv_wakeup_task = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Session %p: Waking up PUT handle\n", + s); + GNUNET_assert (s->server_recv->suspended); + MHD_resume_connection (s->server_recv->mhd_conn); + s->server_recv->suspended = false; +} /** - * Increment session timeout due to activity for session s - * @param s the session + * Reschedule the execution of both IPv4 and IPv6 server. + * + * @param plugin the plugin + * @param server which server to schedule v4 or v6? + * @param now #GNUNET_YES to schedule execution immediately, #GNUNET_NO to wait + * until timeout */ static void -server_reschedule_session_timeout (struct Session *s); +server_reschedule (struct HTTP_Server_Plugin *plugin, + struct MHD_Daemon *server, + int now); /** - * Cancel timeout for session s - * @param s the session + * Deletes the session. Must not be used afterwards. + * + * @param s the session to delete */ static void -server_stop_session_timeout (struct Session *s); +server_delete_session (struct GNUNET_ATS_Session *s) +{ + struct HTTP_Server_Plugin *plugin = s->plugin; + struct HTTP_Message *msg; + + if (NULL != s->timeout_task) + { + GNUNET_SCHEDULER_cancel (s->timeout_task); + s->timeout_task = NULL; + s->timeout = GNUNET_TIME_UNIT_ZERO_ABS; + } + if (NULL != s->recv_wakeup_task) + { + GNUNET_SCHEDULER_cancel (s->recv_wakeup_task); + s->recv_wakeup_task = NULL; + if (NULL != s->server_recv) + { + GNUNET_assert (s->server_recv->suspended); + s->server_recv->suspended = false; + MHD_resume_connection (s->server_recv->mhd_conn); + } + } + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multipeermap_remove (plugin->sessions, + &s->target, + s)); + while (NULL != (msg = s->msg_head)) + { + GNUNET_CONTAINER_DLL_remove (s->msg_head, + s->msg_tail, + msg); + if (NULL != msg->transmit_cont) + msg->transmit_cont (msg->transmit_cont_cls, + &s->target, + GNUNET_SYSERR, + msg->size, + msg->pos + msg->overhead); + GNUNET_assert (s->msgs_in_queue > 0); + s->msgs_in_queue--; + GNUNET_assert (s->bytes_in_queue >= msg->size); + s->bytes_in_queue -= msg->size; + GNUNET_free (msg); + } + + GNUNET_assert (0 == s->msgs_in_queue); + GNUNET_assert (0 == s->bytes_in_queue); + + if (NULL != s->server_send) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server: %p / %p Terminating inbound PUT session to peer `%s'\n", + s, s->server_send, + GNUNET_i2s (&s->target)); + s->server_send->session = NULL; + MHD_set_connection_option (s->server_send->mhd_conn, + MHD_CONNECTION_OPTION_TIMEOUT, + 1 /* 0 = no timeout, so this is MIN */); + if (s->server_send->suspended) + { + s->server_send->suspended = false; + MHD_resume_connection (s->server_send->mhd_conn); + } + server_reschedule (plugin, + s->server_send->mhd_daemon, + GNUNET_YES); + } + + if (NULL != s->server_recv) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Server: %p / %p Terminating inbound GET session to peer `%s'\n", + s, s->server_recv, GNUNET_i2s (&s->target)); + s->server_recv->session = NULL; + MHD_set_connection_option (s->server_recv->mhd_conn, + MHD_CONNECTION_OPTION_TIMEOUT, + 1 /* 0 = no timeout, so this is MIN */); + server_reschedule (plugin, + s->server_recv->mhd_daemon, + GNUNET_YES); + } + notify_session_monitor (plugin, + s, + GNUNET_TRANSPORT_SS_DONE); + if (GNUNET_YES == s->known_to_service) + { + plugin->env->session_end (plugin->env->cls, + s->address, + s); + s->known_to_service = GNUNET_NO; + } + if (NULL != s->msg_tk) + { + GNUNET_MST_destroy (s->msg_tk); + s->msg_tk = NULL; + } + GNUNET_HELLO_address_free (s->address); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Session %p destroyed\n", + s); + GNUNET_free (s); +} /** - * Disconnect a session s + * Disconnect session @a s by telling MHD to close the + * connections (reducing timeout, etc.). + * + * @param cls closure with the `struct HTTP_Server_Plugin` * @param s the session + * @return #GNUNET_OK on success */ static int -server_disconnect (struct Session *s); +http_server_plugin_disconnect_session (void *cls, + struct GNUNET_ATS_Session *s) +{ + server_delete_session (s); + return GNUNET_OK; +} /** - * Does session s exist? + * Session was idle, so disconnect it * - * @param plugin the plugin handle - * @param s the session - * @return GNUNET_YES on success, GNUNET_NO on error + * @param cls the session */ -static int -server_exist_session (struct HTTP_Server_Plugin *plugin, struct Session *s); +static void +server_session_timeout (void *cls) +{ + struct GNUNET_ATS_Session *s = cls; + struct GNUNET_TIME_Relative left; + + s->timeout_task = NULL; + left = GNUNET_TIME_absolute_get_remaining (s->timeout); + if (0 != left.rel_value_us) + { + /* not actually our turn yet, but let's at least update + the monitor, it may think we're about to die ... */ + notify_session_monitor (s->plugin, + s, + GNUNET_TRANSPORT_SS_UP); + s->timeout_task = GNUNET_SCHEDULER_add_delayed (left, + &server_session_timeout, + s); + return; + } + GNUNET_log (TIMEOUT_LOG, + "Session %p was idle for %s, disconnecting\n", + s, + GNUNET_STRINGS_relative_time_to_string (HTTP_SERVER_SESSION_TIMEOUT, + GNUNET_YES)); + server_delete_session (s); +} /** - * Reschedule the execution of both IPv4 and IPv6 server - * @param plugin the plugin - * @param server which server to schedule v4 or v6? - * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait - * until timeout + * Increment session timeout due to activity session @a s + * + * @param s the session */ static void -server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, - int now); +server_reschedule_session_timeout (struct GNUNET_ATS_Session *s) +{ + GNUNET_assert (NULL != s->timeout_task); + s->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); +} /** @@ -511,7 +710,7 @@ server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, * @param cls closure * @param session which session must be used * @param msgbuf the message to transmit - * @param msgbuf_size number of bytes in 'msgbuf' + * @param msgbuf_size number of bytes in @a msgbuf * @param priority how important is the message (most plugins will * ignore message priority and just FIFO) * @param to how long to wait at most for the transmission (does not @@ -522,55 +721,32 @@ server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, * been transmitted (or if the transport is ready * for the next transmission call; or if the * peer disconnected...); can be NULL - * @param cont_cls closure for cont + * @param cont_cls closure for @a cont * @return number of bytes used (on the physical network, with overheads); * -1 on hard errors (i.e. address invalid); 0 is a legal value * and does NOT mean that the message was not transmitted (DV) */ static ssize_t http_server_plugin_send (void *cls, - struct Session *session, - const char *msgbuf, size_t msgbuf_size, - unsigned int priority, - struct GNUNET_TIME_Relative to, - GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls) + struct GNUNET_ATS_Session *session, + const char *msgbuf, + size_t msgbuf_size, + unsigned int priority, + struct GNUNET_TIME_Relative to, + GNUNET_TRANSPORT_TransmitContinuation cont, + void *cont_cls) { struct HTTP_Server_Plugin *plugin = cls; struct HTTP_Message *msg; - int bytes_sent = 0; + ssize_t bytes_sent = 0; char *stat_txt; - GNUNET_assert (plugin != NULL); - GNUNET_assert (session != NULL); - - if (GNUNET_NO == server_exist_session (plugin, session)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (NULL == session->server_send) - { - if (GNUNET_NO == session->connect_in_progress) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, session->plugin->name, - "Session %p/connection %p: Sending message with %u bytes to peer `%s' with FAILED\n", - session, session->server_send, - msgbuf_size, GNUNET_i2s (&session->target)); - GNUNET_break (0); - return GNUNET_SYSERR; - } - } - else - { - if (GNUNET_YES == session->server_send->disconnect) - return GNUNET_SYSERR; - } - - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, session->plugin->name, - "Session %p/connection %p: Sending message with %u to peer `%s' with \n", - session, session->server_send, - msgbuf_size, GNUNET_i2s (&session->target)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Session %p/request %p: Sending message with %u to peer `%s'\n", + session, + session->server_send, + msgbuf_size, + GNUNET_i2s (&session->target)); /* create new message and schedule */ bytes_sent = sizeof (struct HTTP_Message) + msgbuf_size; @@ -581,26 +757,87 @@ http_server_plugin_send (void *cls, msg->buf = (char *) &msg[1]; msg->transmit_cont = cont; msg->transmit_cont_cls = cont_cls; - memcpy (msg->buf, msgbuf, msgbuf_size); - - GNUNET_CONTAINER_DLL_insert_tail (session->msg_head, session->msg_tail, msg); - - GNUNET_asprintf (&stat_txt, "# bytes currently in %s_server buffers", plugin->protocol); + GNUNET_memcpy (msg->buf, + msgbuf, + msgbuf_size); + GNUNET_CONTAINER_DLL_insert_tail (session->msg_head, + session->msg_tail, + msg); + session->msgs_in_queue++; + session->bytes_in_queue += msg->size; + notify_session_monitor (plugin, + session, + GNUNET_TRANSPORT_SS_UP); + GNUNET_asprintf (&stat_txt, + "# bytes currently in %s_server buffers", + plugin->protocol); GNUNET_STATISTICS_update (plugin->env->stats, stat_txt, msgbuf_size, GNUNET_NO); GNUNET_free (stat_txt); if (NULL != session->server_send) { - server_reschedule (session->plugin, - session->server_send->mhd_daemon, - GNUNET_YES); - server_reschedule_session_timeout (session); + if (session->server_send->suspended) + { + MHD_resume_connection (session->server_send->mhd_conn); + session->server_send->suspended = false; + } + server_reschedule (session->plugin, + session->server_send->mhd_daemon, + GNUNET_YES); } return bytes_sent; } +/** + * Terminate session during shutdown. + * + * @param cls the `struct HTTP_Server_Plugin *` + * @param peer for which this is a session + * @param value the `struct GNUNET_ATS_Session` to clean up + * @return #GNUNET_OK (continue to iterate) + */ +static int +destroy_session_shutdown_cb (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *value) +{ + struct GNUNET_ATS_Session *s = value; + struct ServerRequest *sc_send; + struct ServerRequest *sc_recv; + + sc_send = s->server_send; + sc_recv = s->server_recv; + server_delete_session (s); + if (NULL != sc_send) + sc_send->session = NULL; + if (NULL != sc_recv) + sc_recv->session = NULL; + + return GNUNET_OK; +} + + +/** + * Terminate session. + * + * @param cls the `struct HTTP_Server_Plugin *` + * @param peer for which this is a session + * @param value the `struct GNUNET_ATS_Session` to clean up + * @return #GNUNET_OK (continue to iterate) + */ +static int +destroy_session_cb (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *value) +{ + struct GNUNET_ATS_Session *s = value; + + server_delete_session (s); + return GNUNET_OK; +} + /** * Function that can be used to force the plugin to disconnect @@ -611,29 +848,18 @@ http_server_plugin_send (void *cls, * @param target peer from which to disconnect */ static void -http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target) +http_server_plugin_disconnect_peer (void *cls, + const struct GNUNET_PeerIdentity *target) { struct HTTP_Server_Plugin *plugin = cls; - struct Session *next = NULL; - struct Session *pos = NULL; - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Transport tells me to disconnect `%s'\n", - GNUNET_i2s (target)); - - next = plugin->head; - while (NULL != (pos = next)) - { - next = pos->next; - if (0 == memcmp (target, &pos->target, sizeof (struct GNUNET_PeerIdentity))) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Disconnecting session %p to `%s'\n", - pos, GNUNET_i2s (target)); - server_disconnect (pos); - } - } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Transport tells me to disconnect `%s'\n", + GNUNET_i2s (target)); + GNUNET_CONTAINER_multipeermap_get_multiple (plugin->sessions, + target, + &destroy_session_cb, + plugin); } @@ -645,55 +871,56 @@ http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *targ * * @param cls closure * @param addr pointer to the address - * @param addrlen length of addr - * @return GNUNET_OK if this is a plausible address for this peer + * @param addrlen length of @a addr + * @return #GNUNET_OK if this is a plausible address for this peer * and transport */ static int -http_server_plugin_address_suggested (void *cls, const void *addr, - size_t addrlen) +http_server_plugin_address_suggested (void *cls, + const void *addr, + size_t addrlen) { - struct HttpAddressWrapper *next; - struct HttpAddressWrapper *pos; - const struct HttpAddress *haddr = addr; - - if ((NULL != p->ext_addr) && - GNUNET_YES == (http_common_cmp_addresses (addr, addrlen, - p->ext_addr, p->ext_addr_len))) - { - /* Checking HTTP_OPTIONS_VERIFY_CERTIFICATE option for external hostname */ - if ((ntohl(haddr->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE) != - (p->options & HTTP_OPTIONS_VERIFY_CERTIFICATE)) - return GNUNET_NO; /* VERIFY option not set as required! */ - return GNUNET_OK; - } - - next = p->addr_head; - while (NULL != (pos = next)) - { - next = pos->next; - if (GNUNET_YES == (http_common_cmp_addresses(addr, - addrlen, - pos->address, - pos->addrlen))) - return GNUNET_OK; - - } - return GNUNET_NO; + struct HTTP_Server_Plugin *plugin = cls; + struct HttpAddressWrapper *next; + struct HttpAddressWrapper *pos; + const struct HttpAddress *haddr = addr; + + if ((NULL != plugin->ext_addr) && + GNUNET_YES == (http_common_cmp_addresses (addr, addrlen, + plugin->ext_addr->address, + plugin->ext_addr->address_length))) + { + /* Checking HTTP_OPTIONS_VERIFY_CERTIFICATE option for external hostname */ + if ((ntohl (haddr->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE) != + (plugin->options & HTTP_OPTIONS_VERIFY_CERTIFICATE)) + return GNUNET_NO; /* VERIFY option not set as required! */ + return GNUNET_OK; + } + next = plugin->addr_head; + while (NULL != (pos = next)) + { + next = pos->next; + if (GNUNET_YES == (http_common_cmp_addresses(addr, + addrlen, + pos->address, + pos->addrlen))) + return GNUNET_OK; + } + return GNUNET_NO; } /** * Creates a new outbound session the transport - * service will use to send data to the peer + * service will use to send data to the peer. * - * Since HTTP/S server cannot create sessions, always return NULL + * Since HTTP/S server cannot create sessions, always returns NULL. * * @param cls the plugin * @param address the address * @return always NULL */ -static struct Session * +static struct GNUNET_ATS_Session * http_server_plugin_get_session (void *cls, const struct GNUNET_HELLO_Address *address) { @@ -702,92 +929,157 @@ http_server_plugin_get_session (void *cls, /** - * Deleting the session - * Must not be used afterwards + * Call MHD IPv4 to process pending requests and then go back + * and schedule the next run. * - * @param s the session to delete + * @param cls plugin as closure */ static void -server_delete_session (struct Session *s) +server_v4_run (void *cls) { - struct HTTP_Server_Plugin *plugin = s->plugin; - server_stop_session_timeout(s); - - GNUNET_CONTAINER_DLL_remove (plugin->head, plugin->tail, s); - struct HTTP_Message *msg = s->msg_head; - struct HTTP_Message *tmp = NULL; - - while (msg != NULL) - { - tmp = msg->next; - - GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); - if (msg->transmit_cont != NULL) - { - msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR, - msg->size, msg->pos + msg->overhead); - } - GNUNET_free (msg); - msg = tmp; - } + struct HTTP_Server_Plugin *plugin = cls; - if (s->msg_tk != NULL) - { - GNUNET_SERVER_mst_destroy (s->msg_tk); - s->msg_tk = NULL; - } - GNUNET_free (s->addr); - GNUNET_free_non_null (s->server_recv); - GNUNET_free_non_null (s->server_send); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Session %p destroyed\n", s); - GNUNET_free (s); + plugin->server_v4_task = NULL; + plugin->server_v4_immediately = GNUNET_NO; + GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4)); + server_reschedule (plugin, plugin->server_v4, GNUNET_NO); } /** -* Cancel timeout for session s -* -* @param s the session -*/ + * Call MHD IPv6 to process pending requests and then go back + * and schedule the next run. + * + * @param cls plugin as closure + */ static void -server_stop_session_timeout (struct Session *s) +server_v6_run (void *cls) { - GNUNET_assert (NULL != s); - - if (GNUNET_SCHEDULER_NO_TASK != s->timeout_task) - { - GNUNET_SCHEDULER_cancel (s->timeout_task); - s->timeout_task = GNUNET_SCHEDULER_NO_TASK; - GNUNET_log (TIMEOUT_LOG, "Timeout stopped for session %p\n", s); - } + struct HTTP_Server_Plugin *plugin = cls; + + plugin->server_v6_task = NULL; + plugin->server_v6_immediately = GNUNET_NO; + GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6)); + server_reschedule (plugin, plugin->server_v6, GNUNET_NO); } /** * Function that queries MHD's select sets and * starts the task waiting for them. + * * @param plugin plugin * @param daemon_handle the MHD daemon handle - * @param now schedule immediately + * @param now schedule now * @return gnunet task identifier */ -static GNUNET_SCHEDULER_TaskIdentifier +static struct GNUNET_SCHEDULER_Task * server_schedule (struct HTTP_Server_Plugin *plugin, - struct MHD_Daemon *daemon_handle, - int now); + struct MHD_Daemon *daemon_handle, + int now) +{ + struct GNUNET_SCHEDULER_Task * ret; + fd_set rs; + fd_set ws; + fd_set es; + struct GNUNET_NETWORK_FDSet *wrs; + struct GNUNET_NETWORK_FDSet *wws; + int max; + MHD_UNSIGNED_LONG_LONG timeout; + static unsigned long long last_timeout = 0; + int haveto; + struct GNUNET_TIME_Relative tv; + + if (GNUNET_YES == plugin->in_shutdown) + return NULL; + + ret = NULL; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + wrs = GNUNET_NETWORK_fdset_create (); + wws = GNUNET_NETWORK_fdset_create (); + max = -1; + GNUNET_assert (MHD_YES == + MHD_get_fdset (daemon_handle, + &rs, + &ws, + &es, + &max)); + haveto = MHD_get_timeout (daemon_handle, &timeout); + if (haveto == MHD_YES) + { + if (timeout != last_timeout) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "SELECT Timeout changed from %llu to %llu (ms)\n", + last_timeout, timeout); + last_timeout = timeout; + } + if (timeout <= GNUNET_TIME_UNIT_SECONDS.rel_value_us / 1000LL) + tv.rel_value_us = (uint64_t) timeout * 1000LL; + else + tv = GNUNET_TIME_UNIT_SECONDS; + } + else + tv = GNUNET_TIME_UNIT_SECONDS; + /* Force immediate run, since we have outbound data to send */ + if (now == GNUNET_YES) + tv = GNUNET_TIME_UNIT_MILLISECONDS; + GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1); + GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1); + + if (daemon_handle == plugin->server_v4) + { + if (plugin->server_v4_task != NULL) + { + GNUNET_SCHEDULER_cancel (plugin->server_v4_task); + plugin->server_v4_task = NULL; + } +#if 0 + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling IPv4 server task in %llu ms\n", + tv); +#endif + ret = + GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + tv, wrs, wws, + &server_v4_run, plugin); + } + if (daemon_handle == plugin->server_v6) + { + if (plugin->server_v6_task != NULL) + { + GNUNET_SCHEDULER_cancel (plugin->server_v6_task); + plugin->server_v6_task = NULL; + } +#if 0 + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling IPv6 server task in %llu ms\n", tv); +#endif + ret = + GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + tv, wrs, wws, + &server_v6_run, plugin); + } + GNUNET_NETWORK_fdset_destroy (wrs); + GNUNET_NETWORK_fdset_destroy (wws); + return ret; +} /** * Reschedule the execution of both IPv4 and IPv6 server + * * @param plugin the plugin * @param server which server to schedule v4 or v6? - * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait + * @param now #GNUNET_YES to schedule execution immediately, #GNUNET_NO to wait * until timeout */ static void -server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, - int now) +server_reschedule (struct HTTP_Server_Plugin *plugin, + struct MHD_Daemon *server, + int now) { if ((server == plugin->server_v4) && (plugin->server_v4 != NULL)) { @@ -797,10 +1089,10 @@ server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, if (GNUNET_YES == now) plugin->server_v4_immediately = GNUNET_YES; - if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) + if (plugin->server_v4_task != NULL) { GNUNET_SCHEDULER_cancel (plugin->server_v4_task); - plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; + plugin->server_v4_task = NULL; } plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now); } @@ -813,10 +1105,10 @@ server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, if (GNUNET_YES == now) plugin->server_v6_immediately = GNUNET_YES; - if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) + if (plugin->server_v6_task != NULL) { GNUNET_SCHEDULER_cancel (plugin->server_v6_task); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; + plugin->server_v6_task = NULL; } plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now); } @@ -824,57 +1116,38 @@ server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, /** - * Disconnect session s + * Function that is called to get the keepalive factor. + * GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT is divided by this number to + * calculate the interval between keepalive packets. * - * @param s the session - * @return GNUNET_OK on success + * @param cls closure with the `struct HTTP_Server_Plugin` + * @return keepalive factor */ -static int -server_disconnect (struct Session *s) +static unsigned int +http_server_query_keepalive_factor (void *cls) { - struct ServerConnection * send = NULL; - struct ServerConnection * recv = NULL; - - if (GNUNET_NO == server_exist_session (p, s)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - send = (struct ServerConnection *) s->server_send; - if (s->server_send != NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Server: %p / %p Terminating inbound PUT session to peer `%s'\n", - s, s->server_send, GNUNET_i2s (&s->target)); - - send->disconnect = GNUNET_YES; -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - server_reschedule (s->plugin, send->mhd_daemon, GNUNET_YES); - } + return 3; +} - recv = (struct ServerConnection *) s->server_recv; - if (recv != NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Server: %p / %p Terminating inbound GET session to peer `%s'\n", - s, s->server_recv, GNUNET_i2s (&s->target)); - recv->disconnect = GNUNET_YES; -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - server_reschedule (s->plugin, recv->mhd_daemon, GNUNET_YES); - } - return GNUNET_OK; +/** + * Function that will be called whenever the transport service wants to + * notify the plugin that a session is still active and in use and + * therefore the session timeout for this session has to be updated + * + * @param cls closure + * @param peer which peer was the session for + * @param session which session is being updated + */ +static void +http_server_plugin_update_session_timeout (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_ATS_Session *session) +{ + server_reschedule_session_timeout (session); } - /** * Tell MHD that the connection should timeout after @a to seconds. * @@ -884,16 +1157,15 @@ server_disconnect (struct Session *s) */ static void server_mhd_connection_timeout (struct HTTP_Server_Plugin *plugin, - struct Session *s, + struct GNUNET_ATS_Session *s, unsigned int to) { -#if MHD_VERSION >= 0x00090E00 - /* Setting timeouts for other connections */ + /* Setting timeouts for other connections */ if (NULL != s->server_recv) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Setting timeout for %p to %u sec.\n", - s->server_recv, to); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Setting timeout for %p to %u sec.\n", + s->server_recv, to); MHD_set_connection_option (s->server_recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, to); @@ -901,17 +1173,17 @@ server_mhd_connection_timeout (struct HTTP_Server_Plugin *plugin, } if (NULL != s->server_send) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Setting timeout for %p to %u sec.\n", - s->server_send, to); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Setting timeout for %p to %u sec.\n", + s->server_send, to); MHD_set_connection_option (s->server_send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, to); server_reschedule (plugin, s->server_send->mhd_daemon, GNUNET_NO); } -#endif } + /** * Parse incoming URL for tag and target * @@ -919,116 +1191,174 @@ server_mhd_connection_timeout (struct HTTP_Server_Plugin *plugin, * @param url incoming url * @param target where to store the target * @param tag where to store the tag - * @return GNUNET_OK on success, GNUNET_SYSERR on error + * @param options where to store the options + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ - static int server_parse_url (struct HTTP_Server_Plugin *plugin, const char *url, struct GNUNET_PeerIdentity *target, - uint32_t *tag) + uint32_t *tag, + uint32_t *options) { - char * tag_start = NULL; - char * tag_end = NULL; - char * target_start = NULL; - char * separator = NULL; - unsigned int hash_length; - unsigned long int ctag; - - /* URL parsing - * URL is valid if it is in the form [prefix with (multiple) '/'][peerid[103];tag]*/ + regmatch_t matches[4]; + const char *tag_start; + const char *target_start; + char *tag_end; + char *options_end; + size_t hash_length; + unsigned long int rc; + + /* URL parsing */ +#define URL_REGEX \ + ("^.*/([0-9A-Z]+);([0-9]+)(,[0-9]+)?$") if (NULL == url) { - GNUNET_break (0); - return GNUNET_SYSERR; + GNUNET_break (0); + return GNUNET_SYSERR; } - /* convert tag */ - - /* find separator */ - separator = strrchr (url, ';'); - if (NULL == separator) + if (regexec(&plugin->url_regex, url, 4, matches, 0)) { - if (DEBUG_URL_PARSE) GNUNET_break (0); - return GNUNET_SYSERR; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "URL `%s' did not match regex\n", url); + return GNUNET_SYSERR; } - tag_start = separator + 1; - if (strlen (tag_start) == 0) + target_start = &url[matches[1].rm_so]; + tag_start = &url[matches[2].rm_so]; + + /* convert tag */ + rc = strtoul (tag_start, &tag_end, 10); + if (&url[matches[2].rm_eo] != tag_end) { - /* No tag after separator */ - if (DEBUG_URL_PARSE) GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "URL tag did not line up with submatch\n"); return GNUNET_SYSERR; } - ctag = strtoul (tag_start, &tag_end, 10); - if (ctag == 0) + if (rc == 0) { - /* tag == 0 , invalid */ - if (DEBUG_URL_PARSE) GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "URL tag is zero\n"); return GNUNET_SYSERR; } - if ((ctag == ULONG_MAX) && (ERANGE == errno)) + if ((rc == ULONG_MAX) && (ERANGE == errno)) { - /* out of range: > ULONG_MAX */ - if (DEBUG_URL_PARSE) GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "URL tag > ULONG_MAX\n"); return GNUNET_SYSERR; } - if (ctag > UINT32_MAX) + if (rc > UINT32_MAX) { - /* out of range: > UINT32_MAX */ - if (DEBUG_URL_PARSE) GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "URL tag > UINT32_MAX\n"); return GNUNET_SYSERR; } - (*tag) = (uint32_t) ctag; - if (NULL == tag_end) - { - /* no char after tag */ - if (DEBUG_URL_PARSE) GNUNET_break (0); - return GNUNET_SYSERR; - } - if (url[strlen(url)] != tag_end[0]) - { - /* there are more not converted chars after tag */ - if (DEBUG_URL_PARSE) GNUNET_break (0); - return GNUNET_SYSERR; - } - if (DEBUG_URL_PARSE) - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Found tag `%u' in url\n", (*tag)); + (*tag) = (uint32_t)rc; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Found tag `%u' in url\n", + *tag); /* convert peer id */ - target_start = strrchr (url, '/'); - if (NULL == target_start) - { - /* no leading '/' */ - target_start = (char *) url; - } - target_start++; - hash_length = separator - target_start; + hash_length = matches[1].rm_eo - matches[1].rm_so; if (hash_length != plugin->peer_id_length) { - /* no char after tag */ - if (DEBUG_URL_PARSE) GNUNET_break (0); - return GNUNET_SYSERR; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "URL target is %u bytes, expecting %u\n", + hash_length, plugin->peer_id_length); + return GNUNET_SYSERR; } if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (target_start, hash_length, &target->public_key)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "URL target conversion failed\n"); + return GNUNET_SYSERR; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Found target `%s' in URL\n", + GNUNET_i2s_full (target)); + + /* convert options */ + if (-1 == matches[3].rm_so) + { + *options = 0; + } + else + { + rc = strtoul (&url[matches[3].rm_so + 1], &options_end, 10); + if (&url[matches[3].rm_eo] != options_end) { - /* hash conversion failed */ - if (DEBUG_URL_PARSE) GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "URL options did not line up with submatch\n"); return GNUNET_SYSERR; + } + if ((rc == ULONG_MAX) && (ERANGE == errno)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "URL options > ULONG_MAX\n"); + return GNUNET_SYSERR; + } + if (rc > UINT32_MAX) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "URL options > UINT32_MAX\n"); + return GNUNET_SYSERR; + } + (*options) = (uint32_t) rc; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Found options `%u' in url\n", + *options); } - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, - plugin->name, - "Found target `%s' in URL\n", - GNUNET_i2s_full (target)); return GNUNET_OK; } +/** + * Closure for #session_tag_it(). + */ +struct GNUNET_ATS_SessionTagContext +{ + /** + * Set to session matching the tag. + */ + struct GNUNET_ATS_Session *res; + + /** + * Tag we are looking for. + */ + uint32_t tag; +}; + + +/** + * Find a session with a matching tag. + * + * @param cls the `struct GNUNET_ATS_SessionTagContext *` + * @param key peer identity (unused) + * @param value the `struct GNUNET_ATS_Session *` + * @return #GNUNET_NO if we found the session, #GNUNET_OK if not + */ +static int +session_tag_it (void *cls, + const struct GNUNET_PeerIdentity *key, + void *value) +{ + struct GNUNET_ATS_SessionTagContext *stc = cls; + struct GNUNET_ATS_Session *s = value; + + if (s->tag == stc->tag) + { + stc->res = s; + return GNUNET_NO; + } + return GNUNET_YES; +} + + /** * Lookup a mhd connection and create one if none is found * @@ -1038,35 +1368,41 @@ server_parse_url (struct HTTP_Server_Plugin *plugin, * @param method PUT or GET * @return the server connecetion */ -static struct ServerConnection * +static struct ServerRequest * server_lookup_connection (struct HTTP_Server_Plugin *plugin, - struct MHD_Connection *mhd_connection, const char *url, - const char *method) + struct MHD_Connection *mhd_connection, + const char *url, + const char *method) { - struct Session *s = NULL; - struct ServerConnection *sc = NULL; + struct GNUNET_ATS_Session *s = NULL; + struct ServerRequest *sc = NULL; const union MHD_ConnectionInfo *conn_info; - struct GNUNET_ATS_Information ats; struct HttpAddress *addr; - size_t addr_len; struct GNUNET_PeerIdentity target; - uint32_t tag = 0; + size_t addr_len; + struct GNUNET_ATS_SessionTagContext stc; + uint32_t options; int direction = GNUNET_SYSERR; unsigned int to; + enum GNUNET_ATS_Network_Type scope; conn_info = MHD_get_connection_info (mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); if ((conn_info->client_addr->sa_family != AF_INET) && (conn_info->client_addr->sa_family != AF_INET6)) return NULL; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "New %s connection from %s\n", method, url); - - if (GNUNET_SYSERR == server_parse_url (plugin, url, &target, &tag)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Invalid url %s\n", url); - return NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "New %s request from %s\n", + method, + url); + stc.tag = 0; + options = 0; /* make gcc happy */ + if (GNUNET_SYSERR == + server_parse_url (plugin, url, &target, &stc.tag, &options)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Invalid url %s\n", url); + return NULL; } if (0 == strcmp (MHD_HTTP_METHOD_PUT, method)) direction = _RECEIVE; @@ -1074,90 +1410,105 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin, direction = _SEND; else { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Invalid method %s connection from %s\n", method, url); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Invalid method %s for request from %s\n", + method, url); return NULL; } - plugin->cur_connections++; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "New %s connection from %s with tag %u (%u of %u)\n", - method, - GNUNET_i2s (&target), tag, - plugin->cur_connections, plugin->max_connections); - /* find duplicate session */ - s = plugin->head; - while (s != NULL) - { - if ((0 == memcmp (&s->target, &target, sizeof (struct GNUNET_PeerIdentity))) && - (s->tag == tag)) - break; - s = s->next; - } - if (s != NULL) - { - if ((_RECEIVE == direction) && (NULL != s->server_recv)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Duplicate PUT connection from `%s' tag %u, dismissing new connection\n", - GNUNET_i2s (&target), - tag); - return NULL; - - } - if ((_SEND == direction) && (NULL != s->server_send)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Duplicate GET connection from `%s' tag %u, dismissing new connection\n", - GNUNET_i2s (&target), - tag); - return NULL; - } - } - else + plugin->cur_request++; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "New %s request from %s with tag %u (%u of %u)\n", + method, + GNUNET_i2s (&target), + stc.tag, + plugin->cur_request, plugin->max_request); + /* find existing session */ + stc.res = NULL; + GNUNET_CONTAINER_multipeermap_get_multiple (plugin->sessions, + &target, + &session_tag_it, + &stc); + if (NULL == (s = stc.res)) { /* create new session */ + addr = NULL; switch (conn_info->client_addr->sa_family) { case (AF_INET): - addr = http_common_address_from_socket (plugin->protocol, conn_info->client_addr, sizeof (struct sockaddr_in)); + addr = http_common_address_from_socket (plugin->protocol, + conn_info->client_addr, + sizeof (struct sockaddr_in)); addr_len = http_common_address_get_size (addr); - ats = plugin->env->get_address_type (plugin->env->cls, conn_info->client_addr, sizeof (struct sockaddr_in)); + scope = plugin->env->get_address_type (plugin->env->cls, + conn_info->client_addr, + sizeof (struct sockaddr_in)); break; case (AF_INET6): - addr = http_common_address_from_socket (plugin->protocol, conn_info->client_addr, sizeof (struct sockaddr_in6)); + addr = http_common_address_from_socket (plugin->protocol, + conn_info->client_addr, + sizeof (struct sockaddr_in6)); addr_len = http_common_address_get_size (addr); - ats = plugin->env->get_address_type (plugin->env->cls, conn_info->client_addr, sizeof (struct sockaddr_in6)); + scope = plugin->env->get_address_type (plugin->env->cls, + conn_info->client_addr, + sizeof (struct sockaddr_in6)); break; default: - /* external host name */ - ats.type = htonl (GNUNET_ATS_NETWORK_TYPE); - ats.type = htonl (GNUNET_ATS_NET_WAN); + /* external host name */ return NULL; } - - s = GNUNET_malloc (sizeof (struct Session)); - memcpy (&s->target, &target, sizeof (struct GNUNET_PeerIdentity)); + s = GNUNET_new (struct GNUNET_ATS_Session); + s->target = target; s->plugin = plugin; - s->addr = addr; - s->addrlen = addr_len; - s->ats_address_network_type = ats.value; + s->scope = scope; + s->address = GNUNET_HELLO_address_allocate (&s->target, + PLUGIN_NAME, + addr, + addr_len, + GNUNET_HELLO_ADDRESS_INFO_INBOUND); s->next_receive = GNUNET_TIME_UNIT_ZERO_ABS; - s->tag = tag; - s->server_recv = NULL; - s->server_send = NULL; - s->session_passed = GNUNET_NO; - s->session_ended = GNUNET_NO; - s->connect_in_progress = GNUNET_YES; - server_start_session_timeout(s); - GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Creating new session %p for peer `%s' connecting from `%s'\n", - s, GNUNET_i2s (&target), - http_common_plugin_address_to_string (NULL, p->protocol, addr, addr_len)); - } - sc = GNUNET_malloc (sizeof (struct ServerConnection)); + s->tag = stc.tag; + s->timeout = GNUNET_TIME_relative_to_absolute (HTTP_SERVER_SESSION_TIMEOUT); + s->timeout_task = GNUNET_SCHEDULER_add_delayed (HTTP_SERVER_SESSION_TIMEOUT, + &server_session_timeout, + s); + (void) GNUNET_CONTAINER_multipeermap_put (plugin->sessions, + &s->target, + s, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); + notify_session_monitor (plugin, + s, + GNUNET_TRANSPORT_SS_INIT); + notify_session_monitor (plugin, + s, + GNUNET_TRANSPORT_SS_HANDSHAKE); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Creating new session %p for peer `%s' connecting from `%s'\n", + s, GNUNET_i2s (&target), + http_common_plugin_address_to_string (plugin->protocol, + addr, + addr_len)); + GNUNET_free_non_null (addr); + } + + if ( (_RECEIVE == direction) && + (NULL != s->server_recv) ) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Duplicate PUT request from `%s' tag %u, dismissing new request\n", + GNUNET_i2s (&target), + stc.tag); + return NULL; + } + if ((_SEND == direction) && (NULL != s->server_send)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Duplicate GET request from `%s' tag %u, dismissing new request\n", + GNUNET_i2s (&target), + stc.tag); + return NULL; + } + sc = GNUNET_new (struct ServerRequest); if (conn_info->client_addr->sa_family == AF_INET) sc->mhd_daemon = plugin->server_v4; if (conn_info->client_addr->sa_family == AF_INET6) @@ -1166,96 +1517,65 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin, sc->direction = direction; sc->connected = GNUNET_NO; sc->session = s; + sc->options = options; if (direction == _SEND) + { s->server_send = sc; + } if (direction == _RECEIVE) - s->server_recv = sc; - - if ((NULL != s->server_send) && (NULL != s->server_recv)) { - s->connect_in_progress = GNUNET_NO; /* PUT and GET are connected */ - plugin->env->session_start (NULL, &s->target, PLUGIN_NAME, NULL, 0 ,s, NULL, 0); + s->server_recv = sc; } -#if MHD_VERSION >= 0x00090E00 - if ((NULL == s->server_recv) || (NULL == s->server_send)) - { - to = (HTTP_SERVER_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL / 1000LL); - MHD_set_connection_option (mhd_connection, - MHD_CONNECTION_OPTION_TIMEOUT, to); - server_reschedule (plugin, sc->mhd_daemon, GNUNET_NO); - } - else + if ((GNUNET_NO == s->known_to_service) && + (NULL != s->server_send) && + (NULL != s->server_recv) ) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Session %p for peer `%s' fully connected\n", - s, GNUNET_i2s (&target)); - to = (SERVER_SESSION_TIMEOUT.rel_value_us / 1000LL / 1000LL); - server_mhd_connection_timeout (plugin, s, to); + s->known_to_service = GNUNET_YES; + notify_session_monitor (plugin, + s, + GNUNET_TRANSPORT_SS_UP); + plugin->env->session_start (plugin->env->cls, + s->address, + s, + s->scope); } - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Setting timeout for %p to %u sec.\n", sc, to); -#endif + to = (HTTP_SERVER_SESSION_TIMEOUT.rel_value_us / 1000LL / 1000LL); + server_mhd_connection_timeout (plugin, s, to); return sc; } -/** - * Lookup a session for a server connection - * - * @param plugin the plugin - * @param sc the server connection - * @return the session found or NULL - */ -static struct Session * -server_lookup_session (struct HTTP_Server_Plugin *plugin, - struct ServerConnection * sc) -{ - struct Session *s; - - for (s = plugin->head; NULL != s; s = s->next) - if ((s->server_recv == sc) || (s->server_send == sc)) - return s; - return NULL; -} - -int -server_exist_session (struct HTTP_Server_Plugin *plugin, struct Session *s) -{ - struct Session * head; - - GNUNET_assert (NULL != plugin); - GNUNET_assert (NULL != s); - - for (head = plugin->head; head != NULL; head = head->next) - { - if (head == s) - return GNUNET_YES; - } - return GNUNET_NO; -} - - /** * Callback called by MHD when it needs data to send * * @param cls current session * @param pos position in buffer * @param buf the buffer to write data to - * @param max max number of bytes available in buffer - * @return bytes written to buffer + * @param max max number of bytes available in @a buf + * @return bytes written to @a buf */ static ssize_t -server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) +server_send_callback (void *cls, + uint64_t pos, + char *buf, + size_t max) { - struct Session *s = cls; + struct ServerRequest *sc = cls; + struct GNUNET_ATS_Session *s = sc->session; ssize_t bytes_read = 0; struct HTTP_Message *msg; char *stat_txt; - GNUNET_assert (NULL != p); - if (GNUNET_NO == server_exist_session (p, s)) + if (NULL == s) + { + /* session is disconnecting */ + return 0; + } + + sc = s->server_send; + if (NULL == sc) return 0; msg = s->msg_head; if (NULL != msg) @@ -1263,32 +1583,65 @@ server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) /* sending */ bytes_read = GNUNET_MIN (msg->size - msg->pos, max); - memcpy (buf, &msg->buf[msg->pos], bytes_read); + GNUNET_memcpy (buf, &msg->buf[msg->pos], bytes_read); msg->pos += bytes_read; /* removing message */ if (msg->pos == msg->size) { - GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); + GNUNET_CONTAINER_DLL_remove (s->msg_head, + s->msg_tail, + msg); if (NULL != msg->transmit_cont) msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK, msg->size, msg->size + msg->overhead); + GNUNET_assert (s->msgs_in_queue > 0); + s->msgs_in_queue--; + GNUNET_assert (s->bytes_in_queue >= msg->size); + s->bytes_in_queue -= msg->size; GNUNET_free (msg); + notify_session_monitor (s->plugin, + s, + GNUNET_TRANSPORT_SS_UPDATE); } } if (0 < bytes_read) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Sent %u bytes to peer `%s' with session %p \n", bytes_read, GNUNET_i2s (&s->target), s); - GNUNET_asprintf (&stat_txt, "# bytes currently in %s_server buffers", p->protocol); - GNUNET_STATISTICS_update (p->env->stats, - stat_txt, -bytes_read, GNUNET_NO); + sc->connected = GNUNET_YES; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sent %u bytes to peer `%s' with session %p \n", + bytes_read, + GNUNET_i2s (&s->target), + s); + GNUNET_asprintf (&stat_txt, + "# bytes currently in %s_server buffers", + s->plugin->protocol); + GNUNET_STATISTICS_update (s->plugin->env->stats, + stat_txt, + - bytes_read, + GNUNET_NO); GNUNET_free (stat_txt); - GNUNET_asprintf (&stat_txt, "# bytes transmitted via %s_server", p->protocol); - GNUNET_STATISTICS_update (p->env->stats, + GNUNET_asprintf (&stat_txt, + "# bytes transmitted via %s_server", + s->plugin->protocol); + GNUNET_STATISTICS_update (s->plugin->env->stats, stat_txt, bytes_read, GNUNET_NO); GNUNET_free (stat_txt); } + else if ((sc->options & OPTION_LONG_POLL) && sc->connected) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Completing GET response to peer `%s' with session %p\n", + GNUNET_i2s (&s->target), + s); + return MHD_CONTENT_READER_END_OF_STREAM; + } + else + { + MHD_suspend_connection (s->server_send->mhd_conn); + s->server_send->suspended = true; + return 0; + } return bytes_read; } @@ -1297,60 +1650,79 @@ server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) * 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 + * @return #GNUNET_OK (all OK) */ static int -server_receive_mst_cb (void *cls, void *client, +server_receive_mst_cb (void *cls, const struct GNUNET_MessageHeader *message) { - struct Session *s = cls; + struct GNUNET_ATS_Session *s = cls; struct HTTP_Server_Plugin *plugin = s->plugin; - struct GNUNET_ATS_Information atsi; struct GNUNET_TIME_Relative delay; char *stat_txt; - GNUNET_assert (NULL != p); - if (GNUNET_NO == server_exist_session(p, s)) - return GNUNET_OK; - - - atsi.type = htonl (GNUNET_ATS_NETWORK_TYPE); - atsi.value = s->ats_address_network_type; - GNUNET_break (s->ats_address_network_type != ntohl (GNUNET_ATS_NET_UNSPECIFIED)); - - + if (GNUNET_NO == s->known_to_service) + { + s->known_to_service = GNUNET_YES; + plugin->env->session_start (plugin->env->cls, + s->address, + s, + s->scope); + notify_session_monitor (plugin, + s, + GNUNET_TRANSPORT_SS_UP); + } delay = plugin->env->receive (plugin->env->cls, - &s->target, - message, - s, NULL, 0); - - plugin->env->update_address_metrics (plugin->env->cls, - &s->target, - NULL, 0, s, &atsi, 1); - - GNUNET_asprintf (&stat_txt, "# bytes received via %s_server", plugin->protocol); + s->address, + s, + message); + GNUNET_asprintf (&stat_txt, + "# bytes received via %s_server", + plugin->protocol); GNUNET_STATISTICS_update (plugin->env->stats, - stat_txt, ntohs (message->size), GNUNET_NO); + stat_txt, + ntohs (message->size), + GNUNET_NO); GNUNET_free (stat_txt); - - s->session_passed = GNUNET_YES; - s->next_receive = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay); + s->next_receive = GNUNET_TIME_relative_to_absolute (delay); if (delay.rel_value_us > 0) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Peer `%s' address `%s' next read delayed for %s\n", - GNUNET_i2s (&s->target), - http_common_plugin_address_to_string (NULL, p->protocol, s->addr, s->addrlen), - GNUNET_STRINGS_relative_time_to_string (delay, - GNUNET_YES)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer `%s' address `%s' next read delayed for %s\n", + GNUNET_i2s (&s->target), + http_common_plugin_address_to_string (plugin->protocol, + s->address->address, + s->address->address_length), + GNUNET_STRINGS_relative_time_to_string (delay, + GNUNET_YES)); } server_reschedule_session_timeout (s); return GNUNET_OK; } +/** + * Add headers to a request indicating that we allow Cross-Origin Resource + * Sharing. + * + * @param response response object to modify + */ +static void +add_cors_headers(struct MHD_Response *response) +{ + MHD_add_response_header (response, + "Access-Control-Allow-Origin", + "*"); + MHD_add_response_header (response, + "Access-Control-Allow-Methods", + "GET, PUT, OPTIONS"); + MHD_add_response_header (response, + "Access-Control-Max-Age", + "86400"); +} + + /** * MHD callback for a new incoming connection * @@ -1360,82 +1732,88 @@ server_receive_mst_cb (void *cls, void *client, * @param method GET or PUT * @param version HTTP version * @param upload_data upload data - * @param upload_data_size sizeof upload data + * @param upload_data_size size of @a upload_data * @param httpSessionCache the session cache to remember the connection * @return MHD_YES if connection is accepted, MHD_NO on reject */ static int -server_access_cb (void *cls, struct MHD_Connection *mhd_connection, - const char *url, const char *method, const char *version, - const char *upload_data, size_t * upload_data_size, +server_access_cb (void *cls, + struct MHD_Connection *mhd_connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, void **httpSessionCache) { struct HTTP_Server_Plugin *plugin = cls; - int res = MHD_YES; - - struct ServerConnection *sc = *httpSessionCache; - struct Session *s; + struct ServerRequest *sc = *httpSessionCache; + struct GNUNET_ATS_Session *s; struct MHD_Response *response; + int res = MHD_YES; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("Access from connection %p (%u of %u) for `%s' `%s' url `%s' with upload data size %u\n"), - sc, - plugin->cur_connections, plugin->max_connections, - method, version, url, (*upload_data_size)); - - GNUNET_assert (cls != NULL); - if (sc == NULL) - { + LOG (GNUNET_ERROR_TYPE_DEBUG, + _("Access from connection %p (%u of %u) for `%s' `%s' url `%s' with upload data size %u\n"), + sc, + plugin->cur_request, + plugin->max_request, + method, + version, + url, + (*upload_data_size)); + if (NULL == sc) + { + /* CORS pre-flight request */ + if (0 == strcmp (MHD_HTTP_METHOD_OPTIONS, method)) + { + response = MHD_create_response_from_buffer (0, NULL, + MHD_RESPMEM_PERSISTENT); + add_cors_headers(response); + res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + return res; + } /* new connection */ sc = server_lookup_connection (plugin, mhd_connection, url, method); - if (sc != NULL) + if (NULL != sc) { + /* attach to new / existing session */ (*httpSessionCache) = sc; } else { - response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE), HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO); + /* existing session already has matching connection, refuse */ + response = MHD_create_response_from_buffer (strlen (HTTP_ERROR_RESPONSE), + HTTP_ERROR_RESPONSE, + MHD_RESPMEM_PERSISTENT); MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, "text/html"); + add_cors_headers(response); res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response); MHD_destroy_response (response); return res; } } - else - { - /* 'old' connection */ - if (NULL == server_lookup_session (plugin, sc)) - { - /* Session was already disconnected */ - return MHD_NO; - } - } - - /* existing connection */ - sc = (*httpSessionCache); - s = sc->session; - GNUNET_assert (NULL != s); - /* connection is to be disconnected */ - if (sc->disconnect == GNUNET_YES) + /* 'old' connection */ + if (NULL == (s = sc->session)) { - /* Sent HTTP/1.1: 200 OK as response */ - response = MHD_create_response_from_data (strlen ("Thank you!"), - "Thank you!", - MHD_NO, MHD_NO); + /* Session was already disconnected; + sent HTTP/1.1: 200 OK as response */ + response = MHD_create_response_from_buffer (strlen ("Thank you!"), + "Thank you!", + MHD_RESPMEM_PERSISTENT); + add_cors_headers(response); MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); MHD_destroy_response (response); return MHD_YES; } - GNUNET_assert (s != NULL); if (sc->direction == _SEND) { - response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, - 32 * 1024, - &server_send_callback, s, - NULL); + response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 32 * 1024, + &server_send_callback, sc, NULL); + add_cors_headers(response); MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); MHD_destroy_response (response); return MHD_YES; @@ -1445,73 +1823,86 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, if ((*upload_data_size == 0) && (sc->connected == GNUNET_NO)) { /* (*upload_data_size == 0) first callback when header are passed */ - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Session %p / Connection %p: Peer `%s' PUT on address `%s' connected\n", - s, sc, - GNUNET_i2s (&s->target), - http_common_plugin_address_to_string (NULL, - p->protocol, - s->addr, - s->addrlen)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Session %p / Connection %p: Peer `%s' PUT on address `%s' connected\n", + s, sc, + GNUNET_i2s (&s->target), + http_common_plugin_address_to_string (plugin->protocol, + s->address->address, + s->address->address_length)); sc->connected = GNUNET_YES; return MHD_YES; } else if ((*upload_data_size == 0) && (sc->connected == GNUNET_YES)) { /* (*upload_data_size == 0) when upload is complete */ - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Session %p / Connection %p: Peer `%s' PUT on address `%s' finished upload\n", - s, sc, - GNUNET_i2s (&s->target), - http_common_plugin_address_to_string (NULL, - p->protocol, - s->addr, - s->addrlen)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Session %p / Connection %p: Peer `%s' PUT on address `%s' finished upload\n", + s, sc, + GNUNET_i2s (&s->target), + http_common_plugin_address_to_string (plugin->protocol, + s->address->address, + s->address->address_length)); sc->connected = GNUNET_NO; /* Sent HTTP/1.1: 200 OK as PUT Response\ */ - response = MHD_create_response_from_data (strlen ("Thank you!"), - "Thank you!", - MHD_NO, MHD_NO); + response = MHD_create_response_from_buffer (strlen ("Thank you!"), + "Thank you!", + MHD_RESPMEM_PERSISTENT); + add_cors_headers(response); MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); MHD_destroy_response (response); return MHD_YES; } else if ((*upload_data_size > 0) && (sc->connected == GNUNET_YES)) { + struct GNUNET_TIME_Relative delay; + /* (*upload_data_size > 0) for every segment received */ - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Session %p / Connection %p: Peer `%s' PUT on address `%s' received %u bytes\n", - s, sc, - GNUNET_i2s (&s->target), - http_common_plugin_address_to_string (NULL, - p->protocol, - s->addr, - s->addrlen), - *upload_data_size); - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - - if ((s->next_receive.abs_value_us <= now.abs_value_us)) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Session %p / Connection %p: Peer `%s' PUT on address `%s' received %u bytes\n", + s, sc, + GNUNET_i2s (&s->target), + http_common_plugin_address_to_string (plugin->protocol, + s->address->address, + s->address->address_length), + *upload_data_size); + delay = GNUNET_TIME_absolute_get_remaining (s->next_receive); + if (0 == delay.rel_value_us) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "PUT with %u bytes forwarded to MST\n", - *upload_data_size); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "PUT with %u bytes forwarded to MST\n", + *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 { + /* delay processing */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Session %p / Connection %p: no inbound bandwidth available! Next read was delayed by %s\n", - s, sc, - GNUNET_STRINGS_relative_time_to_string (GNUNET_TIME_absolute_get_duration (s->next_receive), + "Session %p / Connection %p: no inbound bandwidth available! Next read was delayed by %s\n", + s, + sc, + GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES)); + GNUNET_assert(s->server_recv->mhd_conn == mhd_connection); + MHD_suspend_connection (s->server_recv->mhd_conn); + s->server_recv->suspended = true; + if (NULL == s->recv_wakeup_task) + s->recv_wakeup_task + = GNUNET_SCHEDULER_add_delayed (delay, + &server_wake_up, + s); } return MHD_YES; } @@ -1528,299 +1919,149 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, /** * Callback from MHD when a connection disconnects * - * @param cls closure + * @param cls closure with the `struct HTTP_Server_Plugin *` * @param connection the disconnected MHD connection * @param httpSessionCache the pointer to distinguish */ static void -server_disconnect_cb (void *cls, struct MHD_Connection *connection, +server_disconnect_cb (void *cls, + struct MHD_Connection *connection, void **httpSessionCache) { - struct ServerConnection *sc = *httpSessionCache; - struct Session *s = NULL; - struct Session *t = NULL; - struct HTTP_Server_Plugin *plugin = NULL; - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, p->name, - "Disconnect for connection %p \n", sc); - - if (sc == NULL) - return; - - if (NULL == (s = server_lookup_session (p, sc))) - return; + struct HTTP_Server_Plugin *plugin = cls; + struct ServerRequest *sc = *httpSessionCache; - GNUNET_assert (NULL != p); - for (t = p->head; t != NULL; t = t->next) + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Disconnect for connection %p\n", + sc); + if (NULL == sc) { - if (t == s) - break; - } - if (NULL == t) + /* CORS pre-flight request finished */ return; + } - plugin = s->plugin; - if (sc->direction == _SEND) + if (NULL != sc->session) { - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Peer `%s' connection %p, GET on address `%s' disconnected\n", - GNUNET_i2s (&s->target), s->server_send, - http_common_plugin_address_to_string (NULL, p->protocol, s->addr, s->addrlen)); - s->server_send = NULL; - if (NULL != (s->server_recv)) + if (sc->direction == _SEND) { - s->server_recv->disconnect = GNUNET_YES; - GNUNET_assert (NULL != s->server_recv->mhd_conn); -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (s->server_recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - server_reschedule (plugin, s->server_recv->mhd_daemon, GNUNET_NO); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer `%s' connection %p, GET on address `%s' disconnected\n", + GNUNET_i2s (&sc->session->target), + sc->session->server_send, + http_common_plugin_address_to_string (plugin->protocol, + sc->session->address->address, + sc->session->address->address_length)); + + sc->session->server_send = NULL; } - } - if (sc->direction == _RECEIVE) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Peer `%s' connection %p PUT on address `%s' disconnected\n", - GNUNET_i2s (&s->target), s->server_recv, - http_common_plugin_address_to_string (NULL, p->protocol, s->addr, s->addrlen)); - s->server_recv = NULL; - /* Do not terminate session when PUT disconnects - if (NULL != (s->server_send)) - { - s->server_send->disconnect = GNUNET_YES; - GNUNET_assert (NULL != s->server_send->mhd_conn); -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (s->server_send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - server_reschedule (plugin, s->server_send->mhd_daemon, GNUNET_NO); - }*/ - if (s->msg_tk != NULL) + else if (sc->direction == _RECEIVE) { - GNUNET_SERVER_mst_destroy (s->msg_tk); - s->msg_tk = NULL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer `%s' connection %p PUT on address `%s' disconnected\n", + GNUNET_i2s (&sc->session->target), + sc->session->server_recv, + http_common_plugin_address_to_string (plugin->protocol, + sc->session->address->address, + sc->session->address->address_length)); + sc->session->server_recv = NULL; + if (NULL != sc->session->msg_tk) + { + GNUNET_MST_destroy (sc->session->msg_tk); + sc->session->msg_tk = NULL; + } } } - GNUNET_free (sc); - plugin->cur_connections--; + plugin->cur_request--; +} - if ((s->server_send == NULL) && (s->server_recv == NULL)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Peer `%s' on address `%s' disconnected\n", - GNUNET_i2s (&s->target), - http_common_plugin_address_to_string (NULL, p->protocol, s->addr, s->addrlen)); - if ((GNUNET_YES == s->session_passed) && (GNUNET_NO == s->session_ended)) - { - /* Notify transport immediately that this session is invalid */ - s->session_ended = GNUNET_YES; - plugin->env->session_end (plugin->env->cls, &s->target, s); - } - server_delete_session (s); - } +/** + * Callback from MHD when a connection starts/stops + * + * @param cls closure with the `struct HTTP_Server_Plugin *` + * @param connection connection handle + * @param socket_context socket-specific pointer + * @param toe reason for connection notification + * @see #MHD_OPTION_NOTIFY_CONNECTION + */ +static void +server_connection_cb (void *cls, + struct MHD_Connection *connection, + void **socket_context, + enum MHD_ConnectionNotificationCode toe) +{ + struct HTTP_Server_Plugin *plugin = cls; + const union MHD_ConnectionInfo *info; + + if (MHD_CONNECTION_NOTIFY_STARTED == toe) + return; + /* Reschedule to remove closed socket from our select set */ + info = MHD_get_connection_info (connection, + MHD_CONNECTION_INFO_DAEMON); + GNUNET_assert (NULL != info); + server_reschedule (plugin, + info->daemon, + GNUNET_YES); } /** * Check if incoming connection is accepted. - + * * @param cls plugin as closure * @param addr address of incoming connection - * @param addr_len address length of incoming connection + * @param addr_len number of bytes in @a addr * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected - * */ static int -server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len) +server_accept_cb (void *cls, + const struct sockaddr *addr, + socklen_t addr_len) { struct HTTP_Server_Plugin *plugin = cls; - if (plugin->cur_connections <= plugin->max_connections) + if (plugin->cur_request <= plugin->max_request) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("Accepting connection (%u of %u) from `%s'\n"), - plugin->cur_connections, plugin->max_connections, - GNUNET_a2s (addr, addr_len)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + _("Accepting connection (%u of %u) from `%s'\n"), + plugin->cur_request, plugin->max_request, + GNUNET_a2s (addr, addr_len)); return MHD_YES; } else { - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name, - _("Server reached maximum number connections (%u), rejecting new connection\n"), - plugin->max_connections); + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Server reached maximum number connections (%u), rejecting new connection\n"), + plugin->max_request); return MHD_NO; } } -static void -server_log (void *arg, const char *fmt, va_list ap) -{ - char text[1024]; - - vsnprintf (text, sizeof (text), fmt, ap); - va_end (ap); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Server: %s\n", text); -} - - -/** - * Call MHD IPv4 to process pending requests and then go back - * and schedule the next run. - * @param cls plugin as closure - * @param tc task context - */ -static void -server_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct HTTP_Server_Plugin *plugin = cls; - - GNUNET_assert (cls != NULL); - - plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; -#if 0 - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Running IPv4 server\n"); -#endif - plugin->server_v4_immediately = GNUNET_NO; - GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4)); - server_reschedule (plugin, plugin->server_v4, GNUNET_NO); -} - - -/** - * Call MHD IPv6 to process pending requests and then go back - * and schedule the next run. - * @param cls plugin as closure - * @param tc task context - */ -static void -server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct HTTP_Server_Plugin *plugin = cls; - - GNUNET_assert (cls != NULL); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; -#if 0 - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Running IPv6 server\n"); -#endif - plugin->server_v6_immediately = GNUNET_NO; - GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6)); - server_reschedule (plugin, plugin->server_v6, GNUNET_NO); -} - - -#define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG /** - * Function that queries MHD's select sets and - * starts the task waiting for them. + * Log function called by MHD. * - * @param plugin plugin - * @param daemon_handle the MHD daemon handle - * @return gnunet task identifier + * @param arg NULL + * @param fmt format string + * @param ap arguments for the format string (va_start() and va_end() + * will be called by MHD) */ -static GNUNET_SCHEDULER_TaskIdentifier -server_schedule (struct HTTP_Server_Plugin *plugin, - struct MHD_Daemon *daemon_handle, - int now) +static void +server_log (void *arg, + const char *fmt, + va_list ap) { - GNUNET_SCHEDULER_TaskIdentifier ret; - fd_set rs; - fd_set ws; - fd_set es; - struct GNUNET_NETWORK_FDSet *wrs; - struct GNUNET_NETWORK_FDSet *wws; - struct GNUNET_NETWORK_FDSet *wes; - int max; - UNSIGNED_MHD_LONG_LONG timeout; - static unsigned long long last_timeout = 0; - int haveto; - - struct GNUNET_TIME_Relative tv; - - if (GNUNET_YES == plugin->in_shutdown) - return GNUNET_SCHEDULER_NO_TASK; - - ret = GNUNET_SCHEDULER_NO_TASK; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - wrs = GNUNET_NETWORK_fdset_create (); - wes = GNUNET_NETWORK_fdset_create (); - wws = GNUNET_NETWORK_fdset_create (); - max = -1; - GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max)); - haveto = MHD_get_timeout (daemon_handle, &timeout); - if (haveto == MHD_YES) - { - if (timeout != last_timeout) - { - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "SELECT Timeout changed from %llu to %llu (ms)\n", - last_timeout, timeout); - last_timeout = timeout; - } - if (timeout <= GNUNET_TIME_UNIT_SECONDS.rel_value_us / 1000LL) - tv.rel_value_us = (uint64_t) timeout * 1000LL; - else - tv = GNUNET_TIME_UNIT_SECONDS; - } - else - tv = GNUNET_TIME_UNIT_SECONDS; - /* Force immediate run, since we have outbound data to send */ - if (now == GNUNET_YES) - tv = GNUNET_TIME_UNIT_MILLISECONDS; - GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1); - GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1); - GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1); + char text[1024]; - if (daemon_handle == plugin->server_v4) - { - if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v4_task); - plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; - } -#if 0 - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Scheduling IPv4 server task in %llu ms\n", tv); -#endif - ret = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - tv, wrs, wws, - &server_v4_run, plugin); - } - if (daemon_handle == plugin->server_v6) - { - if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v6_task); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; - } -#if 0 - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Scheduling IPv6 server task in %llu ms\n", tv); -#endif - ret = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - tv, wrs, wws, - &server_v6_run, plugin); - } - GNUNET_NETWORK_fdset_destroy (wrs); - GNUNET_NETWORK_fdset_destroy (wws); - GNUNET_NETWORK_fdset_destroy (wes); - return ret; + vsnprintf (text, + sizeof (text), + fmt, + ap); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Server: %s\n", + text); } @@ -1845,7 +2086,7 @@ server_load_file (const char *file) gn_file = GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_USER_READ); - if (gn_file == NULL) + if (NULL == gn_file) { GNUNET_free (text); return NULL; @@ -1879,7 +2120,8 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin) if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, + GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, + plugin->name, "KEY_FILE", &key_file)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, @@ -1887,7 +2129,8 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin) return GNUNET_SYSERR; } if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, + GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, + plugin->name, "CERT_FILE", &cert_file)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, @@ -1902,12 +2145,12 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin) plugin->name, "CRYPTO_INIT", &plugin->crypto_init)) - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Using crypto init string `%s'\n", - plugin->crypto_init); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Using crypto init string `%s'\n", + plugin->crypto_init); else - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Using default crypto init string \n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Using default crypto init string \n"); /* read key & certificates from file */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -1930,15 +2173,17 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin) "No usable TLS certificate found, creating certificate\n"); errno = 0; cert_creation = - GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL, + GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + NULL, NULL, NULL, "gnunet-transport-certificate-creation", "gnunet-transport-certificate-creation", - key_file, cert_file, NULL); - if (cert_creation == NULL) + key_file, + cert_file, + NULL); + if (NULL == cert_creation) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - _ - ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n")); + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n")); GNUNET_free (key_file); GNUNET_free (cert_file); @@ -1960,10 +2205,9 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin) if ((plugin->key == NULL) || (plugin->cert == NULL)) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, - plugin->name, - _("No usable TLS certificate found and creating one at `%s/%s' failed!\n"), - key_file, cert_file); + LOG (GNUNET_ERROR_TYPE_ERROR, + _("No usable TLS certificate found and creating one at `%s/%s' failed!\n"), + key_file, cert_file); GNUNET_free (key_file); GNUNET_free (cert_file); @@ -1978,218 +2222,218 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin) } GNUNET_free (key_file); GNUNET_free (cert_file); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n"); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "TLS certificate loaded\n"); return res; } #endif +/** + * Invoke `MHD_start_daemon` with the various options we need to + * setup the HTTP server with the given listen address. + * + * @param plugin our plugin + * @param addr listen address to use + * @param v6 MHD_NO_FLAG or MHD_USE_IPv6, depending on context + * @return NULL on error + */ +static struct MHD_Daemon * +run_mhd_start_daemon (struct HTTP_Server_Plugin *plugin, + const struct sockaddr_in *addr, + int v6) +{ + struct MHD_Daemon *server; + unsigned int timeout; + +#if MHD_VERSION >= 0x00090E00 + timeout = HTTP_SERVER_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL / 1000LL; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "MHD can set timeout per connection! Default time out %u sec.\n", + timeout); +#else + timeout = HTTP_SERVER_SESSION_TIMEOUT.rel_value_us / 1000LL / 1000LL; + LOG (GNUNET_ERROR_TYPE_WARNING, + "MHD cannot set timeout per connection! Default time out %u sec.\n", + timeout); +#endif + server = MHD_start_daemon ( +#if VERBOSE_SERVER + MHD_USE_DEBUG | +#endif +#if BUILD_HTTPS + MHD_USE_SSL | +#endif + MHD_USE_SUSPEND_RESUME | + v6, + plugin->port, + &server_accept_cb, plugin, + &server_access_cb, plugin, + MHD_OPTION_SOCK_ADDR, + addr, + MHD_OPTION_CONNECTION_LIMIT, + (unsigned int) plugin->max_request, +#if BUILD_HTTPS + MHD_OPTION_HTTPS_PRIORITIES, + plugin->crypto_init, + MHD_OPTION_HTTPS_MEM_KEY, + plugin->key, + MHD_OPTION_HTTPS_MEM_CERT, + plugin->cert, +#endif + MHD_OPTION_CONNECTION_TIMEOUT, + timeout, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, + (size_t) (2 * + GNUNET_MAX_MESSAGE_SIZE), + MHD_OPTION_NOTIFY_COMPLETED, + &server_disconnect_cb, plugin, + MHD_OPTION_NOTIFY_CONNECTION, + &server_connection_cb, plugin, + MHD_OPTION_EXTERNAL_LOGGER, + &server_log, NULL, + MHD_OPTION_END); +#ifdef TCP_STEALTH + if ( (NULL != server) && + (0 != (plugin->options & HTTP_OPTIONS_TCP_STEALTH)) ) + { + const union MHD_DaemonInfo *di; + + di = MHD_get_daemon_info (server, + MHD_DAEMON_INFO_LISTEN_FD, + NULL); + if ( (0 != setsockopt ((int) di->listen_fd, + IPPROTO_TCP, + TCP_STEALTH, + plugin->env->my_identity, + sizeof (struct GNUNET_PeerIdentity))) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("TCP_STEALTH not supported on this platform.\n")); + MHD_stop_daemon (server); + server = NULL; + } + } +#endif + return server; +} + + /** * Start the HTTP server * * @param plugin the plugin handle - * @return GNUNET_OK on success, GNUNET_SYSERR on failure + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ static int server_start (struct HTTP_Server_Plugin *plugin) { - unsigned int timeout; - char *msg; - GNUNET_assert (NULL != plugin); + const char *msg; + GNUNET_assert (NULL != plugin); #if BUILD_HTTPS if (GNUNET_SYSERR == server_load_certificate (plugin)) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Could not load or create server certificate! Loading plugin failed!\n"); + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Could not load or create server certificate! Loading plugin failed!\n")); return GNUNET_SYSERR; } #endif -#if MHD_VERSION >= 0x00090E00 - timeout = HTTP_SERVER_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL / 1000LL; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "MHD can set timeout per connection! Default time out %u sec.\n", - timeout); -#else - timeout = SERVER_SESSION_TIMEOUT.rel_value_us / 1000LL / 1000LL; - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name, - "MHD cannot set timeout per connection! Default time out %u sec.\n", - timeout); -#endif plugin->server_v4 = NULL; - if (plugin->use_ipv4 == GNUNET_YES) + if (GNUNET_YES == plugin->use_ipv4) { - plugin->server_v4 = MHD_start_daemon ( -#if VERBOSE_SERVER - MHD_USE_DEBUG | -#endif -#if BUILD_HTTPS - MHD_USE_SSL | -#endif - MHD_NO_FLAG, plugin->port, - &server_accept_cb, plugin, - &server_access_cb, plugin, - MHD_OPTION_SOCK_ADDR, - (struct sockaddr_in *) - plugin->server_addr_v4, - MHD_OPTION_CONNECTION_LIMIT, - (unsigned int) - plugin->max_connections, -#if BUILD_HTTPS - MHD_OPTION_HTTPS_PRIORITIES, - plugin->crypto_init, - MHD_OPTION_HTTPS_MEM_KEY, - plugin->key, - MHD_OPTION_HTTPS_MEM_CERT, - plugin->cert, -#endif - MHD_OPTION_CONNECTION_TIMEOUT, - timeout, - MHD_OPTION_CONNECTION_MEMORY_LIMIT, - (size_t) (2 * - GNUNET_SERVER_MAX_MESSAGE_SIZE), - MHD_OPTION_NOTIFY_COMPLETED, - &server_disconnect_cb, plugin, - MHD_OPTION_EXTERNAL_LOGGER, - server_log, NULL, MHD_OPTION_END); - if (plugin->server_v4 == NULL) + plugin->server_v4 + = run_mhd_start_daemon (plugin, + (const struct sockaddr_in *) plugin->server_addr_v4, + MHD_NO_FLAG); + + if (NULL == plugin->server_v4) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Failed to start %s IPv4 server component on port %u\n", - plugin->name, plugin->port); + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to start %s IPv4 server component on port %u\n", + plugin->name, + plugin->port); } else - server_reschedule (plugin, plugin->server_v4, GNUNET_NO); + server_reschedule (plugin, + plugin->server_v4, + GNUNET_NO); } plugin->server_v6 = NULL; - if (plugin->use_ipv6 == GNUNET_YES) + if (GNUNET_YES == plugin->use_ipv6) { - plugin->server_v6 = MHD_start_daemon ( -#if VERBOSE_SERVER - MHD_USE_DEBUG | -#endif -#if BUILD_HTTPS - MHD_USE_SSL | -#endif - MHD_USE_IPv6, plugin->port, - &server_accept_cb, plugin, - &server_access_cb, plugin, - MHD_OPTION_SOCK_ADDR, - (struct sockaddr_in6 *) - plugin->server_addr_v6, - MHD_OPTION_CONNECTION_LIMIT, - (unsigned int) - plugin->max_connections, -#if BUILD_HTTPS - MHD_OPTION_HTTPS_PRIORITIES, - plugin->crypto_init, - MHD_OPTION_HTTPS_MEM_KEY, - plugin->key, - MHD_OPTION_HTTPS_MEM_CERT, - plugin->cert, -#endif - MHD_OPTION_CONNECTION_TIMEOUT, - timeout, - MHD_OPTION_CONNECTION_MEMORY_LIMIT, - (size_t) (2 * - GNUNET_SERVER_MAX_MESSAGE_SIZE), - MHD_OPTION_NOTIFY_COMPLETED, - &server_disconnect_cb, plugin, - MHD_OPTION_EXTERNAL_LOGGER, - server_log, NULL, MHD_OPTION_END); - if (plugin->server_v6 == NULL) + plugin->server_v6 + = run_mhd_start_daemon (plugin, + (const struct sockaddr_in *) plugin->server_addr_v6, + MHD_USE_IPv6); + if (NULL == plugin->server_v6) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Failed to start %s IPv6 server component on port %u\n", - plugin->name, plugin->port); + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to start %s IPv6 server component on port %u\n", + plugin->name, + plugin->port); } else - server_reschedule (plugin, plugin->server_v6, GNUNET_NO); + { + server_reschedule (plugin, + plugin->server_v6, + GNUNET_NO); + } } - - msg = "No"; - if ((plugin->server_v6 == NULL) && (plugin->server_v4 == NULL)) + msg = "No"; + if ( (NULL == plugin->server_v6) && + (NULL == plugin->server_v4) ) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "%s %s server component started on port %u\n", - msg, plugin->name, plugin->port); - sleep (10); + LOG (GNUNET_ERROR_TYPE_ERROR, + "%s %s server component started on port %u\n", + msg, + plugin->name, + plugin->port); return GNUNET_SYSERR; } - else if ((plugin->server_v6 != NULL) && (plugin->server_v4 != NULL)) - msg = "IPv4 and IPv6"; - else if (plugin->server_v6 != NULL) - msg = "IPv6"; - else if (plugin->server_v4 != NULL) - msg = "IPv4"; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "%s %s server component started on port %u\n", - msg, plugin->name, plugin->port); + if ((NULL != plugin->server_v6) && + (NULL != plugin->server_v4)) + msg = "IPv4 and IPv6"; + else if (NULL != plugin->server_v6) + msg = "IPv6"; + else if (NULL != plugin->server_v4) + msg = "IPv4"; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "%s %s server component started on port %u\n", + msg, + plugin->name, + plugin->port); return GNUNET_OK; } -void -server_stop (struct HTTP_Server_Plugin *plugin) -{ - if (plugin->server_v4 != NULL) - { - MHD_stop_daemon (plugin->server_v4); - plugin->server_v4 = NULL; - } - if ( plugin->server_v6 != NULL) - { - MHD_stop_daemon (plugin->server_v6); - plugin->server_v6 = NULL; - } - - - if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v4_task); - plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; - } - - if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v6_task); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; - } - p = NULL; - -#if BUILD_HTTPS - GNUNET_free_non_null (plugin->crypto_init); - GNUNET_free_non_null (plugin->cert); - GNUNET_free_non_null (plugin->key); -#endif - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "%s server component stopped\n", plugin->name); -} - - /** * Add an address to the server's set of addresses and notify transport * * @param cls the plugin handle - * @param add_remove GNUNET_YES on add, GNUNET_NO on remove + * @param add_remove #GNUNET_YES on add, #GNUNET_NO on remove * @param addr the address * @param addrlen address length */ static void -server_add_address (void *cls, int add_remove, const struct sockaddr *addr, - socklen_t addrlen) +server_add_address (void *cls, + int add_remove, + const struct sockaddr *addr, + socklen_t addrlen) { struct HTTP_Server_Plugin *plugin = cls; + struct GNUNET_HELLO_Address *address; struct HttpAddressWrapper *w = NULL; - w = GNUNET_malloc (sizeof (struct HttpAddressWrapper)); - w->address = http_common_address_from_socket (plugin->protocol, addr, addrlen); + w = GNUNET_new (struct HttpAddressWrapper); + w->address = http_common_address_from_socket (plugin->protocol, + addr, + addrlen); if (NULL == w->address) { GNUNET_free (w); @@ -2197,15 +2441,27 @@ server_add_address (void *cls, int add_remove, const struct sockaddr *addr, } w->addrlen = http_common_address_get_size (w->address); - GNUNET_CONTAINER_DLL_insert(plugin->addr_head, plugin->addr_tail, w); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Notifying transport to add address `%s'\n", - http_common_plugin_address_to_string (NULL, p->protocol, w->address, w->addrlen)); + GNUNET_CONTAINER_DLL_insert (plugin->addr_head, + plugin->addr_tail, + w); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Notifying transport to add address `%s'\n", + http_common_plugin_address_to_string (plugin->protocol, + w->address, + w->addrlen)); + /* modify our published address list */ #if BUILD_HTTPS - plugin->env->notify_address (plugin->env->cls, add_remove, w->address, w->addrlen, "https_client"); + address = GNUNET_HELLO_address_allocate (plugin->env->my_identity, + "https_client", w->address, w->addrlen, GNUNET_HELLO_ADDRESS_INFO_NONE); #else - plugin->env->notify_address (plugin->env->cls, add_remove, w->address, w->addrlen, "http_client"); + address = GNUNET_HELLO_address_allocate (plugin->env->my_identity, + "http_client", w->address, w->addrlen, GNUNET_HELLO_ADDRESS_INFO_NONE); #endif + + plugin->env->notify_address (plugin->env->cls, + add_remove, + address); + GNUNET_HELLO_address_free (address); } @@ -2213,42 +2469,62 @@ server_add_address (void *cls, int add_remove, const struct sockaddr *addr, * Remove an address from the server's set of addresses and notify transport * * @param cls the plugin handle - * @param add_remove GNUNET_YES on add, GNUNET_NO on remove + * @param add_remove #GNUNET_YES on add, #GNUNET_NO on remove * @param addr the address * @param addrlen address length */ static void -server_remove_address (void *cls, int add_remove, const struct sockaddr *addr, - socklen_t addrlen) +server_remove_address (void *cls, + int add_remove, + const struct sockaddr *addr, + socklen_t addrlen) { struct HTTP_Server_Plugin *plugin = cls; + struct GNUNET_HELLO_Address *address; struct HttpAddressWrapper *w = plugin->addr_head; size_t saddr_len; - void * saddr = http_common_address_from_socket (plugin->protocol, addr, addrlen); + void * saddr; + + saddr = http_common_address_from_socket (plugin->protocol, + addr, + addrlen); if (NULL == saddr) return; - saddr_len = http_common_address_get_size (saddr); + saddr_len = http_common_address_get_size (saddr); while (NULL != w) { - if (GNUNET_YES == http_common_cmp_addresses(w->address, w->addrlen, saddr, saddr_len)) - break; - w = w->next; + if (GNUNET_YES == + http_common_cmp_addresses (w->address, + w->addrlen, + saddr, + saddr_len)) + break; + w = w->next; } GNUNET_free (saddr); if (NULL == w) return; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Notifying transport to remove address `%s'\n", - http_common_plugin_address_to_string (NULL, p->protocol, w->address, w->addrlen)); - GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Notifying transport to remove address `%s'\n", + http_common_plugin_address_to_string (plugin->protocol, + w->address, + w->addrlen)); + GNUNET_CONTAINER_DLL_remove (plugin->addr_head, + plugin->addr_tail, + w); + /* modify our published address list */ #if BUILD_HTTPS - plugin->env->notify_address (plugin->env->cls, add_remove, w->address, w->addrlen, "https_client"); + address = GNUNET_HELLO_address_allocate (plugin->env->my_identity, + "https_client", w->address, w->addrlen, GNUNET_HELLO_ADDRESS_INFO_NONE); #else - plugin->env->notify_address (plugin->env->cls, add_remove, w->address, w->addrlen, "http_client"); + address = GNUNET_HELLO_address_allocate (plugin->env->my_identity, + "http_client", w->address, w->addrlen, GNUNET_HELLO_ADDRESS_INFO_NONE); #endif + plugin->env->notify_address (plugin->env->cls, add_remove, address); + GNUNET_HELLO_address_free (address); GNUNET_free (w->address); GNUNET_free (w); } @@ -2259,22 +2535,25 @@ server_remove_address (void *cls, int add_remove, const struct sockaddr *addr, * Our external IP address/port mapping has changed. * * @param cls closure, the 'struct LocalAddrList' - * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean + * @param add_remove #GNUNET_YES to mean the new public IP address, #GNUNET_NO to mean * the previous (now invalid) one + * @param ac address class the address belongs to * @param addr either the previous or the new public IP address * @param addrlen actual lenght of the address */ static void -server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr *addr, - socklen_t addrlen) +server_nat_port_map_callback (void *cls, + int add_remove, + enum GNUNET_NAT_AddressClass ac, + const struct sockaddr *addr, + socklen_t addrlen) { - GNUNET_assert (cls != NULL); struct HTTP_Server_Plugin *plugin = cls; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "NAT called to %s address `%s'\n", - (add_remove == GNUNET_NO) ? "remove" : "add", - GNUNET_a2s (addr, addrlen)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "NAT called to %s address `%s'\n", + (add_remove == GNUNET_NO) ? "remove" : "add", + GNUNET_a2s (addr, addrlen)); if (AF_INET == addr->sa_family) { @@ -2285,11 +2564,12 @@ server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr * if ((NULL != plugin->server_addr_v4) && (0 != memcmp (&plugin->server_addr_v4->sin_addr, - &s4->sin_addr, sizeof (struct in_addr)))) + &s4->sin_addr, + sizeof (struct in_addr)))) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Skipping address `%s' (not bindto address)\n", - GNUNET_a2s (addr, addrlen)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Skipping address `%s' (not bindto address)\n", + GNUNET_a2s (addr, addrlen)); return; } } @@ -2304,10 +2584,10 @@ server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr * (0 != memcmp (&plugin->server_addr_v6->sin6_addr, &s6->sin6_addr, sizeof (struct in6_addr)))) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Skipping address `%s' (not bindto address)\n", - GNUNET_a2s (addr, addrlen)); - return; + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Skipping address `%s' (not bindto address)\n", + GNUNET_a2s (addr, addrlen)); + return; } } @@ -2337,7 +2617,8 @@ static int server_get_addresses (struct HTTP_Server_Plugin *plugin, const char *service_name, const struct GNUNET_CONFIGURATION_Handle *cfg, - struct sockaddr ***addrs, socklen_t ** addr_lens) + struct sockaddr ***addrs, + socklen_t ** addr_lens) { int disablev6; unsigned long long port; @@ -2366,20 +2647,20 @@ server_get_addresses (struct HTTP_Server_Plugin *plugin, if (port > 65535) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ - ("Require valid port number for service in configuration!\n")); + _("Require valid port number for service in configuration!\n")); return GNUNET_SYSERR; } } if (0 == port) { - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name, - "Starting in listen only mode\n"); + LOG (GNUNET_ERROR_TYPE_INFO, + "Starting in listen only mode\n"); return -1; /* listen only */ } - if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "BINDTO")) + if (GNUNET_CONFIGURATION_have_value (cfg, service_name, + "BINDTO")) { GNUNET_break (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, service_name, @@ -2388,19 +2669,21 @@ server_get_addresses (struct HTTP_Server_Plugin *plugin, else hostname = NULL; - if (hostname != NULL) + if (NULL != hostname) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Resolving `%s' since that is where `%s' will bind to.\n", - hostname, service_name); + 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; if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) || - (res == NULL)) + (NULL == res)) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to resolve `%s': %s\n"), - hostname, gai_strerror (ret)); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to resolve `%s': %s\n"), + hostname, + gai_strerror (ret)); GNUNET_free (hostname); return GNUNET_SYSERR; } @@ -2432,19 +2715,20 @@ server_get_addresses (struct HTTP_Server_Plugin *plugin, next = pos->ai_next; if ((disablev6) && (pos->ai_family == AF_INET6)) continue; - if ((pos->ai_protocol != IPPROTO_TCP) && (pos->ai_protocol != 0)) + if ((pos->ai_protocol != IPPROTO_TCP) && (0 != pos->ai_protocol)) continue; /* not TCP */ - if ((pos->ai_socktype != SOCK_STREAM) && (pos->ai_socktype != 0)) + if ((pos->ai_socktype != SOCK_STREAM) && (0 != pos->ai_socktype)) continue; /* huh? */ - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Service will bind to `%s'\n", GNUNET_a2s (pos->ai_addr, - pos->ai_addrlen)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Service will bind to `%s'\n", + GNUNET_a2s (pos->ai_addr, + pos->ai_addrlen)); if (pos->ai_family == AF_INET) { GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in)); saddrlens[i] = pos->ai_addrlen; saddrs[i] = GNUNET_malloc (saddrlens[i]); - memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); + GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); } else @@ -2453,7 +2737,7 @@ server_get_addresses (struct HTTP_Server_Plugin *plugin, GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6)); saddrlens[i] = pos->ai_addrlen; saddrs[i] = GNUNET_malloc (saddrlens[i]); - memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); + GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); } i++; @@ -2524,10 +2808,12 @@ server_start_report_addresses (struct HTTP_Server_Plugin *plugin) socklen_t *addrlens; res = server_get_addresses (plugin, - plugin->name, plugin->env->cfg, + plugin->name, + plugin->env->cfg, &addrs, &addrlens); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("Found %u addresses to report to NAT service\n"), res); + LOG (GNUNET_ERROR_TYPE_DEBUG, + _("Found %u addresses to report to NAT service\n"), + res); if (GNUNET_SYSERR == res) { @@ -2535,15 +2821,20 @@ server_start_report_addresses (struct HTTP_Server_Plugin *plugin) return; } - plugin->nat = - GNUNET_NAT_register (plugin->env->cfg, GNUNET_YES, plugin->port, + plugin->nat + = GNUNET_NAT_register (plugin->env->cfg, + "transport-http_server", + IPPROTO_TCP, (unsigned int) res, - (const struct sockaddr **) addrs, addrlens, - &server_nat_port_map_callback, NULL, plugin); + (const struct sockaddr **) addrs, + addrlens, + &server_nat_port_map_callback, + NULL, + plugin); while (res > 0) { res--; - GNUNET_assert (addrs[res] != NULL); + GNUNET_assert (NULL != addrs[res]); GNUNET_free (addrs[res]); } GNUNET_free_non_null (addrs); @@ -2559,17 +2850,21 @@ server_start_report_addresses (struct HTTP_Server_Plugin *plugin) static void server_stop_report_addresses (struct HTTP_Server_Plugin *plugin) { + struct HttpAddressWrapper *w; + /* Stop NAT handle */ if (NULL != plugin->nat) + { GNUNET_NAT_unregister (plugin->nat); - + plugin->nat = NULL; + } /* Clean up addresses */ - struct HttpAddressWrapper *w; - - while (plugin->addr_head != NULL) + while (NULL != plugin->addr_head) { w = plugin->addr_head; - GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w); + GNUNET_CONTAINER_DLL_remove (plugin->addr_head, + plugin->addr_tail, + w); GNUNET_free (w->address); GNUNET_free (w); } @@ -2580,7 +2875,7 @@ server_stop_report_addresses (struct HTTP_Server_Plugin *plugin) * Check if IPv6 supported on this system * * @param plugin the plugin handle - * @return GNUNET_YES on success, else GNUNET_NO + * @return #GNUNET_YES on success, else #GNUNET_NO */ static int server_check_ipv6_support (struct HTTP_Server_Plugin *plugin) @@ -2589,28 +2884,33 @@ server_check_ipv6_support (struct HTTP_Server_Plugin *plugin) int res = GNUNET_NO; /* Probe IPv6 support */ - desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); + desc = GNUNET_NETWORK_socket_create (PF_INET6, + SOCK_STREAM, + 0); if (NULL == desc) { - if ((errno == ENOBUFS) || (errno == ENOMEM) || (errno == ENFILE) || - (errno == EACCES)) + if ( (errno == ENOBUFS) || + (errno == ENOMEM) || + (errno == ENFILE) || + (errno == EACCES) ) { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "socket"); } - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name, - _ - ("Disabling IPv6 since it is not supported on this system!\n")); + LOG (GNUNET_ERROR_TYPE_WARNING, + _("Disabling IPv6 since it is not supported on this system!\n")); res = GNUNET_NO; } else { - GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); + GNUNET_break (GNUNET_OK == + GNUNET_NETWORK_socket_close (desc)); desc = NULL; res = GNUNET_YES; } - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Testing IPv6 on this system: %s\n", - (res == GNUNET_YES) ? "successful" : "failed"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Testing IPv6 on this system: %s\n", + (res == GNUNET_YES) ? "successful" : "failed"); return res; } @@ -2619,42 +2919,59 @@ server_check_ipv6_support (struct HTTP_Server_Plugin *plugin) * Notify server about our external hostname * * @param cls plugin - * @param tc task context (unused) */ static void -server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +server_notify_external_hostname (void *cls) { struct HTTP_Server_Plugin *plugin = cls; + struct HttpAddress *ext_addr; + size_t ext_addr_len; unsigned int urlen; char *url; - plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; - - GNUNET_asprintf(&url, "%s://%s", plugin->protocol, plugin->external_hostname); - + plugin->notify_ext_task = NULL; + GNUNET_asprintf (&url, + "%s://%s", + plugin->protocol, + plugin->external_hostname); urlen = strlen (url) + 1; - plugin->ext_addr = GNUNET_malloc (sizeof (struct HttpAddress) + urlen); - plugin->ext_addr->options = htonl(plugin->options); - plugin->ext_addr->urlen = htonl (urlen); - plugin->ext_addr_len = sizeof (struct HttpAddress) + urlen; - memcpy (&plugin->ext_addr[1], url, urlen); + ext_addr = GNUNET_malloc (sizeof (struct HttpAddress) + urlen); + ext_addr->options = htonl (plugin->options); + ext_addr->urlen = htonl (urlen); + ext_addr_len = sizeof (struct HttpAddress) + urlen; + GNUNET_memcpy (&ext_addr[1], url, urlen); GNUNET_free (url); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Notifying transport about external hostname address `%s'\n", plugin->external_hostname); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Notifying transport about external hostname address `%s'\n", + plugin->external_hostname); #if BUILD_HTTPS if (GNUNET_YES == plugin->verify_external_hostname) - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name, - "Enabling SSL verification for external hostname address `%s'\n", plugin->external_hostname); - plugin->env->notify_address (plugin->env->cls, GNUNET_YES, - plugin->ext_addr, plugin->ext_addr_len, - "https_client"); + LOG (GNUNET_ERROR_TYPE_INFO, + "Enabling SSL verification for external hostname address `%s'\n", + plugin->external_hostname); + plugin->ext_addr + = GNUNET_HELLO_address_allocate (plugin->env->my_identity, + "https_client", + ext_addr, + ext_addr_len, + GNUNET_HELLO_ADDRESS_INFO_NONE); + plugin->env->notify_address (plugin->env->cls, + GNUNET_YES, + plugin->ext_addr); + GNUNET_free (ext_addr); #else - plugin->env->notify_address (plugin->env->cls, GNUNET_YES, - plugin->ext_addr, plugin->ext_addr_len, - "http_client"); + plugin->ext_addr + = GNUNET_HELLO_address_allocate (plugin->env->my_identity, + "http_client", + ext_addr, + ext_addr_len, + GNUNET_HELLO_ADDRESS_INFO_NONE); + plugin->env->notify_address (plugin->env->cls, + GNUNET_YES, + plugin->ext_addr); + GNUNET_free (ext_addr); #endif } @@ -2663,7 +2980,7 @@ server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskCo * Configure the plugin * * @param plugin plugin handle - * @return GNUNET_OK on success, GNUNET_SYSERR on failure + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ static int server_configure_plugin (struct HTTP_Server_Plugin *plugin) @@ -2680,68 +2997,72 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin) (plugin->env->cfg, plugin->name, "USE_IPv4")) { plugin->use_ipv4 = - GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name, + GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, + plugin->name, "USE_IPv4"); } else plugin->use_ipv4 = GNUNET_YES; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("IPv4 support is %s\n"), - (plugin->use_ipv4 == GNUNET_YES) ? "enabled" : "disabled"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + _("IPv4 support is %s\n"), + (plugin->use_ipv4 == GNUNET_YES) ? "enabled" : "disabled"); /* Use IPv6? */ if (GNUNET_CONFIGURATION_have_value (plugin->env->cfg, plugin->name, "USE_IPv6")) { plugin->use_ipv6 = - GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name, + GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, + plugin->name, "USE_IPv6"); } else plugin->use_ipv6 = GNUNET_YES; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("IPv6 support is %s\n"), - (plugin->use_ipv6 == GNUNET_YES) ? "enabled" : "disabled"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + _("IPv6 support is %s\n"), + (plugin->use_ipv6 == GNUNET_YES) ? "enabled" : "disabled"); if ((plugin->use_ipv4 == GNUNET_NO) && (plugin->use_ipv6 == GNUNET_NO)) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - _ - ("Neither IPv4 nor IPv6 are enabled! Fix in configuration\n"), - plugin->name); + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Neither IPv4 nor IPv6 are enabled! Fix in configuration\n")); return GNUNET_SYSERR; } /* Reading port number from config file */ if ((GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg, plugin->name, + GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg, + plugin->name, "PORT", &port)) || (port > 65535)) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - _("Port is required! Fix in configuration\n"), - plugin->name); + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Port is required! Fix in configuration\n")); return GNUNET_SYSERR; } plugin->port = port; - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name, - _("Using port %u\n"), plugin->port); - - if ((plugin->use_ipv4 == GNUNET_YES) && - (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, - plugin->name, "BINDTO", &bind4_address))) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Binding %s plugin to specific IPv4 address: `%s'\n", - plugin->protocol, bind4_address); - plugin->server_addr_v4 = GNUNET_malloc (sizeof (struct sockaddr_in)); - if (1 != inet_pton (AF_INET, bind4_address, + LOG (GNUNET_ERROR_TYPE_INFO, + _("Using port %u\n"), plugin->port); + + if ( (plugin->use_ipv4 == GNUNET_YES) && + (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, + plugin->name, + "BINDTO", + &bind4_address))) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Binding %s plugin to specific IPv4 address: `%s'\n", + plugin->protocol, + bind4_address); + plugin->server_addr_v4 = GNUNET_new (struct sockaddr_in); + if (1 != inet_pton (AF_INET, + bind4_address, &plugin->server_addr_v4->sin_addr)) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - _ - ("Specific IPv4 address `%s' in configuration file is invalid!\n"), - bind4_address); + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Specific IPv4 address `%s' in configuration file is invalid!\n"), + bind4_address); GNUNET_free (bind4_address); GNUNET_free (plugin->server_addr_v4); plugin->server_addr_v4 = NULL; @@ -2749,8 +3070,9 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin) } else { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("Binding to IPv4 address %s\n"), bind4_address); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Binding to IPv4 address %s\n", + bind4_address); plugin->server_addr_v4->sin_family = AF_INET; plugin->server_addr_v4->sin_port = htons (plugin->port); } @@ -2759,20 +3081,23 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin) if ((plugin->use_ipv6 == GNUNET_YES) && (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name, - "BINDTO6", &bind6_address))) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Binding %s plugin to specific IPv6 address: `%s'\n", - plugin->protocol, bind6_address); - plugin->server_addr_v6 = GNUNET_malloc (sizeof (struct sockaddr_in6)); + GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, + plugin->name, + "BINDTO6", + &bind6_address))) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Binding %s plugin to specific IPv6 address: `%s'\n", + plugin->protocol, bind6_address); + plugin->server_addr_v6 = GNUNET_new (struct sockaddr_in6); if (1 != - inet_pton (AF_INET6, bind6_address, &plugin->server_addr_v6->sin6_addr)) + inet_pton (AF_INET6, + bind6_address, + &plugin->server_addr_v6->sin6_addr)) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - _ - ("Specific IPv6 address `%s' in configuration file is invalid!\n"), - bind6_address); + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Specific IPv6 address `%s' in configuration file is invalid!\n"), + bind6_address); GNUNET_free (bind6_address); GNUNET_free (plugin->server_addr_v6); plugin->server_addr_v6 = NULL; @@ -2780,8 +3105,9 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin) } else { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("Binding to IPv6 address %s\n"), bind6_address); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Binding to IPv6 address %s\n", + bind6_address); plugin->server_addr_v6->sin6_family = AF_INET6; plugin->server_addr_v6->sin6_port = htons (plugin->port); } @@ -2790,82 +3116,101 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin) plugin->verify_external_hostname = GNUNET_NO; #if BUILD_HTTPS - plugin->verify_external_hostname = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name, - "VERIFY_EXTERNAL_HOSTNAME"); + plugin->verify_external_hostname + = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, + plugin->name, + "VERIFY_EXTERNAL_HOSTNAME"); if (GNUNET_SYSERR == plugin->verify_external_hostname) plugin->verify_external_hostname = GNUNET_NO; if (GNUNET_YES == plugin->verify_external_hostname) plugin->options |= HTTP_OPTIONS_VERIFY_CERTIFICATE; #endif - external_hostname_use_port = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name, - "EXTERNAL_HOSTNAME_USE_PORT"); + external_hostname_use_port + = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, + plugin->name, + "EXTERNAL_HOSTNAME_USE_PORT"); if (GNUNET_SYSERR == external_hostname_use_port) external_hostname_use_port = GNUNET_NO; - if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name, - "EXTERNAL_HOSTNAME", &eh_tmp)) + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, + plugin->name, + "EXTERNAL_HOSTNAME", + &eh_tmp)) { - char * tmp = NULL; - char * pos = NULL; - char * pos_url = NULL; - - if (NULL != strstr(eh_tmp, "://")) - { - tmp = &strstr(eh_tmp, "://")[3]; - } - else - tmp = eh_tmp; - - if (GNUNET_YES == external_hostname_use_port) - { - if ( (strlen (tmp) > 1) && (NULL != (pos = strchr(tmp, '/'))) ) - { - pos_url = pos + 1; - pos[0] = '\0'; - GNUNET_asprintf (&plugin->external_hostname, "%s:%u/%s", tmp, (uint16_t) port, (NULL == pos_url) ? "" : pos_url); - } - else - GNUNET_asprintf (&plugin->external_hostname, "%s:%u", tmp, (uint16_t) port); - } - else - plugin->external_hostname = GNUNET_strdup (tmp); - GNUNET_free (eh_tmp); + char *tmp; + char *pos = NULL; + char *pos_url = NULL; - GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name, - _("Using external hostname `%s'\n"), plugin->external_hostname); - plugin->notify_ext_task = GNUNET_SCHEDULER_add_now (&server_notify_external_hostname, plugin); + if (NULL != strstr(eh_tmp, "://")) + tmp = &strstr(eh_tmp, "://")[3]; + else + tmp = eh_tmp; - /* Use only configured external hostname */ - if (GNUNET_CONFIGURATION_have_value - (plugin->env->cfg, plugin->name, "EXTERNAL_HOSTNAME_ONLY")) + if (GNUNET_YES == external_hostname_use_port) + { + if ( (strlen (tmp) > 1) && (NULL != (pos = strchr(tmp, '/'))) ) { - plugin->external_only = - GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name, - "EXTERNAL_HOSTNAME_ONLY"); + pos_url = pos + 1; + pos[0] = '\0'; + GNUNET_asprintf (&plugin->external_hostname, + "%s:%u/%s", + tmp, + (uint16_t) port, + pos_url); } else - plugin->external_only = GNUNET_NO; + GNUNET_asprintf (&plugin->external_hostname, + "%s:%u", + tmp, + (uint16_t) port); + } + else + plugin->external_hostname = GNUNET_strdup (tmp); + GNUNET_free (eh_tmp); + + LOG (GNUNET_ERROR_TYPE_INFO, + _("Using external hostname `%s'\n"), + plugin->external_hostname); + plugin->notify_ext_task = GNUNET_SCHEDULER_add_now (&server_notify_external_hostname, + plugin); + + /* Use only configured external hostname */ + if (GNUNET_CONFIGURATION_have_value + (plugin->env->cfg, + plugin->name, + "EXTERNAL_HOSTNAME_ONLY")) + { + plugin->external_only = + GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, + plugin->name, + "EXTERNAL_HOSTNAME_ONLY"); + } + else + plugin->external_only = GNUNET_NO; - if (GNUNET_YES == plugin->external_only) - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("Notifying transport only about hostname `%s'\n"), plugin->external_hostname); + if (GNUNET_YES == plugin->external_only) + LOG (GNUNET_ERROR_TYPE_DEBUG, + _("Notifying transport only about hostname `%s'\n"), + plugin->external_hostname); } else - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "No external hostname configured\n"); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "No external hostname configured\n"); /* Optional parameters */ - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg, - plugin->name, - "MAX_CONNECTIONS", &max_connections)) + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg, + plugin->name, + "MAX_CONNECTIONS", + &max_connections)) max_connections = 128; - plugin->max_connections = max_connections; - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("Maximum number of connections is %u\n"), - plugin->max_connections); + plugin->max_request = max_connections; + LOG (GNUNET_ERROR_TYPE_DEBUG, + _("Maximum number of connections is %u\n"), + plugin->max_request); plugin->peer_id_length = strlen (GNUNET_i2s_full (plugin->env->my_identity)); @@ -2873,74 +3218,6 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin) } -/** - * Session was idle, so disconnect it - * - * @param cls the session - * @param tc task context - */ -static void -server_session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - GNUNET_assert (NULL != cls); - struct Session *s = cls; - - s->timeout_task = GNUNET_SCHEDULER_NO_TASK; - GNUNET_log (TIMEOUT_LOG, - "Session %p was idle for %s, disconnecting\n", - s, - GNUNET_STRINGS_relative_time_to_string (SERVER_SESSION_TIMEOUT, - GNUNET_YES)); - - /* call session destroy function */ - GNUNET_assert (GNUNET_OK == server_disconnect (s)); -} - - -/** -* Start session timeout for session s -* -* @param s the session -*/ -static void -server_start_session_timeout (struct Session *s) -{ - GNUNET_assert (NULL != s); - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s->timeout_task); - s->timeout_task = GNUNET_SCHEDULER_add_delayed (SERVER_SESSION_TIMEOUT, - &server_session_timeout, - s); - GNUNET_log (TIMEOUT_LOG, - "Timeout for session %p set to %s\n", - s, - GNUNET_STRINGS_relative_time_to_string (SERVER_SESSION_TIMEOUT, - GNUNET_YES)); -} - - -/** -* Increment session timeout due to activity session s -* -* @param s the session -*/ -static void -server_reschedule_session_timeout (struct Session *s) -{ - GNUNET_assert (NULL != s); - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task); - - GNUNET_SCHEDULER_cancel (s->timeout_task); - s->timeout_task = GNUNET_SCHEDULER_add_delayed (SERVER_SESSION_TIMEOUT, - &server_session_timeout, - s); - GNUNET_log (TIMEOUT_LOG, - "Timeout rescheduled for session %p set to %s\n", - s, - GNUNET_STRINGS_relative_time_to_string (SERVER_SESSION_TIMEOUT, - GNUNET_YES)); -} - - /** * Exit point from the plugin. * @@ -2952,8 +3229,6 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) { struct GNUNET_TRANSPORT_PluginFunctions *api = cls; struct HTTP_Server_Plugin *plugin = api->cls; - struct Session *pos; - struct Session *next; if (NULL == api->cls) { @@ -2962,101 +3237,226 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) return NULL; } plugin->in_shutdown = GNUNET_YES; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("Shutting down plugin `%s'\n"), - plugin->name); + LOG (GNUNET_ERROR_TYPE_INFO, + _("Shutting down plugin `%s'\n"), + plugin->name); - if (GNUNET_SCHEDULER_NO_TASK != plugin->notify_ext_task) + if (NULL != plugin->notify_ext_task) { - GNUNET_SCHEDULER_cancel (plugin->notify_ext_task); - plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_SCHEDULER_cancel (plugin->notify_ext_task); + plugin->notify_ext_task = NULL; } if (NULL != plugin->ext_addr) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Notifying transport to remove address `%s'\n", - http_common_plugin_address_to_string (NULL, - p->protocol, - plugin->ext_addr, - plugin->ext_addr_len)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Notifying transport to remove address `%s'\n", + http_common_plugin_address_to_string (plugin->protocol, + plugin->ext_addr->address, + plugin->ext_addr->address_length)); #if BUILD_HTTPS - plugin->env->notify_address (plugin->env->cls, - GNUNET_NO, - plugin->ext_addr, - plugin->ext_addr_len, - "https_client"); + plugin->env->notify_address (plugin->env->cls, + GNUNET_NO, + plugin->ext_addr); #else plugin->env->notify_address (plugin->env->cls, GNUNET_NO, - plugin->ext_addr, - plugin->ext_addr_len, - "http_client"); + plugin->ext_addr); #endif - + GNUNET_HELLO_address_free (plugin->ext_addr); + plugin->ext_addr = NULL; } /* Stop to report addresses to transport service */ server_stop_report_addresses (plugin); - server_stop (plugin); - next = plugin->head; - while (NULL != (pos = next)) + if (NULL != plugin->server_v4_task) { - next = pos->next; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Removing left over session %p\n", pos); - - if ((GNUNET_YES == pos->session_passed) && (GNUNET_NO == pos->session_ended)) - { - /* Notify transport immediately that this session is invalid */ - pos->session_ended = GNUNET_YES; - plugin->env->session_end (plugin->env->cls, &pos->target, pos); - } - - server_delete_session (pos); + GNUNET_SCHEDULER_cancel (plugin->server_v4_task); + plugin->server_v4_task = NULL; } + if (NULL != plugin->server_v6_task) + { + GNUNET_SCHEDULER_cancel (plugin->server_v6_task); + plugin->server_v6_task = NULL; + } +#if BUILD_HTTPS + GNUNET_free_non_null (plugin->crypto_init); + GNUNET_free_non_null (plugin->cert); + GNUNET_free_non_null (plugin->key); +#endif + GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions, + &destroy_session_shutdown_cb, + plugin); + GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions); + plugin->sessions = NULL; + if (NULL != plugin->server_v4) + { + MHD_stop_daemon (plugin->server_v4); + plugin->server_v4 = NULL; + } + if (NULL != plugin->server_v6) + { + MHD_stop_daemon (plugin->server_v6); + plugin->server_v6 = NULL; + } /* Clean up */ GNUNET_free_non_null (plugin->external_hostname); GNUNET_free_non_null (plugin->ext_addr); GNUNET_free_non_null (plugin->server_addr_v4); GNUNET_free_non_null (plugin->server_addr_v6); + regfree (&plugin->url_regex); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - _("Shutdown for plugin `%s' complete\n"), - plugin->name); + LOG (GNUNET_ERROR_TYPE_DEBUG, + _("Shutdown for plugin `%s' complete\n"), + plugin->name); GNUNET_free (plugin); GNUNET_free (api); return NULL; } -const char *http_plugin_address_to_string (void *cls, - const void *addr, - size_t addrlen) -{ -#if BUILD_HTTPS - return http_common_plugin_address_to_string (cls, PLUGIN_NAME, addr, addrlen); -#else - return http_common_plugin_address_to_string (cls, PLUGIN_NAME, addr, addrlen); -#endif +/** + * Function called for a quick conversion of the binary address to + * a numeric address. Note that the caller must not free the + * address and that the next call to this function is allowed + * to override the address again. + * + * @param cls unused + * @param addr binary address + * @param addrlen length of the address + * @return string representing the same address + */ +static const char * +http_server_plugin_address_to_string (void *cls, + const void *addr, + size_t addrlen) +{ + return http_common_plugin_address_to_string (PLUGIN_NAME, + addr, + addrlen); } /** * Function obtain the network type for a session * - * @param cls closure ('struct Plugin*') + * @param cls closure (`struct HTTP_Server_Plugin *`) * @param session the session - * @return the network type in HBO or GNUNET_SYSERR + * @return the network type in HBO or #GNUNET_SYSERR + */ +static enum GNUNET_ATS_Network_Type +http_server_plugin_get_network (void *cls, + struct GNUNET_ATS_Session *session) +{ + return session->scope; +} + + +/** + * Function obtain the network type for an address. + * + * @param cls closure (`struct Plugin *`) + * @param address the address + * @return the network type */ static enum GNUNET_ATS_Network_Type -http_server_get_network (void *cls, - struct Session *session) +http_server_plugin_get_network_for_address (void *cls, + const struct GNUNET_HELLO_Address *address) +{ + struct HTTP_Server_Plugin *plugin = cls; + + return http_common_get_network_for_address (plugin->env, + address); +} + + +/** + * Function that will be called whenever the transport service wants to + * notify the plugin that the inbound quota changed and that the plugin + * should update it's delay for the next receive value + * + * @param cls closure + * @param peer which peer was the session for + * @param session which session is being updated + * @param delay new delay to use for receiving + */ +static void +http_server_plugin_update_inbound_delay (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_ATS_Session *session, + struct GNUNET_TIME_Relative delay) +{ + session->next_receive = GNUNET_TIME_relative_to_absolute (delay); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "New inbound delay %s\n", + GNUNET_STRINGS_relative_time_to_string (delay, + GNUNET_NO)); + if (NULL != session->recv_wakeup_task) + { + GNUNET_SCHEDULER_cancel (session->recv_wakeup_task); + session->recv_wakeup_task + = GNUNET_SCHEDULER_add_delayed (delay, + &server_wake_up, + session); + } +} + + +/** + * Return information about the given session to the + * monitor callback. + * + * @param cls the `struct Plugin` with the monitor callback (`sic`) + * @param peer peer we send information about + * @param value our `struct GNUNET_ATS_Session` to send information about + * @return #GNUNET_OK (continue to iterate) + */ +static int +send_session_info_iter (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *value) +{ + struct HTTP_Server_Plugin *plugin = cls; + struct GNUNET_ATS_Session *session = value; + + notify_session_monitor (plugin, + session, + GNUNET_TRANSPORT_SS_INIT); + return GNUNET_OK; +} + + +/** + * Begin monitoring sessions of a plugin. There can only + * be one active monitor per plugin (i.e. if there are + * multiple monitors, the transport service needs to + * multiplex the generated events over all of them). + * + * @param cls closure of the plugin + * @param sic callback to invoke, NULL to disable monitor; + * plugin will being by iterating over all active + * sessions immediately and then enter monitor mode + * @param sic_cls closure for @a sic + */ +static void +http_server_plugin_setup_monitor (void *cls, + GNUNET_TRANSPORT_SessionInfoCallback sic, + void *sic_cls) { - GNUNET_assert (NULL != session); - return ntohl (session->ats_address_network_type); + struct HTTP_Server_Plugin *plugin = cls; + + plugin->sic = sic; + plugin->sic_cls = sic_cls; + if (NULL != sic) + { + GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions, + &send_session_info_iter, + plugin); + /* signal end of first iteration */ + sic (sic_cls, NULL, NULL); + } } @@ -3073,34 +3473,39 @@ LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) struct GNUNET_TRANSPORT_PluginFunctions *api; struct HTTP_Server_Plugin *plugin; - plugin = GNUNET_malloc (sizeof (struct HTTP_Server_Plugin)); - plugin->env = env; - p = plugin; - if (NULL == env->receive) { /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully initialze the plugin or the API */ - api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); + api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions); api->cls = NULL; - api->address_to_string = &http_plugin_address_to_string; + api->address_to_string = &http_server_plugin_address_to_string; api->string_to_address = &http_common_plugin_string_to_address; api->address_pretty_printer = &http_common_plugin_address_pretty_printer; return api; } + plugin = GNUNET_new (struct HTTP_Server_Plugin); + plugin->env = env; + plugin->sessions = GNUNET_CONTAINER_multipeermap_create (128, + GNUNET_YES); - api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); + api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions); api->cls = plugin; api->send = &http_server_plugin_send; - api->disconnect = &http_server_plugin_disconnect; + api->disconnect_peer = &http_server_plugin_disconnect_peer; + api->disconnect_session = &http_server_plugin_disconnect_session; + api->query_keepalive_factor = &http_server_query_keepalive_factor; api->check_address = &http_server_plugin_address_suggested; api->get_session = &http_server_plugin_get_session; - api->address_to_string = &http_plugin_address_to_string; + api->address_to_string = &http_server_plugin_address_to_string; api->string_to_address = &http_common_plugin_string_to_address; api->address_pretty_printer = &http_common_plugin_address_pretty_printer; - api->get_network = &http_server_get_network; - + api->get_network = &http_server_plugin_get_network; + api->get_network_for_address = &http_server_plugin_get_network_for_address; + api->update_session_timeout = &http_server_plugin_update_session_timeout; + api->update_inbound_delay = &http_server_plugin_update_inbound_delay; + api->setup_monitor = &http_server_plugin_setup_monitor; #if BUILD_HTTPS plugin->name = "transport-https_server"; plugin->protocol = "https"; @@ -3109,11 +3514,37 @@ LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) plugin->protocol = "http"; #endif + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (env->cfg, + plugin->name, + "TCP_STEALTH")) + { +#ifdef TCP_STEALTH + plugin->options |= HTTP_OPTIONS_TCP_STEALTH; +#else + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("TCP_STEALTH not supported on this platform.\n")); + LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); + return NULL; +#endif + } + + /* Compile URL regex */ + if (regcomp (&plugin->url_regex, + URL_REGEX, + REG_EXTENDED)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + _("Unable to compile URL regex\n")); + LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); + return NULL; + } + /* Configure plugin */ if (GNUNET_SYSERR == server_configure_plugin (plugin)) { - LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); - return NULL; + LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); + return NULL; } /* Check IPv6 support */ @@ -3126,8 +3557,8 @@ LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) if (GNUNET_SYSERR == server_start (plugin)) { - LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); - return NULL; + LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); + return NULL; } return api; }