X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fplugin_transport_http_server.c;h=a8731907eafe7f3892a6b6ead986bcdde748e2f2;hb=a03f3a1884c6f423cde604ba5b0bba86f43a7113;hp=c0153e15abb6c63edfa93b9c1b2952c87bd0f0ce;hpb=e5f76bbe80100e58b82e0ee629dd35e975478206;p=oweals%2Fgnunet.git diff --git a/src/transport/plugin_transport_http_server.c b/src/transport/plugin_transport_http_server.c index c0153e15a..a8731907e 100644 --- a/src/transport/plugin_transport_http_server.c +++ b/src/transport/plugin_transport_http_server.c @@ -19,1221 +19,2963 @@ */ /** - * @file transport/plugin_transport_http.c - * @brief http transport service plugin + * @file transport/plugin_transport_http_server.c + * @brief HTTP/S server transport plugin * @author Matthias Wachs */ -#include "plugin_transport_http.h" +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_server_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_plugin.h" +#include "gnunet_nat_lib.h" +#include "plugin_transport_http_common.h" +#include "microhttpd.h" + +#if BUILD_HTTPS +#define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_server_init +#define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_server_done +#else +#define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_server_init +#define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_server_done +#endif #define HTTP_ERROR_RESPONSE "404 Not Found

Not Found

The requested URL was not found on this server.


