From 49bb861ae2f02b2ba5af68cc6aaf13a135032420 Mon Sep 17 00:00:00 2001 From: Matthias Wachs Date: Sun, 25 Jul 2010 13:58:38 +0000 Subject: [PATCH] --- src/transport/Makefile.am | 29 +- src/transport/plugin_transport_http.c | 131 +- src/transport/plugin_transport_https.c | 2585 +++++++++++++++++ .../test_plugin_transport_data_http.conf | 10 +- src/transport/test_plugin_transport_https.c | 1354 +++++++++ .../test_transport_api_http_peer1.conf | 4 +- .../test_transport_api_http_peer2.conf | 4 +- 7 files changed, 4078 insertions(+), 39 deletions(-) create mode 100644 src/transport/plugin_transport_https.c create mode 100644 src/transport/test_plugin_transport_https.c diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index 2a62d0fb6..f4824ab70 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -12,6 +12,9 @@ if HAVE_MHD HTTP_PLUGIN_CHECK = test_plugin_transport_http \ test_transport_api_http \ test_transport_api_reliability_http + + HTTPS_PLUGIN_LA = libgnunet_plugin_transport_https.la + HTTPS_PLUGIN_CHECK = test_plugin_transport_https endif if USE_COVERAGE @@ -76,6 +79,7 @@ plugin_LTLIBRARIES = \ libgnunet_plugin_transport_tcp.la \ libgnunet_plugin_transport_udp.la \ $(HTTP_PLUGIN_LA) \ + $(HTTPS_PLUGIN_LA) \ libgnunet_plugin_transport_template.la # TODO: add nat, etc. @@ -128,6 +132,18 @@ libgnunet_plugin_transport_http_la_LIBADD = \ libgnunet_plugin_transport_http_la_LDFLAGS = \ $(GN_LIBMHD) \ $(GN_PLUGIN_LDFLAGS) + +libgnunet_plugin_transport_https_la_SOURCES = \ + plugin_transport_https.c +libgnunet_plugin_transport_https_la_LIBADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + @LIBCURL@ \ + $(top_builddir)/src/util/libgnunetutil.la +libgnunet_plugin_transport_https_la_LDFLAGS = \ + $(GN_LIBMHD) \ + $(GN_PLUGIN_LDFLAGS) endif check_PROGRAMS = \ @@ -135,6 +151,7 @@ check_PROGRAMS = \ test_transport_api_tcp_nat \ test_transport_api_udp \ $(HTTP_PLUGIN_CHECK) \ + $(HTTPS_PLUGIN_CHECK) \ test_transport_api_udp_nat \ test_transport_api_reliability_tcp \ test_transport_api_reliability_tcp_nat \ @@ -147,6 +164,7 @@ TESTS = \ test_transport_api_udp \ test_transport_api_udp_nat \ $(HTTP_PLUGIN_CHECK) \ + $(HTTPS_PLUGIN_CHECK) \ test_transport_api_reliability_tcp \ test_transport_api_reliability_tcp_nat @@ -211,7 +229,16 @@ test_transport_api_reliability_http_SOURCES = \ test_transport_api_reliability.c test_transport_api_reliability_http_LDADD = \ $(top_builddir)/src/transport/libgnunettransport.la \ - $(top_builddir)/src/util/libgnunetutil.la + $(top_builddir)/src/util/libgnunetutil.la + +test_plugin_transport_https_SOURCES = \ + test_plugin_transport_https.c +test_plugin_transport_https_LDADD = \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + @LIBCURL@ \ + $(top_builddir)/src/util/libgnunetutil.la + endif EXTRA_DIST = \ diff --git a/src/transport/plugin_transport_http.c b/src/transport/plugin_transport_http.c index 7137947c5..909156046 100644 --- a/src/transport/plugin_transport_http.c +++ b/src/transport/plugin_transport_http.c @@ -42,8 +42,8 @@ #define DEBUG_HTTP GNUNET_YES #define DEBUG_CURL GNUNET_YES -#define DEBUG_CONNECTIONS GNUNET_YES -#define DEBUG_SESSION_SELECTION GNUNET_YES +#define DEBUG_CONNECTIONS GNUNET_NO +#define DEBUG_SESSION_SELECTION GNUNET_NO #define INBOUND GNUNET_NO #define OUTBOUND GNUNET_YES @@ -438,6 +438,8 @@ static int remove_http_message (struct Session * ps, struct HTTP_Message * msg) return GNUNET_OK; } +int remove_peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *value); + /** * Removes a session from the linked list of sessions * @param pc peer context @@ -460,6 +462,20 @@ static int remove_session (struct HTTP_PeerContext * pc, struct Session * ps, i GNUNET_SERVER_mst_destroy (ps->msgtok); GNUNET_free(ps->url); + if (ps->direction==INBOUND) + { + if (ps->recv_endpoint != NULL) + { + curl_easy_cleanup(ps->recv_endpoint); + ps->recv_endpoint = NULL; + } + if (ps->send_endpoint != NULL) + { + curl_easy_cleanup(ps->send_endpoint); + ps->send_endpoint = NULL; + } + } + msg = ps->pending_msgs_head; while (msg!=NULL) { @@ -475,6 +491,16 @@ static int remove_session (struct HTTP_PeerContext * pc, struct Session * ps, i GNUNET_CONTAINER_DLL_remove(pc->head,pc->tail,ps); GNUNET_free(ps); ps = NULL; + + /* no sessions left remove peer */ + if (pc->head==NULL) + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No sessions left for peer `%s', removing context\n",GNUNET_i2s(&pc->identity)); +#endif + remove_peer_context_Iterator(plugin, &pc->identity.hashPubKey, pc); + } + return GNUNET_OK; } @@ -489,6 +515,7 @@ int remove_peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *v #if DEBUG_HTTP GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing context for peer `%s'\n",GNUNET_i2s(&pc->identity)); #endif + GNUNET_CONTAINER_multihashmap_remove (plugin->peers, &pc->identity.hashPubKey, pc); while (ps!=NULL) { plugin->env->session_end(plugin, &pc->identity, ps); @@ -518,6 +545,10 @@ int remove_peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *v ps=tmp; } GNUNET_free(pc); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP peers active"), + -1, + GNUNET_NO); return GNUNET_YES; } @@ -639,6 +670,10 @@ static void mhd_termination_cb (void *cls, struct MHD_Connection * connection, v /* if both connections disconnected, remove session */ if ((ps->send_connected == GNUNET_NO) && (ps->recv_connected == GNUNET_NO)) { + GNUNET_STATISTICS_update (pc->plugin->env->stats, + gettext_noop ("# HTTP inbound sessions for peers active"), + -1, + GNUNET_NO); remove_session(pc,ps,GNUNET_YES,GNUNET_SYSERR); } } @@ -817,6 +852,10 @@ mdh_access_cb (void *cls, pc->last_session = NULL; memcpy(&pc->identity, &pi_in, sizeof(struct GNUNET_PeerIdentity)); GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP peers active"), + 1, + GNUNET_NO); } conn_info = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS ); @@ -873,6 +912,10 @@ mdh_access_cb (void *cls, ps->session_id =id_num; ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id); GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP inbound sessions for peers active"), + 1, + GNUNET_NO); } *httpSessionCache = ps; @@ -1085,9 +1128,6 @@ static void http_server_daemon_v6_run (void *cls, static size_t curl_get_header_cb( void *ptr, size_t size, size_t nmemb, void *stream) { struct Session * ps = stream; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: curl_get_header_cb\n",ps); - char * tmp; size_t len = size * nmemb; long http_result = 0; @@ -1356,8 +1396,8 @@ static void curl_perform (void *cls, ps->send_connected = GNUNET_NO; ps->send_active = GNUNET_NO; curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint); - curl_easy_cleanup(ps->send_endpoint); - ps->send_endpoint=NULL; + //curl_easy_cleanup(ps->send_endpoint); + //ps->send_endpoint=NULL; cur_msg = ps->pending_msgs_tail; if (( NULL != cur_msg) && ( NULL != cur_msg->transmit_cont)) cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR); @@ -1377,8 +1417,8 @@ static void curl_perform (void *cls, ps->recv_connected = GNUNET_NO; ps->recv_active = GNUNET_NO; curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint); - curl_easy_cleanup(ps->recv_endpoint); - ps->recv_endpoint=NULL; + //curl_easy_cleanup(ps->recv_endpoint); + //ps->recv_endpoint=NULL; } } else @@ -1411,8 +1451,8 @@ static void curl_perform (void *cls, ps->send_connected = GNUNET_NO; ps->send_active = GNUNET_NO; curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint); - curl_easy_cleanup(ps->send_endpoint); - ps->send_endpoint =NULL; + //curl_easy_cleanup(ps->send_endpoint); + //ps->send_endpoint =NULL; } if (msg->easy_handle == ps->recv_endpoint) { @@ -1427,8 +1467,8 @@ static void curl_perform (void *cls, ps->recv_connected = GNUNET_NO; ps->recv_active = GNUNET_NO; curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint); - curl_easy_cleanup(ps->recv_endpoint); - ps->recv_endpoint=NULL; + //curl_easy_cleanup(ps->recv_endpoint); + //ps->recv_endpoint=NULL; } } if ((ps->recv_connected == GNUNET_NO) && (ps->send_connected == GNUNET_NO)) @@ -1536,9 +1576,14 @@ static ssize_t send_check_connections (void *cls, struct Session *ps) /* Check if session is connected to receive data, otherwise connect to peer */ if (ps->recv_connected == GNUNET_NO) { + int fresh = GNUNET_NO; if (ps->recv_endpoint == NULL) { - ps->recv_endpoint = curl_easy_init(); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "created handle\n"); + fresh = GNUNET_YES; + ps->recv_endpoint = curl_easy_init(); + } #if DEBUG_CURL curl_easy_setopt(ps->recv_endpoint, CURLOPT_VERBOSE, 1L); #endif @@ -1554,15 +1599,18 @@ static ssize_t send_check_connections (void *cls, struct Session *ps) curl_easy_setopt(ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT); curl_easy_setopt(ps->recv_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE); - mret = curl_multi_add_handle(plugin->multi_handle, ps->recv_endpoint); - if (mret != CURLM_OK) + if (fresh==GNUNET_YES) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Connection: %X: %s failed at %s:%d: `%s'\n"), - ps, - "curl_multi_add_handle", __FILE__, __LINE__, - curl_multi_strerror (mret)); - return GNUNET_SYSERR; + mret = curl_multi_add_handle(plugin->multi_handle, ps->recv_endpoint); + if (mret != CURLM_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Connection: %X: %s failed at %s:%d: `%s'\n"), + ps, + "curl_multi_add_handle", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return GNUNET_SYSERR; + } } if (curl_schedule (plugin) == GNUNET_SYSERR) { @@ -1574,7 +1622,6 @@ static ssize_t send_check_connections (void *cls, struct Session *ps) #if DEBUG_CONNECTIONS GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound not connected, initiating connection\n",ps); #endif - } } /* waiting for receive direction */ @@ -1607,9 +1654,14 @@ static ssize_t send_check_connections (void *cls, struct Session *ps) } } /* not connected, initiate connection */ - if ((ps->send_connected==GNUNET_NO) && (NULL == ps->send_endpoint)) + if (ps->send_connected==GNUNET_NO) { - ps->send_endpoint = curl_easy_init(); + int fresh = GNUNET_NO; + if (NULL == ps->send_endpoint) + { + ps->send_endpoint = curl_easy_init(); + fresh = GNUNET_YES; + } GNUNET_assert (ps->send_endpoint != NULL); GNUNET_assert (NULL != ps->pending_msgs_tail); #if DEBUG_CONNECTIONS @@ -1634,15 +1686,18 @@ static ssize_t send_check_connections (void *cls, struct Session *ps) curl_easy_setopt(ps->send_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT); curl_easy_setopt(ps->send_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE); - mret = curl_multi_add_handle(plugin->multi_handle, ps->send_endpoint); - if (mret != CURLM_OK) + if (fresh==GNUNET_YES) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Connection: %X: %s failed at %s:%d: `%s'\n"), - ps, - "curl_multi_add_handle", __FILE__, __LINE__, - curl_multi_strerror (mret)); - return GNUNET_SYSERR; + mret = curl_multi_add_handle(plugin->multi_handle, ps->send_endpoint); + if (mret != CURLM_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Connection: %X: %s failed at %s:%d: `%s'\n"), + ps, + "curl_multi_add_handle", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return GNUNET_SYSERR; + } } } if (curl_schedule (plugin) == GNUNET_SYSERR) @@ -1853,6 +1908,10 @@ http_plugin_send (void *cls, pc->last_session = NULL; memcpy(&pc->identity, target, sizeof(struct GNUNET_PeerIdentity)); GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP peers active"), + 1, + GNUNET_NO); } ps = send_select_session (plugin, pc, addr, addrlen, force_address, session); @@ -1894,6 +1953,12 @@ http_plugin_send (void *cls, if (ps->msgtok == NULL) ps->msgtok = GNUNET_SERVER_mst_create (&curl_receive_mst_cb, ps); GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps); +/* FIXME */ + + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP outbound sessions for peers active"), + 1, + GNUNET_NO); } else { diff --git a/src/transport/plugin_transport_https.c b/src/transport/plugin_transport_https.c new file mode 100644 index 000000000..22e121963 --- /dev/null +++ b/src/transport/plugin_transport_https.c @@ -0,0 +1,2585 @@ +/* + This file is part of GNUnet + (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file transport/plugin_transport_https.c + * @brief https transport service plugin + * @author Matthias Wachs + */ + +#include "platform.h" +#include "gnunet_common.h" +#include "gnunet_constants.h" +#include "gnunet_protocols.h" +#include "gnunet_connection_lib.h" +#include "gnunet_service_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" +#include "gnunet_resolver_service.h" +#include "gnunet_server_lib.h" +#include "gnunet_container_lib.h" +#include "plugin_transport.h" +#include "gnunet_os_lib.h" +#include "microhttpd.h" +#include + +#define DEBUG_HTTPS GNUNET_YES +#define DEBUG_CURL GNUNET_YES +#define DEBUG_CONNECTIONS GNUNET_YES +#define DEBUG_SESSION_SELECTION GNUNET_NO + +#define INBOUND GNUNET_NO +#define OUTBOUND GNUNET_YES + +/** + * Text of the response sent back after the last bytes of a PUT + * request have been received (just to formally obey the HTTP + * protocol). + */ +#define HTTP_PUT_RESPONSE "Thank you!" + +/** + * After how long do we expire an address that we + * learned from another peer if it is not reconfirmed + * by anyone? + */ +#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6) + +/** + * Page returned if request invalid + */ +#define HTTP_ERROR_RESPONSE "404 Not Found

Not Found

The requested URL was not found on this server.


