X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fplugin_transport_http_server.c;h=a8731907eafe7f3892a6b6ead986bcdde748e2f2;hb=a03f3a1884c6f423cde604ba5b0bba86f43a7113;hp=e3cd735599e59fa7969dd2611653c88954372d1d;hpb=7ce8b45c6220fef72b572ea33d077397119f1754;p=oweals%2Fgnunet.git diff --git a/src/transport/plugin_transport_http_server.c b/src/transport/plugin_transport_http_server.c index e3cd73559..a8731907e 100644 --- a/src/transport/plugin_transport_http_server.c +++ b/src/transport/plugin_transport_http_server.c @@ -25,15 +25,10 @@ */ #include "platform.h" -#include "gnunet_protocols.h" -#include "gnunet_connection_lib.h" +#include "gnunet_common.h" #include "gnunet_server_lib.h" -#include "gnunet_service_lib.h" #include "gnunet_statistics_service.h" -#include "gnunet_transport_service.h" #include "gnunet_transport_plugin.h" - -#include "gnunet_container_lib.h" #include "gnunet_nat_lib.h" #include "plugin_transport_http_common.h" #include "microhttpd.h" @@ -46,18 +41,6 @@ #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_server_done #endif -#define HTTP_NOT_VALIDATED_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15) - -#define TESTING GNUNET_NO - -#if TESTING -#define TIMEOUT_LOG GNUNET_ERROR_TYPE_ERROR -#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) -#else -#define TIMEOUT_LOG GNUNET_ERROR_TYPE_DEBUG -#define TIMEOUT GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT -#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 @@ -67,6 +50,7 @@ */ struct Plugin; + /** * Session handle for connections. */ @@ -123,14 +107,10 @@ struct Session */ void *addr; - size_t addrlen; - /** - * Inbound or outbound connection - * Outbound: GNUNET_NO (client is used to send and receive) - * Inbound : GNUNET_YES (server is used to send and receive) + * Address length */ - int inbound; + size_t addrlen; /** * Unique HTTP/S connection tag for this connection @@ -142,6 +122,16 @@ struct Session */ 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 @@ -154,6 +144,7 @@ struct Session GNUNET_SCHEDULER_TaskIdentifier timeout_task; }; + struct ServerConnection { /* _RECV or _SEND */ @@ -162,6 +153,9 @@ struct ServerConnection /* 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; @@ -172,6 +166,7 @@ struct ServerConnection struct MHD_Daemon *mhd_daemon; }; + /** * Encapsulation of all of the state of the plugin. */ @@ -220,12 +215,27 @@ struct HTTP_Server_Plugin */ 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 */ @@ -256,15 +266,6 @@ struct HTTP_Server_Plugin */ struct GNUNET_NAT_Handle *nat; - /** - * Server semi connections - * A full session consists of 2 semi-connections: send and receive - * If not both directions are established the server keeps this sessions here - */ - struct Session *server_semi_head; - - struct Session *server_semi_tail; - /** * List of own addresses */ @@ -344,6 +345,7 @@ struct HTTP_Server_Plugin }; + /** * Wrapper to manage addresses */ @@ -364,6 +366,7 @@ struct HttpAddressWrapper size_t addrlen; }; + /** * Message to send using http */ @@ -394,6 +397,11 @@ struct HTTP_Message */ 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 @@ -408,26 +416,67 @@ struct HTTP_Message }; +/** + * The http_server plugin handle + */ static struct HTTP_Server_Plugin * p; + /** - * Start session timeout + * Start session timeout for session s + * @param s the session */ static void server_start_session_timeout (struct Session *s); + /** - * Increment session timeout due to activity + * Increment session timeout due to activity for session s + * @param s the session */ static void server_reschedule_session_timeout (struct Session *s); + /** - * Cancel timeout + * Cancel timeout for session s + * @param s the session */ static void server_stop_session_timeout (struct Session *s); + +/** + * Disconnect a session s + * @param s the session + */ +static int +server_disconnect (struct Session *s); + + +/** + * 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); + + +/** + * 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); + + /** * Function that can be used by the transport service to transmit * a message using the plugin. Note that in the case of a @@ -464,12 +513,53 @@ http_server_plugin_send (void *cls, GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls) { struct HTTP_Server_Plugin *plugin = cls; + struct HTTP_Message *msg; int bytes_sent = 0; + char *stat_txt; GNUNET_assert (plugin != NULL); GNUNET_assert (session != NULL); - GNUNET_break (0); + if (GNUNET_NO == server_exist_session (plugin, session)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (NULL == session->server_send) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + + if (GNUNET_YES == session->server_send->disconnect) + return GNUNET_SYSERR; + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, session->plugin->name, + "Session %p/connection %p: Sending message with %u to peer `%s' with \n", + session, session->server_send, + msgbuf_size, GNUNET_i2s (&session->target)); + + /* 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; @@ -488,8 +578,27 @@ http_server_plugin_send (void *cls, static void http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target) { - // struct Plugin *plugin = cls; - GNUNET_break (0); + struct HTTP_Server_Plugin *plugin = cls; + struct Session *next = NULL; + struct Session *pos = NULL; + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Transport tells me to disconnect `%s'\n", + GNUNET_i2s (target)); + + next = plugin->head; + while (NULL != (pos = next)) + { + next = pos->next; + if (0 == memcmp (target, &pos->target, sizeof (struct GNUNET_PeerIdentity))) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Disconnecting session %p to `%s'\n", + pos, GNUNET_i2s (target)); + server_disconnect (pos); + } + } + } @@ -506,26 +615,35 @@ http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *targ * and transport */ static int -http_server_plugin_address_suggested (void *cls, const void *addr, size_t addrlen) +http_server_plugin_address_suggested (void *cls, const void *addr, + size_t addrlen) { struct HTTP_Server_Plugin *plugin = cls; - struct HttpAddressWrapper *w = plugin->addr_head; + struct HttpAddressWrapper *next; + struct HttpAddressWrapper *pos; - if ((NULL != plugin->ext_addr) && GNUNET_YES == (http_common_cmp_addresses (addr, addrlen, plugin->ext_addr, plugin->ext_addr_len))) + + if ((NULL != plugin->ext_addr) && + GNUNET_YES == (http_common_cmp_addresses (addr, addrlen, + plugin->ext_addr, plugin->ext_addr_len))) return GNUNET_OK; - while (NULL != w) + next = plugin->addr_head; + while (NULL != (pos = next)) { + next = pos->next; if (GNUNET_YES == (http_common_cmp_addresses(addr, addrlen, - w->addr, - w->addrlen))) + pos->addr, + pos->addrlen))) return GNUNET_OK; + } return GNUNET_NO; } + /** * Creates a new outbound session the transport * service will use to send data to the peer @@ -547,9 +665,10 @@ http_server_plugin_get_session (void *cls, /** * Deleting the session * Must not be used afterwards + * + * @param s the session to delete */ - -void +static void server_delete_session (struct Session *s) { struct HTTP_Server_Plugin *plugin = s->plugin; @@ -566,7 +685,8 @@ server_delete_session (struct Session *s) GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); if (msg->transmit_cont != NULL) { - msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR); + msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR, + msg->size, msg->pos + msg->overhead); } GNUNET_free (msg); msg = tmp; @@ -581,60 +701,16 @@ server_delete_session (struct Session *s) GNUNET_free_non_null (s->server_recv); GNUNET_free_non_null (s->server_send); GNUNET_free (s); -} - -int -server_disconnect (struct Session *s) -{ - struct ServerConnection * send; - struct ServerConnection * recv; - - send = (struct ServerConnection *) s->server_send; - if (s->server_send != NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Server: %p / %p Terminating inbound PUT session to peer `%s'\n", - s, s->server_send, GNUNET_i2s (&s->target)); - - send->disconnect = GNUNET_YES; -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - } - - recv = (struct ServerConnection *) s->server_recv; - if (recv != NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Server: %p / %p Terminating inbound GET session to peer `%s'\n", - s, s->server_recv, GNUNET_i2s (&s->target)); - - recv->disconnect = GNUNET_YES; -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - } - - /* Schedule connection immediately */ -#if 0 - if (s->addrlen == sizeof (struct IPv4HttpAddress)) - { - server_reschedule (s->plugin, s->plugin->server_v4, GNUNET_YES); - } - else if (s->addrlen == sizeof (struct IPv6HttpAddress)) - { - server_reschedule (s->plugin, s->plugin->server_v6, GNUNET_YES); - } -#endif - return GNUNET_OK; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Session %p destroyed\n", s); } /** -* Cancel timeout +* Cancel timeout for session s +* +* @param s the session */ static void server_stop_session_timeout (struct Session *s) @@ -649,17 +725,21 @@ server_stop_session_timeout (struct Session *s) } } + /** * 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, +server_schedule (struct HTTP_Server_Plugin *plugin, + struct MHD_Daemon *daemon_handle, int now); + /** * Reschedule the execution of both IPv4 and IPv6 server * @param plugin the plugin @@ -668,7 +748,8 @@ server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_ha * until timeout */ static void -server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, int now) +server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, + int now) { if ((server == plugin->server_v4) && (plugin->server_v4 != NULL)) { @@ -704,123 +785,248 @@ server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, } -static struct ServerConnection * -server_lookup_connection (struct HTTP_Server_Plugin *plugin, - struct MHD_Connection *mhd_connection, const char *url, - const char *method) +/** + * Disconnect session s + * + * @param s the session + * @return GNUNET_OK on success + */ +static int +server_disconnect (struct Session *s) { - struct Session *s = NULL; - struct Session *t; - struct ServerConnection *sc = NULL; - const union MHD_ConnectionInfo *conn_info; - struct GNUNET_ATS_Information ats; + struct ServerConnection * send = NULL; + struct ServerConnection * recv = NULL; - char *addr; - size_t addr_len; + if (GNUNET_NO == server_exist_session (p, s)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } - struct GNUNET_PeerIdentity target; - uint32_t tag = 0; - int direction = GNUNET_SYSERR; + send = (struct ServerConnection *) s->server_send; + if (s->server_send != NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, + "Server: %p / %p Terminating inbound PUT session to peer `%s'\n", + s, s->server_send, GNUNET_i2s (&s->target)); - /* url parsing variables */ - size_t url_len; - char *url_end; - char *hash_start; - char *hash_end; - char *tag_start; - char *tag_end; + send->disconnect = GNUNET_YES; +#if MHD_VERSION >= 0x00090E00 + MHD_set_connection_option (send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, + 1); +#endif + server_reschedule (s->plugin, send->mhd_daemon, GNUNET_YES); + } - 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; + recv = (struct ServerConnection *) s->server_recv; + if (recv != NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, + "Server: %p / %p Terminating inbound GET session to peer `%s'\n", + s, s->server_recv, GNUNET_i2s (&s->target)); + + recv->disconnect = GNUNET_YES; +#if MHD_VERSION >= 0x00090E00 + MHD_set_connection_option (recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, + 1); +#endif + server_reschedule (s->plugin, recv->mhd_daemon, GNUNET_YES); + } + return GNUNET_OK; +} + +static void +server_mhd_connection_timeout (struct HTTP_Server_Plugin *plugin, struct Session *s, int to) +{ +#if MHD_VERSION >= 0x00090E00 + /* 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 +} + +/** + * 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_parse_url (struct HTTP_Server_Plugin *plugin, const char * url, struct GNUNET_PeerIdentity * target, uint32_t *tag) +{ + int debug = GNUNET_YES; + + 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; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "New %s connection from %s\n", method, url); /* URL parsing - * URL is valid if it is in the form [peerid[103];tag]*/ - url_len = strlen (url); - url_end = (char *) &url[url_len]; + * URL is valid if it is in the form [prefix with (multiple) '/'][peerid[103];tag]*/ - if (url_len < 105) - { - GNUNET_break (0); - goto error; /* too short */ - } - hash_start = strrchr (url, '/'); - if (NULL == hash_start) + if (NULL == url) { GNUNET_break (0); - goto error; /* '/' delimiter not found */ + return GNUNET_SYSERR; } - if (hash_start >= url_end) + /* convert tag */ + + /* find separator */ + separator = strrchr (url, ';'); + + if (NULL == separator) { - GNUNET_break (0); - goto error; /* mal formed */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - hash_start++; + tag_start = separator + 1; - hash_end = strrchr (hash_start, ';'); - if (NULL == hash_end) - goto error; /* ';' delimiter not found */ - if (hash_end >= url_end) + if (strlen (tag_start) == 0) { - GNUNET_break (0); - goto error; /* mal formed */ + /* No tag after separator */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - - if (hash_start >= hash_end) + ctag = strtoul (tag_start, &tag_end, 10); + if (ctag == 0) { - GNUNET_break (0); - goto error; /* mal formed */ + /* tag == 0 , invalid */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - - if ((strlen(hash_start) - strlen(hash_end)) != 103) + if ((ctag == ULONG_MAX) && (ERANGE == errno)) { - GNUNET_break (0); - goto error; /* invalid hash length */ + /* out of range: > ULONG_MAX */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - - char hash[104]; - memcpy (hash, hash_start, 103); - hash[103] = '\0'; - if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target.hashPubKey))) + if (ctag > UINT32_MAX) { - GNUNET_break (0); - goto error; /* mal formed */ + /* out of range: > UINT32_MAX */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - - if (hash_end >= url_end) + (*tag) = (uint32_t) ctag; + if (NULL == tag_end) { - GNUNET_break (0); - goto error; /* mal formed */ + /* 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)); - tag_start = &hash_end[1]; - /* Converting tag */ - tag_end = NULL; - tag = strtoul (tag_start, &tag_end, 10); - if (tag == 0) + /* convert peer id */ + target_start = strrchr (url, '/'); + if (NULL == target_start) { - goto error; /* mal formed */ + /* no leading '/' */ + target_start = (char *) url; } - if (tag_end == NULL) + target_start++; + hash_length = separator - target_start; + if (hash_length != plugin->peer_id_length) { - goto error; /* mal formed */ + /* no char after tag */ + if (debug) GNUNET_break (0); + return GNUNET_SYSERR; } - if (tag_end != url_end) + memcpy (hash, target_start, hash_length); + hash[hash_length] = '\0'; + + if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target->hashPubKey))) { - goto error; /* mal formed */ + /* 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 { - goto error; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Invalid method %s connection from %s\n", method, url); + return NULL; } plugin->cur_connections++; @@ -829,162 +1035,117 @@ server_lookup_connection (struct HTTP_Server_Plugin *plugin, method, GNUNET_i2s (&target), tag, plugin->cur_connections, plugin->max_connections); - /* 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)) + s = plugin->head; + while (s != NULL) + { + if ((0 == memcmp (&s->target, &target, sizeof (struct GNUNET_PeerIdentity))) && + (s->tag == tag)) break; - t = t->next; + s = s->next; } - if (t != NULL) + if (s != NULL) { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Duplicate session, dismissing new connection from peer `%s'\n", - GNUNET_i2s (&target)); - goto error; - } - - /* find semi-session */ - t = plugin->server_semi_head; + 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; - while (t != NULL) - { - /* FIXME add source address comparison */ - if ((0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) - && (t->tag == tag)) + } + if ((_SEND == direction) && (NULL != s->server_send)) { - break; + 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; } - t = t->next; - } - - if (t == NULL) - goto create; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Found existing semi-session for `%s'\n", - GNUNET_i2s (&target)); - - if ((direction == _SEND) && (t->server_send != NULL)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Duplicate GET session, dismissing new connection from peer `%s'\n", - GNUNET_i2s (&target)); - goto error; } 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); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Found matching semi-session, merging session for peer `%s'\n", - GNUNET_i2s (&target)); - GNUNET_assert (NULL != s); - goto found; - } - if ((direction == _RECEIVE) && (t->server_recv != NULL)) - { + /* 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, - "Duplicate PUT session, dismissing new connection from peer `%s'\n", - GNUNET_i2s (&target)); - goto error; - } - else - { - s = t; - GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, - plugin->server_semi_tail, s); + "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); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Found matching semi-session, merging session for peer `%s'\n", - GNUNET_i2s (&target)); - GNUNET_assert (NULL != s); - goto found; - } - -create: -/* 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); - goto error; } - - 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->inbound = GNUNET_YES; - s->next_receive = GNUNET_TIME_UNIT_ZERO_ABS; - s->tag = tag; - s->server_recv = NULL; - s->server_send = NULL; - - server_start_session_timeout(s); - - GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head, - plugin->server_semi_tail, s); - goto found; -error: - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Invalid connection request\n"); - return NULL; - -found: - 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 - int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); + 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); - MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to); - - server_reschedule (plugin, sc->mhd_daemon, GNUNET_NO); #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) @@ -994,9 +1155,6 @@ server_lookup_session (struct HTTP_Server_Plugin *plugin, for (s = plugin->head; NULL != s; s = s->next) if ((s->server_recv == sc) || (s->server_send == sc)) return s; - for (s = plugin->server_semi_head; NULL != s; s = s->next) - if ((s->server_recv == sc) || (s->server_send == sc)) - return s; return NULL; } @@ -1019,6 +1177,7 @@ server_exist_session (struct HTTP_Server_Plugin *plugin, struct Session *s) /** * 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 @@ -1031,6 +1190,7 @@ 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)) @@ -1049,20 +1209,35 @@ server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) { 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->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK, + msg->size, msg->size + msg->overhead); GNUNET_free (msg); } } - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Sent %u bytes\n", s, bytes_read); + 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 clien + * @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, @@ -1071,6 +1246,7 @@ server_receive_mst_cb (void *cls, void *client, 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)) @@ -1084,18 +1260,24 @@ server_receive_mst_cb (void *cls, void *client, 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); - s->next_receive = - GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay); + 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, - "Server: peer `%s' address `%s' next read delayed for %llu ms\n", + "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); @@ -1105,6 +1287,19 @@ server_receive_mst_cb (void *cls, void *client, } +/** + * 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, @@ -1118,13 +1313,21 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, 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); @@ -1146,27 +1349,19 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, /* 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 PUT Response\ */ - response = - MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!", + /* 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); - /* Check if both directions are connected */ - if ((sc->session->server_recv == NULL) || (sc->session->server_send == NULL)) - { - /* Delayed read from since not both semi-connections are connected */ - return MHD_YES; - } if (sc->direction == _SEND) { @@ -1180,22 +1375,44 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, } if (sc->direction == _RECEIVE) { - if (*upload_data_size == 0) + 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, - "Peer `%s' PUT on address `%s' connected\n", + "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; } - - /* Receiving data */ - if ((*upload_data_size > 0)) + 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, - "Peer `%s' PUT on address `%s' received %u bytes\n", + "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, @@ -1206,7 +1423,7 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, if ((s->next_receive.abs_value <= now.abs_value)) { GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "PUT with %u bytes forwarded to MST\n", s, + "PUT with %u bytes forwarded to MST\n", *upload_data_size); if (s->msg_tk == NULL) { @@ -1214,64 +1431,133 @@ server_access_cb (void *cls, struct MHD_Connection *mhd_connection, } GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data, *upload_data_size, GNUNET_NO, GNUNET_NO); - #if MHD_VERSION >= 0x00090E00 - int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); - struct ServerConnection *t = NULL; - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Received %u bytes\n", *upload_data_size); - /* Setting timeouts for other connections */ - if (s->server_recv != NULL) - { - t = s->server_recv; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Setting timeout for %p to %u sec.\n", t, - to); - MHD_set_connection_option (t->mhd_conn, - MHD_CONNECTION_OPTION_TIMEOUT, - to); - server_reschedule (plugin, t->mhd_daemon, GNUNET_NO); - } - if (s->server_send != NULL) - { - t = s->server_send; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Setting timeout for %p to %u sec.\n", t, - to); - MHD_set_connection_option (t->mhd_conn, - MHD_CONNECTION_OPTION_TIMEOUT, - to); - server_reschedule (plugin, t->mhd_daemon, GNUNET_NO); - } + 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, - "Server: %p no inbound bandwidth available! Next read was delayed by %llu ms\n", - s, now.abs_value - s->next_receive.abs_value); + "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) { - /* FIXME SPLIT */ - GNUNET_break (0); + 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. - * 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 @@ -1284,7 +1570,13 @@ 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, @@ -1356,15 +1648,19 @@ server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) } +#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, +server_schedule (struct HTTP_Server_Plugin *plugin, + struct MHD_Daemon *daemon_handle, int now) { GNUNET_SCHEDULER_TaskIdentifier ret; @@ -1375,12 +1671,15 @@ server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_ha struct GNUNET_NETWORK_FDSet *wws; struct GNUNET_NETWORK_FDSet *wes; int max; - unsigned MHD_LONG_LONG timeout; + 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); @@ -1395,14 +1694,16 @@ server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_ha { if (timeout != last_timeout) { -#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; } - tv.rel_value = (uint64_t) 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; @@ -1453,6 +1754,12 @@ server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_ha #if BUILD_HTTPS +/** + * Load ssl certificate from file + * + * @param file filename + * @return content of the file + */ static char * server_load_file (const char *file) { @@ -1486,18 +1793,36 @@ server_load_file (const char *file) #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, @@ -1514,16 +1839,19 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin) GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, "KEY_FILE", &key_file)) { - key_file = GNUNET_strdup ("https_key.key"); + 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_asprintf (&cert_file, "%s", "https_cert.crt"); + 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", @@ -1598,7 +1926,14 @@ server_load_certificate (struct HTTP_Server_Plugin *plugin) } #endif -int + +/** + * 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; @@ -1615,12 +1950,12 @@ server_start (struct HTTP_Server_Plugin *plugin) #if MHD_VERSION >= 0x00090E00 - timeout = HTTP_NOT_VALIDATED_TIMEOUT.rel_value / 1000; + 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 = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000; + 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); @@ -1728,14 +2063,17 @@ server_start (struct HTTP_Server_Plugin *plugin) void server_stop (struct HTTP_Server_Plugin *plugin) { - struct Session *s = NULL; - struct Session *t = NULL; - - struct MHD_Daemon *server_v4_tmp = plugin->server_v4; - plugin->server_v4 = NULL; + 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; + } - struct MHD_Daemon *server_v6_tmp = plugin->server_v6; - plugin->server_v6 = NULL; if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) { @@ -1748,45 +2086,6 @@ server_stop (struct HTTP_Server_Plugin *plugin) GNUNET_SCHEDULER_cancel (plugin->server_v6_task); plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; } - - if (server_v6_tmp != NULL) - { - MHD_stop_daemon (server_v4_tmp); - } - if (server_v6_tmp != NULL) - { - MHD_stop_daemon (server_v6_tmp); - } - - /* cleaning up semi-sessions never propagated */ - s = plugin->server_semi_head; - while (s != NULL) - { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Deleting semi-sessions %p\n", s); -#endif - t = s->next; - struct HTTP_Message *msg = s->msg_head; - struct HTTP_Message *tmp = NULL; - - while (msg != NULL) - { - tmp = msg->next; - - GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); - if (msg->transmit_cont != NULL) - { - msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR); - } - GNUNET_free (msg); - msg = tmp; - } - - server_delete_session (s); - s = t; - } - p = NULL; #if BUILD_HTTPS @@ -1799,6 +2098,15 @@ server_stop (struct HTTP_Server_Plugin *plugin) "%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) @@ -1806,12 +2114,6 @@ server_add_address (void *cls, int add_remove, const struct sockaddr *addr, struct HTTP_Server_Plugin *plugin = cls; struct HttpAddressWrapper *w = NULL; - if ((AF_INET == addr->sa_family) && (GNUNET_NO == plugin->use_ipv4)) - return; - - if ((AF_INET6 == addr->sa_family) && (GNUNET_NO == plugin->use_ipv6)) - return; - w = GNUNET_malloc (sizeof (struct HttpAddressWrapper)); w->addr = http_common_address_from_socket (plugin->protocol, addr, addrlen); if (NULL == w->addr) @@ -1825,11 +2127,22 @@ server_add_address (void *cls, int add_remove, const struct sockaddr *addr, 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)); - - plugin->env->notify_address (plugin->env->cls, add_remove, 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 } +/** + * 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) @@ -1857,7 +2170,11 @@ server_remove_address (void *cls, int add_remove, const struct sockaddr *addr, "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); - plugin->env->notify_address (plugin->env->cls, add_remove, 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 GNUNET_free (w->addr); GNUNET_free (w); } @@ -1881,10 +2198,45 @@ server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr * struct HTTP_Server_Plugin *plugin = cls; GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "NPMC called %s to address `%s'\n", + "NAT called to %s address `%s'\n", (add_remove == GNUNET_NO) ? "remove" : "add", GNUNET_a2s (addr, addrlen)); + if (AF_INET == addr->sa_family) + { + struct sockaddr_in *s4 = (struct sockaddr_in *) addr; + + 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)))) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Skipping address `%s' (not bindto address)\n", + GNUNET_a2s (addr, addrlen)); + return; + } + } + + if (AF_INET6 == addr->sa_family) + { + 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_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Skipping address `%s' (not bindto address)\n", + GNUNET_a2s (addr, addrlen)); + return; + } + } + switch (add_remove) { case GNUNET_YES: @@ -1897,6 +2249,16 @@ server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr * } +/** + * 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, @@ -2074,6 +2436,12 @@ server_get_addresses (struct HTTP_Server_Plugin *plugin, return resi; } + +/** + * Ask NAT for addresses + * + * @param plugin the plugin handle + */ static void server_start_report_addresses (struct HTTP_Server_Plugin *plugin) { @@ -2109,6 +2477,11 @@ server_start_report_addresses (struct HTTP_Server_Plugin *plugin) } +/** + * Stop NAT for addresses + * + * @param plugin the plugin handle + */ static void server_stop_report_addresses (struct HTTP_Server_Plugin *plugin) { @@ -2131,6 +2504,9 @@ server_stop_report_addresses (struct HTTP_Server_Plugin *plugin) /** * Check if IPv6 supported on this system + * + * @param plugin the plugin handle + * @return GNUNET_YES on success, else GNUNET_NO */ static int server_check_ipv6_support (struct HTTP_Server_Plugin *plugin) @@ -2186,10 +2562,25 @@ server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskCo plugin->ext_addr_len = strlen (plugin->ext_addr) + 1; GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, "Notifying transport about external hostname address `%s'\n", plugin->ext_addr); - plugin->env->notify_address (plugin->env->cls, GNUNET_YES, plugin->ext_addr, plugin->ext_addr_len ); + +#if BUILD_HTTPS + plugin->env->notify_address (plugin->env->cls, GNUNET_YES, + plugin->ext_addr, plugin->ext_addr_len, + "https_client"); +#else + plugin->env->notify_address (plugin->env->cls, GNUNET_YES, + plugin->ext_addr, plugin->ext_addr_len, + "http_client"); +#endif } +/** + * 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) { @@ -2314,15 +2705,37 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin) 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, @@ -2333,13 +2746,20 @@ server_configure_plugin (struct HTTP_Server_Plugin *plugin) GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, _("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; } + /** * 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) { @@ -2349,31 +2769,36 @@ server_session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc s->timeout_task = GNUNET_SCHEDULER_NO_TASK; GNUNET_log (TIMEOUT_LOG, "Session %p was idle for %llu ms, disconnecting\n", - s, (unsigned long long) TIMEOUT.rel_value); + s, (unsigned long long) SERVER_SESSION_TIMEOUT.rel_value); /* call session destroy function */ GNUNET_assert (GNUNET_OK == server_disconnect (s)); } + /** -* Start session timeout +* 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 (TIMEOUT, + 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) TIMEOUT.rel_value); + s, (unsigned long long) SERVER_SESSION_TIMEOUT.rel_value); } /** -* Increment session timeout due to activity +* Increment session timeout due to activity session s +* +* @param s the session */ static void server_reschedule_session_timeout (struct Session *s) @@ -2382,22 +2807,39 @@ server_reschedule_session_timeout (struct Session *s) GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task); GNUNET_SCHEDULER_cancel (s->timeout_task); - s->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + 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) TIMEOUT.rel_value); + 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) + { + /* 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 (GNUNET_SCHEDULER_NO_TASK != plugin->notify_ext_task) { @@ -2412,16 +2854,41 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) 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); + 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 + } /* Stop to report addresses to transport service */ server_stop_report_addresses (plugin); - server_stop (plugin); + next = plugin->head; + while (NULL != (pos = next)) + { + next = pos->next; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Removing left over session %p\n", pos); + + if ((GNUNET_YES == pos->session_passed) && (GNUNET_NO == pos->session_ended)) + { + /* Notify transport immediately that this session is invalid */ + pos->session_ended = GNUNET_YES; + plugin->env->session_end (plugin->env->cls, &pos->target, pos); + } + + server_delete_session (pos); + } /* Clean up */ GNUNET_free_non_null (plugin->external_hostname); @@ -2429,6 +2896,10 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) GNUNET_free_non_null (plugin->server_addr_v4); GNUNET_free_non_null (plugin->server_addr_v6); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + _("Shutdown for plugin `%s' complete\n"), + plugin->name); + GNUNET_free (plugin); GNUNET_free (api); return NULL; @@ -2437,6 +2908,9 @@ LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) /** * Entry point for the plugin. + * + * @param cls env + * @return api */ void * LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) @@ -2448,6 +2922,19 @@ LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) 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; @@ -2479,7 +2966,8 @@ LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) plugin->use_ipv6 = server_check_ipv6_support (plugin); /* Report addresses to transport service */ - server_start_report_addresses (plugin); + if (GNUNET_NO == plugin->external_only) + server_start_report_addresses (plugin); if (GNUNET_SYSERR == server_start (plugin)) { @@ -2490,7 +2978,4 @@ LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) return api; } - - - /* end of plugin_transport_http_server.c */