" #define _RECEIVE 0 #define _SEND 1 +/** + * Encapsulation of all of the state of the plugin. + */ +struct Plugin; + + +/** + * Session handle for connections. + */ +struct Session +{ + /** + * Stored in a linked list. + */ + struct Session *next; + + /** + * Stored in a linked list. + */ + struct Session *prev; + + /** + * To whom are we talking to (set to our identity + * if we are still waiting for the welcome message) + */ + struct GNUNET_PeerIdentity target; + + /** + * Pointer to the global plugin struct. + */ + struct HTTP_Server_Plugin *plugin; + + /** + * next pointer for double linked list + */ + struct HTTP_Message *msg_head; + + /** + * previous pointer for double linked list + */ + struct HTTP_Message *msg_tail; + + /** + * Message stream tokenizer for incoming data + */ + struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk; + + /** + * Client send handle + */ + struct ServerConnection *server_recv; + + /** + * Client send handle + */ + struct ServerConnection *server_send; + + /** + * Address + */ + void *addr; + + /** + * Address length + */ + size_t addrlen; + + /** + * Unique HTTP/S connection tag for this connection + */ + uint32_t tag; + + /** + * ATS network type in NBO + */ + uint32_t ats_address_network_type; + + /** + * Was session given to transport service? + */ + int session_passed; + + /** + * Did we immediately end the session in disconnect_cb + */ + int session_ended; + + /** + * Absolute time when to receive data again + * Used for receive throttling + */ + struct GNUNET_TIME_Absolute next_receive; + + /** + * Session timeout task + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; +}; + + struct ServerConnection { /* _RECV or _SEND */ int direction; - /* should this connection get disconnected? GNUNET_YES/NO */ + /* 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; + + /* The MHD daemon */ + struct MHD_Daemon *mhd_daemon; }; + /** - * Function that queries MHD's select sets and - * starts the task waiting for them. - * @param plugin plugin - * @param daemon_handle the MHD daemon handle - * @return gnunet task identifier + * Encapsulation of all of the state of the plugin. */ -static GNUNET_SCHEDULER_TaskIdentifier -server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle, - int now); - -static void -server_log (void *arg, const char *fmt, va_list ap) +struct HTTP_Server_Plugin { - char text[1024]; + /** + * Our environment. + */ + struct GNUNET_TRANSPORT_PluginEnvironment *env; + + /** + * Linked list head of open sessions. + */ + + struct Session *head; + + /** + * Linked list tail of open sessions. + */ + struct Session *tail; + + /** + * Plugin name + */ + char *name; + + /** + * Protocol + */ + char *protocol; + + /** + * External address + */ + char *external_hostname; + + /** + * Maximum number of sockets the plugin can use + * Each http inbound /outbound connections are two connections + */ + unsigned int max_connections; + + /** + * Current number of sockets the plugin can use + * Each http inbound /outbound connections are two connections + */ + unsigned int cur_connections; + + /** + * Did we immediately end the session in disconnect_cb + */ + int in_shutdown; + + /** + * Length of peer id + */ + int peer_id_length; + + /** + * External hostname the plugin can be connected to, can be different to + * the host's FQDN, used e.g. for reverse proxying + */ + char *ext_addr; + + /** + * Notify transport only about external address + */ + unsigned int external_only; + + /** + * External address length + */ + size_t ext_addr_len; + + /** + * use IPv6 + */ + uint16_t use_ipv6; + + /** + * use IPv4 + */ + uint16_t use_ipv4; + + /** + * Port used + */ + uint16_t port; + + /** + * Task calling transport service about external address + */ + GNUNET_SCHEDULER_TaskIdentifier notify_ext_task; + + /** + * NAT handle & address management + */ + struct GNUNET_NAT_Handle *nat; + + /** + * List of own addresses + */ + + /** + * IPv4 addresses DLL head + */ + struct HttpAddressWrapper *addr_head; + + /** + * IPv4 addresses DLL tail + */ + struct HttpAddressWrapper *addr_tail; + + /** + * IPv4 server socket to bind to + */ + struct sockaddr_in *server_addr_v4; + + /** + * IPv6 server socket to bind to + */ + struct sockaddr_in6 *server_addr_v6; + + /** + * MHD IPv4 task + */ + GNUNET_SCHEDULER_TaskIdentifier server_v4_task; + + /** + * MHD IPv6 task + */ + GNUNET_SCHEDULER_TaskIdentifier server_v6_task; + + /** + * The IPv4 server is scheduled to run asap + */ + int server_v4_immediately; + + /** + * The IPv6 server is scheduled to run asap + */ + int server_v6_immediately; + + /** + * MHD IPv4 daemon + */ + struct MHD_Daemon *server_v4; + + /** + * MHD IPv4 daemon + */ + struct MHD_Daemon *server_v6; + +#if BUILD_HTTPS + /** + * Crypto related + * + * Example: + * + * Use RC4-128 instead of AES: + * NONE:+VERS-TLS1.0:+ARCFOUR-128:+SHA1:+RSA:+COMP-NULL + * + */ + char *crypto_init; + + /** + * TLS key + */ + char *key; + + /** + * TLS certificate + */ + char *cert; +#endif + +}; - vsnprintf (text, sizeof (text), fmt, ap); - va_end (ap); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Server: %s\n", text); -} /** - * Check if incoming connection is accepted. - * NOTE: Here every connection is accepted - * @param cls plugin as closure - * @param addr address of incoming connection - * @param addr_len address length of incoming connection - * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected - * + * Wrapper to manage addresses */ -static int -server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len) +struct HttpAddressWrapper { - struct Plugin *plugin = cls; + /** + * Linked list next + */ + struct HttpAddressWrapper *next; - if (plugin->cur_connections <= plugin->max_connections) - return MHD_YES; - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Server: Cannot accept new connections\n"); - return MHD_NO; - } -} + /** + * Linked list previous + */ + struct HttpAddressWrapper *prev; + void *addr; -#if BUILD_HTTPS -static char * -server_load_file (const char *file) + size_t addrlen; +}; + + +/** + * Message to send using http + */ +struct HTTP_Message { - struct GNUNET_DISK_FileHandle *gn_file; - struct stat fstat; - char *text = NULL; + /** + * next pointer for double linked list + */ + struct HTTP_Message *next; + + /** + * previous pointer for double linked list + */ + struct HTTP_Message *prev; + + /** + * buffer containing data to send + */ + char *buf; + + /** + * amount of data already sent + */ + size_t pos; + + /** + * buffer length + */ + size_t size; + + /** + * HTTP/S specific overhead + */ + size_t overhead; + + /** + * Continuation function to call once the transmission buffer + * has again space available. NULL if there is no + * continuation to call. + */ + GNUNET_TRANSPORT_TransmitContinuation transmit_cont; + + /** + * Closure for transmit_cont. + */ + void *transmit_cont_cls; +}; - if (0 != STAT (file, &fstat)) - return NULL; - text = GNUNET_malloc (fstat.st_size + 1); - gn_file = - GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ, - GNUNET_DISK_PERM_USER_READ); - if (gn_file == NULL) - { - GNUNET_free (text); - return NULL; - } - if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fstat.st_size)) - { - GNUNET_free (text); - GNUNET_DISK_file_close (gn_file); - return NULL; - } - text[fstat.st_size] = '\0'; - GNUNET_DISK_file_close (gn_file); - return text; -} -#endif +/** + * The http_server plugin handle + */ +static struct HTTP_Server_Plugin * p; -#if BUILD_HTTPS -static int -server_load_certificate (struct Plugin *plugin) -{ - int res = GNUNET_OK; +/** + * Start session timeout for session s + * @param s the session + */ +static void +server_start_session_timeout (struct Session *s); - char *key_file; - char *cert_file; - /* Get crypto init string from config - * If not present just use default values */ +/** + * Increment session timeout due to activity for session s + * @param s the session + */ +static void +server_reschedule_session_timeout (struct Session *s); - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, - plugin->name, - "CRYPTO_INIT", - &plugin->crypto_init)); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, - "KEY_FILE", &key_file)) - { - key_file = GNUNET_strdup ("https_key.key"); - } +/** + * Cancel timeout for session s + * @param s the session + */ +static void +server_stop_session_timeout (struct Session *s); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, - "CERT_FILE", &cert_file)) - { - GNUNET_asprintf (&cert_file, "%s", "https_cert.crt"); - } - /* read key & certificates from file */ -#if VERBOSE_SERVER - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Loading TLS certificate from key-file `%s' cert-file`%s'\n", - key_file, cert_file); -#endif +/** + * Disconnect a session s + * @param s the session + */ +static int +server_disconnect (struct Session *s); - plugin->key = server_load_file (key_file); - plugin->cert = server_load_file (cert_file); - if ((plugin->key == NULL) || (plugin->cert == NULL)) - { - struct GNUNET_OS_Process *cert_creation; +/** + * Does session s exist? + * + * @param plugin the plugin handle + * @param s the session + * @return GNUNET_YES on success, GNUNET_NO on error + */ +static int +server_exist_session (struct HTTP_Server_Plugin *plugin, struct Session *s); - GNUNET_free_non_null (plugin->key); - plugin->key = NULL; - GNUNET_free_non_null (plugin->cert); - plugin->cert = NULL; -#if VERBOSE_SERVER - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No usable TLS certificate found, creating certificate\n"); -#endif - errno = 0; - cert_creation = - GNUNET_OS_start_process (NULL, NULL, - "gnunet-transport-certificate-creation", - "gnunet-transport-certificate-creation", - key_file, cert_file, NULL); - if (cert_creation == NULL) - { - 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")); - GNUNET_free (key_file); - GNUNET_free (cert_file); +/** + * 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 (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, + int now); - GNUNET_free_non_null (plugin->key); - plugin->key = NULL; - GNUNET_free_non_null (plugin->cert); - plugin->cert = NULL; - GNUNET_free_non_null (plugin->crypto_init); - plugin->crypto_init = NULL; - return GNUNET_SYSERR; - } - GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation)); - GNUNET_OS_process_close (cert_creation); +/** + * Function that can be used by the transport service to transmit + * a message using the plugin. Note that in the case of a + * peer disconnecting, the continuation MUST be called + * prior to the disconnect notification itself. This function + * will be called with this peer's HELLO message to initiate + * a fresh connection to another peer. + * + * @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 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 + * require plugins to discard the message after the timeout, + * just advisory for the desired delay; most plugins will ignore + * this as well) + * @param cont continuation to call once the message has + * been transmitted (or if the transport is ready + * for the next transmission call; or if the + * peer disconnected...); can be NULL + * @param cont_cls closure for 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 HTTP_Server_Plugin *plugin = cls; + struct HTTP_Message *msg; + int bytes_sent = 0; + char *stat_txt; - plugin->key = server_load_file (key_file); - plugin->cert = server_load_file (cert_file); - } + GNUNET_assert (plugin != NULL); + GNUNET_assert (session != NULL); - if ((plugin->key == NULL) || (plugin->cert == NULL)) + if (GNUNET_NO == server_exist_session (plugin, session)) { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - _ - ("No usable TLS certificate found and creating one failed!\n"), - "transport-https"); - GNUNET_free (key_file); - GNUNET_free (cert_file); + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (NULL == session->server_send) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } - GNUNET_free_non_null (plugin->key); - plugin->key = NULL; - GNUNET_free_non_null (plugin->cert); - plugin->cert = NULL; - GNUNET_free_non_null (plugin->crypto_init); - plugin->crypto_init = NULL; + if (GNUNET_YES == session->server_send->disconnect) return GNUNET_SYSERR; - } - GNUNET_free (key_file); - GNUNET_free (cert_file); -#if DEBUG_HTTP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n"); -#endif - return res; + 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)); + + /* create new message and schedule */ + bytes_sent = sizeof (struct HTTP_Message) + msgbuf_size; + msg = GNUNET_malloc (bytes_sent); + msg->next = NULL; + msg->size = msgbuf_size; + msg->pos = 0; + 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_STATISTICS_update (plugin->env->stats, + stat_txt, msgbuf_size, GNUNET_NO); + GNUNET_free (stat_txt); + + server_reschedule (session->plugin, session->server_send->mhd_daemon, GNUNET_YES); + server_reschedule_session_timeout (session); + + /* struct Plugin *plugin = cls; */ + return bytes_sent; } -#endif + /** - * Reschedule the execution of both IPv4 and IPv6 server - * @param plugin the plugin - * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait - * until timeout + * Function that can be used to force the plugin to disconnect + * from the given peer and cancel all previous transmissions + * (and their continuationc). + * + * @param cls closure + * @param target peer from which to disconnect */ - static void -server_reschedule (struct Plugin *plugin, int now) +http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target) { - if (plugin->server_v4 != 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; - } - plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now); - } + 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)); - if (plugin->server_v6 != NULL) + next = plugin->head; + while (NULL != (pos = next)) { - if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) + next = pos->next; + if (0 == memcmp (target, &pos->target, sizeof (struct GNUNET_PeerIdentity))) { - GNUNET_SCHEDULER_cancel (plugin->server_v6_task); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Disconnecting session %p to `%s'\n", + pos, GNUNET_i2s (target)); + server_disconnect (pos); } - plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now); } + } + /** - * Callback called by MessageStreamTokenizer when a message has arrived - * @param cls current session as closure - * @param client clien - * @param message the message to be forwarded to transport service + * Another peer has suggested an address for this + * peer and transport plugin. Check that this could be a valid + * address. If so, consider adding it to the list + * of addresses. + * + * @param cls closure + * @param addr pointer to the address + * @param addrlen length of addr + * @return GNUNET_OK if this is a plausible address for this peer + * and transport */ -static void -server_receive_mst_cb (void *cls, void *client, - const struct GNUNET_MessageHeader *message) +static int +http_server_plugin_address_suggested (void *cls, const void *addr, + size_t addrlen) { - struct Session *s = cls; - -#if VERBOSE_SERVER - struct Plugin *plugin = s->plugin; -#endif - struct GNUNET_TIME_Relative delay; + struct HTTP_Server_Plugin *plugin = cls; + struct HttpAddressWrapper *next; + struct HttpAddressWrapper *pos; - delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen); - s->next_receive = - GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay); + if ((NULL != plugin->ext_addr) && + GNUNET_YES == (http_common_cmp_addresses (addr, addrlen, + plugin->ext_addr, plugin->ext_addr_len))) + return GNUNET_OK; - if (delay.rel_value > 0) + next = plugin->addr_head; + while (NULL != (pos = next)) { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: peer `%s' address `%s' next read delayed for %llu ms\n", - GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, s->addrlen), - delay); -#endif + next = pos->next; + if (GNUNET_YES == (http_common_cmp_addresses(addr, + addrlen, + pos->addr, + pos->addrlen))) + return GNUNET_OK; + } + + 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 - */ -static ssize_t -server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) + * Creates a new outbound session the transport + * service will use to send data to the peer + * + * Since HTTP/S server cannot create sessions, always return NULL + * + * @param cls the plugin + * @param address the address + * @return always NULL + */ +static struct Session * +http_server_plugin_get_session (void *cls, + const struct GNUNET_HELLO_Address *address) { - struct Session *s = cls; -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, s->plugin->name, - "Server: %X can sent maximum %u \n", s, max); -#endif - struct HTTP_Message *msg; - int bytes_read = 0; + return NULL; +} - //static int c = 0; - msg = s->msg_head; - if (msg != NULL) + +/** + * Deleting the session + * Must not be used afterwards + * + * @param s the session to delete + */ +static void +server_delete_session (struct Session *s) +{ + 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) { - /* sending */ - if ((msg->size - msg->pos) <= max) - { - memcpy (buf, &msg->buf[msg->pos], (msg->size - msg->pos)); - bytes_read = msg->size - msg->pos; - msg->pos += (msg->size - msg->pos); - } - else - { - memcpy (buf, &msg->buf[msg->pos], max); - msg->pos += max; - bytes_read = max; - } + tmp = msg->next; - /* removing message */ - if (msg->pos == msg->size) + GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); + if (msg->transmit_cont != NULL) { - if (NULL != msg->transmit_cont) - msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK); - GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); - GNUNET_free (msg); + msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR, + msg->size, msg->pos + msg->overhead); } + GNUNET_free (msg); + msg = tmp; } -#if VERBOSE_CLIENT - struct Plugin *plugin = s->plugin; - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Server: %X: sent %u bytes\n", s, bytes_read); -#endif - return bytes_read; + 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_free (s); + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Session %p destroyed\n", s); } -static struct ServerConnection * -server_lookup_session (struct Plugin *plugin, - struct MHD_Connection *mhd_connection, const char *url, - const char *method) + +/** +* Cancel timeout for session s +* +* @param s the session +*/ +static void +server_stop_session_timeout (struct Session *s) { - struct Session *s = NULL; - struct Session *t; - struct ServerConnection *sc = NULL; - const union MHD_ConnectionInfo *conn_info; + 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 IPv4HttpAddress a4; - struct IPv6HttpAddress a6; - struct sockaddr_in *s4; - struct sockaddr_in6 *s6; - void *a; - size_t a_len; - struct GNUNET_PeerIdentity target; - int check = GNUNET_NO; - uint32_t tag = 0; - int direction; - 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 MHD_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 + * @return gnunet task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier +server_schedule (struct HTTP_Server_Plugin *plugin, + struct MHD_Daemon *daemon_handle, + int now); + - if ((strlen (&url[1]) >= 105) && (url[104] == ';')) +/** + * 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 (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, + int now) +{ + if ((server == plugin->server_v4) && (plugin->server_v4 != NULL)) { - char hash[104]; - char *tagc = (char *) &url[105]; + if (GNUNET_YES == plugin->server_v4_immediately) + return; /* No rescheduling, server will run asap */ - memcpy (&hash, &url[1], 103); - hash[103] = '\0'; - if (GNUNET_OK == - GNUNET_CRYPTO_hash_from_string ((const char *) &hash, - &(target.hashPubKey))) + if (GNUNET_YES == now) + plugin->server_v4_immediately = GNUNET_YES; + + if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) { - tag = strtoul (tagc, NULL, 10); - if (tagc > 0) - check = GNUNET_YES; + GNUNET_SCHEDULER_cancel (plugin->server_v4_task); + plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; } + plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now); } - if (0 == strcmp (MHD_HTTP_METHOD_PUT, method)) - direction = _RECEIVE; - if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) - direction = _SEND; - - if (check == GNUNET_NO) - goto error; - - plugin->cur_connections++; - -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: New inbound connection from %s with tag %u\n", - GNUNET_i2s (&target), tag); -#endif - /* find duplicate session */ - - t = plugin->head; - - while (t != NULL) - { - if ((t->inbound) && - (0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) - && - /* FIXME add source address comparison */ - (t->tag == tag)) - break; - t = t->next; - } - if (t != NULL) + if ((server == plugin->server_v6) && (plugin->server_v6 != NULL)) { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Duplicate session, dismissing new connection from peer `%s'\n", - GNUNET_i2s (&target)); -#endif - goto error; - } + if (GNUNET_YES == plugin->server_v6_immediately) + return; /* No rescheduling, server will run asap */ - /* find semi-session */ - t = plugin->server_semi_head; + if (GNUNET_YES == now) + plugin->server_v6_immediately = GNUNET_YES; - while (t != NULL) - { - /* FIXME add source address comparison */ - if ((0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) - && (t->tag == tag)) + if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) { - break; + GNUNET_SCHEDULER_cancel (plugin->server_v6_task); + plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; } - t = t->next; + plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now); } +} - if (t == NULL) - goto create; -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Found existing semi-session for `%s'\n", - GNUNET_i2s (&target)); -#endif +/** + * Disconnect session s + * + * @param s the session + * @return GNUNET_OK on success + */ +static int +server_disconnect (struct Session *s) +{ + struct ServerConnection * send = NULL; + struct ServerConnection * recv = NULL; - if ((direction == _SEND) && (t->server_send != NULL)) + if (GNUNET_NO == server_exist_session (p, s)) { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Duplicate GET session, dismissing new connection from peer `%s'\n", - GNUNET_i2s (&target)); -#endif - goto error; + GNUNET_break (0); + return GNUNET_SYSERR; } - else - { - s = t; - GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, - plugin->server_semi_tail, s); - GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Found matching semi-session, merging session for peer `%s'\n", - GNUNET_i2s (&target)); -#endif - goto found; - } - if ((direction == _RECEIVE) && (t->server_recv != NULL)) - { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Duplicate PUT session, dismissing new connection from peer `%s'\n", - GNUNET_i2s (&target)); -#endif - goto error; - } - else + send = (struct ServerConnection *) s->server_send; + if (s->server_send != NULL) { - s = t; - GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, - plugin->server_semi_tail, s); - GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Found matching semi-session, merging session for peer `%s'\n", - GNUNET_i2s (&target)); -#endif - goto found; - } + 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)); -create: -/* create new session */ -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Creating new session for peer `%s' \n", - GNUNET_i2s (&target)); + send->disconnect = GNUNET_YES; +#if MHD_VERSION >= 0x00090E00 + MHD_set_connection_option (send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, + 1); #endif - - switch (conn_info->client_addr->sa_family) - { - case (AF_INET): - s4 = ((struct sockaddr_in *) conn_info->client_addr); - a4.u4_port = s4->sin_port; - memcpy (&a4.ipv4_addr, &s4->sin_addr, sizeof (struct in_addr)); - a = &a4; - a_len = sizeof (struct IPv4HttpAddress); - break; - case (AF_INET6): - s6 = ((struct sockaddr_in6 *) conn_info->client_addr); - a6.u6_port = s6->sin6_port; - memcpy (&a6.ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr)); - a = &a6; - a_len = sizeof (struct IPv6HttpAddress); - break; - default: - GNUNET_break (0); - goto error; + server_reschedule (s->plugin, send->mhd_daemon, GNUNET_YES); } - s = create_session (plugin, &target, a, a_len, NULL, NULL); + 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)); - s->inbound = GNUNET_YES; - s->next_receive = GNUNET_TIME_absolute_get_zero (); - s->tag = tag; - if (0 == strcmp (MHD_HTTP_METHOD_PUT, method)) - s->server_recv = s; - if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) - s->server_send = s; - GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head, - plugin->server_semi_tail, s); - goto found; - -error: -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Invalid connection request\n"); + recv->disconnect = GNUNET_YES; +#if MHD_VERSION >= 0x00090E00 + MHD_set_connection_option (recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, + 1); #endif - return NULL; - -found: - sc = GNUNET_malloc (sizeof (struct ServerConnection)); - sc->mhd_conn = mhd_connection; - sc->direction = direction; - sc->session = s; - if (direction == _SEND) - s->server_send = sc; - if (direction == _RECEIVE) - s->server_recv = sc; + server_reschedule (s->plugin, recv->mhd_daemon, GNUNET_YES); + } + return GNUNET_OK; +} +static void +server_mhd_connection_timeout (struct HTTP_Server_Plugin *plugin, struct Session *s, int to) +{ #if MHD_VERSION >= 0x00090E00 - int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); - -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Setting timeout for %X to %u sec.\n", sc, to); -#endif - MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to); - server_reschedule (plugin, GNUNET_NO); + /* Setting timeouts for other connections */ + if (s->server_recv != NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "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); + server_reschedule (plugin, s->server_recv->mhd_daemon, GNUNET_NO); + } + if (s->server_send != NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "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 - return sc; } /** - * Process GET or PUT request received via MHD. For - * GET, queue response that will send back our pending - * messages. For PUT, process incoming data and send - * to GNUnet core. In either case, check if a session - * already exists and create a new one if not. + * Parse incoming URL for tag and target + * + * @param 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 */ + 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, - void **httpSessionCache) +server_parse_url (struct HTTP_Server_Plugin *plugin, const char * url, struct GNUNET_PeerIdentity * target, uint32_t *tag) { + int debug = GNUNET_YES; - struct Plugin *plugin = cls; - struct ServerConnection *sc = *httpSessionCache; - struct Session *s = NULL; + char * tag_start = NULL; + char * tag_end = NULL; + char * target_start = NULL; + char * separator = NULL; + char hash[plugin->peer_id_length+1]; + int hash_length; + unsigned long int ctag; - int res = MHD_YES; - struct MHD_Response *response; + /* URL parsing + * URL is valid if it is in the form [prefix with (multiple) '/'][peerid[103];tag]*/ - GNUNET_assert (cls != NULL); - /* new connection */ - if (sc == NULL) + if (NULL == url) { - sc = server_lookup_session (plugin, mhd_connection, url, method); - if (sc != NULL) - (*httpSessionCache) = sc; - else - { - response = - MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE), - HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO); - res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response); - MHD_destroy_response (response); - return res; - } + GNUNET_break (0); + return GNUNET_SYSERR; } + /* convert tag */ - /* existing connection */ - sc = (*httpSessionCache); - s = sc->session; + /* find separator */ + separator = strrchr (url, ';'); - /* connection is to be disconnected */ - if (sc->disconnect == GNUNET_YES) + if (NULL == separator) { - response = - MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!", - MHD_NO, MHD_NO); - res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); -#if VERBOSE_SERVER - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sent HTTP/1.1: 200 OK as PUT Response\n"); -#endif - MHD_destroy_response (response); - return MHD_YES; + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } + tag_start = separator + 1; - GNUNET_assert (s != NULL); - /* Check if both directions are connected */ - if ((sc->session->server_recv == NULL) || (sc->session->server_send == NULL)) + if (strlen (tag_start) == 0) { - (*upload_data_size) = 0; - return MHD_YES; + /* No tag after separator */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; + } + ctag = strtoul (tag_start, &tag_end, 10); + if (ctag == 0) + { + /* tag == 0 , invalid */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; + } + if ((ctag == ULONG_MAX) && (ERANGE == errno)) + { + /* out of range: > ULONG_MAX */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; + } + if (ctag > UINT32_MAX) + { + /* out of range: > UINT32_MAX */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; + } + (*tag) = (uint32_t) ctag; + if (NULL == tag_end) + { + /* no char after tag */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } + if (url[strlen(url)] != tag_end[0]) + { + /* there are more not converted chars after tag */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; + } + if (debug) + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Found tag `%u' in url\n", (*tag)); - if (sc->direction == _SEND) + /* convert peer id */ + target_start = strrchr (url, '/'); + if (NULL == target_start) { - response = - MHD_create_response_from_callback (-1, 32 * 1024, &server_send_callback, - s, NULL); - res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - return MHD_YES; + /* no leading '/' */ + target_start = (char *) url; } - if (sc->direction == _RECEIVE) + target_start++; + hash_length = separator - target_start; + if (hash_length != plugin->peer_id_length) { - if (*upload_data_size == 0) - { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Peer `%s' PUT on address `%s' connected\n", - GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, s->addrlen)); -#endif - return MHD_YES; - } + /* no char after tag */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; + } + memcpy (hash, target_start, hash_length); + hash[hash_length] = '\0'; - /* Recieving data */ - if ((*upload_data_size > 0)) - { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: peer `%s' PUT on address `%s' received %u bytes\n", - GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL,s->addr, s->addrlen), - *upload_data_size); -#endif - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target->hashPubKey))) + { + /* hash conversion failed */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; + } + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Found target `%s' in url\n", GNUNET_h2s_full(&target->hashPubKey)); + return GNUNET_OK; +} + + +/** + * Lookup a mhd connection and create one if none is found + * + * @param plugin the plugin handle + * @param mhd_connection the incoming mhd_connection + * @param url incoming requested URL + * @param method PUT or GET + * @return the server connecetion + */ +static struct ServerConnection * +server_lookup_connection (struct HTTP_Server_Plugin *plugin, + struct MHD_Connection *mhd_connection, const char *url, + const char *method) +{ + struct Session *s = NULL; + struct ServerConnection *sc = NULL; + const union MHD_ConnectionInfo *conn_info; + struct GNUNET_ATS_Information ats; + + char *addr; + size_t addr_len; + + struct GNUNET_PeerIdentity target; + uint32_t tag = 0; + int direction = GNUNET_SYSERR; + int to; + + 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; + } + if (0 == strcmp (MHD_HTTP_METHOD_PUT, method)) + direction = _RECEIVE; + else if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) + direction = _SEND; + else + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Invalid method %s connection 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 + { + /* create new session */ + 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_len = http_common_address_get_size (addr); + ats = 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_len = http_common_address_get_size (addr); + ats = plugin->env->get_address_type (plugin->env->cls, conn_info->client_addr, sizeof (struct sockaddr_in6)); + break; + default: + GNUNET_break (0); + return NULL; + } + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Creating new session for peer `%s' connecting from `%s'\n", + GNUNET_i2s (&target), + http_common_plugin_address_to_string (NULL, addr, addr_len)); + + s = GNUNET_malloc (sizeof (struct Session)); + memcpy (&s->target, &target, sizeof (struct GNUNET_PeerIdentity)); + s->plugin = plugin; + s->addr = addr; + s->addrlen = addr_len; + s->ats_address_network_type = ats.value; + 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; + server_start_session_timeout(s); + GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); + } + sc = GNUNET_malloc (sizeof (struct ServerConnection)); + if (conn_info->client_addr->sa_family == AF_INET) + sc->mhd_daemon = plugin->server_v4; + if (conn_info->client_addr->sa_family == AF_INET6) + sc->mhd_daemon = plugin->server_v6; + sc->mhd_conn = mhd_connection; + sc->direction = direction; + sc->connected = GNUNET_NO; + sc->session = s; + if (direction == _SEND) + s->server_send = sc; + if (direction == _RECEIVE) + 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 / 1000); + MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to); + server_reschedule (plugin, sc->mhd_daemon, GNUNET_NO); + } + else + { + 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 / 1000); + server_mhd_connection_timeout (plugin, s, to); + } + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Setting timeout for %p to %u sec.\n", sc, to); +#endif + 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 + */ +static ssize_t +server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) +{ + struct Session *s = cls; + ssize_t bytes_read = 0; + struct HTTP_Message *msg; + char *stat_txt; + + GNUNET_assert (NULL != p); + if (GNUNET_NO == server_exist_session (p, s)) + return 0; + msg = s->msg_head; + if (NULL != msg) + { + /* sending */ + bytes_read = GNUNET_MIN (msg->size - msg->pos, + max); + 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); + if (NULL != msg->transmit_cont) + msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK, + msg->size, msg->size + msg->overhead); + GNUNET_free (msg); + } + } + 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); + GNUNET_free (stat_txt); + GNUNET_asprintf (&stat_txt, "# bytes transmitted via %s_server", p->protocol); + GNUNET_STATISTICS_update (p->env->stats, + stat_txt, bytes_read, GNUNET_NO); + GNUNET_free (stat_txt); + } + return bytes_read; +} + + +/** + * Callback called by MessageStreamTokenizer when a message has arrived + * + * @param cls current session as closure + * @param client client + * @param message the message to be forwarded to transport service + * @return GNUNET_OK + */ +static int +server_receive_mst_cb (void *cls, void *client, + const struct GNUNET_MessageHeader *message) +{ + struct Session *s = cls; + struct GNUNET_ATS_Information atsi[2]; + struct GNUNET_TIME_Relative delay; + char *stat_txt; + + GNUNET_assert (NULL != p); + if (GNUNET_NO == server_exist_session(p, s)) + return GNUNET_OK; + + struct HTTP_Server_Plugin *plugin = s->plugin; + + atsi[0].type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE); + atsi[0].value = htonl (1); + atsi[1].type = htonl (GNUNET_ATS_NETWORK_TYPE); + atsi[1].value = s->ats_address_network_type; + GNUNET_break (s->ats_address_network_type != ntohl (GNUNET_ATS_NET_UNSPECIFIED)); + + + delay = plugin->env->receive (plugin->env->cls, + &s->target, + message, + (const struct GNUNET_ATS_Information *) &atsi, 2, + s, s->addr, s->addrlen); + + GNUNET_asprintf (&stat_txt, "# bytes received via %s_server", plugin->protocol); + GNUNET_STATISTICS_update (plugin->env->stats, + 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); + if (delay.rel_value > 0) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Peer `%s' address `%s' next read delayed for %llu ms\n", + GNUNET_i2s (&s->target), + http_common_plugin_address_to_string (NULL, s->addr, s->addrlen), + delay); + } + server_reschedule_session_timeout (s); + return GNUNET_OK; +} + + +/** + * MHD callback for a new incoming connection + * + * @param cls the plugin handle + * @param mhd_connection the mhd connection + * @param url the requested URL + * @param method GET or PUT + * @param version HTTP version + * @param upload_data upload data + * @param upload_data_size sizeof 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, + void **httpSessionCache) +{ + struct HTTP_Server_Plugin *plugin = cls; + int res = MHD_YES; + + struct ServerConnection *sc = *httpSessionCache; + struct Session *s; + struct MHD_Response *response; + + 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) + { + /* new connection */ + sc = server_lookup_connection (plugin, mhd_connection, url, method); + if (sc != NULL) + { + (*httpSessionCache) = sc; + } + else + { + response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE), HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO); + 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) + { + /* Sent HTTP/1.1: 200 OK as response */ + response = MHD_create_response_from_data (strlen ("Thank you!"), + "Thank you!", + MHD_NO, MHD_NO); + res = 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); + MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + return MHD_YES; + } + if (sc->direction == _RECEIVE) + { + 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, + s->addr, + s->addrlen)); + 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, + s->addr, + s->addrlen)); + 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); + res = 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)) + { + /* (*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, + s->addr, + s->addrlen), + *upload_data_size); + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); if ((s->next_receive.abs_value <= now.abs_value)) { -#if VERBOSE_SERVER GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: %X: PUT with %u bytes forwarded to MST\n", s, + "PUT with %u bytes forwarded to MST\n", *upload_data_size); -#endif if (s->msg_tk == NULL) { s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s); } - res = GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data, *upload_data_size, GNUNET_NO, GNUNET_NO); +#if MHD_VERSION >= 0x00090E00 + server_mhd_connection_timeout (plugin, s, GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); +#endif + (*upload_data_size) = 0; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Session %p / Connection %p: no inbound bandwidth available! Next read was delayed by %llu ms\n", + s, sc, now.abs_value - s->next_receive.abs_value); + } + return MHD_YES; + } + else + { + GNUNET_break (0); + return MHD_NO; + } + } + return res; +} + + +/** + * Callback from MHD when a connection disconnects + * + * @param cls closure + * @param connection the disconnected MHD connection + * @param httpSessionCache the pointer to distinguish + */ +static void +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; + + GNUNET_assert (NULL != p); + for (t = p->head; t != NULL; t = t->next) + { + if (t == s) + break; + } + if (NULL == t) + return; + + plugin = s->plugin; + if (sc->direction == _SEND) + { + + 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, s->addr, s->addrlen)); + s->server_send = NULL; + if (NULL != (s->server_recv)) + { + 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); + } + } + 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, 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) + { + GNUNET_SERVER_mst_destroy (s->msg_tk); + s->msg_tk = NULL; + } + } + + GNUNET_free (sc); + plugin->cur_connections--; + + 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, 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); + } + +} + + +/** + * 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 + * @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) +{ + struct HTTP_Server_Plugin *plugin = cls; + + if (plugin->cur_connections <= plugin->max_connections) + { + 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)); + 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); + 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. + * + * @param plugin plugin + * @param daemon_handle the MHD daemon handle + * @return gnunet task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier +server_schedule (struct HTTP_Server_Plugin *plugin, + struct MHD_Daemon *daemon_handle, + int now) +{ + 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\n", + last_timeout, timeout); + last_timeout = timeout; + } + if (timeout <= GNUNET_TIME_UNIT_SECONDS.rel_value) + tv.rel_value = (uint64_t) timeout; + 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); + + 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; +} + + +#if BUILD_HTTPS +/** + * Load ssl certificate from file + * + * @param file filename + * @return content of the file + */ +static char * +server_load_file (const char *file) +{ + struct GNUNET_DISK_FileHandle *gn_file; + uint64_t fsize; + char *text = NULL; + + if (GNUNET_OK != GNUNET_DISK_file_size (file, + &fsize, GNUNET_NO, GNUNET_YES)) + return NULL; + text = GNUNET_malloc (fsize + 1); + gn_file = + GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ, + GNUNET_DISK_PERM_USER_READ); + if (gn_file == NULL) + { + GNUNET_free (text); + return NULL; + } + if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fsize)) + { + GNUNET_free (text); + GNUNET_DISK_file_close (gn_file); + return NULL; + } + text[fsize] = '\0'; + GNUNET_DISK_file_close (gn_file); + return text; +} +#endif + + +#if BUILD_HTTPS +/** + * Load ssl certificate + * + * @param plugin the plugin + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ +static int +server_load_certificate (struct HTTP_Server_Plugin *plugin) +{ + int res = GNUNET_OK; + + char *sh; + char *key_file; + char *cert_file; + + /* Get crypto init string from config + * If not present just use default values */ + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, + "PATHS", + "SERVICEHOME", + &sh)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + "Failed to get servicehome!\n"); + return GNUNET_SYSERR; + } + + + if (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, + 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); + else + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Using default crypto init string \n"); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, + "KEY_FILE", &key_file)) + { + GNUNET_break (0); + GNUNET_asprintf (&key_file, "%s/%s", sh, "https_key.key"); + } + + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, + "CERT_FILE", &cert_file)) + { + GNUNET_break (0); + GNUNET_asprintf (&cert_file, "%s/%s", sh, "https_cert.crt"); + } + GNUNET_free (sh); + /* read key & certificates from file */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Trying to loading TLS certificate from key-file `%s' cert-file`%s'\n", + key_file, cert_file); + + plugin->key = server_load_file (key_file); + plugin->cert = server_load_file (cert_file); + + if ((plugin->key == NULL) || (plugin->cert == NULL)) + { + struct GNUNET_OS_Process *cert_creation; + + GNUNET_free_non_null (plugin->key); + plugin->key = NULL; + GNUNET_free_non_null (plugin->cert); + plugin->cert = NULL; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "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-transport-certificate-creation", + "gnunet-transport-certificate-creation", + key_file, cert_file, NULL); + if (cert_creation == NULL) + { + 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")); + GNUNET_free (key_file); + GNUNET_free (cert_file); + + GNUNET_free_non_null (plugin->key); + plugin->key = NULL; + GNUNET_free_non_null (plugin->cert); + plugin->cert = NULL; + GNUNET_free_non_null (plugin->crypto_init); + plugin->crypto_init = NULL; + + return GNUNET_SYSERR; + } + GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation)); + GNUNET_OS_process_destroy (cert_creation); + + plugin->key = server_load_file (key_file); + plugin->cert = server_load_file (cert_file); + } + + if ((plugin->key == NULL) || (plugin->cert == NULL)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + _ + ("No usable TLS certificate found and creating one failed!\n"), + "transport-https"); + GNUNET_free (key_file); + GNUNET_free (cert_file); + + GNUNET_free_non_null (plugin->key); + plugin->key = NULL; + GNUNET_free_non_null (plugin->cert); + plugin->cert = NULL; + GNUNET_free_non_null (plugin->crypto_init); + plugin->crypto_init = NULL; + + return GNUNET_SYSERR; + } + GNUNET_free (key_file); + GNUNET_free (cert_file); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n"); + return res; +} +#endif + + +/** + * Start the HTTP server + * + * @param plugin the plugin handle + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ +static int +server_start (struct HTTP_Server_Plugin *plugin) +{ + unsigned int timeout; + 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"); + return GNUNET_SYSERR; + } +#endif + + +#if MHD_VERSION >= 0x00090E00 + timeout = HTTP_SERVER_NOT_VALIDATED_TIMEOUT.rel_value / 1000; + 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 / 1000; + 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) + { + 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); + } + plugin->server_v6 = NULL; + if (plugin->use_ipv6 == GNUNET_YES) + { + 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->use_ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + "Failed to start %s IPv4 server component on port %u\n", + plugin->name, plugin->port); + return GNUNET_SYSERR; + } + server_reschedule (plugin, plugin->server_v4, GNUNET_NO); + + if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + "Failed to start %s IPv6 server component on port %u\n", + plugin->name, plugin->port); + return GNUNET_SYSERR; + } + server_reschedule (plugin, plugin->server_v6, GNUNET_NO); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "%s server component started on port %u\n", 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 addr the address + * @param addrlen address length + */ +static void +server_add_address (void *cls, int add_remove, const struct sockaddr *addr, + socklen_t addrlen) +{ + struct HTTP_Server_Plugin *plugin = cls; + struct HttpAddressWrapper *w = NULL; + + w = GNUNET_malloc (sizeof (struct HttpAddressWrapper)); + w->addr = http_common_address_from_socket (plugin->protocol, addr, addrlen); + if (NULL == w->addr) + { + GNUNET_free (w); + return; + } + w->addrlen = http_common_address_get_size (w->addr); + + 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, w->addr, w->addrlen)); +#if BUILD_HTTPS + plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "https_client"); +#else + plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "http_client"); +#endif +} -#if MHD_VERSION >= 0x00090E00 - int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); - struct ServerConnection *t = NULL; -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Received %u bytes\n", *upload_data_size); -#endif +/** + * 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 addr the address + * @param addrlen address length + */ +static void +server_remove_address (void *cls, int add_remove, const struct sockaddr *addr, + socklen_t addrlen) +{ + struct HTTP_Server_Plugin *plugin = cls; + struct HttpAddressWrapper *w = plugin->addr_head; + size_t saddr_len; + void * saddr = http_common_address_from_socket (plugin->protocol, addr, addrlen); + if (NULL == saddr) + return; + saddr_len = http_common_address_get_size (saddr); - /* Setting timeouts for other connections */ - if (s->server_recv != NULL) - { - t = s->server_recv; -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Setting timeout for %X to %u sec.\n", t, - to); -#endif - MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - to); - } - if (s->server_send != NULL) - { - t = s->server_send; -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Setting timeout for %X to %u sec.\n", t, - to); -#endif - MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - to); - } - server_reschedule (plugin, GNUNET_NO); -#endif - (*upload_data_size) = 0; - } - else - { -#if DEBUG_HTTP - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Server: %X no inbound bandwidth available! Next read was delayed by %llu ms\n", - s, now.abs_value - s->next_receive.abs_value); -#endif - } - return MHD_YES; - } - else - return MHD_NO; + while (NULL != w) + { + if (GNUNET_YES == http_common_cmp_addresses(w->addr, w->addrlen, saddr, saddr_len)) + break; + w = w->next; } - return res; + 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, w->addr, w->addrlen)); + GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w); +#if BUILD_HTTPS + plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "https_client"); +#else + plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen, "http_client"); +#endif + GNUNET_free (w->addr); + GNUNET_free (w); } + + +/** + * 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 + * the previous (now invalid) one + * @param addr either the previous or the new public IP address + * @param addrlen actual lenght of the address + */ static void -server_disconnect_cb (void *cls, struct MHD_Connection *connection, - void **httpSessionCache) +server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr *addr, + socklen_t addrlen) { - struct ServerConnection *sc = *httpSessionCache; - struct ServerConnection *tc = *httpSessionCache; - struct Session *s = NULL; - struct Session *t = NULL; - struct Plugin *plugin = NULL; + GNUNET_assert (cls != NULL); + struct HTTP_Server_Plugin *plugin = cls; - if (sc == NULL) - return; + 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)); - s = sc->session; - plugin = s->plugin; - if (sc->direction == _SEND) + if (AF_INET == addr->sa_family) { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Server: %X peer `%s' GET on address `%s' disconnected\n", - s->server_send, GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, s->addrlen)); -#endif - s->server_send = NULL; + struct sockaddr_in *s4 = (struct sockaddr_in *) addr; - if (s->server_recv != NULL) + if (GNUNET_NO == plugin->use_ipv4) + return; + + if ((NULL != plugin->server_addr_v4) && + (0 != memcmp (&plugin->server_addr_v4->sin_addr, + &s4->sin_addr, sizeof (struct in_addr)))) { - tc = s->server_recv; - tc->disconnect = GNUNET_YES; -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (sc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Skipping address `%s' (not bindto address)\n", + GNUNET_a2s (addr, addrlen)); + return; } } - if (sc->direction == _RECEIVE) + + if (AF_INET6 == addr->sa_family) { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Server: %X peer `%s' PUT on address `%s' disconnected\n", - s->server_recv, GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, s->addrlen)); -#endif - s->server_recv = NULL; - if (s->server_send != NULL) - { - tc = s->server_send; - tc->disconnect = GNUNET_YES; -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (sc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - } - if (s->msg_tk != NULL) + struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) addr; + if (GNUNET_NO == plugin->use_ipv6) + return; + + if ((NULL != plugin->server_addr_v6) && + (0 != memcmp (&plugin->server_addr_v6->sin6_addr, + &s6->sin6_addr, sizeof (struct in6_addr)))) { - GNUNET_SERVER_mst_destroy (s->msg_tk); - s->msg_tk = NULL; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Skipping address `%s' (not bindto address)\n", + GNUNET_a2s (addr, addrlen)); + return; } } - GNUNET_free (sc); - t = plugin->server_semi_head; - while (t != NULL) + switch (add_remove) { - if (t == s) - { - GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, - plugin->server_semi_tail, s); - GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); - break; - } - t = t->next; + case GNUNET_YES: + server_add_address (cls, add_remove, addr, addrlen); + break; + case GNUNET_NO: + server_remove_address (cls, add_remove, addr, addrlen); + break; } - plugin->cur_connections--; +} - server_reschedule (plugin, GNUNET_NO); - if ((s->server_send == NULL) && (s->server_recv == NULL)) +/** + * Get valid server addresses + * + * @param plugin the plugin handle + * @param serviceName the servicename + * @param cfg configuration handle + * @param addrs addresses + * @param addr_lens address length + * @return number of addresses + */ +static int +server_get_addresses (struct HTTP_Server_Plugin *plugin, + const char *serviceName, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct sockaddr ***addrs, socklen_t ** addr_lens) +{ + int disablev6; + unsigned long long port; + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *pos; + struct addrinfo *next; + unsigned int i; + int resi; + int ret; + struct sockaddr **saddrs; + socklen_t *saddrlens; + char *hostname; + + *addrs = NULL; + *addr_lens = NULL; + + disablev6 = !plugin->use_ipv6; + + port = 0; + if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "PORT")) { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: peer `%s' on address `%s' disconnected\n", - GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, s->addrlen)); -#endif - if (s->msg_tk != NULL) + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_number (cfg, serviceName, + "PORT", &port)); + if (port > 65535) { - GNUNET_SERVER_mst_destroy (s->msg_tk); - s->msg_tk = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Require valid port number for service in configuration!\n")); + return GNUNET_SYSERR; } - - notify_session_end (s->plugin, &s->target, s); } -} + if (0 == port) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name, + "Starting in listen only mode\n"); + return -1; /* listen only */ + } -int -server_disconnect (struct Session *s) -{ - struct Plugin *plugin = s->plugin; - struct Session *t = plugin->head; - while (t != NULL) + if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "BINDTO")) + { + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (cfg, serviceName, + "BINDTO", &hostname)); + } + else + hostname = NULL; + + if (hostname != NULL) { - if (t->inbound == GNUNET_YES) + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Resolving `%s' since that is where `%s' will bind to.\n", + hostname, serviceName); + memset (&hints, 0, sizeof (struct addrinfo)); + if (disablev6) + hints.ai_family = AF_INET; + if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) || + (res == NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to resolve `%s': %s\n"), + hostname, gai_strerror (ret)); + GNUNET_free (hostname); + return GNUNET_SYSERR; + } + next = res; + i = 0; + while (NULL != (pos = next)) + { + next = pos->ai_next; + if ((disablev6) && (pos->ai_family == AF_INET6)) + continue; + i++; + } + if (0 == i) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to find %saddress for `%s'.\n"), + disablev6 ? "IPv4 " : "", hostname); + freeaddrinfo (res); + GNUNET_free (hostname); + return GNUNET_SYSERR; + } + resi = i; + saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); + i = 0; + next = res; + while (NULL != (pos = next)) { - if (t->server_send != NULL) + next = pos->ai_next; + if ((disablev6) && (pos->ai_family == AF_INET6)) + continue; + if ((pos->ai_protocol != IPPROTO_TCP) && (pos->ai_protocol != 0)) + continue; /* not TCP */ + if ((pos->ai_socktype != SOCK_STREAM) && (pos->ai_socktype != 0)) + 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)); + if (pos->ai_family == AF_INET) { - ((struct ServerConnection *) t->server_send)->disconnect = GNUNET_YES; + 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]); + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); } - if (t->server_send != NULL) + else { - ((struct ServerConnection *) t->server_send)->disconnect = GNUNET_YES; + GNUNET_assert (pos->ai_family == AF_INET6); + 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]); + ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); } + i++; } - t = t->next; + GNUNET_free (hostname); + freeaddrinfo (res); + resi = i; } - return GNUNET_OK; -} - -int -server_send (struct Session *s, struct HTTP_Message *msg) -{ - GNUNET_CONTAINER_DLL_insert (s->msg_head, s->msg_tail, msg); - server_reschedule (s->plugin, GNUNET_YES); - return GNUNET_OK; + else + { + /* will bind against everything, just set port */ + if (disablev6) + { + /* V4-only */ + resi = 1; + i = 0; + saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); + + saddrlens[i] = sizeof (struct sockaddr_in); + saddrs[i] = GNUNET_malloc (saddrlens[i]); +#if HAVE_SOCKADDR_IN_SIN_LEN + ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i]; +#endif + ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); + } + else + { + /* dual stack */ + resi = 2; + saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); + saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); + i = 0; + saddrlens[i] = sizeof (struct sockaddr_in6); + saddrs[i] = GNUNET_malloc (saddrlens[i]); +#if HAVE_SOCKADDR_IN_SIN_LEN + ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0]; +#endif + ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6; + ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); + i++; + saddrlens[i] = sizeof (struct sockaddr_in); + saddrs[i] = GNUNET_malloc (saddrlens[i]); +#if HAVE_SOCKADDR_IN_SIN_LEN + ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1]; +#endif + ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; + ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); + } + } + *addrs = saddrs; + *addr_lens = saddrlens; + return resi; } - /** - * 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 + * Ask NAT for addresses + * + * @param plugin the plugin handle */ static void -server_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +server_start_report_addresses (struct HTTP_Server_Plugin *plugin) { - struct Plugin *plugin = cls; - - GNUNET_assert (cls != NULL); + int res = GNUNET_OK; + struct sockaddr **addrs; + socklen_t *addrlens; - plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; + res = server_get_addresses (plugin, + 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); - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + if (GNUNET_SYSERR == res) + { + plugin->nat = NULL; return; -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Running IPv4 server\n"); -#endif - GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4)); - if (plugin->server_v4 != NULL) - plugin->server_v4_task = - server_schedule (plugin, plugin->server_v4, GNUNET_NO); + } + + plugin->nat = + GNUNET_NAT_register (plugin->env->cfg, GNUNET_YES, plugin->port, + (unsigned int) res, + (const struct sockaddr **) addrs, addrlens, + &server_nat_port_map_callback, NULL, plugin); + while (res > 0) + { + res--; + GNUNET_assert (addrs[res] != NULL); + GNUNET_free (addrs[res]); + } + GNUNET_free_non_null (addrs); + GNUNET_free_non_null (addrlens); } /** - * 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 + * Stop NAT for addresses + * + * @param plugin the plugin handle */ static void -server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +server_stop_report_addresses (struct HTTP_Server_Plugin *plugin) { - struct Plugin *plugin = cls; - - GNUNET_assert (cls != NULL); + /* Stop NAT handle */ + if (NULL != plugin->nat) + GNUNET_NAT_unregister (plugin->nat); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; + /* Clean up addresses */ + struct HttpAddressWrapper *w; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Running IPv6 server\n"); -#endif - GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6)); - if (plugin->server_v6 != NULL) - plugin->server_v6_task = - server_schedule (plugin, plugin->server_v6, GNUNET_NO); + while (plugin->addr_head != NULL) + { + w = plugin->addr_head; + GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w); + GNUNET_free (w->addr); + GNUNET_free (w); + } } + /** - * Function that queries MHD's select sets and - * starts the task waiting for them. - * @param plugin plugin - * @param daemon_handle the MHD daemon handle - * @return gnunet task identifier + * Check if IPv6 supported on this system + * + * @param plugin the plugin handle + * @return GNUNET_YES on success, else GNUNET_NO */ -static GNUNET_SCHEDULER_TaskIdentifier -server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle, - int now) +static int +server_check_ipv6_support (struct HTTP_Server_Plugin *plugin) { - 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 long long timeout; - static unsigned long long last_timeout = 0; - int haveto; - - struct GNUNET_TIME_Relative tv; + struct GNUNET_NETWORK_Handle *desc = NULL; + int res = GNUNET_NO; - 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) + /* Probe IPv6 support */ + desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); + if (NULL == desc) { - if (timeout != last_timeout) + if ((errno == ENOBUFS) || (errno == ENOMEM) || (errno == ENFILE) || + (errno == EACCES)) { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "SELECT Timeout changed from %llu to %llu\n", - last_timeout, timeout); -#endif - last_timeout = timeout; + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket"); } - tv.rel_value = (uint64_t) timeout; + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name, + _ + ("Disabling IPv6 since it is not supported on this system!\n")); + res = GNUNET_NO; } 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); - - 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 VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Scheduling IPv4 server task in %llu ms\n", tv); -#endif - ret = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - GNUNET_SCHEDULER_NO_TASK, 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 VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Scheduling IPv6 server task in %llu ms\n", tv); -#endif - ret = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws, - &server_v6_run, plugin); + GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); + desc = NULL; + res = GNUNET_YES; } - GNUNET_NETWORK_fdset_destroy (wrs); - GNUNET_NETWORK_fdset_destroy (wws); - GNUNET_NETWORK_fdset_destroy (wes); - return ret; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Testing IPv6 on this system: %s\n", + (res == GNUNET_YES) ? "successful" : "failed"); + return res; } -int -server_start (struct Plugin *plugin) + +/** + * Function called when the service shuts down. Unloads our plugins + * and cancels pending validations. + * + * @param cls closure, unused + * @param tc task context (unused) + */ +static void +server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { - int res = GNUNET_OK; - unsigned int timeout; + struct HTTP_Server_Plugin *plugin = cls; -#if BUILD_HTTPS - res = server_load_certificate (plugin); - if (res == GNUNET_SYSERR) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Could not load or create server certificate! Loading plugin failed!\n"); - return res; - } -#endif + plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; -#if MHD_VERSION >= 0x00090E00 - timeout = GNUNET_CONSTANTS_DISCONNECT_SESSION_TIMEOUT.rel_value / 1000; + GNUNET_asprintf(&plugin->ext_addr, "%s://%s", plugin->protocol, plugin->external_hostname); + plugin->ext_addr_len = strlen (plugin->ext_addr) + 1; GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "MHD can set timeout per connection! Default time out %u sec.\n", - timeout); + "Notifying transport about external hostname address `%s'\n", plugin->ext_addr); + +#if BUILD_HTTPS + plugin->env->notify_address (plugin->env->cls, GNUNET_YES, + plugin->ext_addr, plugin->ext_addr_len, + "https_client"); #else - timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000; - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name, - "MHD cannot set timeout per connection! Default time out %u sec.\n", - timeout); + plugin->env->notify_address (plugin->env->cls, GNUNET_YES, + plugin->ext_addr, plugin->ext_addr_len, + "http_client"); #endif - plugin->server_v4 = NULL; - if (plugin->ipv4 == GNUNET_YES) +} + + +/** + * Configure the plugin + * + * @param plugin plugin handle + * @return GNUNET_OK on success, GNUNET_SYSERR on failure + */ +static int +server_configure_plugin (struct HTTP_Server_Plugin *plugin) +{ + unsigned long long port; + unsigned long long max_connections; + char *bind4_address = NULL; + char *bind6_address = NULL; + + /* Use IPv4? */ + if (GNUNET_CONFIGURATION_have_value + (plugin->env->cfg, plugin->name, "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); + plugin->use_ipv4 = + GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name, + "USE_IPv4"); } - plugin->server_v6 = NULL; - if (plugin->ipv6 == GNUNET_YES) - { - 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); + 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"); + /* 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, + "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"); - if ((plugin->ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL)) + if ((plugin->use_ipv4 == GNUNET_NO) && (plugin->use_ipv6 == GNUNET_NO)) { GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Failed to start %s IPv4 server component on port %u\n", - plugin->name, plugin->port); + _ + ("Neither IPv4 nor IPv6 are enabled! Fix in configuration\n"), + plugin->name); return GNUNET_SYSERR; } - if ((plugin->ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL)) + + /* Reading port number from config file */ + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg, plugin->name, + "PORT", &port)) || (port > 65535)) { GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Failed to start %s IPv6 server component on port %u\n", - plugin->name, plugin->port); + _("Port is required! Fix in configuration\n"), + plugin->name); return GNUNET_SYSERR; } + plugin->port = port; + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 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, + &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); + GNUNET_free (bind4_address); + GNUNET_free (plugin->server_addr_v4); + plugin->server_addr_v4 = NULL; + return GNUNET_SYSERR; + } + else + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + _("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); + } + GNUNET_free (bind4_address); + } + + 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)); + if (1 != + 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); + GNUNET_free (bind6_address); + GNUNET_free (plugin->server_addr_v6); + plugin->server_addr_v6 = NULL; + return GNUNET_SYSERR; + } + else + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + _("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); + } + GNUNET_free (bind6_address); + } - server_reschedule (plugin, GNUNET_NO); + if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name, + "EXTERNAL_HOSTNAME", &plugin->external_hostname)) + { + char * tmp = NULL; + if (NULL != strstr(plugin->external_hostname, "://")) + { + tmp = strdup(&strstr(plugin->external_hostname, "://")[3]); + GNUNET_free (plugin->external_hostname); + plugin->external_hostname = tmp; + + } + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + _("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); + } + else + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "No external hostname configured\n"); + + /* Optional parameters */ + 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; -#if DEBUG_HTTP GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "%s server component started on port %u\n", plugin->name, - plugin->port); -#endif - return res; + _("Maximum number of connections is %u\n"), + plugin->max_connections); + + + plugin->peer_id_length = strlen (GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey)); + + return GNUNET_OK; } -void -server_stop (struct 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) { - struct Session *s = NULL; - struct Session *t = NULL; + GNUNET_assert (NULL != cls); + struct Session *s = cls; -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "server_stop\n"); -#endif + s->timeout_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_log (TIMEOUT_LOG, + "Session %p was idle for %llu ms, disconnecting\n", + s, (unsigned long long) SERVER_SESSION_TIMEOUT.rel_value); - struct MHD_Daemon *server_v4_tmp = plugin->server_v4; + /* call session destroy function */ + GNUNET_assert (GNUNET_OK == server_disconnect (s)); +} - plugin->server_v4 = NULL; - struct MHD_Daemon *server_v6_tmp = plugin->server_v6; - plugin->server_v6 = NULL; +/** +* 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 %llu ms\n", + s, (unsigned long long) SERVER_SESSION_TIMEOUT.rel_value); +} - 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) +/** +* 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 %llu ms\n", + s, (unsigned long long) SERVER_SESSION_TIMEOUT.rel_value); +} + + +/** + * Exit point from the plugin. + * + * @param cls api + * @return NULL + */ +void * +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) { - GNUNET_SCHEDULER_cancel (plugin->server_v6_task); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; + /* Free for stub mode */ + GNUNET_free (api); + return NULL; } + plugin->in_shutdown = GNUNET_YES; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + _("Shutting down plugin `%s'\n"), + plugin->name); - if (server_v6_tmp != NULL) + if (GNUNET_SCHEDULER_NO_TASK != plugin->notify_ext_task) { - MHD_stop_daemon (server_v4_tmp); + GNUNET_SCHEDULER_cancel (plugin->notify_ext_task); + plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK; } - if (server_v6_tmp != NULL) + + if (NULL != plugin->ext_addr) { - MHD_stop_daemon (server_v6_tmp); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Notifying transport to remove address `%s'\n", + http_common_plugin_address_to_string (NULL, + plugin->ext_addr, + plugin->ext_addr_len)); +#if BUILD_HTTPS + plugin->env->notify_address (plugin->env->cls, + GNUNET_NO, + plugin->ext_addr, + plugin->ext_addr_len, + "https_client"); +#else + plugin->env->notify_address (plugin->env->cls, + GNUNET_NO, + plugin->ext_addr, + plugin->ext_addr_len, + "http_client"); +#endif + } - /* cleaning up semi-sessions never propagated */ - s = plugin->server_semi_head; - while (s != NULL) + /* Stop to report addresses to transport service */ + server_stop_report_addresses (plugin); + server_stop (plugin); + next = plugin->head; + while (NULL != (pos = next)) { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Deleting semi-sessions %p\n", s); -#endif - t = s->next; - struct HTTP_Message * msg = s->msg_head; - struct HTTP_Message * tmp = s->msg_head; - while (msg != NULL) - { - tmp = msg->next; + next = pos->next; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Removing left over session %p\n", pos); - GNUNET_CONTAINER_DLL_remove(s->msg_head,s->msg_tail, msg); - if (msg->transmit_cont != NULL) + if ((GNUNET_YES == pos->session_passed) && (GNUNET_NO == pos->session_ended)) { - msg->transmit_cont(msg->transmit_cont_cls, &s->target, GNUNET_SYSERR); + /* Notify transport immediately that this session is invalid */ + pos->session_ended = GNUNET_YES; + plugin->env->session_end (plugin->env->cls, &pos->target, pos); } - GNUNET_free (msg); - msg = tmp; - } - delete_session (s); - s = t; + server_delete_session (pos); } -#if BUILD_HTTPS - GNUNET_free_non_null (plugin->crypto_init); - GNUNET_free_non_null (plugin->cert); - GNUNET_free_non_null (plugin->key); -#endif + /* 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); -#if DEBUG_HTTP GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "%s server component stopped\n", plugin->name); -#endif + _("Shutdown for plugin `%s' complete\n"), + plugin->name); + + GNUNET_free (plugin); + GNUNET_free (api); + return NULL; } +/** + * Entry point for the plugin. + * + * @param cls env + * @return api + */ +void * +LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) +{ + struct GNUNET_TRANSPORT_PluginEnvironment *env = 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->cls = NULL; + api->address_to_string = &http_common_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; + } + + api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); + api->cls = plugin; + api->send = &http_server_plugin_send; + api->disconnect = &http_server_plugin_disconnect; + api->check_address = &http_server_plugin_address_suggested; + api->get_session = &http_server_plugin_get_session; + + api->address_to_string = &http_common_plugin_address_to_string; + api->string_to_address = &http_common_plugin_string_to_address; + api->address_pretty_printer = &http_common_plugin_address_pretty_printer; + +#if BUILD_HTTPS + plugin->name = "transport-https_server"; + plugin->protocol = "https"; +#else + plugin->name = "transport-http_server"; + plugin->protocol = "http"; +#endif + + /* Configure plugin */ + if (GNUNET_SYSERR == server_configure_plugin (plugin)) + { + LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); + return NULL; + } + + /* Check IPv6 support */ + if (GNUNET_YES == plugin->use_ipv6) + plugin->use_ipv6 = server_check_ipv6_support (plugin); + + /* Report addresses to transport service */ + if (GNUNET_NO == plugin->external_only) + server_start_report_addresses (plugin); + + if (GNUNET_SYSERR == server_start (plugin)) + { + LIBGNUNET_PLUGIN_TRANSPORT_DONE (api); + return NULL; + } + + return api; +} -/* end of plugin_transport_http.c */ +/* end of plugin_transport_http_server.c */