" + +/** + * Timeout for a http connect + */ +#define HTTP_CONNECT_TIMEOUT 30 + +/* Test Certificate */ +const char cert_pem[] = + "-----BEGIN CERTIFICATE-----\n" + "MIICpjCCAZCgAwIBAgIESEPtjjALBgkqhkiG9w0BAQUwADAeFw0wODA2MDIxMjU0\n" + "MzhaFw0wOTA2MDIxMjU0NDZaMAAwggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKC\n" + "AQC03TyUvK5HmUAirRp067taIEO4bibh5nqolUoUdo/LeblMQV+qnrv/RNAMTx5X\n" + "fNLZ45/kbM9geF8qY0vsPyQvP4jumzK0LOJYuIwmHaUm9vbXnYieILiwCuTgjaud\n" + "3VkZDoQ9fteIo+6we9UTpVqZpxpbLulBMh/VsvX0cPJ1VFC7rT59o9hAUlFf9jX/\n" + "GmKdYI79MtgVx0OPBjmmSD6kicBBfmfgkO7bIGwlRtsIyMznxbHu6VuoX/eVxrTv\n" + "rmCwgEXLWRZ6ru8MQl5YfqeGXXRVwMeXU961KefbuvmEPccgCxm8FZ1C1cnDHFXh\n" + "siSgAzMBjC/b6KVhNQ4KnUdZAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0O\n" + "BBYEFJcUvpjvE5fF/yzUshkWDpdYiQh/MAsGCSqGSIb3DQEBBQOCAQEARP7eKSB2\n" + "RNd6XjEjK0SrxtoTnxS3nw9sfcS7/qD1+XHdObtDFqGNSjGYFB3Gpx8fpQhCXdoN\n" + "8QUs3/5ZVa5yjZMQewWBgz8kNbnbH40F2y81MHITxxCe1Y+qqHWwVaYLsiOTqj2/\n" + "0S3QjEJ9tvklmg7JX09HC4m5QRYfWBeQLD1u8ZjA1Sf1xJriomFVyRLI2VPO2bNe\n" + "JDMXWuP+8kMC7gEvUnJ7A92Y2yrhu3QI3bjPk8uSpHea19Q77tul1UVBJ5g+zpH3\n" + "OsF5p0MyaVf09GTzcLds5nE/osTdXGUyHJapWReVmPm3Zn6gqYlnzD99z+DPIgIV\n" + "RhZvQx74NQnS6g==\n" "-----END CERTIFICATE-----\n"; + +const char key_pem[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEowIBAAKCAQEAtN08lLyuR5lAIq0adOu7WiBDuG4m4eZ6qJVKFHaPy3m5TEFf\n" + "qp67/0TQDE8eV3zS2eOf5GzPYHhfKmNL7D8kLz+I7psytCziWLiMJh2lJvb2152I\n" + "niC4sArk4I2rnd1ZGQ6EPX7XiKPusHvVE6VamacaWy7pQTIf1bL19HDydVRQu60+\n" + "faPYQFJRX/Y1/xpinWCO/TLYFcdDjwY5pkg+pInAQX5n4JDu2yBsJUbbCMjM58Wx\n" + "7ulbqF/3lca0765gsIBFy1kWeq7vDEJeWH6nhl10VcDHl1PetSnn27r5hD3HIAsZ\n" + "vBWdQtXJwxxV4bIkoAMzAYwv2+ilYTUOCp1HWQIDAQABAoIBAArOQv3R7gmqDspj\n" + "lDaTFOz0C4e70QfjGMX0sWnakYnDGn6DU19iv3GnX1S072ejtgc9kcJ4e8VUO79R\n" + "EmqpdRR7k8dJr3RTUCyjzf/C+qiCzcmhCFYGN3KRHA6MeEnkvRuBogX4i5EG1k5l\n" + "/5t+YBTZBnqXKWlzQLKoUAiMLPg0eRWh+6q7H4N7kdWWBmTpako7TEqpIwuEnPGx\n" + "u3EPuTR+LN6lF55WBePbCHccUHUQaXuav18NuDkcJmCiMArK9SKb+h0RqLD6oMI/\n" + "dKD6n8cZXeMBkK+C8U/K0sN2hFHACsu30b9XfdnljgP9v+BP8GhnB0nCB6tNBCPo\n" + "32srOwECgYEAxWh3iBT4lWqL6bZavVbnhmvtif4nHv2t2/hOs/CAq8iLAw0oWGZc\n" + "+JEZTUDMvFRlulr0kcaWra+4fN3OmJnjeuFXZq52lfMgXBIKBmoSaZpIh2aDY1Rd\n" + "RbEse7nQl9hTEPmYspiXLGtnAXW7HuWqVfFFP3ya8rUS3t4d07Hig8ECgYEA6ou6\n" + "OHiBRTbtDqLIv8NghARc/AqwNWgEc9PelCPe5bdCOLBEyFjqKiT2MttnSSUc2Zob\n" + "XhYkHC6zN1Mlq30N0e3Q61YK9LxMdU1vsluXxNq2rfK1Scb1oOlOOtlbV3zA3VRF\n" + "hV3t1nOA9tFmUrwZi0CUMWJE/zbPAyhwWotKyZkCgYEAh0kFicPdbABdrCglXVae\n" + "SnfSjVwYkVuGd5Ze0WADvjYsVkYBHTvhgRNnRJMg+/vWz3Sf4Ps4rgUbqK8Vc20b\n" + "AU5G6H6tlCvPRGm0ZxrwTWDHTcuKRVs+pJE8C/qWoklE/AAhjluWVoGwUMbPGuiH\n" + "6Gf1bgHF6oj/Sq7rv/VLZ8ECgYBeq7ml05YyLuJutuwa4yzQ/MXfghzv4aVyb0F3\n" + "QCdXR6o2IYgR6jnSewrZKlA9aPqFJrwHNR6sNXlnSmt5Fcf/RWO/qgJQGLUv3+rG\n" + "7kuLTNDR05azSdiZc7J89ID3Bkb+z2YkV+6JUiPq/Ei1+nDBEXb/m+/HqALU/nyj\n" + "P3gXeQKBgBusb8Rbd+KgxSA0hwY6aoRTPRt8LNvXdsB9vRcKKHUFQvxUWiUSS+L9\n" + "/Qu1sJbrUquKOHqksV5wCnWnAKyJNJlhHuBToqQTgKXjuNmVdYSe631saiI7PHyC\n" + "eRJ6DxULPxABytJrYCRrNqmXi5TCiqR2mtfalEMOPxz8rUU8dYyx\n" + "-----END RSA PRIVATE KEY-----\n"; + +/** + * Network format for IPv4 addresses. + */ +struct IPv4HttpAddress +{ + /** + * IPv4 address, in network byte order. + */ + uint32_t ipv4_addr GNUNET_PACKED; + + /** + * Port number, in network byte order. + */ + uint16_t u_port GNUNET_PACKED; + +}; + + +/** + * Network format for IPv6 addresses. + */ +struct IPv6HttpAddress +{ + /** + * IPv6 address. + */ + struct in6_addr ipv6_addr GNUNET_PACKED; + + /** + * Port number, in network byte order. + */ + uint16_t u6_port GNUNET_PACKED; + +}; + + +/** + * Message to send using http + */ +struct HTTP_Message +{ + /** + * next pointer for double linked list + */ + struct HTTP_Message * next; + + /** + * previous pointer for double linked list + */ + struct HTTP_Message * prev; + + /** + * buffer containing data to send + */ + char *buf; + + /** + * amount of data already sent + */ + size_t pos; + + /** + * buffer length + */ + size_t size; + + /** + * Continuation function to call once the transmission buffer + * has again space available. NULL if there is no + * continuation to call. + */ + GNUNET_TRANSPORT_TransmitContinuation transmit_cont; + + /** + * Closure for transmit_cont. + */ + void *transmit_cont_cls; +}; + + +struct HTTP_PeerContext +{ + /** + * peer's identity + */ + struct GNUNET_PeerIdentity identity; + + /** + * Pointer to the global plugin struct. + */ + struct Plugin *plugin; + + /** + * Linked list of connections with this peer + * head + */ + struct Session * head; + + /** + * Linked list of connections with this peer + * tail + */ + struct Session * tail; + + /** + * id for next session + */ + size_t session_id_counter; + + /** + * Last session used to send data + */ + struct Session * last_session; +}; + + +struct Session +{ + /** + * API requirement. + */ + struct SessionHeader header; + + /** + * next session in linked list + */ + struct Session * next; + + /** + * previous session in linked list + */ + struct Session * prev; + + /** + * address of this session + */ + void * addr; + + /** + * address length + */ + size_t addrlen; + + /** + * target url + */ + char * url; + + /** + * Message queue for outbound messages + * head of queue + */ + struct HTTP_Message * pending_msgs_head; + + /** + * Message queue for outbound messages + * tail of queue + */ + struct HTTP_Message * pending_msgs_tail; + + /** + * partner peer this connection belongs to + */ + struct HTTP_PeerContext * peercontext; + + /** + * message stream tokenizer for incoming data + */ + struct GNUNET_SERVER_MessageStreamTokenizer *msgtok; + + /** + * session direction + * outbound: OUTBOUND (GNUNET_YES) + * inbound : INBOUND (GNUNET_NO) + */ + unsigned int direction; + + /** + * is session connected to send data? + */ + unsigned int send_connected; + + /** + * is send connection active? + */ + unsigned int send_active; + + /** + * connection disconnect forced (e.g. from transport) + */ + unsigned int send_force_disconnect; + + /** + * is session connected to receive data? + */ + unsigned int recv_connected; + + /** + * is receive connection active? + */ + unsigned int recv_active; + + /** + * connection disconnect forced (e.g. from transport) + */ + unsigned int recv_force_disconnect; + + /** + * id for next session + * NOTE: 0 is not an ID, zero is not defined. A correct ID is always > 0 + */ + size_t session_id; + + /** + * entity managing sending data + * outbound session: CURL * + * inbound session: mhd_connection * + */ + void * send_endpoint; + + /** + * entity managing recieving data + * outbound session: CURL * + * inbound session: mhd_connection * + */ + void * recv_endpoint; +}; + +/** + * Encapsulation of all of the state of the plugin. + */ +struct Plugin +{ + /** + * Our environment. + */ + struct GNUNET_TRANSPORT_PluginEnvironment *env; + + /** + * Handle for reporting statistics. + */ + struct GNUNET_STATISTICS_Handle *stats; + + unsigned int port_inbound; + + struct GNUNET_CONTAINER_MultiHashMap *peers; + + /** + * Daemon for listening for new IPv4 connections. + */ + struct MHD_Daemon *http_server_daemon_v4; + + /** + * Daemon for listening for new IPv6connections. + */ + struct MHD_Daemon *http_server_daemon_v6; + + /** + * Our primary task for http daemon handling IPv4 connections + */ + GNUNET_SCHEDULER_TaskIdentifier http_server_task_v4; + + /** + * Our primary task for http daemon handling IPv6 connections + */ + GNUNET_SCHEDULER_TaskIdentifier http_server_task_v6; + + /** + * The task sending data + */ + GNUNET_SCHEDULER_TaskIdentifier http_curl_task; + + /** + * cURL Multihandle + */ + CURLM * multi_handle; + + /** + * Our ASCII encoded, hashed peer identity + * This string is used to distinguish between connections and is added to the urls + */ + struct GNUNET_CRYPTO_HashAsciiEncoded my_ascii_hash_ident; + + struct sockaddr_in * bind4_address; + struct sockaddr_in6 * bind6_address; + char * bind_hostname; + int use_ipv6; + int use_ipv4; +}; + + +/** + * Function called for a quick conversion of the binary address to + * a numeric address. Note that the caller must not free the + * address and that the next call to this function is allowed + * to override the address again. + * + * @param cls closure + * @param addr binary address + * @param addrlen length of the address + * @return string representing the same address + */ +static const char* +http_plugin_address_to_string (void *cls, + const void *addr, + size_t addrlen); + + +/** + * Call MHD to process pending ipv4 requests and then go back + * and schedule the next run. + */ +static void http_server_daemon_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +/** + * Call MHD to process pending ipv6 requests and then go back + * and schedule the next run. + */ +static void http_server_daemon_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + +/** + * Function setting up curl handle and selecting message to send + * @param cls plugin + * @param ses session to send data to + * @param con connection + * @return bytes sent to peer + */ +static ssize_t send_check_connections (void *cls, struct Session *ps); + +/** + * Function setting up file descriptors and scheduling task to run + * @param cls closure + * @param ses session to send data to + * @param + */ +static int curl_schedule(void *cls ); + + + +static char * create_url(void * cls, const void * addr, size_t addrlen, size_t id) +{ + struct Plugin *plugin = cls; + char *url = NULL; + + GNUNET_assert ((addr!=NULL) && (addrlen != 0)); + GNUNET_asprintf(&url, + "https://%s/%s;%u", + http_plugin_address_to_string(NULL, addr, addrlen), + (char *) (&plugin->my_ascii_hash_ident),id); + + return url; +} + +/** + * Removes a message from the linked list of messages + * @param con connection to remove message from + * @param msg message to remove + * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success + */ +static int remove_http_message (struct Session * ps, struct HTTP_Message * msg) +{ + GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_tail,msg); + GNUNET_free(msg); + return GNUNET_OK; +} + +int remove_peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *value); + +/** + * Removes a session from the linked list of sessions + * @param pc peer context + * @param ps session + * @param call_msg_cont GNUNET_YES to call pending message continuations, otherwise no + * @param call_msg_cont_result, result to call message continuations with + * @return GNUNET_SYSERR if msg not found, GNUNET_OK on success + */ +static int remove_session (struct HTTP_PeerContext * pc, struct Session * ps, int call_msg_cont, int call_msg_cont_result) +{ + struct HTTP_Message * msg; + struct Plugin * plugin = ps->peercontext->plugin; + +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: removing %s session %X with id %u\n", ps, (ps->direction == INBOUND) ? "inbound" : "outbound", ps, ps->session_id); +#endif + plugin->env->session_end(plugin, &pc->identity, ps); + + GNUNET_free_non_null (ps->addr); + GNUNET_SERVER_mst_destroy (ps->msgtok); + GNUNET_free(ps->url); + + if (ps->direction==INBOUND) + { + if (ps->recv_endpoint != NULL) + { + curl_easy_cleanup(ps->recv_endpoint); + ps->recv_endpoint = NULL; + } + if (ps->send_endpoint != NULL) + { + curl_easy_cleanup(ps->send_endpoint); + ps->send_endpoint = NULL; + } + } + + msg = ps->pending_msgs_head; + while (msg!=NULL) + { + if ((call_msg_cont == GNUNET_YES) && (msg->transmit_cont!=NULL)) + { + msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,call_msg_cont_result); + } + GNUNET_free(msg); + GNUNET_CONTAINER_DLL_remove(ps->pending_msgs_head,ps->pending_msgs_head,msg); + msg = ps->pending_msgs_head; + } + + GNUNET_CONTAINER_DLL_remove(pc->head,pc->tail,ps); + GNUNET_free(ps); + ps = NULL; + + /* no sessions left remove peer */ + if (pc->head==NULL) + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No sessions left for peer `%s', removing context\n",GNUNET_i2s(&pc->identity)); +#endif + remove_peer_context_Iterator(plugin, &pc->identity.hashPubKey, pc); + } + + return GNUNET_OK; +} + +int remove_peer_context_Iterator (void *cls, const GNUNET_HashCode *key, void *value) +{ + struct Plugin *plugin = cls; + struct HTTP_PeerContext * pc = value; + struct Session * ps = pc->head; + struct Session * tmp = NULL; + struct HTTP_Message * msg = NULL; + struct HTTP_Message * msg_tmp = NULL; +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Freeing context for peer `%s'\n",GNUNET_i2s(&pc->identity)); +#endif + GNUNET_CONTAINER_multihashmap_remove (plugin->peers, &pc->identity.hashPubKey, pc); + while (ps!=NULL) + { + plugin->env->session_end(plugin, &pc->identity, ps); + tmp = ps->next; + + GNUNET_free_non_null (ps->addr); + GNUNET_free(ps->url); + if (ps->msgtok != NULL) + GNUNET_SERVER_mst_destroy (ps->msgtok); + + msg = ps->pending_msgs_head; + while (msg!=NULL) + { + msg_tmp = msg->next; + GNUNET_free(msg); + msg = msg_tmp; + } + if (ps->direction==OUTBOUND) + { + if (ps->send_endpoint!=NULL) + curl_easy_cleanup(ps->send_endpoint); + if (ps->recv_endpoint!=NULL) + curl_easy_cleanup(ps->recv_endpoint); + } + + GNUNET_free(ps); + ps=tmp; + } + GNUNET_free(pc); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP peers active"), + -1, + GNUNET_NO); + return GNUNET_YES; +} + +/** + * Add the IP of our network interface to the list of + * our external IP addresses. + * + * @param cls the 'struct Plugin*' + * @param name name of the interface + * @param isDefault do we think this may be our default interface + * @param addr address of the interface + * @param addrlen number of bytes in addr + * @return GNUNET_OK to continue iterating + */ +static int +process_interfaces (void *cls, + const char *name, + int isDefault, + const struct sockaddr *addr, socklen_t addrlen) +{ + struct Plugin *plugin = cls; + struct IPv4HttpAddress * t4; + struct IPv6HttpAddress * t6; + int af; + + + GNUNET_assert(cls !=NULL); + af = addr->sa_family; + if ((af == AF_INET) && (plugin->use_ipv4 == GNUNET_YES) && (plugin->bind6_address == NULL)) + { + struct in_addr bnd_cmp = ((struct sockaddr_in *) addr)->sin_addr; + t4 = GNUNET_malloc(sizeof(struct IPv4HttpAddress)); + /* Not skipping loopback addresses + if (INADDR_LOOPBACK == ntohl(((struct sockaddr_in *) addr)->sin_addr.s_addr)) + { + + return GNUNET_OK; + } + */ + t4->ipv4_addr = ((struct sockaddr_in *) addr)->sin_addr.s_addr; + t4->u_port = htons (plugin->port_inbound); + if (plugin->bind4_address != NULL) + { + if (0 == memcmp(&plugin->bind4_address->sin_addr, &bnd_cmp, sizeof (struct in_addr))) + { + plugin->env->notify_address(plugin->env->cls,"https",t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL); + } + } + else + { + plugin->env->notify_address(plugin->env->cls,"https",t4, sizeof (struct IPv4HttpAddress), GNUNET_TIME_UNIT_FOREVER_REL); + } + GNUNET_free (t4); + } + else if ((af == AF_INET6) && (plugin->use_ipv6 == GNUNET_YES) && (plugin->bind4_address == NULL)) + { + struct in6_addr bnd_cmp6 = ((struct sockaddr_in6 *) addr)->sin6_addr; + t6 = GNUNET_malloc(sizeof(struct IPv6HttpAddress)); + if (IN6_IS_ADDR_LINKLOCAL (&((struct sockaddr_in6 *) addr)->sin6_addr)) + { + return GNUNET_OK; + } + + if (plugin->bind6_address != NULL) + { + if (0 == memcmp(&plugin->bind6_address->sin6_addr, &bnd_cmp6, sizeof (struct in6_addr))) + { + memcpy (&t6->ipv6_addr, + &((struct sockaddr_in6 *) addr)->sin6_addr, + sizeof (struct in6_addr)); + t6->u6_port = htons (plugin->port_inbound); + plugin->env->notify_address(plugin->env->cls,"https",t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL); + } + } + else + { + memcpy (&t6->ipv6_addr, + &((struct sockaddr_in6 *) addr)->sin6_addr, + sizeof (struct in6_addr)); + t6->u6_port = htons (plugin->port_inbound); + plugin->env->notify_address(plugin->env->cls,"https",t6,sizeof (struct IPv6HttpAddress) , GNUNET_TIME_UNIT_FOREVER_REL); + } + GNUNET_free (t6); + } + return GNUNET_OK; +} + + +/** + * Callback called by MHD when a connection is terminated + */ +static void mhd_termination_cb (void *cls, struct MHD_Connection * connection, void **httpSessionCache) +{ + struct Session * ps = *httpSessionCache; + if (ps == NULL) + return; + struct HTTP_PeerContext * pc = ps->peercontext; + + if (connection==ps->recv_endpoint) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connection from peer `%s' was terminated\n", ps, GNUNET_i2s(&pc->identity)); +#endif + ps->recv_active = GNUNET_NO; + ps->recv_connected = GNUNET_NO; + ps->recv_endpoint = NULL; + } + if (connection==ps->send_endpoint) + { + + ps->send_active = GNUNET_NO; + ps->send_connected = GNUNET_NO; + ps->send_endpoint = NULL; +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound connection from peer `%s' was terminated\n", ps, GNUNET_i2s(&pc->identity)); +#endif + } + + /* if both connections disconnected, remove session */ + if ((ps->send_connected == GNUNET_NO) && (ps->recv_connected == GNUNET_NO)) + { + GNUNET_STATISTICS_update (pc->plugin->env->stats, + gettext_noop ("# HTTPS inbound sessions for peers active"), + -1, + GNUNET_NO); + remove_session(pc,ps,GNUNET_YES,GNUNET_SYSERR); + } +} + +static void mhd_write_mst_cb (void *cls, + void *client, + const struct GNUNET_MessageHeader *message) +{ + + struct Session *ps = cls; + struct HTTP_PeerContext *pc = ps->peercontext; + GNUNET_assert(ps != NULL); + GNUNET_assert(pc != NULL); +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n", + ps, + ntohs(message->type), + ntohs(message->size), + GNUNET_i2s(&(ps->peercontext)->identity),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen)); +#endif + pc->plugin->env->receive (ps->peercontext->plugin->env->cls, + &pc->identity, + message, 1, ps, + NULL, + 0); +} + +/** + * Check if ip is allowed to connect. + */ +static int +mhd_accept_cb (void *cls, + const struct sockaddr *addr, socklen_t addr_len) +{ +#if 0 + struct Plugin *plugin = cls; +#endif + /* Every connection is accepted, nothing more to do here */ + return MHD_YES; +} + +int mhd_send_callback (void *cls, uint64_t pos, char *buf, int max) +{ + int bytes_read = 0; + + struct Session * ps = cls; + struct HTTP_PeerContext * pc; + struct HTTP_Message * msg; + + GNUNET_assert (ps!=NULL); + pc = ps->peercontext; + msg = ps->pending_msgs_tail; + if (ps->send_force_disconnect==GNUNET_YES) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound forced to disconnect\n",ps); +#endif + return -1; + } + + if (msg!=NULL) + { + if ((msg->size-msg->pos) <= max) + { + memcpy(buf,&msg->buf[msg->pos],(msg->size-msg->pos)); + bytes_read = msg->size-msg->pos; + msg->pos+=(msg->size-msg->pos); + } + else + { + memcpy(buf,&msg->buf[msg->pos],max); + msg->pos+=max; + bytes_read = max; + } + + if (msg->pos==msg->size) + { + if (NULL!=msg->transmit_cont) + msg->transmit_cont (msg->transmit_cont_cls,&pc->identity,GNUNET_OK); + remove_http_message(ps,msg); + } + } + return bytes_read; +} + +/** + * Process GET or PUT request received via MHD. For + * GET, queue response that will send back our pending + * messages. For PUT, process incoming data and send + * to GNUnet core. In either case, check if a session + * already exists and create a new one if not. + */ +static int +mdh_access_cb (void *cls, + struct MHD_Connection *mhd_connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t * upload_data_size, void **httpSessionCache) +{ + struct Plugin *plugin = cls; + struct MHD_Response *response; + const union MHD_ConnectionInfo * conn_info; + + struct sockaddr_in *addrin; + struct sockaddr_in6 *addrin6; + + char address[INET6_ADDRSTRLEN+14]; + struct GNUNET_PeerIdentity pi_in; + size_t id_num = 0; + + struct IPv4HttpAddress ipv4addr; + struct IPv6HttpAddress ipv6addr; + + struct HTTP_PeerContext *pc; + struct Session *ps = NULL; + struct Session *ps_tmp = NULL; + + int res = GNUNET_NO; + int send_error_to_client; + void * addr; + size_t addr_len; + + GNUNET_assert(cls !=NULL); + send_error_to_client = GNUNET_NO; + + if (NULL == *httpSessionCache) + { + /* check url for peer identity , if invalid send HTTP 404*/ + size_t len = strlen(&url[1]); + char * peer = GNUNET_malloc(104+1); + + if ((len>104) && (url[104]==';')) + { + char * id = GNUNET_malloc((len-104)+1); + strcpy(id,&url[105]); + memcpy(peer,&url[1],103); + peer[103] = '\0'; + id_num = strtoul ( id, NULL , 10); + GNUNET_free(id); + } + res = GNUNET_CRYPTO_hash_from_string (peer, &(pi_in.hashPubKey)); + GNUNET_free(peer); + if ( GNUNET_SYSERR == res ) + { + response = MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE),HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO); + res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response (response); +#if DEBUG_CONNECTIONS + if (res == MHD_YES) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, sent HTTP 1.1/404\n"); + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Peer has no valid ident, could not send error\n"); +#endif + return res; + } + } + else + { + ps = *httpSessionCache; + pc = ps->peercontext; + } + + if (NULL == *httpSessionCache) + { + /* get peer context */ + pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &pi_in.hashPubKey); + /* Peer unknown */ + if (pc==NULL) + { + pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext)); + pc->plugin = plugin; + pc->session_id_counter=1; + pc->last_session = NULL; + memcpy(&pc->identity, &pi_in, sizeof(struct GNUNET_PeerIdentity)); + GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP peers active"), + 1, + GNUNET_NO); + } + + conn_info = MHD_get_connection_info(mhd_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS ); + /* Incoming IPv4 connection */ + if ( AF_INET == conn_info->client_addr->sin_family) + { + addrin = conn_info->client_addr; + inet_ntop(addrin->sin_family, &(addrin->sin_addr),address,INET_ADDRSTRLEN); + memcpy(&ipv4addr.ipv4_addr,&(addrin->sin_addr),sizeof(struct in_addr)); + ipv4addr.u_port = addrin->sin_port; + addr = &ipv4addr; + addr_len = sizeof(struct IPv4HttpAddress); + } + /* Incoming IPv6 connection */ + if ( AF_INET6 == conn_info->client_addr->sin_family) + { + addrin6 = (struct sockaddr_in6 *) conn_info->client_addr; + inet_ntop(addrin6->sin6_family, &(addrin6->sin6_addr),address,INET6_ADDRSTRLEN); + memcpy(&ipv6addr.ipv6_addr,&(addrin6->sin6_addr),sizeof(struct in6_addr)); + ipv6addr.u6_port = addrin6->sin6_port; + addr = &ipv6addr; + addr_len = sizeof(struct IPv6HttpAddress); + } + + ps = NULL; + /* only inbound sessions here */ + + ps_tmp = pc->head; + while (ps_tmp!=NULL) + { + if ((ps_tmp->direction==INBOUND) && (ps_tmp->session_id == id_num) && (id_num!=0)) + { + if ((ps_tmp->recv_force_disconnect!=GNUNET_YES) && (ps_tmp->send_force_disconnect!=GNUNET_YES)) + ps=ps_tmp; + break; + } + ps_tmp=ps_tmp->next; + } + + if (ps==NULL) + { + ps = GNUNET_malloc(sizeof (struct Session)); + ps->addr = GNUNET_malloc(addr_len); + memcpy(ps->addr,addr,addr_len); + ps->addrlen = addr_len; + ps->direction=INBOUND; + ps->pending_msgs_head = NULL; + ps->pending_msgs_tail = NULL; + ps->send_connected=GNUNET_NO; + ps->send_active=GNUNET_NO; + ps->recv_connected=GNUNET_NO; + ps->recv_active=GNUNET_NO; + ps->peercontext=pc; + ps->session_id =id_num; + ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id); + GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTPS inbound sessions for peers active"), + 1, + GNUNET_NO); + } + + *httpSessionCache = ps; + if (ps->msgtok==NULL) + ps->msgtok = GNUNET_SERVER_mst_create (&mhd_write_mst_cb, ps); +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: HTTPS Daemon has new an incoming `%s' request from peer `%s' (`%s')\n", + ps, + method, + GNUNET_i2s(&pc->identity), + http_plugin_address_to_string(NULL, ps->addr, ps->addrlen)); +#endif + } + + /* Is it a PUT or a GET request */ + if (0 == strcmp (MHD_HTTP_METHOD_PUT, method)) + { + if (ps->recv_force_disconnect == GNUNET_YES) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound connection was forced to disconnect\n",ps); +#endif + ps->recv_active = GNUNET_NO; + return MHD_NO; + } + if ((*upload_data_size == 0) && (ps->recv_active==GNUNET_NO)) + { + ps->recv_endpoint = mhd_connection; + ps->recv_connected = GNUNET_YES; + ps->recv_active = GNUNET_YES; + ps->recv_force_disconnect = GNUNET_NO; +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound PUT connection connected\n",ps); +#endif + return MHD_YES; + } + + /* Transmission of all data complete */ + if ((*upload_data_size == 0) && (ps->recv_active == GNUNET_YES)) + { + response = MHD_create_response_from_data (strlen (HTTP_PUT_RESPONSE),HTTP_PUT_RESPONSE, MHD_NO, MHD_NO); + res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Sent HTTP/1.1: 200 OK as PUT Response\n",ps); +#endif + MHD_destroy_response (response); + ps->recv_active=GNUNET_NO; + return MHD_YES; + } + + /* Recieving data */ + if ((*upload_data_size > 0) && (ps->recv_active == GNUNET_YES)) + { + res = GNUNET_SERVER_mst_receive(ps->msgtok, ps, upload_data,*upload_data_size, GNUNET_NO, GNUNET_NO); + (*upload_data_size) = 0; + return MHD_YES; + } + else + return MHD_NO; + } + if ( 0 == strcmp (MHD_HTTP_METHOD_GET, method) ) + { + if (ps->send_force_disconnect == GNUNET_YES) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound connection was forced to disconnect\n",ps); +#endif + ps->send_active = GNUNET_NO; + return MHD_NO; + } + ps->send_connected = GNUNET_YES; + ps->send_active = GNUNET_YES; + ps->send_endpoint = mhd_connection; + ps->send_force_disconnect = GNUNET_NO; +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound GET connection connected\n",ps); +#endif + response = MHD_create_response_from_callback(-1,32 * 1024, &mhd_send_callback, ps, NULL); + res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + return MHD_YES; + } + return MHD_NO; +} + +/** + * Function that queries MHD's select sets and + * starts the task waiting for them. + */ +static GNUNET_SCHEDULER_TaskIdentifier +http_server_daemon_prepare (void * cls, struct MHD_Daemon *daemon_handle) +{ + struct Plugin *plugin = cls; + GNUNET_SCHEDULER_TaskIdentifier ret; + fd_set rs; + fd_set ws; + fd_set es; + struct GNUNET_NETWORK_FDSet *wrs; + struct GNUNET_NETWORK_FDSet *wws; + struct GNUNET_NETWORK_FDSet *wes; + int max; + unsigned long long timeout; + int haveto; + struct GNUNET_TIME_Relative tv; + + GNUNET_assert(cls !=NULL); + ret = GNUNET_SCHEDULER_NO_TASK; + FD_ZERO(&rs); + FD_ZERO(&ws); + FD_ZERO(&es); + wrs = GNUNET_NETWORK_fdset_create (); + wes = GNUNET_NETWORK_fdset_create (); + wws = GNUNET_NETWORK_fdset_create (); + max = -1; + GNUNET_assert (MHD_YES == + MHD_get_fdset (daemon_handle, + &rs, + &ws, + &es, + &max)); + haveto = MHD_get_timeout (daemon_handle, &timeout); + if (haveto == MHD_YES) + tv.value = (uint64_t) timeout; + else + tv = GNUNET_TIME_UNIT_FOREVER_REL; + GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max); + GNUNET_NETWORK_fdset_copy_native (wws, &ws, max); + GNUNET_NETWORK_fdset_copy_native (wes, &es, max); + if (daemon_handle == plugin->http_server_daemon_v4) + { + if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4); + plugin->http_server_daemon_v4 = GNUNET_SCHEDULER_NO_TASK; + } + + ret = GNUNET_SCHEDULER_add_select (plugin->env->sched, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_TASK, + tv, + wrs, + wws, + &http_server_daemon_v4_run, + plugin); + } + if (daemon_handle == plugin->http_server_daemon_v6) + { + if (plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6); + plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK; + } + + ret = GNUNET_SCHEDULER_add_select (plugin->env->sched, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_TASK, + tv, + wrs, + wws, + &http_server_daemon_v6_run, + plugin); + } + GNUNET_NETWORK_fdset_destroy (wrs); + GNUNET_NETWORK_fdset_destroy (wws); + GNUNET_NETWORK_fdset_destroy (wes); + return ret; +} + +/** + * Call MHD to process pending requests and then go back + * and schedule the next run. + */ +static void http_server_daemon_v4_run (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Plugin *plugin = cls; + + GNUNET_assert(cls !=NULL); + plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK; + + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + + GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v4)); + plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4); + return; +} + + +/** + * Call MHD to process pending requests and then go back + * and schedule the next run. + */ +static void http_server_daemon_v6_run (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Plugin *plugin = cls; + + GNUNET_assert(cls !=NULL); + plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK; + + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + + GNUNET_assert (MHD_YES == MHD_run (plugin->http_server_daemon_v6)); + plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6); + return; +} + +static size_t curl_get_header_cb( void *ptr, size_t size, size_t nmemb, void *stream) +{ + struct Session * ps = stream; + char * tmp; + size_t len = size * nmemb; + long http_result = 0; + int res; + /* Getting last http result code */ + if (ps->recv_connected==GNUNET_NO) + { + GNUNET_assert(NULL!=ps); + res = curl_easy_getinfo(ps->recv_endpoint, CURLINFO_RESPONSE_CODE, &http_result); + if (CURLE_OK == res) + { + if (http_result == 200) + { + ps->recv_connected = GNUNET_YES; + ps->recv_active = GNUNET_YES; +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: connected to recieve data\n",ps); +#endif + // Calling send_check_connections again since receive is established + send_check_connections (ps->peercontext->plugin, ps); + } + } + } + + tmp = NULL; + if ((size * nmemb) < SIZE_MAX) + tmp = GNUNET_malloc (len+1); + + if ((tmp != NULL) && (len > 0)) + { + memcpy(tmp,ptr,len); + if (len>=2) + { + if (tmp[len-2] == 13) + tmp[len-2]= '\0'; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Header: %s\n",ps,tmp); + } + if (NULL != tmp) + GNUNET_free (tmp); + + return size * nmemb; +} + +static size_t curl_put_header_cb( void *ptr, size_t size, size_t nmemb, void *stream) +{ + struct Session * ps = stream; + + char * tmp; + size_t len = size * nmemb; + long http_result = 0; + int res; + + /* Getting last http result code */ + GNUNET_assert(NULL!=ps); + res = curl_easy_getinfo(ps->send_endpoint, CURLINFO_RESPONSE_CODE, &http_result); + if (CURLE_OK == res) + { + if ((http_result == 100) && (ps->send_connected==GNUNET_NO)) + { + ps->send_connected = GNUNET_YES; + ps->send_active = GNUNET_YES; +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: connected to send data\n",ps); +#endif + } + if ((http_result == 200) && (ps->send_connected==GNUNET_YES)) + { + ps->send_connected = GNUNET_NO; + ps->send_active = GNUNET_NO; +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: sending disconnected\n",ps); +#endif + } + } + + tmp = NULL; + if ((size * nmemb) < SIZE_MAX) + tmp = GNUNET_malloc (len+1); + + if ((tmp != NULL) && (len > 0)) + { + memcpy(tmp,ptr,len); + if (len>=2) + { + if (tmp[len-2] == 13) + tmp[len-2]= '\0'; + } + } + if (NULL != tmp) + GNUNET_free (tmp); + + return size * nmemb; +} + +/** + * Callback method used with libcurl + * Method is called when libcurl needs to read data during sending + * @param stream pointer where to write data + * @param size size of an individual element + * @param nmemb count of elements that can be written to the buffer + * @param ptr source pointer, passed to the libcurl handle + * @return bytes written to stream + */ +static size_t curl_send_cb(void *stream, size_t size, size_t nmemb, void *ptr) +{ + struct Session * ps = ptr; + struct HTTP_Message * msg = ps->pending_msgs_tail; + size_t bytes_sent; + size_t len; + + if (ps->send_active == GNUNET_NO) + return CURL_READFUNC_PAUSE; + + + if ((ps->pending_msgs_tail == NULL) && (ps->send_active == GNUNET_YES)) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: No Message to send, pausing connection\n",ps); +#endif + ps->send_active = GNUNET_NO; + return CURL_READFUNC_PAUSE; + } + + msg = ps->pending_msgs_tail; + /* data to send */ + if (msg->pos < msg->size) + { + /* data fit in buffer */ + if ((msg->size - msg->pos) <= (size * nmemb)) + { + len = (msg->size - msg->pos); + memcpy(stream, &msg->buf[msg->pos], len); + msg->pos += len; + bytes_sent = len; + } + else + { + len = size*nmemb; + memcpy(stream, &msg->buf[msg->pos], len); + msg->pos += len; + bytes_sent = len; + } + } + /* no data to send */ + else + { + bytes_sent = 0; + } + + if ( msg->pos == msg->size) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: Message with %u bytes sent, removing message from queue \n",ps, msg->pos); +#endif + /* Calling transmit continuation */ + if (( NULL != ps->pending_msgs_tail) && (NULL != ps->pending_msgs_tail->transmit_cont)) + msg->transmit_cont (ps->pending_msgs_tail->transmit_cont_cls,&(ps->peercontext)->identity,GNUNET_OK); + remove_http_message(ps, msg); + } + return bytes_sent; +} + +static void curl_receive_mst_cb (void *cls, + void *client, + const struct GNUNET_MessageHeader *message) +{ + struct Session *ps = cls; + struct HTTP_PeerContext *pc = ps->peercontext; + GNUNET_assert(ps != NULL); + GNUNET_assert(pc != NULL); +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection %X: Forwarding message to transport service, type %u and size %u from `%s' (`%s')\n", + ps, + ntohs(message->type), + ntohs(message->size), + GNUNET_i2s(&(pc->identity)),http_plugin_address_to_string(NULL,ps->addr,ps->addrlen)); +#endif + pc->plugin->env->receive (pc->plugin->env->cls, + &pc->identity, + message, 1, ps, + ps->addr, + ps->addrlen); +} + + +/** +* Callback method used with libcurl +* Method is called when libcurl needs to write data during sending +* @param stream pointer where to write data +* @param size size of an individual element +* @param nmemb count of elements that can be written to the buffer +* @param ptr destination pointer, passed to the libcurl handle +* @return bytes read from stream +*/ +static size_t curl_receive_cb( void *stream, size_t size, size_t nmemb, void *ptr) +{ + struct Session * ps = ptr; +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: %u bytes received\n",ps, size*nmemb); +#endif + GNUNET_SERVER_mst_receive(ps->msgtok, ps, stream, size*nmemb, GNUNET_NO, GNUNET_NO); + return (size * nmemb); + +} + +static void curl_perform (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Plugin *plugin = cls; + static unsigned int handles_last_run; + int running; + struct CURLMsg *msg; + CURLMcode mret; + struct Session *ps = NULL; + struct HTTP_PeerContext *pc = NULL; + struct HTTP_Message * cur_msg = NULL; + long http_result; + char * tmp; + + GNUNET_assert(cls !=NULL); + + plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + + do + { + running = 0; + mret = curl_multi_perform (plugin->multi_handle, &running); + if ((running < handles_last_run) && (running>0)) + { + do + { + + msg = curl_multi_info_read (plugin->multi_handle, &running); + if (running == 0) + break; + /* get session for affected curl handle */ + GNUNET_assert ( msg->easy_handle != NULL ); + curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &tmp); + ps = (struct Session *) tmp; + GNUNET_assert ( ps != NULL ); + pc = ps->peercontext; + GNUNET_assert ( pc != NULL ); + switch (msg->msg) + { + + case CURLMSG_DONE: + if ( (msg->data.result != CURLE_OK) && + (msg->data.result != CURLE_GOT_NOTHING) ) + { + /* sending msg failed*/ + if (msg->easy_handle == ps->send_endpoint) + { +#if DEBUG_CONNECTIONS + GNUNET_log(GNUNET_ERROR_TYPE_INFO, + _("Connection %X: HTTPS PUT to peer `%s' (`%s') failed: `%s' `%s'\n"), + ps, + GNUNET_i2s(&pc->identity), + http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), + "curl_multi_perform", + curl_easy_strerror (msg->data.result)); +#endif + ps->send_connected = GNUNET_NO; + ps->send_active = GNUNET_NO; + curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint); + //curl_easy_cleanup(ps->send_endpoint); + //ps->send_endpoint=NULL; + cur_msg = ps->pending_msgs_tail; + if (( NULL != cur_msg) && ( NULL != cur_msg->transmit_cont)) + cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR); + } + /* GET connection failed */ + if (msg->easy_handle == ps->recv_endpoint) + { +#if DEBUG_CONNECTIONS + GNUNET_log(GNUNET_ERROR_TYPE_INFO, + _("Connection %X: HTTPS GET to peer `%s' (`%s') failed: `%s' `%s'\n"), + ps, + GNUNET_i2s(&pc->identity), + http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), + "curl_multi_perform", + curl_easy_strerror (msg->data.result)); +#endif + ps->recv_connected = GNUNET_NO; + ps->recv_active = GNUNET_NO; + curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint); + //curl_easy_cleanup(ps->recv_endpoint); + //ps->recv_endpoint=NULL; + } + } + else + { + if (msg->easy_handle == ps->send_endpoint) + { + GNUNET_assert (CURLE_OK == curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &http_result)); +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection %X: HTTPS PUT connection to peer `%s' (`%s') was closed with HTTP code %u\n", + ps, + GNUNET_i2s(&pc->identity), + http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), + http_result); +#endif + /* Calling transmit continuation */ + cur_msg = ps->pending_msgs_tail; + if (( NULL != cur_msg) && (NULL != cur_msg->transmit_cont)) + { + /* HTTP 1xx : Last message before here was informational */ + if ((http_result >=100) && (http_result < 200)) + cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK); + /* HTTP 2xx: successful operations */ + if ((http_result >=200) && (http_result < 300)) + cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_OK); + /* HTTP 3xx..5xx: error */ + if ((http_result >=300) && (http_result < 600)) + cur_msg->transmit_cont (cur_msg->transmit_cont_cls,&pc->identity,GNUNET_SYSERR); + } + ps->send_connected = GNUNET_NO; + ps->send_active = GNUNET_NO; + curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint); + //curl_easy_cleanup(ps->send_endpoint); + //ps->send_endpoint =NULL; + } + if (msg->easy_handle == ps->recv_endpoint) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Connection %X: HTTP GET connection to peer `%s' (`%s') was closed with HTTP code %u\n", + ps, + GNUNET_i2s(&pc->identity), + http_plugin_address_to_string(NULL, ps->addr, ps->addrlen), + http_result); +#endif + ps->recv_connected = GNUNET_NO; + ps->recv_active = GNUNET_NO; + curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint); + //curl_easy_cleanup(ps->recv_endpoint); + //ps->recv_endpoint=NULL; + } + } + if ((ps->recv_connected == GNUNET_NO) && (ps->send_connected == GNUNET_NO)) + remove_session (pc, ps, GNUNET_YES, GNUNET_SYSERR); + break; + default: + break; + } + + } + while ( (running > 0) ); + } + handles_last_run = running; + } + while (mret == CURLM_CALL_MULTI_PERFORM); + curl_schedule(plugin); +} + + +/** + * Function setting up file descriptors and scheduling task to run + * @param ses session to send data to + * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok + */ +static int curl_schedule(void *cls) +{ + struct Plugin *plugin = cls; + fd_set rs; + fd_set ws; + fd_set es; + int max; + struct GNUNET_NETWORK_FDSet *grs; + struct GNUNET_NETWORK_FDSet *gws; + long to; + CURLMcode mret; + + GNUNET_assert(cls !=NULL); + + /* Cancel previous scheduled task */ + if (plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task); + plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK; + } + max = -1; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + mret = curl_multi_fdset (plugin->multi_handle, &rs, &ws, &es, &max); + if (mret != CURLM_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("%s failed at %s:%d: `%s'\n"), + "curl_multi_fdset", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return GNUNET_SYSERR; + } + mret = curl_multi_timeout (plugin->multi_handle, &to); + if (mret != CURLM_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("%s failed at %s:%d: `%s'\n"), + "curl_multi_timeout", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return GNUNET_SYSERR; + } + + grs = GNUNET_NETWORK_fdset_create (); + gws = GNUNET_NETWORK_fdset_create (); + GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1); + GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1); + plugin->http_curl_task = GNUNET_SCHEDULER_add_select (plugin->env->sched, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0), + grs, + gws, + &curl_perform, + plugin); + GNUNET_NETWORK_fdset_destroy (gws); + GNUNET_NETWORK_fdset_destroy (grs); + return GNUNET_OK; +} + +/** + * Function setting up curl handle and selecting message to send + * @param cls plugin + * @param ses session to send data to + * @param con connection + * @return GNUNET_SYSERR on failure, GNUNET_NO if connecting, GNUNET_YES if ok + */ +static ssize_t send_check_connections (void *cls, struct Session *ps) +{ + struct Plugin *plugin = cls; + CURLMcode mret; + struct HTTP_Message * msg; + + struct GNUNET_TIME_Relative timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT; + + GNUNET_assert(cls !=NULL); + + if (ps->direction == OUTBOUND) + { + /* RECV DIRECTION */ + /* Check if session is connected to receive data, otherwise connect to peer */ + if (ps->recv_connected == GNUNET_NO) + { + int fresh = GNUNET_NO; + if (ps->recv_endpoint == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "created handle\n"); + fresh = GNUNET_YES; + ps->recv_endpoint = curl_easy_init(); + } +#if DEBUG_CURL + curl_easy_setopt(ps->recv_endpoint, CURLOPT_VERBOSE, 1L); +#endif + curl_easy_setopt(ps->recv_endpoint, CURLOPT_URL, ps->url); + curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); + //curl_easy_setopt (ps->recv_endpoint, CURLOPT_SSL_CIPHER_LIST, cipher_suite); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_HEADERFUNCTION, &curl_get_header_cb); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEHEADER, ps); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_READFUNCTION, curl_send_cb); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_READDATA, ps); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_WRITEDATA, ps); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_TIMEOUT, (long) timeout.value); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_PRIVATE, ps); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT); + curl_easy_setopt(ps->recv_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE); + + if (fresh==GNUNET_YES) + { + mret = curl_multi_add_handle(plugin->multi_handle, ps->recv_endpoint); + if (mret != CURLM_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Connection: %X: %s failed at %s:%d: `%s'\n"), + ps, + "curl_multi_add_handle", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return GNUNET_SYSERR; + } + } + if (curl_schedule (plugin) == GNUNET_SYSERR) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: could not schedule curl task\n",ps); +#endif + return GNUNET_SYSERR; + } +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: inbound not connected, initiating connection\n",ps); +#endif + } + + /* waiting for receive direction */ + if (ps->recv_connected==GNUNET_NO) + return GNUNET_NO; + + /* SEND DIRECTION */ + /* Check if session is connected to send data, otherwise connect to peer */ + if ((ps->send_connected == GNUNET_YES) && (ps->send_endpoint!= NULL)) + { + if (ps->send_active == GNUNET_YES) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound active, enqueueing message\n",ps); +#endif + return GNUNET_YES; + } + if (ps->send_active == GNUNET_NO) + { +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound paused, unpausing existing connection and enqueueing message\n",ps); +#endif + if (CURLE_OK == curl_easy_pause(ps->send_endpoint,CURLPAUSE_CONT)) + { + ps->send_active=GNUNET_YES; + return GNUNET_YES; + } + else + return GNUNET_SYSERR; + } + } + /* not connected, initiate connection */ + if (ps->send_connected==GNUNET_NO) + { + int fresh = GNUNET_NO; + if (NULL == ps->send_endpoint) + { + ps->send_endpoint = curl_easy_init(); + fresh = GNUNET_YES; + } + GNUNET_assert (ps->send_endpoint != NULL); + GNUNET_assert (NULL != ps->pending_msgs_tail); +#if DEBUG_CONNECTIONS + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Connection %X: outbound not connected, initiating connection\n",ps); +#endif + ps->send_active = GNUNET_NO; + msg = ps->pending_msgs_tail; + +#if DEBUG_CURL + curl_easy_setopt(ps->send_endpoint, CURLOPT_VERBOSE, 1L); +#endif + curl_easy_setopt(ps->send_endpoint, CURLOPT_URL, ps->url); + curl_easy_setopt(ps->send_endpoint, CURLOPT_PUT, 1L); + curl_easy_setopt(ps->send_endpoint, CURLOPT_HEADERFUNCTION, &curl_put_header_cb); + curl_easy_setopt(ps->send_endpoint, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); + curl_easy_setopt(ps->send_endpoint, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(ps->send_endpoint, CURLOPT_SSL_VERIFYHOST, 0); + + curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEHEADER, ps); + curl_easy_setopt(ps->send_endpoint, CURLOPT_READFUNCTION, curl_send_cb); + curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps); + curl_easy_setopt(ps->send_endpoint, CURLOPT_WRITEFUNCTION, curl_receive_cb); + curl_easy_setopt(ps->send_endpoint, CURLOPT_READDATA, ps); + curl_easy_setopt(ps->send_endpoint, CURLOPT_TIMEOUT, (long) timeout.value); + curl_easy_setopt(ps->send_endpoint, CURLOPT_PRIVATE, ps); + curl_easy_setopt(ps->send_endpoint, CURLOPT_CONNECTTIMEOUT, HTTP_CONNECT_TIMEOUT); + curl_easy_setopt(ps->send_endpoint, CURLOPT_BUFFERSIZE, GNUNET_SERVER_MAX_MESSAGE_SIZE); + + if (fresh==GNUNET_YES) + { + mret = curl_multi_add_handle(plugin->multi_handle, ps->send_endpoint); + if (mret != CURLM_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Connection: %X: %s failed at %s:%d: `%s'\n"), + ps, + "curl_multi_add_handle", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return GNUNET_SYSERR; + } + } + } + if (curl_schedule (plugin) == GNUNET_SYSERR) + return GNUNET_SYSERR; + return GNUNET_YES; + } + if (ps->direction == INBOUND) + { + GNUNET_assert (NULL != ps->pending_msgs_tail); + if ((ps->recv_connected==GNUNET_YES) && (ps->send_connected==GNUNET_YES) && + (ps->recv_force_disconnect==GNUNET_NO) && (ps->recv_force_disconnect==GNUNET_NO)) + return GNUNET_YES; + } + return GNUNET_SYSERR; +} + +static struct Session * send_select_session (void * cls, struct HTTP_PeerContext *pc, const void * addr, size_t addrlen, int force_address, struct Session * session) +{ + struct Session * tmp = NULL; + int addr_given = GNUNET_NO; + + if ((addr!=NULL) && (addrlen>0)) + addr_given = GNUNET_YES; + + if (force_address == GNUNET_YES) + { + /* check session given as argument */ + if ((session != NULL) && (addr_given == GNUNET_YES)) + { + if (0 == memcmp(session->addr, addr, addrlen)) + { + /* connection can not be used, since it is disconnected */ + if ((session->recv_force_disconnect==GNUNET_NO) && (session->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using session passed by transport to send to forced address \n", session); +#endif + return session; + } + } + } + /* check last session used */ + if ((pc->last_session != NULL)&& (addr_given == GNUNET_YES)) + { + if (0 == memcmp(pc->last_session->addr, addr, addrlen)) + { + /* connection can not be used, since it is disconnected */ + if ((pc->last_session->recv_force_disconnect==GNUNET_NO) && (pc->last_session->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using last session used to send to forced address \n", pc->last_session); +#endif + return pc->last_session; + } + } + } + /* find session in existing sessions */ + tmp = pc->head; + while ((tmp!=NULL) && (addr_given == GNUNET_YES)) + { + + if (0 == memcmp(tmp->addr, addr, addrlen)) + { + /* connection can not be used, since it is disconnected */ + if ((tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using existing session to send to forced address \n", session); +#endif + return session; + } + + } + tmp=tmp->next; + } + /* no session to use */ + return NULL; + } + if ((force_address == GNUNET_NO) || (force_address == GNUNET_SYSERR)) + { + /* check session given as argument */ + if (session != NULL) + { + /* connection can not be used, since it is disconnected */ + if ((session->recv_force_disconnect==GNUNET_NO) && (session->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using session passed by transport to send not-forced address \n", session); +#endif + return session; + } + + } + /* check last session used */ + if (pc->last_session != NULL) + { + /* connection can not be used, since it is disconnected */ + if ((pc->last_session->recv_force_disconnect==GNUNET_NO) && (pc->last_session->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using last session to send to not-forced address \n", pc->last_session); +#endif + return pc->last_session; + } + } + /* find session in existing sessions */ + tmp = pc->head; + while (tmp!=NULL) + { + /* connection can not be used, since it is disconnected */ + if ((tmp->recv_force_disconnect==GNUNET_NO) && (tmp->send_force_disconnect==GNUNET_NO)) + { +#if DEBUG_SESSION_SELECTION + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Session %X selected: Using existing session to send to not-forced address \n", tmp); +#endif + return tmp; + } + tmp=tmp->next; + } + return NULL; + } + return NULL; +} + +/** + * Function that can be used by the transport service to transmit + * a message using the plugin. Note that in the case of a + * peer disconnecting, the continuation MUST be called + * prior to the disconnect notification itself. This function + * will be called with this peer's HELLO message to initiate + * a fresh connection to another peer. + * + * @param cls closure + * @param target who should receive this message + * @param msgbuf the message to transmit + * @param msgbuf_size number of bytes in 'msgbuf' + * @param priority how important is the message (most plugins will + * ignore message priority and just FIFO) + * @param timeout how long to wait at most for the transmission (does not + * require plugins to discard the message after the timeout, + * just advisory for the desired delay; most plugins will ignore + * this as well) + * @param session which session must be used (or NULL for "any") + * @param addr the address to use (can be NULL if the plugin + * is "on its own" (i.e. re-use existing TCP connection)) + * @param addrlen length of the address in bytes + * @param force_address GNUNET_YES if the plugin MUST use the given address, + * GNUNET_NO means the plugin may use any other address and + * GNUNET_SYSERR means that only reliable existing + * bi-directional connections should be used (regardless + * of address) + * @param cont continuation to call once the message has + * been transmitted (or if the transport is ready + * for the next transmission call; or if the + * peer disconnected...); can be NULL + * @param cont_cls closure for cont + * @return number of bytes used (on the physical network, with overheads); + * -1 on hard errors (i.e. address invalid); 0 is a legal value + * and does NOT mean that the message was not transmitted (DV) + */ +static ssize_t +http_plugin_send (void *cls, + const struct GNUNET_PeerIdentity *target, + const char *msgbuf, + size_t msgbuf_size, + unsigned int priority, + struct GNUNET_TIME_Relative to, + struct Session *session, + const void *addr, + size_t addrlen, + int force_address, + GNUNET_TRANSPORT_TransmitContinuation cont, + void *cont_cls) +{ + struct Plugin *plugin = cls; + struct HTTP_Message *msg; + struct HTTP_PeerContext * pc; + struct Session * ps = NULL; + + GNUNET_assert(cls !=NULL); + +#if DEBUG_HTTP + char * force = GNUNET_malloc(40); + if (force_address == GNUNET_YES) + strcpy(force,"forced addr."); + if (force_address == GNUNET_NO) + strcpy(force,"any addr."); + if (force_address == GNUNET_SYSERR) + strcpy(force,"reliable bi-direc. address addr."); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Transport tells me to send %u bytes to `%s' using %s (%s) and session: %X\n", + msgbuf_size, + GNUNET_i2s(target), + force, + http_plugin_address_to_string(NULL, addr, addrlen), + session); + + GNUNET_free(force); +#endif + + pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey); + /* Peer unknown */ + if (pc==NULL) + { + pc = GNUNET_malloc(sizeof (struct HTTP_PeerContext)); + pc->plugin = plugin; + pc->session_id_counter=1; + pc->last_session = NULL; + memcpy(&pc->identity, target, sizeof(struct GNUNET_PeerIdentity)); + GNUNET_CONTAINER_multihashmap_put(plugin->peers, &pc->identity.hashPubKey, pc, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY); + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP peers active"), + 1, + GNUNET_NO); + } + + ps = send_select_session (plugin, pc, addr, addrlen, force_address, session); + + /* session not existing, but address forced -> creating new session */ + if (ps==NULL) + { + if ((addr!=NULL) && (addrlen!=0)) + { + ps = GNUNET_malloc(sizeof (struct Session)); +#if DEBUG_SESSION_SELECTION + if (force_address == GNUNET_YES) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection & forced address: creating new session %X to peer %s\n", ps, GNUNET_i2s(target)); + if (force_address != GNUNET_YES) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing connection: creating new session %X to peer %s\n", ps, GNUNET_i2s(target)); +#endif + if ((addrlen!=0) && (addr!=NULL)) + { + ps->addr = GNUNET_malloc(addrlen); + memcpy(ps->addr,addr,addrlen); + ps->addrlen = addrlen; + } + else + { + ps->addr = NULL; + ps->addrlen = 0; + } + ps->direction=OUTBOUND; + ps->recv_connected = GNUNET_NO; + ps->recv_force_disconnect = GNUNET_NO; + ps->send_connected = GNUNET_NO; + ps->send_force_disconnect = GNUNET_NO; + ps->pending_msgs_head = NULL; + ps->pending_msgs_tail = NULL; + ps->peercontext=pc; + ps->session_id = pc->session_id_counter; + pc->session_id_counter++; + ps->url = create_url (plugin, ps->addr, ps->addrlen, ps->session_id); + if (ps->msgtok == NULL) + ps->msgtok = GNUNET_SERVER_mst_create (&curl_receive_mst_cb, ps); + GNUNET_CONTAINER_DLL_insert(pc->head,pc->tail,ps); +/* FIXME */ + + GNUNET_STATISTICS_update (plugin->env->stats, + gettext_noop ("# HTTP outbound sessions for peers active"), + 1, + GNUNET_NO); + } + else + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No existing session found & and no address given: no way to send this message to peer `%s'!\n", GNUNET_i2s(target)); +#endif + return GNUNET_SYSERR; + } + } + + /* create msg */ + msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size); + 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(ps->pending_msgs_head,ps->pending_msgs_tail,msg); + + if (send_check_connections (plugin, ps) != GNUNET_SYSERR) + { + if (force_address != GNUNET_YES) + pc->last_session = ps; + + if (pc->last_session==NULL) + pc->last_session = ps; + return msg->size; + } + else + return GNUNET_SYSERR; +} + + + +/** + * Function that can be used to force the plugin to disconnect + * from the given peer and cancel all previous transmissions + * (and their continuationc). + * + * @param cls closure + * @param target peer from which to disconnect + */ +static void +http_plugin_disconnect (void *cls, + const struct GNUNET_PeerIdentity *target) +{ + + + struct Plugin *plugin = cls; + struct HTTP_PeerContext *pc = NULL; + struct Session *ps = NULL; + //struct Session *tmp = NULL; + + pc = GNUNET_CONTAINER_multihashmap_get (plugin->peers, &target->hashPubKey); + if (pc==NULL) + return; + ps = pc->head; + + while (ps!=NULL) + { + /* Telling transport that session is getting disconnected */ + plugin->env->session_end(plugin, target, ps); + if (ps->direction==OUTBOUND) + { + if (ps->send_endpoint!=NULL) + { + //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->send_endpoint)); + //curl_easy_cleanup(ps->send_endpoint); + //ps->send_endpoint=NULL; + ps->send_force_disconnect = GNUNET_YES; + } + if (ps->recv_endpoint!=NULL) + { + //GNUNET_assert(CURLM_OK == curl_multi_remove_handle(plugin->multi_handle,ps->recv_endpoint)); + //curl_easy_cleanup(ps->recv_endpoint); + //ps->recv_endpoint=NULL; + ps->recv_force_disconnect = GNUNET_YES; + } + } + + if (ps->direction==INBOUND) + { + ps->recv_force_disconnect = GNUNET_YES; + ps->send_force_disconnect = GNUNET_YES; + } + + while (ps->pending_msgs_head!=NULL) + { + remove_http_message(ps, ps->pending_msgs_head); + } + ps->recv_active = GNUNET_NO; + ps->send_active = GNUNET_NO; + ps=ps->next; + } +} + + +/** + * Convert the transports address to a nice, human-readable + * format. + * + * @param cls closure + * @param type name of the transport that generated the address + * @param addr one of the addresses of the host, NULL for the last address + * the specific address format depends on the transport + * @param addrlen length of the address + * @param numeric should (IP) addresses be displayed in numeric form? + * @param timeout after how long should we give up? + * @param asc function to call on each string + * @param asc_cls closure for asc + */ +static void +http_plugin_address_pretty_printer (void *cls, + const char *type, + const void *addr, + size_t addrlen, + int numeric, + struct GNUNET_TIME_Relative timeout, + GNUNET_TRANSPORT_AddressStringCallback + asc, void *asc_cls) +{ + const struct IPv4HttpAddress *t4; + const struct IPv6HttpAddress *t6; + struct sockaddr_in a4; + struct sockaddr_in6 a6; + char * address; + char * ret; + unsigned int port; + unsigned int res; + + GNUNET_assert(cls !=NULL); + if (addrlen == sizeof (struct IPv6HttpAddress)) + { + address = GNUNET_malloc (INET6_ADDRSTRLEN); + t6 = addr; + a6.sin6_addr = t6->ipv6_addr; + inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN); + port = ntohs(t6->u6_port); + } + else if (addrlen == sizeof (struct IPv4HttpAddress)) + { + address = GNUNET_malloc (INET_ADDRSTRLEN); + t4 = addr; + a4.sin_addr.s_addr = t4->ipv4_addr; + inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN); + port = ntohs(t4->u_port); + } + else + { + /* invalid address */ + GNUNET_break_op (0); + asc (asc_cls, NULL); + return; + } + res = GNUNET_asprintf(&ret,"http://%s:%u/",address,port); + GNUNET_free (address); + GNUNET_assert(res != 0); + asc (asc_cls, ret); + GNUNET_free_non_null (ret); +} + + + +/** + * Another peer has suggested an address for this + * peer and transport plugin. Check that this could be a valid + * address. If so, consider adding it to the list + * of addresses. + * + * @param cls closure + * @param addr pointer to the address + * @param addrlen length of addr + * @return GNUNET_OK if this is a plausible address for this peer + * and transport + */ +static int +http_plugin_address_suggested (void *cls, + const void *addr, size_t addrlen) +{ + struct Plugin *plugin = cls; + struct IPv4HttpAddress *v4; + struct IPv6HttpAddress *v6; + unsigned int port; + + GNUNET_assert(cls !=NULL); + if ((addrlen != sizeof (struct IPv4HttpAddress)) && + (addrlen != sizeof (struct IPv6HttpAddress))) + { + return GNUNET_SYSERR; + } + if (addrlen == sizeof (struct IPv4HttpAddress)) + { + v4 = (struct IPv4HttpAddress *) addr; + /* Not skipping loopback + if (INADDR_LOOPBACK == ntohl(v4->ipv4_addr)) + { + return GNUNET_SYSERR; + } */ + port = ntohs (v4->u_port); + if (port != plugin->port_inbound) + { + return GNUNET_SYSERR; + } + } + if (addrlen == sizeof (struct IPv6HttpAddress)) + { + v6 = (struct IPv6HttpAddress *) addr; + if (IN6_IS_ADDR_LINKLOCAL (&v6->ipv6_addr)) + { + return GNUNET_SYSERR; + } + port = ntohs (v6->u6_port); + if (port != plugin->port_inbound) + { + return GNUNET_SYSERR; + } + } + + return GNUNET_OK; +} + + +/** + * Function called for a quick conversion of the binary address to + * a numeric address. Note that the caller must not free the + * address and that the next call to this function is allowed + * to override the address again. + * + * @param cls closure + * @param addr binary address + * @param addrlen length of the address + * @return string representing the same address + */ +static const char* +http_plugin_address_to_string (void *cls, + const void *addr, + size_t addrlen) +{ + const struct IPv4HttpAddress *t4; + const struct IPv6HttpAddress *t6; + struct sockaddr_in a4; + struct sockaddr_in6 a6; + char * address; + char * ret; + uint16_t port; + unsigned int res; + + if (addrlen == sizeof (struct IPv6HttpAddress)) + { + address = GNUNET_malloc (INET6_ADDRSTRLEN); + t6 = addr; + a6.sin6_addr = t6->ipv6_addr; + inet_ntop(AF_INET6, &(a6.sin6_addr),address,INET6_ADDRSTRLEN); + port = ntohs(t6->u6_port); + } + else if (addrlen == sizeof (struct IPv4HttpAddress)) + { + address = GNUNET_malloc (INET_ADDRSTRLEN); + t4 = addr; + a4.sin_addr.s_addr = t4->ipv4_addr; + inet_ntop(AF_INET, &(a4.sin_addr),address,INET_ADDRSTRLEN); + port = ntohs(t4->u_port); + } + else + { + /* invalid address */ + return NULL; + } + res = GNUNET_asprintf(&ret,"%s:%u",address,port); + GNUNET_free (address); + GNUNET_assert(res != 0); + return ret; +} + + +/** + * Exit point from the plugin. + */ +void * +libgnunet_plugin_transport_https_done (void *cls) +{ + struct GNUNET_TRANSPORT_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + CURLMcode mret; + GNUNET_assert(cls !=NULL); + + if (plugin->http_server_daemon_v4 != NULL) + { + MHD_stop_daemon (plugin->http_server_daemon_v4); + plugin->http_server_daemon_v4 = NULL; + } + if (plugin->http_server_daemon_v6 != NULL) + { + MHD_stop_daemon (plugin->http_server_daemon_v6); + plugin->http_server_daemon_v6 = NULL; + } + + if ( plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v4); + plugin->http_server_task_v4 = GNUNET_SCHEDULER_NO_TASK; + } + + if ( plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_server_task_v6); + plugin->http_server_task_v6 = GNUNET_SCHEDULER_NO_TASK; + } + + + /* free all peer information */ + if (plugin->peers!=NULL) + { + GNUNET_CONTAINER_multihashmap_iterate (plugin->peers, + &remove_peer_context_Iterator, + plugin); + GNUNET_CONTAINER_multihashmap_destroy (plugin->peers); + } + if (plugin->multi_handle!=NULL) + { + mret = curl_multi_cleanup(plugin->multi_handle); +#if DEBUG_HTTP + if ( CURLM_OK != mret) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"curl multihandle clean up failed\n"); +#endif + plugin->multi_handle = NULL; + } + curl_global_cleanup(); + + if ( plugin->http_curl_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(plugin->env->sched, plugin->http_curl_task); + plugin->http_curl_task = GNUNET_SCHEDULER_NO_TASK; + } + + GNUNET_free_non_null (plugin->bind4_address); + GNUNET_free_non_null (plugin->bind6_address); + GNUNET_free_non_null(plugin->bind_hostname); + GNUNET_free (plugin); + GNUNET_free (api); +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Unload http plugin complete...\n"); +#endif + return NULL; +} + + +/** + * Entry point for the plugin. + */ +void * +libgnunet_plugin_transport_https_init (void *cls) +{ + struct GNUNET_TRANSPORT_PluginEnvironment *env = cls; + struct Plugin *plugin; + struct GNUNET_TRANSPORT_PluginFunctions *api; + struct GNUNET_TIME_Relative gn_timeout; + long long unsigned int port; + + GNUNET_assert(cls !=NULL); +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting https plugin...\n"); +#endif + + plugin = GNUNET_malloc (sizeof (struct Plugin)); + plugin->stats = env->stats; + plugin->env = env; + plugin->peers = NULL; + plugin->bind4_address = NULL; + plugin->use_ipv6 = GNUNET_YES; + plugin->use_ipv4 = GNUNET_YES; + + api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); + api->cls = plugin; + api->send = &http_plugin_send; + api->disconnect = &http_plugin_disconnect; + api->address_pretty_printer = &http_plugin_address_pretty_printer; + api->check_address = &http_plugin_address_suggested; + api->address_to_string = &http_plugin_address_to_string; + + /* Hashing our identity to use it in URLs */ + GNUNET_CRYPTO_hash_to_enc ( &(plugin->env->my_identity->hashPubKey), &plugin->my_ascii_hash_ident); + + /* Reading port number from config file */ + if (GNUNET_CONFIGURATION_have_value (env->cfg, + "transport-https", "USE_IPv6")) + { + plugin->use_ipv6 = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, + "transport-https", + "USE_IPv6"); + } + /* Reading port number from config file */ + if (GNUNET_CONFIGURATION_have_value (env->cfg, + "transport-https", "USE_IPv4")) + { + plugin->use_ipv4 = GNUNET_CONFIGURATION_get_value_yesno (env->cfg, + "transport-https", + "USE_IPv4"); + } + /* Reading port number from config file */ + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (env->cfg, + "transport-https", + "PORT", + &port)) || + (port > 65535) ) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "http", + _("Require valid port number for transport plugin `%s' in configuration!\n"), + "transport-https"); + libgnunet_plugin_transport_https_done (api); + return NULL; + } + + /* Reading ipv4 addresse to bind to from config file */ + if ((plugin->use_ipv4==GNUNET_YES) && (GNUNET_CONFIGURATION_have_value (env->cfg, + "transport-https", "BINDTO4"))) + { + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (env->cfg, + "transport-https", + "BINDTO4", + &plugin->bind_hostname)); + plugin->bind4_address = GNUNET_malloc(sizeof(struct sockaddr_in)); + plugin->bind4_address->sin_family = AF_INET; + plugin->bind4_address->sin_port = htons (port); + + if (inet_pton(AF_INET,plugin->bind_hostname, &plugin->bind4_address->sin_addr)<=0) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "http", + _("Misconfigured address to bind to in configuration!\n"), + "transport-https"); + GNUNET_free(plugin->bind4_address); + GNUNET_free(plugin->bind_hostname); + plugin->bind_hostname = NULL; + plugin->bind4_address = NULL; + } + } + + /* Reading ipv4 addresse to bind to from config file */ + if ((plugin->use_ipv6==GNUNET_YES) && (GNUNET_CONFIGURATION_have_value (env->cfg, + "transport-https", "BINDTO6"))) + { + GNUNET_break (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (env->cfg, + "transport-https", + "BINDTO6", + &plugin->bind_hostname)); + + plugin->bind6_address = GNUNET_malloc(sizeof(struct sockaddr_in6)); + plugin->bind6_address->sin6_family = AF_INET6; + plugin->bind6_address->sin6_port = htons (port); + + if (inet_pton(AF_INET6,plugin->bind_hostname, &plugin->bind6_address->sin6_addr)<=0) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "http", + _("Misconfigured address to bind to in configuration!\n"), + "transport-https"); + GNUNET_free(plugin->bind6_address); + GNUNET_free(plugin->bind_hostname); + plugin->bind_hostname = NULL; + plugin->bind6_address = NULL; + } + } + + GNUNET_assert ((port > 0) && (port <= 65535)); + plugin->port_inbound = port; + gn_timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT; + unsigned int timeout = (gn_timeout.value) / 1000; + if ((plugin->http_server_daemon_v6 == NULL) && (plugin->use_ipv6 == GNUNET_YES) && (port != 0)) + { + struct sockaddr * tmp = (struct sockaddr *) plugin->bind6_address; + plugin->http_server_daemon_v6 = MHD_start_daemon ( +#if DEBUG_CONNECTIONS + MHD_USE_DEBUG | +#endif + MHD_USE_IPv6 | MHD_USE_SSL, + port, + &mhd_accept_cb, + plugin , &mdh_access_cb, plugin, + MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, + MHD_OPTION_SOCK_ADDR, tmp, + MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 32, + //MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 6, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) timeout, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024), + MHD_OPTION_NOTIFY_COMPLETED, &mhd_termination_cb, NULL, + MHD_OPTION_END); + } + if ((plugin->http_server_daemon_v4 == NULL) && (plugin->use_ipv4 == GNUNET_YES) && (port != 0)) + { + plugin->http_server_daemon_v4 = MHD_start_daemon ( +#if DEBUG_CONNECTIONS + MHD_USE_DEBUG | +#endif + MHD_NO_FLAG | MHD_USE_SSL, + port, + &mhd_accept_cb, + plugin , &mdh_access_cb, plugin, + MHD_OPTION_HTTPS_MEM_KEY, key_pem, + MHD_OPTION_HTTPS_MEM_CERT, cert_pem, + MHD_OPTION_SOCK_ADDR, (struct sockaddr_in *)plugin->bind4_address, + MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 32, + //MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 6, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) timeout, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (16 * 1024), + MHD_OPTION_NOTIFY_COMPLETED, &mhd_termination_cb, NULL, + MHD_OPTION_END); + } + if (plugin->http_server_daemon_v4 != NULL) + plugin->http_server_task_v4 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v4); + if (plugin->http_server_daemon_v6 != NULL) + plugin->http_server_task_v6 = http_server_daemon_prepare (plugin, plugin->http_server_daemon_v6); + + + if (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK) + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address",port); +#endif + } + else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) && (plugin->http_server_task_v4 != GNUNET_SCHEDULER_NO_TASK)) + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv6 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address", port); +#endif + } + else if ((plugin->http_server_task_v6 != GNUNET_SCHEDULER_NO_TASK) && (plugin->http_server_task_v4 == GNUNET_SCHEDULER_NO_TASK)) + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Starting MHD with IPv4 and IPv6 bound to %s with port %u\n",(plugin->bind_hostname!=NULL) ? plugin->bind_hostname : "every address", port); +#endif + } + else + { +#if DEBUG_HTTP + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No MHD was started, transport plugin not functional!\n"); +#endif + libgnunet_plugin_transport_https_done (api); + return NULL; + } + + /* Initializing cURL */ + curl_global_init(CURL_GLOBAL_ALL); + plugin->multi_handle = curl_multi_init(); + + if ( NULL == plugin->multi_handle ) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "http", + _("Could not initialize curl multi handle, failed to start http plugin!\n"), + "transport-https"); + libgnunet_plugin_transport_https_done (api); + return NULL; + } + + plugin->peers = GNUNET_CONTAINER_multihashmap_create (10); + GNUNET_OS_network_interfaces_list (&process_interfaces, plugin); + + return api; +} + +/* end of plugin_transport_http.c */ diff --git a/src/transport/test_plugin_transport_data_http.conf b/src/transport/test_plugin_transport_data_http.conf index 9418ac751..476229622 100644 --- a/src/transport/test_plugin_transport_data_http.conf +++ b/src/transport/test_plugin_transport_data_http.conf @@ -7,13 +7,21 @@ WEAKRANDOM = YES [transport-http] PORT = 12389 -DEBUG = NO +DEBUG = YES #USE_IPv4 = NO #USE_IPv6 = NO USE_IPv6 = YES #BINDTO4 = 127.0.0.1 #BINDTO6 = ::1 +[transport-https] +PORT = 12389 +DEBUG = NO +#USE_IPv4 = NO +#USE_IPv6 = NO +USE_IPv6 = YES +#BINDTO4 = 127.0.0.1 +#BINDTO6 = ::1 [transport] PREFIX = valgrind --leak-check=full diff --git a/src/transport/test_plugin_transport_https.c b/src/transport/test_plugin_transport_https.c new file mode 100644 index 000000000..7c9229111 --- /dev/null +++ b/src/transport/test_plugin_transport_https.c @@ -0,0 +1,1354 @@ +/* + This file is part of GNUnet. + (C) 2010 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +/** + * @file transport/test_plugin_transport_https.c + * @brief testcase for plugin_transport_https.c + * @author Matthias Wachs + */ + +#include "platform.h" +#include "gnunet_constants.h" +#include "gnunet_common.h" +#include "gnunet_getopt_lib.h" +#include "gnunet_hello_lib.h" +#include "gnunet_os_lib.h" +#include "gnunet_peerinfo_service.h" +#include "gnunet_plugin_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_program_lib.h" +#include "gnunet_signatures.h" +#include "gnunet_service_lib.h" +#include "gnunet_crypto_lib.h" + +#include "plugin_transport.h" +#include "gnunet_statistics_service.h" +#include "transport.h" +#include +#include +#include + +#define VERBOSE GNUNET_YES +#define DEBUG GNUNET_YES +#define DEBUG_CURL GNUNET_YES +#define HTTP_BUFFER_SIZE 2048 + +#define PLUGIN libgnunet_plugin_transport_template + +/** + * How long until we give up on transmitting the message? + */ +#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 90) + +/** + * Testcase timeout + */ +#define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20) + +/** + * How long between recieve and send? + */ +#define WAIT_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1) + + + +/** + * Struct for plugin addresses + */ +struct Plugin_Address +{ + /** + * Next field for linked list + */ + struct Plugin_Address * next; + + /** + * buffer containing data to send + */ + void * addr; + + /** + * amount of data to sent + */ + size_t addrlen; +}; + +/** + * Message to send using http + */ +struct HTTP_Message +{ + /** + * buffer + */ + unsigned char buf[HTTP_BUFFER_SIZE]; + + /** + * current position in buffer + */ + size_t pos; + + /** + * buffer size + */ + size_t size; + + /** + * data size + */ + size_t len; +}; + + +/** + * Struct for plugin addresses + */ +struct HTTP_Transfer +{ + /** + * amount of bytes we recieved + */ + size_t data_size; + + /** + * buffer for http transfers + */ + unsigned char buf[HTTP_BUFFER_SIZE]; + + /** + * buffer size this transfer + */ + size_t size; + + /** + * amount of bytes we recieved + */ + size_t pos; + + /** + * HTTP Header result for transfer + */ + unsigned int http_result_code; + + /** + * did the test fail? + */ + unsigned int test_failed; + + /** + * was this test already executed? + */ + unsigned int test_executed; +}; + + +/** + * Network format for IPv4 addresses. + */ +struct IPv4HttpAddress +{ + /** + * IPv4 address, in network byte order. + */ + uint32_t ipv4_addr GNUNET_PACKED; + + /** + * Port number, in network byte order. + */ + uint16_t u_port GNUNET_PACKED; + +}; + + +/** + * Network format for IPv6 addresses. + */ +struct IPv6HttpAddress +{ + /** + * IPv6 address. + */ + struct in6_addr ipv6_addr GNUNET_PACKED; + + /** + * Port number, in network byte order. + */ + uint16_t u6_port GNUNET_PACKED; + +}; + +/** + * Our public key. + */ +/* static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; */ + +/** + * Our public key. + */ +static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; + +/** + * Our identity. + */ +static struct GNUNET_PeerIdentity my_identity; + +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key; + +/** + * Peer's port + */ +static long long unsigned int port; + +/** + * Peer's addr + */ +static char * test_addr; + +/** + * Our scheduler. + */ +struct GNUNET_SCHEDULER_Handle *sched; + +/** + * Our statistics handle. + */ +struct GNUNET_STATISTICS_Handle *stats; + + +/** + * Our configuration. + */ +const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Number of neighbours we'd like to have. + */ +static uint32_t max_connect_per_transport; + +/** + * Environment for this plugin. + */ +static struct GNUNET_TRANSPORT_PluginEnvironment env; + +/** + *handle for the api provided by this plugin + */ +static struct GNUNET_TRANSPORT_PluginFunctions *api; + +/** + * ID of the task controlling the testcase timeout + */ +static GNUNET_SCHEDULER_TaskIdentifier ti_timeout; + +static GNUNET_SCHEDULER_TaskIdentifier ti_send; + +//const struct GNUNET_PeerIdentity * p; + +/** + * buffer for data to send + */ +static struct HTTP_Message buffer_out; + +/** + * buffer for data to recieve + */ +static struct HTTP_Message buffer_in; + + +struct Plugin_Address * addr_head; + +/** + * Did the test pass or fail? + */ +static int fail_notify_address; +/** + * Did the test pass or fail? + */ +static int fail_notify_address_count; + +/** + * Did the test pass or fail? + */ +static int fail_pretty_printer; + +/** + * Did the test pass or fail? + */ +static int fail_pretty_printer_count; + +/** + * Did the test pass or fail? + */ +static int fail_addr_to_str; + +/** + * No. of msgs transmitted successfully to local addresses + */ +static int fail_msgs_transmited_to_local_addrs; + +/** + * Test: transmit msg of max. size + */ +static int fail_msg_transmited_bigger_max_size; + +/** + * Test: transmit msg of max. size + */ +static int fail_msg_transmited_max_size; + +/** + * Test: transmit 2 msgs. in in send operation + */ +static int fail_multiple_msgs_in_transmission; + +/** + * Test: connect to peer without peer identification + */ +static struct HTTP_Transfer test_no_ident; + +/** + * Test: connect to peer without peer identification + */ +static struct HTTP_Transfer test_too_short_ident; + +/** + * Test: connect to peer without peer identification + */ +static struct HTTP_Transfer test_too_long_ident; + +/** + * Test: connect to peer with valid peer identification + */ +static struct HTTP_Transfer test_valid_ident; + +/** + * Test: session selection, use any existing + */ +static int fail_session_selection_any; + +/** + * Test: session selection, use existing inbound session + */ +static int fail_session_selection_session; + +/** + * Test: session selection, use existing inbound session + * max message, not fitting in send & recv buffers at one time + */ +static int fail_session_selection_session_big; + +/** +* Test: session selection, use reliable existing + */ +static int fail_session_selection_reliable; + +/** + * Did the test pass or fail? + */ +static int fail; + +/** + * Number of local addresses + */ +static unsigned int count_str_addr; + +CURL *curl_handle; + +/** + * cURL Multihandle + */ +static CURLM *multi_handle; + +/** + * The task sending data + */ +static GNUNET_SCHEDULER_TaskIdentifier http_task_send; + +/** + * Shutdown testcase + */ +static void +shutdown_clean () +{ + struct Plugin_Address * cur; + struct Plugin_Address * tmp; + + /* Evaluate results */ + fail = 0; + if ((fail_notify_address == GNUNET_YES) || (fail_pretty_printer == GNUNET_YES) || (fail_addr_to_str == GNUNET_YES)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Phase 0: Test plugin functions failed\n"); + fail = 1; + } + if ((test_no_ident.test_failed == GNUNET_YES) || (test_too_short_ident.test_failed == GNUNET_YES) || (test_too_long_ident.test_failed == GNUNET_YES) || (test_valid_ident.test_failed == GNUNET_YES)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Phase 1: Test connect with wrong data failed\n"); + fail = 1; + } + if ((fail_session_selection_any != GNUNET_NO) || (fail_session_selection_reliable != GNUNET_NO) || (fail_session_selection_session != GNUNET_NO) || (fail_session_selection_session_big != GNUNET_NO)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Phase 2: Test session selection failed\n"); + fail = 1; + } + if ((fail_msgs_transmited_to_local_addrs != count_str_addr) || (fail_multiple_msgs_in_transmission != 2) || (fail_msg_transmited_max_size == GNUNET_YES)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Phase 3: Test sending with plugin failed\n"); + fail = 1; + } + if (fail != 1) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All tests successful\n"); + } + + api->disconnect(api->cls,&my_identity); + + curl_multi_cleanup(multi_handle); + + if (NULL != curl_handle) + curl_easy_cleanup (curl_handle); + + /* cleaning addresses */ + while (addr_head != NULL) + { + cur = addr_head; + tmp = addr_head->next; + GNUNET_free (addr_head->addr); + GNUNET_free (addr_head); + addr_head=tmp; + } + + if (ti_send != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(sched,ti_send); + ti_send = GNUNET_SCHEDULER_NO_TASK; + } + + if (http_task_send != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(sched,http_task_send); + http_task_send = GNUNET_SCHEDULER_NO_TASK; + } + + if (ti_timeout != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel(sched,ti_timeout); + ti_timeout = GNUNET_SCHEDULER_NO_TASK; + } + + GNUNET_free(test_addr); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Unloading http plugin\n"); + GNUNET_assert (NULL == GNUNET_PLUGIN_unload ("libgnunet_plugin_transport_http", api)); + + GNUNET_SCHEDULER_shutdown(sched); + GNUNET_DISK_directory_remove ("/tmp/test_plugin_transport_http"); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Exiting testcase\n"); + exit(fail); + return; +} + + +/** + * Continuation called after plugin send message + * @cls closure + * @target target + * @result GNUNET_OK or GNUNET_SYSERR + */ + +static void task_send_cont (void *cls, + const struct GNUNET_PeerIdentity * target, + int result) +{ + struct Plugin_Address * tmp_addr; + tmp_addr = addr_head; + + if ((cls == &fail_msg_transmited_bigger_max_size) && (result == GNUNET_SYSERR)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Message bigger max msg size was not sent!\n"); + fail_msg_transmited_bigger_max_size = GNUNET_NO; + return; + } + + if ((cls == &fail_msg_transmited_max_size) && (result == GNUNET_OK)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Message with max msg size succesfully sent!\n",fail_msgs_transmited_to_local_addrs); + fail_msg_transmited_max_size = GNUNET_NO; + } +} + + +static void run_connection_tests( int phase , void * cls); + +/** + * Recieves messages from plugin, in real world transport + */ +static struct GNUNET_TIME_Relative +receive (void *cls, + const struct GNUNET_PeerIdentity * peer, + const struct GNUNET_MessageHeader * message, + uint32_t distance, + struct Session *session, + const char *sender_address, + uint16_t sender_address_len) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testcase recieved new message from peer `%s' with type %u and length %u, session %X\n", GNUNET_i2s(peer), ntohs(message->type), ntohs(message->size),session); + + if ((ntohs(message->type)>=10) && (ntohs(message->type)<20)) + { + fail_msgs_transmited_to_local_addrs++; + if (fail_msgs_transmited_to_local_addrs == count_str_addr) + run_connection_tests(2, session); + } + + + if ((ntohs(message->type)==20)) + { + fail_session_selection_reliable = GNUNET_NO; + } + + if ((ntohs(message->type)==21)) + { + fail_session_selection_any = GNUNET_NO; + } + if ((ntohs(message->type)==22)) + { + fail_session_selection_session = GNUNET_NO; + } + + if ((ntohs(message->type)==23)) + { + fail_session_selection_session_big = GNUNET_NO; + run_connection_tests(3, NULL); + } + + if ((ntohs(message->type)==30) || (ntohs(message->type)==31)) + { + fail_multiple_msgs_in_transmission ++; + } + + if ((ntohs(message->type)==32) && (ntohs(message->size) == GNUNET_SERVER_MAX_MESSAGE_SIZE-1)) + { + fail_msg_transmited_max_size = GNUNET_NO; + //shutdown_clean(); + } + + return GNUNET_TIME_UNIT_ZERO; +} + +static size_t send_function (void *stream, size_t size, size_t nmemb, void *ptr) +{ + unsigned int len; + + len = buffer_out.len; + + if (( buffer_out.pos == len) || (len > (size * nmemb))) + return 0; + memcpy(stream, buffer_out.buf, len); + buffer_out.pos = len; + return len; +} + +static size_t recv_function (void *ptr, size_t size, size_t nmemb, void *ctx) +{ + + if (buffer_in.pos + size * nmemb > buffer_in.size) + return 0; /* overflow */ + + buffer_in.len = size * nmemb; + memcpy (&buffer_in.buf[buffer_in.pos], ptr, size * nmemb); + buffer_in.pos += size * nmemb; + buffer_in.len = buffer_in.pos; + buffer_in.buf[buffer_in.pos] = '\0'; + return buffer_in.pos; +} + +static size_t header_function( void *ptr, size_t size, size_t nmemb, void *stream) +{ + struct HTTP_Transfer * res = (struct HTTP_Transfer *) stream; + char * tmp; + unsigned int len = size * nmemb; + + tmp = GNUNET_malloc ( len+1 ); + memcpy(tmp,ptr,len); + if (tmp[len-2] == 13) + tmp[len-2]= '\0'; +#if DEBUG_CURL + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Header: `%s'\n",tmp); +#endif + if (0==strcmp (tmp,"HTTP/1.1 100 Continue")) + { + res->http_result_code=100; + } + if (0==strcmp (tmp,"HTTP/1.1 200 OK")) + { + res->http_result_code=200; + } + if (0==strcmp (tmp,"HTTP/1.1 400 Bad Request")) + { + res->http_result_code=400; + } + if (0==strcmp (tmp,"HTTP/1.1 404 Not Found")) + { + res->http_result_code=404; + } + if (0==strcmp (tmp,"HTTP/1.1 413 Request entity too large")) + { + res->http_result_code=413; + } + + GNUNET_free (tmp); + return size * nmemb; +} + +static size_t send_prepare( struct HTTP_Transfer * result); + + + +static void send_execute (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct HTTP_Transfer *res; + + int running; + struct CURLMsg *msg; + CURLMcode mret; + + res = (struct HTTP_Transfer *) cls; + http_task_send = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + + do + { + running = 0; + mret = curl_multi_perform (multi_handle, &running); + if (running == 0) + { + do + { + + msg = curl_multi_info_read (multi_handle, &running); + if (msg == NULL) + break; + /* get session for affected curl handle */ + //cs = find_session_by_curlhandle (msg->easy_handle); + //GNUNET_assert ( cs != NULL ); + switch (msg->msg) + { + + case CURLMSG_DONE: + if ( (msg->data.result != CURLE_OK) && + (msg->data.result != CURLE_GOT_NOTHING) ) + { + + GNUNET_log(GNUNET_ERROR_TYPE_INFO, + _("curl failed for `%s' at %s:%d: `%s'\n"), + "curl_multi_perform", + __FILE__, + __LINE__, + curl_easy_strerror (msg->data.result)); + /* sending msg failed*/ + curl_easy_cleanup(curl_handle); + curl_handle=NULL; + + run_connection_tests(0, NULL); + } + if (res == &test_no_ident) + { + if ((res->http_result_code==404) && (buffer_in.len==208)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer without any peer identification: test passed\n")); + res->test_failed = GNUNET_NO; + } + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer without any peer identification: test failed\n")); + } + if (res == &test_too_short_ident) + { + if ((res->http_result_code==404) && (buffer_in.len==208)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer with too short peer identification: test passed\n")); + res->test_failed = GNUNET_NO; + } + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer with too short peer identification: test failed\n")); + } + if (res == &test_too_long_ident) + { + if ((res->http_result_code==404) && (buffer_in.len==208)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer with too long peer identification: test passed\n")); + res->test_failed = GNUNET_NO; + } + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer with too long peer identification: test failed\n")); + } + if (res == &test_valid_ident) + { + if ((res->http_result_code==200)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer with valid peer identification: test passed\n")); + res->test_failed = GNUNET_NO; + } + else + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer with valid peer identification: test failed\n")); + } + curl_easy_cleanup(curl_handle); + curl_handle=NULL; + if ((res == &test_valid_ident) && (res->test_failed == GNUNET_NO)) + run_connection_tests(1, NULL); + run_connection_tests(0, NULL); + return; + default: + break; + } + + } + while ( (running > 0) ); + } + } + while (mret == CURLM_CALL_MULTI_PERFORM); + send_prepare(cls); +} + +/** + * Function setting up file descriptors and scheduling task to run + * @param ses session to send data to + * @return bytes sent to peer + */ +static size_t send_prepare( struct HTTP_Transfer * result) +{ + fd_set rs; + fd_set ws; + fd_set es; + int max; + struct GNUNET_NETWORK_FDSet *grs; + struct GNUNET_NETWORK_FDSet *gws; + long to; + CURLMcode mret; + + max = -1; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + mret = curl_multi_fdset (multi_handle, &rs, &ws, &es, &max); + if (mret != CURLM_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("%s failed at %s:%d: `%s'\n"), + "curl_multi_fdset", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return -1; + } + mret = curl_multi_timeout (multi_handle, &to); + if (mret != CURLM_OK) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("%s failed at %s:%d: `%s'\n"), + "curl_multi_timeout", __FILE__, __LINE__, + curl_multi_strerror (mret)); + return -1; + } + + grs = GNUNET_NETWORK_fdset_create (); + gws = GNUNET_NETWORK_fdset_create (); + GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1); + GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1); + http_task_send = GNUNET_SCHEDULER_add_select (sched, + GNUNET_SCHEDULER_PRIORITY_DEFAULT, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 0), + grs, + gws, + &send_execute, + result); + GNUNET_NETWORK_fdset_destroy (gws); + GNUNET_NETWORK_fdset_destroy (grs); + + /* FIXME: return bytes REALLY sent */ + return 0; +} + +/** + * function to send data to server + */ +static int send_data( struct HTTP_Transfer * result, char * url) +{ + + curl_handle = curl_easy_init(); + if( NULL == curl_handle) + { + printf("easy_init failed \n"); + return GNUNET_SYSERR; + } +#if DEBUG_CURL + curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L); +#endif + curl_easy_setopt(curl_handle, CURLOPT_URL, url); + curl_easy_setopt(curl_handle, CURLOPT_PUT, 1L); + //curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt (curl_handle, CURLOPT_HEADERFUNCTION, &header_function); + curl_easy_setopt (curl_handle, CURLOPT_WRITEHEADER, result); + curl_easy_setopt (curl_handle, CURLOPT_WRITEFUNCTION, &recv_function); + curl_easy_setopt (curl_handle, CURLOPT_WRITEDATA, result); + curl_easy_setopt (curl_handle, CURLOPT_READFUNCTION, &send_function); + curl_easy_setopt (curl_handle, CURLOPT_READDATA, result); + curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t) buffer_out.len); + curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 30); + curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 20); + + curl_multi_add_handle(multi_handle, curl_handle); + + send_prepare(result); + + return GNUNET_OK; +} + +/** + * Plugin notifies transport (aka testcase) about its addresses + */ +void +notify_address (void *cls, + const char *name, + const void *addr, + uint16_t addrlen, + struct GNUNET_TIME_Relative expires) +{ + char address[INET6_ADDRSTRLEN]; + unsigned int port; + struct Plugin_Address * pl_addr; + struct Plugin_Address * cur; + + if (addrlen == (sizeof (struct IPv4HttpAddress))) + { + inet_ntop(AF_INET, (struct in_addr *) addr,address,INET_ADDRSTRLEN); + port = ntohs(((struct IPv4HttpAddress *) addr)->u_port); + } + else if (addrlen == (sizeof (struct IPv6HttpAddress))) + { + inet_ntop(AF_INET6, (struct in6_addr *) addr,address,INET6_ADDRSTRLEN); + port = ntohs(((struct IPv6HttpAddress *) addr)->u6_port); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Unknown address size: ipv6 has %u ipv4 has %u but this has %u\n"), + sizeof (struct IPv6HttpAddress), + sizeof (struct IPv4HttpAddress), + addrlen); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + _("Transport plugin notification for address: `%s':%u\n"), + address, + port); + pl_addr = GNUNET_malloc (sizeof (struct Plugin_Address) ); + pl_addr->addrlen = addrlen; + pl_addr->addr = GNUNET_malloc(addrlen); + memcpy(pl_addr->addr,addr,addrlen); + pl_addr->next = NULL; + + if ( NULL == addr_head) + { + addr_head = pl_addr; + } + else + { + cur = addr_head; + while (NULL != cur->next) + { + cur = cur->next; + } + cur->next = pl_addr; + } + fail_notify_address_count++; + fail_notify_address = GNUNET_NO; +} + +static void +plugin_env_session_end (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct Session *session) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Pluging tells me: session %X to peer `%s' ended\n", session, GNUNET_i2s(peer)); +} + + +/** + * Setup plugin environment + */ +static void +setup_plugin_environment () +{ + env.cfg = cfg; + env.sched = sched; + env.stats = stats; + env.my_identity = &my_identity; + env.cls = &env; + env.receive = &receive; + env.notify_address = ¬ify_address; + env.max_connections = max_connect_per_transport; + env.session_end = &plugin_env_session_end; +} + + +/** + * Task shutting down testcase if it a timeout occurs + */ +static void +task_timeout (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + ti_timeout = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testcase timeout\n"); + fail = GNUNET_YES; + shutdown_clean(); + return; +} + +static void pretty_printer_cb (void *cls, + const char *address) +{ + if (NULL==address) + return; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Plugin returned pretty address: `%s'\n",address); + fail_pretty_printer_count++; +} + +/** + * Runs every single test to test the plugin + */ +static void run_connection_tests( int phase , void * cls) +{ + struct GNUNET_MessageHeader * msg; + unsigned int size; + + if (phase==0) + { + char * host_str = NULL; + /* resetting buffers */ + buffer_in.size = HTTP_BUFFER_SIZE; + buffer_in.pos = 0; + buffer_in.len = 0; + + buffer_out.size = HTTP_BUFFER_SIZE; + buffer_out.pos = 0; + buffer_out.len = 0; + + if (test_no_ident.test_executed == GNUNET_NO) + { + /* Connecting to peer without identification */ + char * ident = ""; + GNUNET_asprintf (&host_str, "http://%s/%s",test_addr,ident); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer without any peer identification.\n")); + test_no_ident.test_executed = GNUNET_YES; + send_data ( &test_no_ident, host_str); + GNUNET_free (host_str); + return; + } + if (test_too_short_ident.test_executed == GNUNET_NO) + { + char * ident = "AAAAAAAAAA"; + /* Connecting to peer with too short identification */ + GNUNET_asprintf (&host_str, "http://%s/%s",test_addr,ident); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer with too short peer identification.\n")); + test_too_short_ident.test_executed = GNUNET_YES; + send_data ( &test_too_short_ident, host_str); + GNUNET_free (host_str); + return; + } + + if (test_too_long_ident.test_executed == GNUNET_NO) + { + char * ident = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + /* Connecting to peer with too long identification */ + GNUNET_asprintf (&host_str, "http://%s/%s",test_addr,ident); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer with too long peer identification.\n")); + test_too_long_ident.test_executed = GNUNET_YES; + send_data ( &test_too_long_ident, host_str); + GNUNET_free (host_str); + return; + } + if (test_valid_ident.test_executed == GNUNET_NO) + { + struct GNUNET_CRYPTO_HashAsciiEncoded ident; + GNUNET_CRYPTO_hash_to_enc(&my_identity.hashPubKey,&ident); + GNUNET_asprintf (&host_str, "http://%s/%s%s",test_addr,(char *) &ident,";0"); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Connecting to peer with valid peer identification.\n")); + test_valid_ident.test_executed = GNUNET_YES; + send_data ( &test_valid_ident, host_str); + GNUNET_free (host_str); + return; + } + } + if (phase==1) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("\nPhase 1: transmit data to all suggested addresses\n\n")); + /* Using one of the addresses the plugin proposed */ + GNUNET_assert (addr_head->addr != NULL); + + struct Plugin_Address * tmp_addr; + struct GNUNET_MessageHeader msg; + char * tmp = GNUNET_malloc(sizeof(struct GNUNET_MessageHeader)); + char address[INET6_ADDRSTRLEN]; + unsigned int port; + unsigned int type = 10; + + msg.size=htons(sizeof(struct GNUNET_MessageHeader)); + tmp_addr = addr_head; + /* send a message to all addresses advertised by plugin */ + + int count = 0; + while (tmp_addr != NULL) + { + if (tmp_addr->addrlen == (sizeof (struct IPv4HttpAddress))) + { + inet_ntop(AF_INET, (struct in_addr *) tmp_addr->addr,address,INET_ADDRSTRLEN); + port = ntohs(((struct IPv4HttpAddress *) tmp_addr->addr)->u_port); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sending message to addres no. %u: `%s':%u\n", count,address, port); + } + if (tmp_addr->addrlen == (sizeof (struct IPv6HttpAddress))) + { + inet_ntop(AF_INET6, (struct in6_addr *) tmp_addr->addr,address,INET6_ADDRSTRLEN); + port = ntohs(((struct IPv6HttpAddress *) tmp_addr->addr)->u6_port); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Sending message to addres no. %u: `%s':%u\n", count,address,port); + } + msg.type=htons(type); + memcpy(tmp,&msg,sizeof(struct GNUNET_MessageHeader)); + api->send(api->cls, &my_identity, tmp, sizeof(struct GNUNET_MessageHeader), 0, TIMEOUT, NULL,tmp_addr->addr, tmp_addr->addrlen, GNUNET_YES, &task_send_cont, &fail_msgs_transmited_to_local_addrs); + tmp_addr = tmp_addr->next; + + count ++; + type ++; + } + GNUNET_free(tmp); + return; + } + + if (phase==2) + { + struct Session * session = cls; + msg = GNUNET_malloc (sizeof(struct GNUNET_MessageHeader)); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("\nPhase 2: session selection\n\n")); + size = sizeof(struct GNUNET_MessageHeader); + msg->size=htons(size); + msg->type = htons(20); + api->send(api->cls, &my_identity, (const char *) msg, size, 0, TIMEOUT, NULL, NULL, 0, GNUNET_NO, &task_send_cont, NULL); + + msg->type = htons(21); + api->send(api->cls, &my_identity, (const char *) msg, size, 0, TIMEOUT, NULL, NULL, 0, GNUNET_SYSERR, &task_send_cont, NULL); + + /* answer on session*/ + size = sizeof( struct GNUNET_MessageHeader); + msg->size = htons(size); + msg->type = htons(22); + api->send(api->cls, &my_identity, (const char *) msg, size, 0, TIMEOUT, session, NULL, 0, GNUNET_SYSERR, &task_send_cont, NULL); + + GNUNET_free(msg); + + /* answer on session with big message not fitting in mhd send buffer*/ + size = GNUNET_SERVER_MAX_MESSAGE_SIZE-1; + msg = GNUNET_malloc (size); + msg->size=htons(size); + msg->type = htons(23); + api->send(api->cls, &my_identity, (const char *) msg, size, 0, TIMEOUT, session, NULL, 0, GNUNET_NO, &task_send_cont, NULL); + GNUNET_free(msg); + return; + } + + if (phase==3) + { + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("\nPhase 3: send multiple or big messages after disconnect\n\n")); + /* disconnect from peer, so new connections are created */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"Disconnect from peer: `%s'\n", GNUNET_i2s(&my_identity)); + api->disconnect(api->cls, &my_identity); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Phase 3: sending messages\n")); + /* send a multiple GNUNET_messages at a time*/ + size = 2 * sizeof(struct GNUNET_MessageHeader); + msg = GNUNET_malloc( 2* size); + msg->size = htons(size); + msg->type = htons(30); + struct GNUNET_MessageHeader * msg2 = &msg[2]; + msg2->size = htons(2 * sizeof(struct GNUNET_MessageHeader)); + msg2->type = htons(31); + api->send(api->cls, &my_identity, (const char *) msg, 4 * sizeof(struct GNUNET_MessageHeader), 0, TIMEOUT, NULL,addr_head->addr, addr_head->addrlen, GNUNET_NO, &task_send_cont, &fail_multiple_msgs_in_transmission); + GNUNET_free(msg); + /* send a message with size GNUNET_SERVER_MAX_MESSAGE_SIZE-1 */ + + size = GNUNET_SERVER_MAX_MESSAGE_SIZE-1; + msg = GNUNET_malloc(size); + msg->size = htons(size); + msg->type = htons(32); + api->send(api->cls, &my_identity, (const char *) msg, size, 0, TIMEOUT, NULL,addr_head->addr, addr_head->addrlen, GNUNET_NO, &task_send_cont, &fail_msg_transmited_max_size); + GNUNET_free(msg); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"No more tests to run\n"); + } +} + + +/** + * Runs the test. + * + * @param cls closure + * @param s scheduler to use + * @param c configuration to use + */ +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *s, + char *const *args, + const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *c) +{ + char * libname; + sched = s; + cfg = c; + char *keyfile; + unsigned long long tneigh; + struct Plugin_Address * cur; + const char * addr_str; + + + unsigned int suggest_res; + + fail_pretty_printer = GNUNET_YES; + fail_notify_address = GNUNET_YES; + fail_addr_to_str = GNUNET_YES; + fail_msgs_transmited_to_local_addrs = 0; + fail_msg_transmited_max_size = GNUNET_YES; + fail_multiple_msgs_in_transmission = 0; + fail_session_selection_reliable = GNUNET_YES; + fail_session_selection_reliable = GNUNET_YES; + fail_session_selection_session = GNUNET_YES; + fail_session_selection_session_big = GNUNET_YES; + + addr_head = NULL; + count_str_addr = 0; + /* parse configuration */ + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (c, + "TRANSPORT", + "NEIGHBOUR_LIMIT", + &tneigh)) || + (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (c, + "GNUNETD", + "HOSTKEY", &keyfile))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ + ("Transport service is lacking key configuration settings. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (s); + fail = 1; + return; + } + + if ((GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + "transport-https", + "PORT", + &port)) || + (port > 65535) || (port == 0)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, + "https", + _ + ("Require valid port number for transport plugin `%s' in configuration!\n"), + "transport-http"); + } + + max_connect_per_transport = (uint32_t) tneigh; + my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile); + GNUNET_free (keyfile); + if (my_private_key == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Transport service could not access hostkey. Exiting.\n")); + GNUNET_SCHEDULER_shutdown (s); + fail = 1; + return; + } + + GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key); + GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &my_identity.hashPubKey); + + /* assertions before start */ + GNUNET_assert ((port > 0) && (port <= 65535)); + GNUNET_assert(&my_public_key != NULL); + GNUNET_assert(&my_identity.hashPubKey != NULL); + + /* load plugins... */ + setup_plugin_environment (); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading HTTPS transport plugin `%s'\n"),"libgnunet_plugin_transport_http"); + GNUNET_asprintf (&libname, "libgnunet_plugin_transport_https"); + api = GNUNET_PLUGIN_load (libname, &env); + GNUNET_free (libname); + if (api == NULL) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _("Failed to load transport plugin for https\n")); + fail = 1; + return; + } + + ti_timeout = GNUNET_SCHEDULER_add_delayed (sched, TEST_TIMEOUT, &task_timeout, NULL); + + /* testing plugin functionality */ + GNUNET_assert (0!=fail_notify_address_count); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transport plugin returned %u addresses to connect to\n"), fail_notify_address_count); + + /* testing pretty printer with all addresses obtained from the plugin*/ + cur = addr_head; + while (cur != NULL) + { + + api->address_pretty_printer (api->cls, "http",cur->addr,cur->addrlen, GNUNET_NO,TEST_TIMEOUT, &pretty_printer_cb,NULL); + addr_str = api->address_to_string (api->cls, cur->addr, cur->addrlen); + suggest_res = api->check_address (api->cls, cur->addr, cur->addrlen); + + GNUNET_assert (GNUNET_OK == suggest_res); + GNUNET_assert (NULL != addr_str); + count_str_addr++; + GNUNET_free ( (char *) addr_str); + cur = cur->next; + } + GNUNET_assert (fail_pretty_printer_count > 0); + GNUNET_assert (fail_pretty_printer_count==fail_notify_address_count); + GNUNET_assert (fail_pretty_printer_count==count_str_addr); + fail_pretty_printer=GNUNET_NO; + fail_addr_to_str=GNUNET_NO; + + /* Suggesting addresses with wrong port*/ + struct IPv4HttpAddress failing_addr; + failing_addr.ipv4_addr = htonl(INADDR_LOOPBACK); + failing_addr.u_port = htons(0); + suggest_res = api->check_address (api->cls,&failing_addr,sizeof (struct IPv4HttpAddress)); + GNUNET_assert (GNUNET_SYSERR == suggest_res); + + /* Suggesting addresses with wrong size*/ + failing_addr.ipv4_addr = htonl(INADDR_LOOPBACK); + failing_addr.u_port = htons(0); + suggest_res = api->check_address (api->cls,&failing_addr,sizeof (struct IPv6HttpAddress)); + GNUNET_assert (GNUNET_SYSERR == suggest_res); + + /* Suggesting addresses with wrong address*/ + failing_addr.ipv4_addr = htonl(0xffc00000); + failing_addr.u_port = htons(12389); + suggest_res = api->check_address (api->cls,&failing_addr,100); + GNUNET_assert (GNUNET_SYSERR == suggest_res); + + /* test sending to client */ + multi_handle = curl_multi_init(); + + /* Setting up buffers */ + buffer_in.size = HTTP_BUFFER_SIZE; + buffer_in.pos = 0; + buffer_in.len = 0; + + buffer_out.size = HTTP_BUFFER_SIZE; + buffer_out.pos = 0; + buffer_out.len = 0; + + /* Setting up connection tests */ + + /* Test: connecting without a peer identification */ + test_no_ident.test_executed = GNUNET_NO; + test_no_ident.test_failed = GNUNET_YES; + + /* Test: connecting with too short peer identification */ + test_too_short_ident.test_executed = GNUNET_NO; + test_too_short_ident.test_failed = GNUNET_YES; + + /* Test: connecting with too long peer identification */ + test_too_long_ident.test_executed = GNUNET_NO; + test_too_long_ident.test_failed = GNUNET_YES; + + /* Test: connecting with valid identification */ + test_valid_ident.test_executed = GNUNET_NO; + test_valid_ident.test_failed = GNUNET_YES; + + test_addr = (char *) api->address_to_string (api->cls,addr_head->addr,addr_head->addrlen); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("\nPhase 0\n\n")); + run_connection_tests(0, NULL); + + /* testing finished */ + + return; +} + + +/** + * The main function for the transport service. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + + static struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + int ret; + char *const argv_prog[] = { + "test_plugin_transport_https", + "-c", + "test_plugin_transport_data_http.conf", + "-L", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL + }; + GNUNET_log_setup ("test_plugin_transport_https", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + + ret = (GNUNET_OK == + GNUNET_PROGRAM_run (5, + argv_prog, + "test_plugin_transport_https", + "testcase", options, &run, NULL)) ? GNUNET_NO : GNUNET_YES; + + GNUNET_DISK_directory_remove ("/tmp/test_plugin_transport_https"); + + return fail; +} + +/* end of test_plugin_transport_http.c */ diff --git a/src/transport/test_transport_api_http_peer1.conf b/src/transport/test_transport_api_http_peer1.conf index f27e570b9..bb7027c0d 100644 --- a/src/transport/test_transport_api_http_peer1.conf +++ b/src/transport/test_transport_api_http_peer1.conf @@ -1,9 +1,9 @@ [transport-http] PORT = 12389 DEBUG = NO -USE_IPv6 = NO +USE_IPv6 = YES USE_IPv4 = YES -BINDTO4 = 127.0.0.1 +#BINDTO4 = 127.0.0.1 #BINDTO6 = ::1 diff --git a/src/transport/test_transport_api_http_peer2.conf b/src/transport/test_transport_api_http_peer2.conf index d60d98baa..910a63c9c 100644 --- a/src/transport/test_transport_api_http_peer2.conf +++ b/src/transport/test_transport_api_http_peer2.conf @@ -1,9 +1,9 @@ [transport-http] PORT = 22368 DEBUG = NO -USE_IPv6 = NO +USE_IPv6 = YES USE_IPv4 = YES -BINDTO4 = 127.0.0.1 +#BINDTO4 = 127.0.0.1 #BINDTO6 = ::1 [fs] -- 2.25.1