2 This file is part of GNUnet
3 (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file transport/plugin_transport_http_server.c
23 * @brief HTTP/S server transport plugin
24 * @author Matthias Wachs
28 #include "gnunet_protocols.h"
29 #include "gnunet_connection_lib.h"
30 #include "gnunet_server_lib.h"
31 #include "gnunet_service_lib.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_transport_plugin.h"
36 #include "gnunet_container_lib.h"
37 #include "gnunet_nat_lib.h"
38 #include "plugin_transport_http_common.h"
39 #include "microhttpd.h"
42 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_server_init
43 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_server_done
45 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_server_init
46 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_server_done
49 #define HTTP_NOT_VALIDATED_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
51 #define TESTING GNUNET_NO
54 #define TIMEOUT_LOG GNUNET_ERROR_TYPE_ERROR
55 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
57 #define TIMEOUT_LOG GNUNET_ERROR_TYPE_DEBUG
58 #define TIMEOUT GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT
61 #define HTTP_ERROR_RESPONSE "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\"><HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested URL was not found on this server.<P><HR><ADDRESS></ADDRESS></BODY></HTML>"
66 * Encapsulation of all of the state of the plugin.
72 * Session handle for connections.
74 struct HttpServerSession
77 * Stored in a linked list.
79 struct HttpServerSession *next;
82 * Stored in a linked list.
84 struct HttpServerSession *prev;
87 * To whom are we talking to (set to our identity
88 * if we are still waiting for the welcome message)
90 struct GNUNET_PeerIdentity target;
93 * Pointer to the global plugin struct.
95 struct HTTP_Server_Plugin *plugin;
98 * next pointer for double linked list
100 struct HTTP_Message *msg_head;
103 * previous pointer for double linked list
105 struct HTTP_Message *msg_tail;
108 * Message stream tokenizer for incoming data
110 struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk;
115 struct ServerConnection *server_recv;
120 struct ServerConnection *server_send;
130 * Inbound or outbound connection
131 * Outbound: GNUNET_NO (client is used to send and receive)
132 * Inbound : GNUNET_YES (server is used to send and receive)
137 * Unique HTTP/S connection tag for this connection
142 * ATS network type in NBO
144 uint32_t ats_address_network_type;
147 * Absolute time when to receive data again
148 * Used for receive throttling
150 struct GNUNET_TIME_Absolute next_receive;
153 * Session timeout task
155 GNUNET_SCHEDULER_TaskIdentifier timeout_task;
158 struct ServerConnection
163 /* Should this connection get disconnected? GNUNET_YES/NO */
166 /* The session this server connection belongs to */
167 struct HttpServerSession *session;
169 /* The MHD connection */
170 struct MHD_Connection *mhd_conn;
174 * Encapsulation of all of the state of the plugin.
176 struct HTTP_Server_Plugin
181 struct GNUNET_TRANSPORT_PluginEnvironment *env;
184 * Linked list head of open sessions.
187 struct HttpServerSession *head;
190 * Linked list tail of open sessions.
192 struct HttpServerSession *tail;
207 char *external_hostname;
210 * Maximum number of sockets the plugin can use
211 * Each http inbound /outbound connections are two connections
213 unsigned int max_connections;
216 * Current number of sockets the plugin can use
217 * Each http inbound /outbound connections are two connections
219 unsigned int cur_connections;
222 * External hostname the plugin can be connected to, can be different to
223 * the host's FQDN, used e.g. for reverse proxying
228 * External address length
248 * Task calling transport service about external address
250 GNUNET_SCHEDULER_TaskIdentifier notify_ext_task;
253 * NAT handle & address management
255 struct GNUNET_NAT_Handle *nat;
258 * Server semi connections
259 * A full session consists of 2 semi-connections: send and receive
260 * If not both directions are established the server keeps this sessions here
262 struct HttpServerSession *server_semi_head;
264 struct HttpServerSession *server_semi_tail;
267 * List of own addresses
271 * IPv4 addresses DLL head
273 struct HttpAddressWrapper *addr_head;
276 * IPv4 addresses DLL tail
278 struct HttpAddressWrapper *addr_tail;
281 * IPv4 server socket to bind to
283 struct sockaddr_in *server_addr_v4;
286 * IPv6 server socket to bind to
288 struct sockaddr_in6 *server_addr_v6;
293 GNUNET_SCHEDULER_TaskIdentifier server_v4_task;
298 GNUNET_SCHEDULER_TaskIdentifier server_v6_task;
301 * The IPv4 server is scheduled to run asap
303 int server_v4_immediately;
306 * The IPv6 server is scheduled to run asap
308 int server_v6_immediately;
313 struct MHD_Daemon *server_v4;
318 struct MHD_Daemon *server_v6;
326 * Use RC4-128 instead of AES:
327 * NONE:+VERS-TLS1.0:+ARCFOUR-128:+SHA1:+RSA:+COMP-NULL
346 * Wrapper to manage addresses
348 struct HttpAddressWrapper
353 struct HttpAddressWrapper *next;
356 * Linked list previous
358 struct HttpAddressWrapper *prev;
366 * Message to send using http
371 * next pointer for double linked list
373 struct HTTP_Message *next;
376 * previous pointer for double linked list
378 struct HTTP_Message *prev;
381 * buffer containing data to send
386 * amount of data already sent
396 * Continuation function to call once the transmission buffer
397 * has again space available. NULL if there is no
398 * continuation to call.
400 GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
403 * Closure for transmit_cont.
405 void *transmit_cont_cls;
409 static struct Plugin * p;
412 * Start session timeout
415 server_start_session_timeout (struct HttpServerSession *s);
419 * Increment session timeout due to activity
422 server_reschedule_session_timeout (struct HttpServerSession *s);
428 server_stop_session_timeout (struct HttpServerSession *s);
431 * Function that can be used by the transport service to transmit
432 * a message using the plugin. Note that in the case of a
433 * peer disconnecting, the continuation MUST be called
434 * prior to the disconnect notification itself. This function
435 * will be called with this peer's HELLO message to initiate
436 * a fresh connection to another peer.
439 * @param session which session must be used
440 * @param msgbuf the message to transmit
441 * @param msgbuf_size number of bytes in 'msgbuf'
442 * @param priority how important is the message (most plugins will
443 * ignore message priority and just FIFO)
444 * @param to how long to wait at most for the transmission (does not
445 * require plugins to discard the message after the timeout,
446 * just advisory for the desired delay; most plugins will ignore
448 * @param cont continuation to call once the message has
449 * been transmitted (or if the transport is ready
450 * for the next transmission call; or if the
451 * peer disconnected...); can be NULL
452 * @param cont_cls closure for cont
453 * @return number of bytes used (on the physical network, with overheads);
454 * -1 on hard errors (i.e. address invalid); 0 is a legal value
455 * and does NOT mean that the message was not transmitted (DV)
458 http_server_plugin_send (void *cls,
459 struct Session *session,
460 const char *msgbuf, size_t msgbuf_size,
461 unsigned int priority,
462 struct GNUNET_TIME_Relative to,
463 GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
465 struct HTTP_Server_Plugin *plugin = cls;
468 GNUNET_assert (plugin != NULL);
469 GNUNET_assert (session != NULL);
473 /* struct Plugin *plugin = cls; */
480 * Function that can be used to force the plugin to disconnect
481 * from the given peer and cancel all previous transmissions
482 * (and their continuationc).
485 * @param target peer from which to disconnect
488 http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
490 // struct Plugin *plugin = cls;
496 * Another peer has suggested an address for this
497 * peer and transport plugin. Check that this could be a valid
498 * address. If so, consider adding it to the list
502 * @param addr pointer to the address
503 * @param addrlen length of addr
504 * @return GNUNET_OK if this is a plausible address for this peer
508 http_server_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
510 struct HTTP_Server_Plugin *plugin = cls;
511 struct HttpAddressWrapper *w = plugin->addr_head;
513 if ((NULL != plugin->ext_addr) && GNUNET_YES == (http_common_cmp_addresses (addr, addrlen, plugin->ext_addr, plugin->ext_addr_len)))
518 if (GNUNET_YES == (http_common_cmp_addresses(addr,
529 * Creates a new outbound session the transport
530 * service will use to send data to the peer
532 * Since HTTP/S server cannot create sessions, always return NULL
534 * @param cls the plugin
535 * @param address the address
536 * @return always NULL
538 static struct Session *
539 http_server_plugin_get_session (void *cls,
540 const struct GNUNET_HELLO_Address *address)
547 * Deleting the session
548 * Must not be used afterwards
552 server_delete_session (struct HttpServerSession *s)
554 struct HTTP_Server_Plugin *plugin = s->plugin;
555 server_stop_session_timeout(s);
557 GNUNET_CONTAINER_DLL_remove (plugin->head, plugin->tail, s);
558 struct HTTP_Message *msg = s->msg_head;
559 struct HTTP_Message *tmp = NULL;
565 GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
566 if (msg->transmit_cont != NULL)
568 msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
574 if (s->msg_tk != NULL)
576 GNUNET_SERVER_mst_destroy (s->msg_tk);
579 GNUNET_free (s->addr);
580 GNUNET_free_non_null (s->server_recv);
581 GNUNET_free_non_null (s->server_send);
586 server_disconnect (struct HttpServerSession *s)
588 struct ServerConnection * send;
589 struct ServerConnection * recv;
591 send = (struct ServerConnection *) s->server_send;
592 if (s->server_send != NULL)
594 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
595 "Server: %p / %p Terminating inbound PUT session to peer `%s'\n",
596 s, s->server_send, GNUNET_i2s (&s->target));
598 send->disconnect = GNUNET_YES;
599 #if MHD_VERSION >= 0x00090E00
600 MHD_set_connection_option (send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
605 recv = (struct ServerConnection *) s->server_recv;
608 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
609 "Server: %p / %p Terminating inbound GET session to peer `%s'\n",
610 s, s->server_recv, GNUNET_i2s (&s->target));
612 recv->disconnect = GNUNET_YES;
613 #if MHD_VERSION >= 0x00090E00
614 MHD_set_connection_option (recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
619 /* Schedule connection immediately */
621 if (s->addrlen == sizeof (struct IPv4HttpAddress))
623 server_reschedule (s->plugin, s->plugin->server_v4, GNUNET_YES);
625 else if (s->addrlen == sizeof (struct IPv6HttpAddress))
627 server_reschedule (s->plugin, s->plugin->server_v6, GNUNET_YES);
639 server_stop_session_timeout (struct HttpServerSession *s)
641 GNUNET_assert (NULL != s);
643 if (GNUNET_SCHEDULER_NO_TASK != s->timeout_task)
645 GNUNET_SCHEDULER_cancel (s->timeout_task);
646 s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
647 GNUNET_log (TIMEOUT_LOG, "Timeout stopped for session %p\n", s);
652 * Function that queries MHD's select sets and
653 * starts the task waiting for them.
654 * @param plugin plugin
655 * @param daemon_handle the MHD daemon handle
656 * @return gnunet task identifier
658 static GNUNET_SCHEDULER_TaskIdentifier
659 server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_handle,
663 * Reschedule the execution of both IPv4 and IPv6 server
664 * @param plugin the plugin
665 * @param server which server to schedule v4 or v6?
666 * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait
670 server_reschedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *server, int now)
672 if ((server == plugin->server_v4) && (plugin->server_v4 != NULL))
674 if (GNUNET_YES == plugin->server_v4_immediately)
675 return; /* No rescheduling, server will run asap */
677 if (GNUNET_YES == now)
678 plugin->server_v4_immediately = GNUNET_YES;
680 if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
682 GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
683 plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
685 plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now);
688 if ((server == plugin->server_v6) && (plugin->server_v6 != NULL))
690 if (GNUNET_YES == plugin->server_v6_immediately)
691 return; /* No rescheduling, server will run asap */
693 if (GNUNET_YES == now)
694 plugin->server_v6_immediately = GNUNET_YES;
696 if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
698 GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
699 plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
701 plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now);
706 static struct ServerConnection *
707 server_lookup_connection (struct HTTP_Server_Plugin *plugin,
708 struct MHD_Connection *mhd_connection, const char *url,
711 struct HttpServerSession *s = NULL;
712 struct HttpServerSession *t;
713 struct ServerConnection *sc = NULL;
714 const union MHD_ConnectionInfo *conn_info;
715 struct GNUNET_ATS_Information ats;
720 struct GNUNET_PeerIdentity target;
722 int direction = GNUNET_SYSERR;
724 /* url parsing variables */
732 conn_info = MHD_get_connection_info (mhd_connection,
733 MHD_CONNECTION_INFO_CLIENT_ADDRESS);
734 if ((conn_info->client_addr->sa_family != AF_INET) &&
735 (conn_info->client_addr->sa_family != AF_INET6))
738 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
739 "New %s connection from %s\n", method, url);
741 * URL is valid if it is in the form [peerid[103];tag]*/
742 url_len = strlen (url);
743 url_end = (char *) &url[url_len];
747 goto error; /* too short */
749 hash_start = strrchr (url, '/');
750 if (NULL == hash_start)
752 goto error; /* '/' delimiter not found */
754 if (hash_start >= url_end)
756 goto error; /* mal formed */
760 hash_end = strrchr (hash_start, ';');
761 if (NULL == hash_end)
762 goto error; /* ';' delimiter not found */
763 if (hash_end >= url_end)
765 goto error; /* mal formed */
768 if (hash_start >= hash_end)
770 goto error; /* mal formed */
773 if ((strlen(hash_start) - strlen(hash_end)) != 103)
775 goto error; /* invalid hash length */
779 memcpy (hash, hash_start, 103);
781 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target.hashPubKey)))
783 goto error; /* mal formed */
786 if (hash_end >= url_end)
788 goto error; /* mal formed */
791 tag_start = &hash_end[1];
794 tag = strtoul (tag_start, &tag_end, 10);
797 goto error; /* mal formed */
801 goto error; /* mal formed */
803 if (tag_end != url_end)
805 goto error; /* mal formed */
808 if (0 == strcmp (MHD_HTTP_METHOD_PUT, method))
809 direction = _RECEIVE;
810 else if (0 == strcmp (MHD_HTTP_METHOD_GET, method))
817 plugin->cur_connections++;
818 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
819 "New %s connection from %s with tag %u (%u of %u)\n",
821 GNUNET_i2s (&target), tag,
822 plugin->cur_connections, plugin->max_connections);
824 /* find duplicate session */
829 (0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity)))
831 /* FIXME add source address comparison */
838 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
839 "Duplicate session, dismissing new connection from peer `%s'\n",
840 GNUNET_i2s (&target));
844 /* find semi-session */
845 t = plugin->server_semi_head;
849 /* FIXME add source address comparison */
850 if ((0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity)))
860 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
861 "Found existing semi-session for `%s'\n",
862 GNUNET_i2s (&target));
864 if ((direction == _SEND) && (t->server_send != NULL))
866 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
867 "Duplicate GET session, dismissing new connection from peer `%s'\n",
868 GNUNET_i2s (&target));
874 GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head,
875 plugin->server_semi_tail, s);
876 GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
877 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
878 "Found matching semi-session, merging session for peer `%s'\n",
879 GNUNET_i2s (&target));
880 GNUNET_assert (NULL != s);
883 if ((direction == _RECEIVE) && (t->server_recv != NULL))
885 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
886 "Duplicate PUT session, dismissing new connection from peer `%s'\n",
887 GNUNET_i2s (&target));
893 GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head,
894 plugin->server_semi_tail, s);
895 GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
896 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
897 "Found matching semi-session, merging session for peer `%s'\n",
898 GNUNET_i2s (&target));
899 GNUNET_assert (NULL != s);
904 /* create new session */
905 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
906 "Creating new session for peer `%s' \n",
907 GNUNET_i2s (&target));
908 switch (conn_info->client_addr->sa_family)
911 addr = http_common_address_from_socket (plugin->protocol, conn_info->client_addr, sizeof (struct sockaddr_in));
912 addr_len = http_common_address_get_size (addr);
913 ats = plugin->env->get_address_type (plugin->env->cls, conn_info->client_addr, sizeof (struct sockaddr_in));
916 addr = http_common_address_from_socket (plugin->protocol, conn_info->client_addr, sizeof (struct sockaddr_in6));
917 addr_len = http_common_address_get_size (addr);
918 ats = plugin->env->get_address_type (plugin->env->cls, conn_info->client_addr, sizeof (struct sockaddr_in6));
925 s = GNUNET_malloc (sizeof (struct HttpServerSession));
926 memcpy (&s->target, &target, sizeof (struct GNUNET_PeerIdentity));
929 s->addrlen = addr_len;
930 s->ats_address_network_type = ats.value;
931 s->inbound = GNUNET_YES;
932 s->next_receive = GNUNET_TIME_UNIT_ZERO_ABS;
934 s->server_recv = NULL;
935 s->server_send = NULL;
937 server_start_session_timeout(s);
939 GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head,
940 plugin->server_semi_tail, s);
943 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
944 "Invalid connection request\n");
948 sc = GNUNET_malloc (sizeof (struct ServerConnection));
949 sc->mhd_conn = mhd_connection;
950 sc->direction = direction;
952 if (direction == _SEND)
954 if (direction == _RECEIVE)
957 #if MHD_VERSION >= 0x00090E00
958 int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
960 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
961 "Setting timeout for %p to %u sec.\n", sc, to);
962 MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to);
964 struct MHD_Daemon *d = NULL;
966 if (s->addrlen == sizeof (struct IPv6HttpAddress))
967 d = plugin->server_v6;
968 if (s->addrlen == sizeof (struct IPv4HttpAddress))
969 d = plugin->server_v4;
971 server_reschedule (plugin, d, GNUNET_NO);
977 static struct HttpServerSession *
978 server_lookup_session (struct HTTP_Server_Plugin *plugin,
979 struct ServerConnection * sc)
981 struct HttpServerSession *s;
983 for (s = plugin->head; NULL != s; s = s->next)
984 if ((s->server_recv == sc) || (s->server_send == sc))
986 for (s = plugin->server_semi_head; NULL != s; s = s->next)
987 if ((s->server_recv == sc) || (s->server_send == sc))
994 server_access_cb (void *cls, struct MHD_Connection *mhd_connection,
995 const char *url, const char *method, const char *version,
996 const char *upload_data, size_t * upload_data_size,
997 void **httpSessionCache)
999 struct HTTP_Server_Plugin *plugin = cls;
1002 struct ServerConnection *sc = *httpSessionCache;
1003 struct HttpServerSession *s;
1004 struct MHD_Response *response;
1006 GNUNET_assert (cls != NULL);
1009 /* new connection */
1010 sc = server_lookup_connection (plugin, mhd_connection, url, method);
1012 (*httpSessionCache) = sc;
1015 response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE), HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO);
1016 res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response);
1017 MHD_destroy_response (response);
1023 /* 'old' connection */
1024 if (NULL == server_lookup_session (plugin, sc))
1026 /* Session was already disconnected */
1031 /* existing connection */
1032 sc = (*httpSessionCache);
1035 GNUNET_assert (NULL != s);
1037 /* connection is to be disconnected */
1038 if (sc->disconnect == GNUNET_YES)
1040 /* Sent HTTP/1.1: 200 OK as PUT Response\ */
1042 MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!",
1044 res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1045 MHD_destroy_response (response);
1049 GNUNET_assert (s != NULL);
1050 /* Check if both directions are connected */
1051 if ((sc->session->server_recv == NULL) || (sc->session->server_send == NULL))
1053 /* Delayed read from since not both semi-connections are connected */
1057 if (sc->direction == _SEND)
1060 MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
1062 &server_send_callback, s,
1064 MHD_queue_response (mhd_connection, MHD_HTTP_OK, response);
1065 MHD_destroy_response (response);
1068 if (sc->direction == _RECEIVE)
1070 if (*upload_data_size == 0)
1072 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1073 "Server: Peer `%s' PUT on address `%s' connected\n",
1074 GNUNET_i2s (&s->target),
1075 http_plugin_address_to_string (NULL, s->addr,
1080 /* Receiving data */
1081 if ((*upload_data_size > 0))
1083 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1084 "Server: peer `%s' PUT on address `%s' received %u bytes\n",
1085 GNUNET_i2s (&s->target),
1086 http_plugin_address_to_string (NULL, s->addr,
1089 struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1091 if ((s->next_receive.abs_value <= now.abs_value))
1093 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1094 "Server: %p: PUT with %u bytes forwarded to MST\n", s,
1096 if (s->msg_tk == NULL)
1098 s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s);
1100 GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data,
1101 *upload_data_size, GNUNET_NO, GNUNET_NO);
1103 #if MHD_VERSION >= 0x00090E00
1104 int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000);
1105 struct ServerConnection *t = NULL;
1107 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1108 "Server: Received %u bytes\n", *upload_data_size);
1109 /* Setting timeouts for other connections */
1110 if (s->server_recv != NULL)
1113 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1114 "Server: Setting timeout for %p to %u sec.\n", t,
1116 MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
1119 if (s->server_send != NULL)
1122 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1123 "Server: Setting timeout for %p to %u sec.\n", t,
1125 MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT,
1128 struct MHD_Daemon *d = NULL;
1130 if (s->addrlen == sizeof (struct IPv6HttpAddress))
1131 d = plugin->server_v6;
1132 if (s->addrlen == sizeof (struct IPv4HttpAddress))
1133 d = plugin->server_v4;
1134 server_reschedule (plugin, d, GNUNET_NO);
1136 (*upload_data_size) = 0;
1140 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1141 "Server: %p no inbound bandwidth available! Next read was delayed by %llu ms\n",
1142 s, now.abs_value - s->next_receive.abs_value);
1154 server_disconnect_cb (void *cls, struct MHD_Connection *connection,
1155 void **httpSessionCache)
1162 * Check if incoming connection is accepted.
1163 * NOTE: Here every connection is accepted
1164 * @param cls plugin as closure
1165 * @param addr address of incoming connection
1166 * @param addr_len address length of incoming connection
1167 * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected
1171 server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len)
1173 struct HTTP_Server_Plugin *plugin = cls;
1175 if (plugin->cur_connections <= plugin->max_connections)
1179 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
1180 _("Server reached maximum number connections (%u), rejecting new connection\n"),
1181 plugin->max_connections);
1187 server_log (void *arg, const char *fmt, va_list ap)
1191 vsnprintf (text, sizeof (text), fmt, ap);
1193 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Server: %s\n", text);
1198 * Call MHD IPv4 to process pending requests and then go back
1199 * and schedule the next run.
1200 * @param cls plugin as closure
1201 * @param tc task context
1204 server_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1206 struct HTTP_Server_Plugin *plugin = cls;
1208 GNUNET_assert (cls != NULL);
1210 plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1211 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1214 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1215 "Running IPv4 server\n");
1217 plugin->server_v4_immediately = GNUNET_NO;
1218 GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4));
1219 server_reschedule (plugin, plugin->server_v4, GNUNET_NO);
1224 * Call MHD IPv6 to process pending requests and then go back
1225 * and schedule the next run.
1226 * @param cls plugin as closure
1227 * @param tc task context
1230 server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1232 struct HTTP_Server_Plugin *plugin = cls;
1234 GNUNET_assert (cls != NULL);
1235 plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1236 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1239 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1240 "Running IPv6 server\n");
1242 plugin->server_v6_immediately = GNUNET_NO;
1243 GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6));
1244 server_reschedule (plugin, plugin->server_v6, GNUNET_NO);
1249 * Function that queries MHD's select sets and
1250 * starts the task waiting for them.
1251 * @param plugin plugin
1252 * @param daemon_handle the MHD daemon handle
1253 * @return gnunet task identifier
1255 static GNUNET_SCHEDULER_TaskIdentifier
1256 server_schedule (struct HTTP_Server_Plugin *plugin, struct MHD_Daemon *daemon_handle,
1259 GNUNET_SCHEDULER_TaskIdentifier ret;
1263 struct GNUNET_NETWORK_FDSet *wrs;
1264 struct GNUNET_NETWORK_FDSet *wws;
1265 struct GNUNET_NETWORK_FDSet *wes;
1267 unsigned MHD_LONG_LONG timeout;
1268 static unsigned long long last_timeout = 0;
1271 struct GNUNET_TIME_Relative tv;
1273 ret = GNUNET_SCHEDULER_NO_TASK;
1277 wrs = GNUNET_NETWORK_fdset_create ();
1278 wes = GNUNET_NETWORK_fdset_create ();
1279 wws = GNUNET_NETWORK_fdset_create ();
1281 GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max));
1282 haveto = MHD_get_timeout (daemon_handle, &timeout);
1283 if (haveto == MHD_YES)
1285 if (timeout != last_timeout)
1288 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1289 "SELECT Timeout changed from %llu to %llu\n",
1290 last_timeout, timeout);
1292 last_timeout = timeout;
1294 tv.rel_value = (uint64_t) timeout;
1297 tv = GNUNET_TIME_UNIT_SECONDS;
1298 /* Force immediate run, since we have outbound data to send */
1299 if (now == GNUNET_YES)
1300 tv = GNUNET_TIME_UNIT_MILLISECONDS;
1301 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
1302 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
1303 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
1305 if (daemon_handle == plugin->server_v4)
1307 if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
1309 GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
1310 plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1313 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1314 "Scheduling IPv4 server task in %llu ms\n", tv);
1317 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1319 &server_v4_run, plugin);
1321 if (daemon_handle == plugin->server_v6)
1323 if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
1325 GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
1326 plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1329 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1330 "Scheduling IPv6 server task in %llu ms\n", tv);
1333 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1335 &server_v6_run, plugin);
1337 GNUNET_NETWORK_fdset_destroy (wrs);
1338 GNUNET_NETWORK_fdset_destroy (wws);
1339 GNUNET_NETWORK_fdset_destroy (wes);
1346 server_load_file (const char *file)
1348 struct GNUNET_DISK_FileHandle *gn_file;
1352 if (GNUNET_OK != GNUNET_DISK_file_size (file,
1353 &fsize, GNUNET_NO, GNUNET_YES))
1355 text = GNUNET_malloc (fsize + 1);
1357 GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ,
1358 GNUNET_DISK_PERM_USER_READ);
1359 if (gn_file == NULL)
1364 if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fsize))
1367 GNUNET_DISK_file_close (gn_file);
1371 GNUNET_DISK_file_close (gn_file);
1380 server_load_certificate (struct HTTP_Server_Plugin *plugin)
1382 int res = GNUNET_OK;
1387 /* Get crypto init string from config
1388 * If not present just use default values */
1391 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1394 &plugin->crypto_init))
1395 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1396 "Using crypto init string `%s'\n",
1397 plugin->crypto_init);
1399 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1400 "Using default crypto init string \n");
1403 GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
1404 "KEY_FILE", &key_file))
1406 key_file = GNUNET_strdup ("https_key.key");
1410 GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name,
1411 "CERT_FILE", &cert_file))
1413 GNUNET_asprintf (&cert_file, "%s", "https_cert.crt");
1416 /* read key & certificates from file */
1417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1418 "Trying to loading TLS certificate from key-file `%s' cert-file`%s'\n",
1419 key_file, cert_file);
1421 plugin->key = server_load_file (key_file);
1422 plugin->cert = server_load_file (cert_file);
1424 if ((plugin->key == NULL) || (plugin->cert == NULL))
1426 struct GNUNET_OS_Process *cert_creation;
1428 GNUNET_free_non_null (plugin->key);
1430 GNUNET_free_non_null (plugin->cert);
1431 plugin->cert = NULL;
1433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1434 "No usable TLS certificate found, creating certificate\n");
1437 GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL,
1438 "gnunet-transport-certificate-creation",
1439 "gnunet-transport-certificate-creation",
1440 key_file, cert_file, NULL);
1441 if (cert_creation == NULL)
1443 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1445 ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n"));
1446 GNUNET_free (key_file);
1447 GNUNET_free (cert_file);
1449 GNUNET_free_non_null (plugin->key);
1451 GNUNET_free_non_null (plugin->cert);
1452 plugin->cert = NULL;
1453 GNUNET_free_non_null (plugin->crypto_init);
1454 plugin->crypto_init = NULL;
1456 return GNUNET_SYSERR;
1458 GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation));
1459 GNUNET_OS_process_destroy (cert_creation);
1461 plugin->key = server_load_file (key_file);
1462 plugin->cert = server_load_file (cert_file);
1465 if ((plugin->key == NULL) || (plugin->cert == NULL))
1467 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1469 ("No usable TLS certificate found and creating one failed!\n"),
1471 GNUNET_free (key_file);
1472 GNUNET_free (cert_file);
1474 GNUNET_free_non_null (plugin->key);
1476 GNUNET_free_non_null (plugin->cert);
1477 plugin->cert = NULL;
1478 GNUNET_free_non_null (plugin->crypto_init);
1479 plugin->crypto_init = NULL;
1481 return GNUNET_SYSERR;
1483 GNUNET_free (key_file);
1484 GNUNET_free (cert_file);
1485 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n");
1491 server_start (struct HTTP_Server_Plugin *plugin)
1493 unsigned int timeout;
1494 GNUNET_assert (NULL != plugin);
1497 if (GNUNET_SYSERR == server_load_certificate (plugin))
1499 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1500 "Could not load or create server certificate! Loading plugin failed!\n");
1501 return GNUNET_SYSERR;
1506 #if MHD_VERSION >= 0x00090E00
1507 timeout = HTTP_NOT_VALIDATED_TIMEOUT.rel_value / 1000;
1508 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1509 "MHD can set timeout per connection! Default time out %u sec.\n",
1512 timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000;
1513 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
1514 "MHD cannot set timeout per connection! Default time out %u sec.\n",
1517 plugin->server_v4 = NULL;
1518 if (plugin->use_ipv4 == GNUNET_YES)
1520 plugin->server_v4 = MHD_start_daemon (
1527 MHD_NO_FLAG, plugin->port,
1528 &server_accept_cb, plugin,
1529 &server_access_cb, plugin,
1530 MHD_OPTION_SOCK_ADDR,
1531 (struct sockaddr_in *)
1532 plugin->server_addr_v4,
1533 MHD_OPTION_CONNECTION_LIMIT,
1535 plugin->max_connections,
1537 MHD_OPTION_HTTPS_PRIORITIES,
1538 plugin->crypto_init,
1539 MHD_OPTION_HTTPS_MEM_KEY,
1541 MHD_OPTION_HTTPS_MEM_CERT,
1544 MHD_OPTION_CONNECTION_TIMEOUT,
1546 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1548 GNUNET_SERVER_MAX_MESSAGE_SIZE),
1549 MHD_OPTION_NOTIFY_COMPLETED,
1550 &server_disconnect_cb, plugin,
1551 MHD_OPTION_EXTERNAL_LOGGER,
1552 server_log, NULL, MHD_OPTION_END);
1554 plugin->server_v6 = NULL;
1555 if (plugin->use_ipv6 == GNUNET_YES)
1557 plugin->server_v6 = MHD_start_daemon (
1564 MHD_USE_IPv6, plugin->port,
1565 &server_accept_cb, plugin,
1566 &server_access_cb, plugin,
1567 MHD_OPTION_SOCK_ADDR,
1568 (struct sockaddr_in6 *)
1569 plugin->server_addr_v6,
1570 MHD_OPTION_CONNECTION_LIMIT,
1572 plugin->max_connections,
1574 MHD_OPTION_HTTPS_PRIORITIES,
1575 plugin->crypto_init,
1576 MHD_OPTION_HTTPS_MEM_KEY,
1578 MHD_OPTION_HTTPS_MEM_CERT,
1581 MHD_OPTION_CONNECTION_TIMEOUT,
1583 MHD_OPTION_CONNECTION_MEMORY_LIMIT,
1585 GNUNET_SERVER_MAX_MESSAGE_SIZE),
1586 MHD_OPTION_NOTIFY_COMPLETED,
1587 &server_disconnect_cb, plugin,
1588 MHD_OPTION_EXTERNAL_LOGGER,
1589 server_log, NULL, MHD_OPTION_END);
1593 if ((plugin->use_ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL))
1595 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1596 "Failed to start %s IPv4 server component on port %u\n",
1597 plugin->name, plugin->port);
1598 return GNUNET_SYSERR;
1600 server_reschedule (plugin, plugin->server_v4, GNUNET_NO);
1602 if ((plugin->use_ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL))
1604 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1605 "Failed to start %s IPv6 server component on port %u\n",
1606 plugin->name, plugin->port);
1607 return GNUNET_SYSERR;
1609 server_reschedule (plugin, plugin->server_v6, GNUNET_NO);
1610 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1611 "%s server component started on port %u\n", plugin->name,
1618 server_stop (struct HTTP_Server_Plugin *plugin)
1620 struct HttpServerSession *s = NULL;
1621 struct HttpServerSession *t = NULL;
1623 struct MHD_Daemon *server_v4_tmp = plugin->server_v4;
1624 plugin->server_v4 = NULL;
1626 struct MHD_Daemon *server_v6_tmp = plugin->server_v6;
1627 plugin->server_v6 = NULL;
1629 if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK)
1631 GNUNET_SCHEDULER_cancel (plugin->server_v4_task);
1632 plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK;
1635 if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK)
1637 GNUNET_SCHEDULER_cancel (plugin->server_v6_task);
1638 plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK;
1641 if (server_v6_tmp != NULL)
1643 MHD_stop_daemon (server_v4_tmp);
1645 if (server_v6_tmp != NULL)
1647 MHD_stop_daemon (server_v6_tmp);
1650 /* cleaning up semi-sessions never propagated */
1651 s = plugin->server_semi_head;
1655 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1656 "Deleting semi-sessions %p\n", s);
1659 struct HTTP_Message *msg = s->msg_head;
1660 struct HTTP_Message *tmp = NULL;
1666 GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
1667 if (msg->transmit_cont != NULL)
1669 msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
1675 server_delete_session (s);
1682 GNUNET_free_non_null (plugin->crypto_init);
1683 GNUNET_free_non_null (plugin->cert);
1684 GNUNET_free_non_null (plugin->key);
1687 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1688 "%s server component stopped\n", plugin->name);
1692 server_add_address (void *cls, int add_remove, const struct sockaddr *addr,
1695 struct HTTP_Server_Plugin *plugin = cls;
1696 struct HttpAddressWrapper *w = NULL;
1698 if ((AF_INET == addr->sa_family) && (GNUNET_NO == plugin->use_ipv4))
1701 if ((AF_INET6 == addr->sa_family) && (GNUNET_NO == plugin->use_ipv6))
1704 w = GNUNET_malloc (sizeof (struct HttpAddressWrapper));
1705 w->addr = http_common_address_from_socket (plugin->protocol, addr, addrlen);
1706 if (NULL == w->addr)
1711 w->addrlen = http_common_address_get_size (w->addr);
1713 GNUNET_CONTAINER_DLL_insert(plugin->addr_head, plugin->addr_tail, w);
1714 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1715 "Notifying transport to add address `%s'\n",
1716 http_common_plugin_address_to_string(NULL, w->addr, w->addrlen));
1718 plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen);
1723 server_remove_address (void *cls, int add_remove, const struct sockaddr *addr,
1726 struct HTTP_Server_Plugin *plugin = cls;
1727 struct HttpAddressWrapper *w = plugin->addr_head;
1729 void * saddr = http_common_address_from_socket (plugin->protocol, addr, addrlen);
1732 saddr_len = http_common_address_get_size (saddr);
1736 if (GNUNET_YES == http_common_cmp_addresses(w->addr, w->addrlen, saddr, saddr_len))
1740 GNUNET_free (saddr);
1745 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1746 "Notifying transport to remove address `%s'\n",
1747 http_common_plugin_address_to_string (NULL, w->addr, w->addrlen));
1748 GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w);
1749 plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, w->addrlen);
1750 GNUNET_free (w->addr);
1757 * Our external IP address/port mapping has changed.
1759 * @param cls closure, the 'struct LocalAddrList'
1760 * @param add_remove GNUNET_YES to mean the new public IP address, GNUNET_NO to mean
1761 * the previous (now invalid) one
1762 * @param addr either the previous or the new public IP address
1763 * @param addrlen actual lenght of the address
1766 server_nat_port_map_callback (void *cls, int add_remove, const struct sockaddr *addr,
1769 GNUNET_assert (cls != NULL);
1770 struct HTTP_Server_Plugin *plugin = cls;
1772 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1773 "NPMC called %s to address `%s'\n",
1774 (add_remove == GNUNET_NO) ? "remove" : "add",
1775 GNUNET_a2s (addr, addrlen));
1780 server_add_address (cls, add_remove, addr, addrlen);
1783 server_remove_address (cls, add_remove, addr, addrlen);
1790 server_get_addresses (struct HTTP_Server_Plugin *plugin,
1791 const char *serviceName,
1792 const struct GNUNET_CONFIGURATION_Handle *cfg,
1793 struct sockaddr ***addrs, socklen_t ** addr_lens)
1796 unsigned long long port;
1797 struct addrinfo hints;
1798 struct addrinfo *res;
1799 struct addrinfo *pos;
1800 struct addrinfo *next;
1804 struct sockaddr **saddrs;
1805 socklen_t *saddrlens;
1811 disablev6 = !plugin->use_ipv6;
1814 if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "PORT"))
1816 GNUNET_break (GNUNET_OK ==
1817 GNUNET_CONFIGURATION_get_value_number (cfg, serviceName,
1821 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1823 ("Require valid port number for service in configuration!\n"));
1824 return GNUNET_SYSERR;
1829 GNUNET_log_from (GNUNET_ERROR_TYPE_INFO, plugin->name,
1830 "Starting in listen only mode\n");
1831 return -1; /* listen only */
1835 if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "BINDTO"))
1837 GNUNET_break (GNUNET_OK ==
1838 GNUNET_CONFIGURATION_get_value_string (cfg, serviceName,
1839 "BINDTO", &hostname));
1844 if (hostname != NULL)
1846 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1847 "Resolving `%s' since that is where `%s' will bind to.\n",
1848 hostname, serviceName);
1849 memset (&hints, 0, sizeof (struct addrinfo));
1851 hints.ai_family = AF_INET;
1852 if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) ||
1855 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to resolve `%s': %s\n"),
1856 hostname, gai_strerror (ret));
1857 GNUNET_free (hostname);
1858 return GNUNET_SYSERR;
1862 while (NULL != (pos = next))
1864 next = pos->ai_next;
1865 if ((disablev6) && (pos->ai_family == AF_INET6))
1871 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1872 _("Failed to find %saddress for `%s'.\n"),
1873 disablev6 ? "IPv4 " : "", hostname);
1875 GNUNET_free (hostname);
1876 return GNUNET_SYSERR;
1879 saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
1880 saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
1883 while (NULL != (pos = next))
1885 next = pos->ai_next;
1886 if ((disablev6) && (pos->ai_family == AF_INET6))
1888 if ((pos->ai_protocol != IPPROTO_TCP) && (pos->ai_protocol != 0))
1889 continue; /* not TCP */
1890 if ((pos->ai_socktype != SOCK_STREAM) && (pos->ai_socktype != 0))
1891 continue; /* huh? */
1892 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1893 "Service will bind to `%s'\n", GNUNET_a2s (pos->ai_addr,
1895 if (pos->ai_family == AF_INET)
1897 GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in));
1898 saddrlens[i] = pos->ai_addrlen;
1899 saddrs[i] = GNUNET_malloc (saddrlens[i]);
1900 memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
1901 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1905 GNUNET_assert (pos->ai_family == AF_INET6);
1906 GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6));
1907 saddrlens[i] = pos->ai_addrlen;
1908 saddrs[i] = GNUNET_malloc (saddrlens[i]);
1909 memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
1910 ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
1914 GNUNET_free (hostname);
1920 /* will bind against everything, just set port */
1926 saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
1927 saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
1929 saddrlens[i] = sizeof (struct sockaddr_in);
1930 saddrs[i] = GNUNET_malloc (saddrlens[i]);
1931 #if HAVE_SOCKADDR_IN_SIN_LEN
1932 ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i];
1934 ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
1935 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1941 saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
1942 saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
1944 saddrlens[i] = sizeof (struct sockaddr_in6);
1945 saddrs[i] = GNUNET_malloc (saddrlens[i]);
1946 #if HAVE_SOCKADDR_IN_SIN_LEN
1947 ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0];
1949 ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6;
1950 ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
1952 saddrlens[i] = sizeof (struct sockaddr_in);
1953 saddrs[i] = GNUNET_malloc (saddrlens[i]);
1954 #if HAVE_SOCKADDR_IN_SIN_LEN
1955 ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1];
1957 ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
1958 ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
1962 *addr_lens = saddrlens;
1967 server_start_report_addresses (struct HTTP_Server_Plugin *plugin)
1969 int res = GNUNET_OK;
1970 struct sockaddr **addrs;
1971 socklen_t *addrlens;
1973 res = server_get_addresses (plugin,
1974 plugin->name, plugin->env->cfg,
1976 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1977 _("Found %u addresses to report to NAT service\n"), res);
1979 if (GNUNET_SYSERR == res)
1986 GNUNET_NAT_register (plugin->env->cfg, GNUNET_YES, plugin->port,
1988 (const struct sockaddr **) addrs, addrlens,
1989 &server_nat_port_map_callback, NULL, plugin);
1993 GNUNET_assert (addrs[res] != NULL);
1994 GNUNET_free (addrs[res]);
1996 GNUNET_free_non_null (addrs);
1997 GNUNET_free_non_null (addrlens);
2002 server_stop_report_addresses (struct HTTP_Server_Plugin *plugin)
2004 /* Stop NAT handle */
2005 if (NULL != plugin->nat)
2006 GNUNET_NAT_unregister (plugin->nat);
2008 /* Clean up addresses */
2009 struct HttpAddressWrapper *w;
2011 while (plugin->addr_head != NULL)
2013 w = plugin->addr_head;
2014 GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w);
2015 GNUNET_free (w->addr);
2022 * Check if IPv6 supported on this system
2025 server_check_ipv6_support (struct HTTP_Server_Plugin *plugin)
2027 struct GNUNET_NETWORK_Handle *desc = NULL;
2028 int res = GNUNET_NO;
2030 /* Probe IPv6 support */
2031 desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
2034 if ((errno == ENOBUFS) || (errno == ENOMEM) || (errno == ENFILE) ||
2037 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "socket");
2039 GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
2041 ("Disabling IPv6 since it is not supported on this system!\n"));
2046 GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
2050 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2051 "Testing IPv6 on this system: %s\n",
2052 (res == GNUNET_YES) ? "successful" : "failed");
2058 * Function called when the service shuts down. Unloads our plugins
2059 * and cancels pending validations.
2061 * @param cls closure, unused
2062 * @param tc task context (unused)
2065 server_notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2067 struct HTTP_Server_Plugin *plugin = cls;
2069 plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK;
2071 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
2074 GNUNET_asprintf(&plugin->ext_addr, "%s://%s", plugin->protocol, plugin->external_hostname);
2075 plugin->ext_addr_len = strlen (plugin->ext_addr) + 1;
2076 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2077 "Notifying transport about external hostname address `%s'\n", plugin->ext_addr);
2078 plugin->env->notify_address (plugin->env->cls, GNUNET_YES, plugin->ext_addr, plugin->ext_addr_len );
2083 server_configure_plugin (struct HTTP_Server_Plugin *plugin)
2085 unsigned long long port;
2086 unsigned long long max_connections;
2087 char *bind4_address = NULL;
2088 char *bind6_address = NULL;
2091 if (GNUNET_CONFIGURATION_have_value
2092 (plugin->env->cfg, plugin->name, "USE_IPv4"))
2095 GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name,
2099 plugin->use_ipv4 = GNUNET_YES;
2100 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2101 _("IPv4 support is %s\n"),
2102 (plugin->use_ipv4 == GNUNET_YES) ? "enabled" : "disabled");
2105 if (GNUNET_CONFIGURATION_have_value
2106 (plugin->env->cfg, plugin->name, "USE_IPv6"))
2109 GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg, plugin->name,
2113 plugin->use_ipv6 = GNUNET_YES;
2114 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2115 _("IPv6 support is %s\n"),
2116 (plugin->use_ipv6 == GNUNET_YES) ? "enabled" : "disabled");
2118 if ((plugin->use_ipv4 == GNUNET_NO) && (plugin->use_ipv6 == GNUNET_NO))
2120 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2122 ("Neither IPv4 nor IPv6 are enabled! Fix in configuration\n"),
2124 return GNUNET_SYSERR;
2127 /* Reading port number from config file */
2129 GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg, plugin->name,
2130 "PORT", &port)) || (port > 65535))
2132 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2133 _("Port is required! Fix in configuration\n"),
2135 return GNUNET_SYSERR;
2137 plugin->port = port;
2139 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2140 _("Using port %u\n"), plugin->port);
2142 if ((plugin->use_ipv4 == GNUNET_YES) &&
2143 (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2144 plugin->name, "BINDTO", &bind4_address)))
2146 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2147 "Binding %s plugin to specific IPv4 address: `%s'\n",
2148 plugin->protocol, bind4_address);
2149 plugin->server_addr_v4 = GNUNET_malloc (sizeof (struct sockaddr_in));
2150 if (1 != inet_pton (AF_INET, bind4_address,
2151 &plugin->server_addr_v4->sin_addr))
2153 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2155 ("Specific IPv4 address `%s' in configuration file is invalid!\n"),
2157 GNUNET_free (bind4_address);
2158 GNUNET_free (plugin->server_addr_v4);
2159 plugin->server_addr_v4 = NULL;
2160 return GNUNET_SYSERR;
2164 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2165 _("Binding to IPv4 address %s\n"), bind4_address);
2166 plugin->server_addr_v4->sin_family = AF_INET;
2167 plugin->server_addr_v4->sin_port = htons (plugin->port);
2169 GNUNET_free (bind4_address);
2172 if ((plugin->use_ipv6 == GNUNET_YES) &&
2174 GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
2175 "BINDTO6", &bind6_address)))
2177 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2178 "Binding %s plugin to specific IPv6 address: `%s'\n",
2179 plugin->protocol, bind6_address);
2180 plugin->server_addr_v6 = GNUNET_malloc (sizeof (struct sockaddr_in6));
2182 inet_pton (AF_INET6, bind6_address, &plugin->server_addr_v6->sin6_addr))
2184 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
2186 ("Specific IPv6 address `%s' in configuration file is invalid!\n"),
2188 GNUNET_free (bind6_address);
2189 GNUNET_free (plugin->server_addr_v6);
2190 plugin->server_addr_v6 = NULL;
2191 return GNUNET_SYSERR;
2195 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2196 _("Binding to IPv6 address %s\n"), bind6_address);
2197 plugin->server_addr_v6->sin6_family = AF_INET6;
2198 plugin->server_addr_v6->sin6_port = htons (plugin->port);
2200 GNUNET_free (bind6_address);
2203 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, plugin->name,
2204 "EXTERNAL_HOSTNAME", &plugin->external_hostname))
2206 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2207 _("Using external hostname `%s'\n"), plugin->external_hostname);
2208 plugin->notify_ext_task = GNUNET_SCHEDULER_add_now (&server_notify_external_hostname, plugin);
2211 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2212 "No external hostname configured\n");
2215 /* Optional parameters */
2216 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
2218 "MAX_CONNECTIONS", &max_connections))
2219 max_connections = 128;
2220 plugin->max_connections = max_connections;
2222 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2223 _("Maximum number of connections is %u\n"),
2224 plugin->max_connections);
2230 server_session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2232 GNUNET_assert (NULL != cls);
2233 struct HttpServerSession *s = cls;
2235 s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
2236 GNUNET_log (TIMEOUT_LOG,
2237 "Session %p was idle for %llu ms, disconnecting\n",
2238 s, (unsigned long long) TIMEOUT.rel_value);
2240 /* call session destroy function */
2241 GNUNET_assert (GNUNET_OK == server_disconnect (s));
2245 * Start session timeout
2248 server_start_session_timeout (struct HttpServerSession *s)
2250 GNUNET_assert (NULL != s);
2251 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s->timeout_task);
2252 s->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
2253 &server_session_timeout,
2255 GNUNET_log (TIMEOUT_LOG,
2256 "Timeout for session %p set to %llu ms\n",
2257 s, (unsigned long long) TIMEOUT.rel_value);
2262 * Session was idle, so disconnect it
2267 * Increment session timeout due to activity
2270 server_reschedule_session_timeout (struct HttpServerSession *s)
2272 GNUNET_assert (NULL != s);
2273 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task);
2275 GNUNET_SCHEDULER_cancel (s->timeout_task);
2276 s->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
2279 GNUNET_log (TIMEOUT_LOG,
2280 "Timeout rescheduled for session %p set to %llu ms\n",
2281 s, (unsigned long long) TIMEOUT.rel_value);
2286 * Exit point from the plugin.
2289 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
2291 struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2292 struct HTTP_Server_Plugin *plugin = api->cls;
2294 if (GNUNET_SCHEDULER_NO_TASK != plugin->notify_ext_task)
2296 GNUNET_SCHEDULER_cancel (plugin->notify_ext_task);
2297 plugin->notify_ext_task = GNUNET_SCHEDULER_NO_TASK;
2300 if (NULL != plugin->ext_addr)
2302 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
2303 "Notifying transport to remove address `%s'\n",
2304 http_common_plugin_address_to_string (NULL,
2306 plugin->ext_addr_len));
2307 plugin->env->notify_address (plugin->env->cls,
2310 plugin->ext_addr_len);
2313 /* Stop to report addresses to transport service */
2314 server_stop_report_addresses (plugin);
2316 server_stop (plugin);
2319 GNUNET_free_non_null (plugin->external_hostname);
2320 GNUNET_free_non_null (plugin->ext_addr);
2321 GNUNET_free_non_null (plugin->server_addr_v4);
2322 GNUNET_free_non_null (plugin->server_addr_v6);
2324 GNUNET_free (plugin);
2331 * Entry point for the plugin.
2334 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
2336 struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2337 struct GNUNET_TRANSPORT_PluginFunctions *api;
2338 struct HTTP_Server_Plugin *plugin;
2340 plugin = GNUNET_malloc (sizeof (struct HTTP_Server_Plugin));
2342 api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
2344 api->send = &http_server_plugin_send;
2345 api->disconnect = &http_server_plugin_disconnect;
2346 api->check_address = &http_server_plugin_address_suggested;
2347 api->get_session = &http_server_plugin_get_session;
2349 api->address_to_string = &http_common_plugin_address_to_string;
2350 api->string_to_address = &http_common_plugin_string_to_address;
2351 api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2354 plugin->name = "transport-https_server";
2355 plugin->protocol = "https";
2357 plugin->name = "transport-http_server";
2358 plugin->protocol = "http";
2361 /* Configure plugin */
2362 if (GNUNET_SYSERR == server_configure_plugin (plugin))
2364 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2368 /* Check IPv6 support */
2369 if (GNUNET_YES == plugin->use_ipv6)
2370 plugin->use_ipv6 = server_check_ipv6_support (plugin);
2372 /* Report addresses to transport service */
2373 server_start_report_addresses (plugin);
2375 if (GNUNET_SYSERR == server_start (plugin))
2377 LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2387 /* end of plugin_transport_http_server.c */