From b109b888a1a99ce9c9396e606cea57204762c1d4 Mon Sep 17 00:00:00 2001 From: Matthias Wachs Date: Tue, 21 Aug 2012 14:28:55 +0000 Subject: - preprations for http plugin split --- src/transport/Makefile.am | 186 ++- src/transport/plugin_transport_http.c | 496 ++----- src/transport/plugin_transport_http.h | 13 +- src/transport/plugin_transport_http_client.c | 784 +++-------- src/transport/plugin_transport_http_client_old.c | 645 +++++++++ src/transport/plugin_transport_http_server.c | 1461 +++----------------- src/transport/plugin_transport_http_server_old.c | 1336 ++++++++++++++++++ .../test_transport_api_http_reverse_proxy.conf | 2 +- 8 files changed, 2624 insertions(+), 2299 deletions(-) create mode 100644 src/transport/plugin_transport_http_client_old.c create mode 100644 src/transport/plugin_transport_http_server_old.c diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index c6fdafa6b..6447b1152 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -10,7 +10,15 @@ pkgcfg_DATA = \ if HAVE_MHD GN_LIBMHD = -lmicrohttpd - HTTP_PLUGIN_LA = libgnunet_plugin_transport_http.la + HTTP_SERVER_PLUGIN_LA = libgnunet_plugin_transport_http_server.la + HTTPS_SERVER_PLUGIN_LA = libgnunet_plugin_transport_https_server.la +endif + +HTTP_CLIENT_PLUGIN_LA = libgnunet_plugin_transport_http_client.la +HTTPS_CLIENT_PLUGIN_LA = libgnunet_plugin_transport_https_client.la + + +if FALSE HTTP_API_TEST = test_transport_api_http HTTP_NAT_API_TEST = test_transport_api_http_nat HTTP_API_TIMEOUT_TEST = test_transport_api_timeout_http @@ -18,7 +26,7 @@ if HAVE_MHD HTTP_NAT_REL_TEST = test_transport_api_reliability_http_nat HTTP_QUOTA_TEST = test_quota_compliance_http \ test_quota_compliance_http_asymmetric - HTTPS_PLUGIN_LA = libgnunet_plugin_transport_https.la + HTTPS_API_TEST = test_transport_api_https HTTPS_NAT_API_TEST = test_transport_api_https_nat HTTPS_API_TIMEOUT_TEST = test_transport_api_timeout_https @@ -28,6 +36,7 @@ if HAVE_MHD test_quota_compliance_https_asymmetric endif + if USE_COVERAGE AM_CFLAGS = --coverage -O0 endif @@ -154,8 +163,10 @@ plugin_LTLIBRARIES = \ libgnunet_plugin_transport_tcp.la \ libgnunet_plugin_transport_udp.la \ $(UNIX_PLUGIN_LA) \ - $(HTTP_PLUGIN_LA) \ - $(HTTPS_PLUGIN_LA) \ + $(HTTP_CLIENT_PLUGIN_LA) \ + $(HTTPS_CLIENT_PLUGIN_LA) \ + $(HTTP_SERVER_PLUGIN_LA) \ + $(HTTPS_SERVER_PLUGIN_LA) \ $(WLAN_PLUGIN_LA) \ libgnunet_plugin_transport_template.la @@ -214,43 +225,69 @@ libgnunet_plugin_transport_unix_la_LIBADD = \ $(LTLIBINTL) libgnunet_plugin_transport_unix_la_LDFLAGS = \ $(GN_PLUGIN_LDFLAGS) - -libgnunet_plugin_transport_http_la_SOURCES = \ - plugin_transport_http.c plugin_transport_http.h \ - plugin_transport_http_client.c plugin_transport_http_server.c -libgnunet_plugin_transport_http_la_LIBADD = \ + + +libgnunet_plugin_transport_http_client_la_SOURCES = \ + plugin_transport_http_client.c +libgnunet_plugin_transport_http_client_la_LIBADD = \ $(top_builddir)/src/hello/libgnunethello.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ @LIBCURL@ \ $(top_builddir)/src/nat/libgnunetnat.la \ $(top_builddir)/src/util/libgnunetutil.la -libgnunet_plugin_transport_http_la_LDFLAGS = \ - $(GN_LIBMHD) \ +libgnunet_plugin_transport_http_client_laLDFLAGS = \ $(GN_PLUGIN_LDFLAGS) -libgnunet_plugin_transport_http_la_CFLAGS = \ +libgnunet_plugin_transport_http_client_la_CFLAGS = \ $(CFLAGS) -libgnunet_plugin_transport_http_la_CPPFLAGS = \ +libgnunet_plugin_transport_http_client_la_CPPFLAGS = \ @LIBCURL_CPPFLAGS@ + + +libgnunet_plugin_transport_http_server_la_SOURCES = \ + plugin_transport_http_server.c +libgnunet_plugin_transport_http_server_la_LIBADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/nat/libgnunetnat.la \ + $(top_builddir)/src/util/libgnunetutil.la +libgnunet_plugin_transport_http_server_la_LDFLAGS = \ + $(GN_LIBMHD) \ + $(GN_PLUGIN_LDFLAGS) +libgnunet_plugin_transport_http_server_la_CFLAGS = \ + $(CFLAGS) -libgnunet_plugin_transport_https_la_SOURCES = \ - plugin_transport_http.c plugin_transport_http.h \ - plugin_transport_http_client.c plugin_transport_http_server.c -libgnunet_plugin_transport_https_la_LIBADD = \ +libgnunet_plugin_transport_https_client_la_SOURCES = \ + plugin_transport_http_client.c +libgnunet_plugin_transport_https_client_la_LIBADD = \ $(top_builddir)/src/hello/libgnunethello.la \ $(top_builddir)/src/statistics/libgnunetstatistics.la \ $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ @LIBCURL@ \ $(top_builddir)/src/nat/libgnunetnat.la \ $(top_builddir)/src/util/libgnunetutil.la -libgnunet_plugin_transport_https_la_LDFLAGS = \ - $(GN_LIBMHD) \ +libgnunet_plugin_transport_https_client_laLDFLAGS = \ $(GN_PLUGIN_LDFLAGS) -libgnunet_plugin_transport_https_la_CFLAGS = \ - $(CFLAGS) -DBUILD_HTTPS -libgnunet_plugin_transport_https_la_CPPFLAGS = \ +libgnunet_plugin_transport_https_client_la_CFLAGS = \ + $(CFLAGS) +libgnunet_plugin_transport_https_client_la_CPPFLAGS = \ @LIBCURL_CPPFLAGS@ - + + +libgnunet_plugin_transport_https_server_la_SOURCES = \ + plugin_transport_http_server.c +libgnunet_plugin_transport_https_server_la_LIBADD = \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/statistics/libgnunetstatistics.la \ + $(top_builddir)/src/peerinfo/libgnunetpeerinfo.la \ + $(top_builddir)/src/nat/libgnunetnat.la \ + $(top_builddir)/src/util/libgnunetutil.la +libgnunet_plugin_transport_https_server_la_LDFLAGS = \ + $(GN_LIBMHD) \ + $(GN_PLUGIN_LDFLAGS) +libgnunet_plugin_transport_https_server_la_CFLAGS = \ + $(CFLAGS) check_PROGRAMS = \ test_transport_testing_startstop \ @@ -471,22 +508,6 @@ test_transport_api_timeout_unix_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/transport/libgnunettransporttesting.la -test_transport_api_timeout_http_SOURCES = \ - test_transport_api_timeout.c -test_transport_api_timeout_http_LDADD = \ - $(top_builddir)/src/transport/libgnunettransport.la \ - $(top_builddir)/src/hello/libgnunethello.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/transport/libgnunettransporttesting.la - -test_transport_api_timeout_https_SOURCES = \ - test_transport_api_timeout.c -test_transport_api_timeout_https_LDADD = \ - $(top_builddir)/src/transport/libgnunettransport.la \ - $(top_builddir)/src/hello/libgnunethello.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/transport/libgnunettransporttesting.la - test_transport_api_reliability_tcp_nat_SOURCES = \ test_transport_api_reliability.c test_transport_api_reliability_tcp_nat_LDADD = \ @@ -543,6 +564,26 @@ test_transport_api_unix_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/transport/libgnunettransporttesting.la +# HTTP/S tests +if FALSE + +test_transport_api_timeout_http_SOURCES = \ + test_transport_api_timeout.c +test_transport_api_timeout_http_LDADD = \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/transport/libgnunettransporttesting.la + +test_transport_api_timeout_https_SOURCES = \ + test_transport_api_timeout.c +test_transport_api_timeout_https_LDADD = \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/transport/libgnunettransporttesting.la + + test_transport_api_http_SOURCES = \ test_transport_api.c test_transport_api_http_LDADD = \ @@ -607,6 +648,41 @@ test_transport_api_reliability_https_nat_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/transport/libgnunettransporttesting.la +test_quota_compliance_http_SOURCES = \ + test_quota_compliance.c +test_quota_compliance_http_LDADD = \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/transport/libgnunettransporttesting.la + +test_quota_compliance_http_asymmetric_SOURCES = \ + test_quota_compliance.c +test_quota_compliance_http_asymmetric_LDADD = \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/transport/libgnunettransporttesting.la + +test_quota_compliance_https_SOURCES = \ + test_quota_compliance.c +test_quota_compliance_https_LDADD = \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/transport/libgnunettransporttesting.la + +test_quota_compliance_https_asymmetric_SOURCES = \ + test_quota_compliance.c +test_quota_compliance_https_asymmetric_LDADD = \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(top_builddir)/src/transport/libgnunettransporttesting.la + + +endif + test_transport_api_unreliability_unix_SOURCES = \ test_transport_api_unreliability.c test_transport_api_unreliability_unix_LDADD = \ @@ -657,38 +733,6 @@ test_quota_compliance_tcp_asymmetric_LDADD = \ $(top_builddir)/src/util/libgnunetutil.la \ $(top_builddir)/src/transport/libgnunettransporttesting.la -test_quota_compliance_http_SOURCES = \ - test_quota_compliance.c -test_quota_compliance_http_LDADD = \ - $(top_builddir)/src/transport/libgnunettransport.la \ - $(top_builddir)/src/hello/libgnunethello.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/transport/libgnunettransporttesting.la - -test_quota_compliance_http_asymmetric_SOURCES = \ - test_quota_compliance.c -test_quota_compliance_http_asymmetric_LDADD = \ - $(top_builddir)/src/transport/libgnunettransport.la \ - $(top_builddir)/src/hello/libgnunethello.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/transport/libgnunettransporttesting.la - -test_quota_compliance_https_SOURCES = \ - test_quota_compliance.c -test_quota_compliance_https_LDADD = \ - $(top_builddir)/src/transport/libgnunettransport.la \ - $(top_builddir)/src/hello/libgnunethello.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/transport/libgnunettransporttesting.la - -test_quota_compliance_https_asymmetric_SOURCES = \ - test_quota_compliance.c -test_quota_compliance_https_asymmetric_LDADD = \ - $(top_builddir)/src/transport/libgnunettransport.la \ - $(top_builddir)/src/hello/libgnunethello.la \ - $(top_builddir)/src/util/libgnunetutil.la \ - $(top_builddir)/src/transport/libgnunettransporttesting.la - test_quota_compliance_udp_SOURCES = \ test_quota_compliance.c test_quota_compliance_udp_LDADD = \ diff --git a/src/transport/plugin_transport_http.c b/src/transport/plugin_transport_http.c index af0d9fcf8..7f78f0e4f 100644 --- a/src/transport/plugin_transport_http.c +++ b/src/transport/plugin_transport_http.c @@ -52,43 +52,6 @@ struct HttpAddressWrapper struct HttpAddress *addr; }; -/** - * Wrapper to manage IPv4 addresses - */ -struct IPv4HttpAddressWrapper -{ - /** - * Linked list next - */ - struct IPv4HttpAddressWrapper *next; - - /** - * Linked list previous - */ - struct IPv4HttpAddressWrapper *prev; - - struct IPv4HttpAddress addr; -}; - -/** - * Wrapper for IPv4 addresses. - */ -struct IPv6HttpAddressWrapper -{ - /** - * Linked list next - */ - struct IPv6HttpAddressWrapper *next; - - /** - * Linked list previous - */ - struct IPv6HttpAddressWrapper *prev; - - struct IPv6HttpAddress addr6; -}; - - /** * Context for address to string conversion. */ @@ -143,52 +106,6 @@ reschedule_session_timeout (struct Session *s); static void stop_session_timeout (struct Session *s); -/** - * Append our port and forward the result. - * - * @param cls the 'struct PrettyPrinterContext*' - * @param hostname hostname part of the address - */ -static void -append_port (void *cls, const char *hostname) -{ - struct PrettyPrinterContext *ppc = cls; - static char rbuf[INET6_ADDRSTRLEN + 13]; - - if (hostname == NULL) - { - ppc->asc (ppc->asc_cls, NULL); - GNUNET_free (ppc); - return; - } - -#if !BUILD_HTTPS - const char *protocol = "http"; -#else - const char *protocol = "https"; -#endif - GNUNET_assert ((strlen (hostname) + 7) < (INET6_ADDRSTRLEN + 13)); - if (ppc->addrlen == sizeof (struct IPv6HttpAddress)) - { - if (ppc->numeric == GNUNET_YES) - GNUNET_snprintf (rbuf, sizeof (rbuf), "%s://[%s]:%u/", protocol, hostname, ppc->port); - else - { - if (strchr(hostname, ':') != NULL) - GNUNET_snprintf (rbuf, sizeof (rbuf), "%s://[%s]:%u/", protocol, hostname, ppc->port); - else - GNUNET_snprintf (rbuf, sizeof (rbuf), "%s://%s:%u/", protocol, hostname, ppc->port); - } - } - else if (ppc->addrlen == sizeof (struct IPv4HttpAddress)) - GNUNET_snprintf (rbuf, sizeof (rbuf), "%s://%s:%u/", protocol, hostname, ppc->port); - ppc->asc (ppc->asc_cls, rbuf); -} - - - - - /** * Convert the transports address to a nice, human-readable * format. @@ -212,62 +129,16 @@ http_plugin_address_pretty_printer (void *cls, const char *type, void *asc_cls) { GNUNET_assert (cls != NULL); - struct PrettyPrinterContext *ppc; - const void *sb; - struct sockaddr_in s4; - struct sockaddr_in6 s6; - size_t sbs; - uint16_t port = 0; - - if ((addrlen == sizeof (struct IPv6HttpAddress)) && (addr != NULL)) - { - struct IPv6HttpAddress *a6 = (struct IPv6HttpAddress *) addr; - s6.sin6_family = AF_INET6; - s6.sin6_addr = a6->ipv6_addr; - s6.sin6_port = a6->u6_port; -#if HAVE_SOCKADDR_IN_SIN_LEN - s6.sin6_len = sizeof (struct sockaddr_in6); -#endif - sb = &s6; - sbs = sizeof (struct sockaddr_in6); - port = ntohs (a6->u6_port); - - } - else if ((addrlen == sizeof (struct IPv4HttpAddress)) && (addr != NULL)) - { - struct IPv4HttpAddress *a4 = (struct IPv4HttpAddress *) addr; + struct HttpAddress *haddr = (struct HttpAddress *) addr; - s4.sin_family = AF_INET; - s4.sin_addr.s_addr = a4->ipv4_addr; - s4.sin_port = a4->u4_port; -#if HAVE_SOCKADDR_IN_SIN_LEN - s4.sin_len = sizeof (struct sockaddr_in); -#endif - sb = &s4; - sbs = sizeof (struct sockaddr_in); - port = ntohs (a4->u4_port); - } - else if (0 == addrlen) - { - asc (asc_cls, ""); - asc (asc_cls, NULL); - return; - } - else + if (addrlen < (sizeof (struct HttpAddress))) { /* invalid address */ GNUNET_break_op (0); asc (asc_cls, NULL); return; } - ppc = GNUNET_malloc (sizeof (struct PrettyPrinterContext)); - ppc->asc = asc; - ppc->asc_cls = asc_cls; - ppc->port = port; - ppc->plugin = cls; - ppc->addrlen = addrlen; - ppc->numeric = numeric; - GNUNET_RESOLVER_hostname_get (sb, sbs, !numeric, timeout, &append_port, ppc); + asc (asc_cls, haddr->addr); } @@ -288,51 +159,24 @@ static int http_plugin_address_suggested (void *cls, const void *addr, size_t addrlen) { struct Plugin *plugin = cls; - struct IPv4HttpAddressWrapper *w_tv4 = plugin->ipv4_addr_head; - struct IPv6HttpAddressWrapper *w_tv6 = plugin->ipv6_addr_head; + struct HttpAddressWrapper *w = plugin->addr_head; + struct HttpAddress *haddr = (struct HttpAddress *) addr; GNUNET_assert (cls != NULL); - if ((addrlen != sizeof (struct sockaddr_in)) || - (addrlen != sizeof (struct sockaddr_in6))) - return GNUNET_SYSERR; - if (addrlen == sizeof (struct IPv4HttpAddress)) - { - struct IPv4HttpAddress *a4 = (struct IPv4HttpAddress *) addr; + if (addrlen <= sizeof (struct HttpAddress)) + return GNUNET_SYSERR; - while (w_tv4 != NULL) - { - if ((0 == - memcmp (&w_tv4->addr.ipv4_addr, &a4->ipv4_addr, - sizeof (struct in_addr))) && - (w_tv4->addr.u4_port == a4->u4_port)) - break; - w_tv4 = w_tv4->next; - } - if (w_tv4 != NULL) + if (0 == (strcmp (plugin->ext_addr->addr, haddr->addr))) return GNUNET_OK; - else - return GNUNET_SYSERR; - } - if (addrlen == sizeof (struct sockaddr_in6)) - { - struct IPv6HttpAddress *a6 = (struct IPv6HttpAddress *) addr; - while (w_tv6 != NULL) - { - if ((0 == - memcmp (&w_tv6->addr6.ipv6_addr, &a6->ipv6_addr, - sizeof (struct in6_addr))) && - (w_tv6->addr6.u6_port == a6->u6_port)) - break; - w_tv6 = w_tv6->next; - } - if (w_tv6 != NULL) - return GNUNET_OK; - else - return GNUNET_SYSERR; + while (NULL != w) + { + if (0 == (strcmp (w->addr->addr, haddr->addr))) + return GNUNET_OK; + w = w->next; } - return GNUNET_OK; + return GNUNET_SYSERR; } struct GNUNET_TIME_Relative @@ -381,6 +225,7 @@ http_string_to_address (void *cls, void **buf, size_t *added) { +#if 0 #if !BUILD_HTTPS char *protocol = "http"; #else @@ -455,7 +300,8 @@ http_string_to_address (void *cls, "Invalid address string `%s' to convert to address\n", addr_str); GNUNET_break (0); - return GNUNET_SYSERR; + return GNUNET_SYSERR; +#endif } @@ -473,57 +319,19 @@ http_string_to_address (void *cls, const char * http_plugin_address_to_string (void *cls, const void *addr, size_t addrlen) { - - struct IPv4HttpAddress *a4; - struct IPv6HttpAddress *a6; - char *address; - static char rbuf[INET6_ADDRSTRLEN + 13]; - uint16_t port; - int res = 0; - - if (addrlen == sizeof (struct IPv6HttpAddress)) - { - a6 = (struct IPv6HttpAddress *) addr; - address = GNUNET_malloc (INET6_ADDRSTRLEN); - GNUNET_assert (NULL != - inet_ntop (AF_INET6, &a6->ipv6_addr, address, - INET6_ADDRSTRLEN)); - port = ntohs (a6->u6_port); - } - else if (addrlen == sizeof (struct IPv4HttpAddress)) + struct HttpAddress *haddr; + if (addrlen < sizeof (struct HttpAddress)) { - a4 = (struct IPv4HttpAddress *) addr; - address = GNUNET_malloc (INET_ADDRSTRLEN); - GNUNET_assert (NULL != - inet_ntop (AF_INET, &(a4->ipv4_addr), address, - INET_ADDRSTRLEN)); - port = ntohs (a4->u4_port); + /* invalid address */ + GNUNET_break (0); + return NULL; } else { - /* invalid address */ - GNUNET_break (0); - return NULL; + haddr = (struct HttpAddress *) addr; + GNUNET_assert (NULL != haddr->addr); + return (const char *) haddr->addr; } -#if !BUILD_HTTPS - char *protocol = "http"; -#else - char *protocol = "https"; -#endif - - GNUNET_assert (strlen (address) + 7 < (INET6_ADDRSTRLEN + 13)); - if (addrlen == sizeof (struct IPv6HttpAddress)) - res = - GNUNET_snprintf (rbuf, sizeof (rbuf), "%s://[%s]:%u/", protocol, - address, port); - else if (addrlen == sizeof (struct IPv4HttpAddress)) - res = - GNUNET_snprintf (rbuf, sizeof (rbuf), "%s://%s:%u/", protocol, address, - port); - - GNUNET_free (address); - GNUNET_assert (res != 0); - return rbuf; } @@ -651,44 +459,16 @@ create_session (struct Plugin *plugin, const struct GNUNET_PeerIdentity *target, struct Session *s = NULL; struct GNUNET_ATS_Information ats; - GNUNET_assert ((addrlen == sizeof (struct IPv6HttpAddress)) || - (addrlen == sizeof (struct IPv4HttpAddress))); - - - - - if (addrlen == sizeof (struct IPv4HttpAddress)) + /* + * ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) &s6, sizeof (struct sockaddr_in6)); + */ + if (addrlen < sizeof (struct HttpAddress)) { - struct IPv4HttpAddress *a4 = (struct IPv4HttpAddress *) addr; - struct sockaddr_in s4; - - if (0 == ntohs(a4->u4_port)) + GNUNET_break (0); return NULL; - - s4.sin_family = AF_INET; - s4.sin_addr.s_addr = a4->ipv4_addr; - s4.sin_port = a4->u4_port; -#if HAVE_SOCKADDR_IN_SIN_LEN - s4.sin_len = sizeof (struct sockaddr_in); -#endif - ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) &s4, sizeof (struct sockaddr_in)); } - if (addrlen == sizeof (struct IPv6HttpAddress)) - { - struct IPv6HttpAddress *a6 = (struct IPv6HttpAddress *) addr; - struct sockaddr_in6 s6; - if (0 == ntohs(a6->u6_port)) - return NULL; - s6.sin6_family = AF_INET6; - s6.sin6_addr = a6->ipv6_addr; - s6.sin6_port = a6->u6_port; -#if HAVE_SOCKADDR_IN_SIN_LEN - s6.sin6_len = sizeof (struct sockaddr_in6); -#endif - ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) &s6, sizeof (struct sockaddr_in6)); - } s = GNUNET_malloc (sizeof (struct Session)); memcpy (&s->target, target, sizeof (struct GNUNET_PeerIdentity)); @@ -756,8 +536,7 @@ http_get_session (void *cls, /* create new session */ addrlen = address->address_length; - GNUNET_assert ((addrlen == sizeof (struct IPv6HttpAddress)) || - (addrlen == sizeof (struct IPv4HttpAddress))); + GNUNET_assert (addrlen > sizeof (struct HttpAddress)); s = create_session (plugin, &address->peer, address->address, address->address_length); @@ -913,120 +692,49 @@ http_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target) static void * find_address (struct Plugin *plugin, const struct sockaddr *addr, socklen_t addrlen) { - int af; - struct IPv4HttpAddressWrapper *w_t4 = NULL; - struct IPv6HttpAddressWrapper *w_t6 = NULL; + struct HttpAddressWrapper *w = NULL; + char *saddr; - af = addr->sa_family; - switch (af) + GNUNET_asprintf(&saddr, "%s://%s", plugin->protocol, GNUNET_a2s (addr, addrlen)); + w = plugin->addr_head; + while (NULL != w) { - case AF_INET: - w_t4 = plugin->ipv4_addr_head; - struct sockaddr_in *a4 = (struct sockaddr_in *) addr; - - while (w_t4 != NULL) - { - int res = memcmp (&w_t4->addr.ipv4_addr, - &a4->sin_addr, - sizeof (struct in_addr)); - - if (res == 0) - { - if (a4->sin_port != w_t4->addr.u4_port) - res = -1; - } - - if (0 == res) + if (0 == strcmp (saddr, w->addr->addr)) break; - w_t4 = w_t4->next; - } - return w_t4; - break; - case AF_INET6: - w_t6 = plugin->ipv6_addr_head; - struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) addr; - - while (w_t6) - { - int res = memcmp (&w_t6->addr6.ipv6_addr, &a6->sin6_addr, - sizeof (struct in6_addr)); - - if (res == 0) - { - if (a6->sin6_port != w_t6->addr6.u6_port) - res = -1; - } - if (0 == res) - break; - w_t6 = w_t6->next; - } - return w_t6; - break; - default: - return NULL; + w = w->next; } - return NULL; + + GNUNET_free (saddr); + return w; } + + static void nat_add_address (void *cls, int add_remove, const struct sockaddr *addr, socklen_t addrlen) { struct Plugin *plugin = cls; - struct IPv4HttpAddressWrapper *w_t4 = NULL; - struct IPv6HttpAddressWrapper *w_t6 = NULL; - int af; - - af = addr->sa_family; - switch (af) - { - case AF_INET: - w_t4 = find_address (plugin, addr, addrlen); - if (w_t4 == NULL) - { - struct sockaddr_in *a4 = (struct sockaddr_in *) addr; - w_t4 = GNUNET_malloc (sizeof (struct IPv4HttpAddressWrapper)); - memcpy (&w_t4->addr.ipv4_addr, &a4->sin_addr, sizeof (struct in_addr)); - w_t4->addr.u4_port = a4->sin_port; + struct HttpAddressWrapper *w = NULL; + char *saddr; + size_t haddrlen; - GNUNET_CONTAINER_DLL_insert (plugin->ipv4_addr_head, - plugin->ipv4_addr_tail, w_t4); - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Notifying transport to add IPv4 address `%s'\n", - http_plugin_address_to_string (NULL, &w_t4->addr, - sizeof (struct - IPv4HttpAddress))); - plugin->env->notify_address (plugin->env->cls, add_remove, &w_t4->addr, - sizeof (struct IPv4HttpAddress)); - } - break; - case AF_INET6: - w_t6 = find_address (plugin, addr, addrlen); - if (w_t6 == NULL) - { - w_t6 = GNUNET_malloc (sizeof (struct IPv6HttpAddressWrapper)); - struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) addr; - memcpy (&w_t6->addr6.ipv6_addr, &a6->sin6_addr, sizeof (struct in6_addr)); - w_t6->addr6.u6_port = a6->sin6_port; + GNUNET_asprintf(&saddr, "%s://%s", plugin->protocol, GNUNET_a2s (addr, addrlen)); - GNUNET_CONTAINER_DLL_insert (plugin->ipv6_addr_head, - plugin->ipv6_addr_tail, w_t6); + haddrlen = sizeof (struct HttpAddress) + strlen(saddr) + 1; + w = GNUNET_malloc (sizeof (struct HttpAddressWrapper)); + w->addr = GNUNET_malloc (haddrlen); + w->addr->addr = &w->addr[1]; + w->addr->addr_len = htonl (strlen(saddr) + 1); + memcpy (w->addr->addr, saddr, strlen(saddr) + 1); + GNUNET_free (saddr); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Notifying transport to add IPv6 address `%s'\n", - http_plugin_address_to_string (NULL, &w_t6->addr6, - sizeof (struct - IPv6HttpAddress))); - plugin->env->notify_address (plugin->env->cls, add_remove, &w_t6->addr6, - sizeof (struct IPv6HttpAddress)); - } - break; - default: - return; - } + GNUNET_CONTAINER_DLL_insert(plugin->addr_head, plugin->addr_tail, w); + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + "Notifying transport to add address `%s'\n", w->addr->addr); + plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, haddrlen); } @@ -1035,52 +743,23 @@ nat_remove_address (void *cls, int add_remove, const struct sockaddr *addr, socklen_t addrlen) { struct Plugin *plugin = cls; - struct IPv4HttpAddressWrapper *w_t4 = NULL; - struct IPv6HttpAddressWrapper *w_t6 = NULL; - int af; + struct HttpAddressWrapper *w = NULL; + size_t haddrlen; - af = addr->sa_family; - switch (af) - { - case AF_INET: - w_t4 = find_address (plugin, addr, addrlen); - if (w_t4 == NULL) - return; - - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Notifying transport to remove IPv4 address `%s'\n", - http_plugin_address_to_string (NULL, &w_t4->addr, - sizeof (struct - IPv4HttpAddress))); - plugin->env->notify_address (plugin->env->cls, add_remove, &w_t4->addr, - sizeof (struct IPv4HttpAddress)); - - GNUNET_CONTAINER_DLL_remove (plugin->ipv4_addr_head, plugin->ipv4_addr_tail, - w_t4); - GNUNET_free (w_t4); - break; - case AF_INET6: - w_t6 = find_address (plugin, addr, addrlen); - if (w_t6 == NULL) - return; + w = find_address (plugin, addr, addrlen); + if (NULL == w) + return; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Notifying transport to remove IPv6 address `%s'\n", - http_plugin_address_to_string (NULL, &w_t6->addr6, - sizeof (struct - IPv6HttpAddress))); + haddrlen = sizeof (struct HttpAddress) + ntohl (w->addr->addr_len); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Notifying transport to remove address `%s'\n", http_plugin_address_to_string(NULL, w->addr, haddrlen)); - plugin->env->notify_address (plugin->env->cls, add_remove, &w_t6->addr6, - sizeof (struct IPv6HttpAddress)); - GNUNET_CONTAINER_DLL_remove (plugin->ipv6_addr_head, plugin->ipv6_addr_tail, - w_t6); - GNUNET_free (w_t6); - break; - default: - return; - } + GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w); + plugin->env->notify_address (plugin->env->cls, add_remove, w->addr, + sizeof (struct HttpAddress) + ntohl (w->addr->addr_len)); + GNUNET_free (w->addr); + GNUNET_free (w); } @@ -1371,23 +1050,14 @@ stop_report_addresses (struct Plugin *plugin) GNUNET_NAT_unregister (plugin->nat); /* Clean up addresses */ - struct IPv4HttpAddressWrapper *w_t4; - struct IPv6HttpAddressWrapper *w_t6; + struct HttpAddressWrapper *w; - while (plugin->ipv4_addr_head != NULL) + while (plugin->addr_head != NULL) { - w_t4 = plugin->ipv4_addr_head; - GNUNET_CONTAINER_DLL_remove (plugin->ipv4_addr_head, plugin->ipv4_addr_tail, - w_t4); - GNUNET_free (w_t4); - } - - while (plugin->ipv6_addr_head != NULL) - { - w_t6 = plugin->ipv6_addr_head; - GNUNET_CONTAINER_DLL_remove (plugin->ipv6_addr_head, plugin->ipv6_addr_tail, - w_t6); - GNUNET_free (w_t6); + w = plugin->addr_head; + GNUNET_CONTAINER_DLL_remove (plugin->addr_head, plugin->addr_tail, w); + GNUNET_free (w->addr); + GNUNET_free (w); } } @@ -1403,6 +1073,7 @@ notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskContext * { struct Plugin *plugin = cls; struct HttpAddress *eaddr; + char *addr; size_t eaddr_len; size_t uri_len; @@ -1411,12 +1082,17 @@ notify_external_hostname (void *cls, const struct GNUNET_SCHEDULER_TaskContext * if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) return; - uri_len = strlen (plugin->external_hostname) + 1; + GNUNET_asprintf(&addr, "%s://%s", plugin->protocol, plugin->external_hostname); + uri_len = strlen (addr) + 1; eaddr_len = sizeof (struct HttpAddress) + uri_len; eaddr = GNUNET_malloc (eaddr_len); - eaddr->addr_len = htonl (strlen (plugin->external_hostname) + 1); + eaddr->addr_len = htonl (uri_len); eaddr->addr = (void *) &eaddr[1]; - memcpy (&eaddr->addr, plugin->external_hostname, uri_len); + memcpy (&eaddr->addr, addr, uri_len); + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + "Notifying transport about external hostname address `%s'\n", addr); + + GNUNET_free (addr); plugin->env->notify_address (plugin->env->cls, GNUNET_YES, eaddr, eaddr_len); plugin->ext_addr = eaddr; plugin->ext_addr_len = eaddr_len; diff --git a/src/transport/plugin_transport_http.h b/src/transport/plugin_transport_http.h index 1469fd8a9..1ac2f9859 100644 --- a/src/transport/plugin_transport_http.h +++ b/src/transport/plugin_transport_http.h @@ -96,22 +96,13 @@ struct Plugin /** * IPv4 addresses DLL head */ - struct IPv4HttpAddressWrapper *ipv4_addr_head; + struct HttpAddressWrapper *addr_head; /** * IPv4 addresses DLL tail */ - struct IPv4HttpAddressWrapper *ipv4_addr_tail; + struct HttpAddressWrapper *addr_tail; - /** - * IPv6 addresses DLL head - */ - struct IPv6HttpAddressWrapper *ipv6_addr_head; - - /** - * IPv6 addresses DLL tail - */ - struct IPv6HttpAddressWrapper *ipv6_addr_tail; /** * Plugin configuration diff --git a/src/transport/plugin_transport_http_client.c b/src/transport/plugin_transport_http_client.c index ff23da974..f4790b0f6 100644 --- a/src/transport/plugin_transport_http_client.c +++ b/src/transport/plugin_transport_http_client.c @@ -1,6 +1,6 @@ /* This file is part of GNUnet - (C) 2002--2012 Christian Grothoff (and other contributing authors) + (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 @@ -20,626 +20,288 @@ /** * @file transport/plugin_transport_http_client.c - * @brief http transport service plugin + * @brief HTTP/S client transport plugin * @author Matthias Wachs */ -#include "plugin_transport_http.h" +#if BUILD_HTTPS +#define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_client_init +#define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_client_done +#else +#define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_client_init +#define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_client_done +#endif + -static struct Plugin * p; +#include "platform.h" +#include "gnunet_protocols.h" +#include "gnunet_connection_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_service_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" +#include "gnunet_transport_plugin.h" + +#define DEBUG_TEMPLATE GNUNET_EXTRA_LOGGING -#if VERBOSE_CURL /** - * Function to log curl debug messages with GNUNET_log - * @param curl handle - * @param type curl_infotype - * @param data data - * @param size size - * @param cls closure - * @return 0 + * After how long do we expire an address that we + * learned from another peer if it is not reconfirmed + * by anyone? */ -static int -client_log (CURL * curl, curl_infotype type, char *data, size_t size, void *cls) -{ - if (type == CURLINFO_TEXT) - { - char text[size + 2]; - - memcpy (text, data, size); - if (text[size - 1] == '\n') - text[size] = '\0'; - else - { - text[size] = '\n'; - text[size + 1] = '\0'; - } -#if BUILD_HTTPS - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-https", - "Client: %p - %s", cls, text); -#else - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-http", - "Client: %p - %s", cls, text); -#endif - } - return 0; -} -#endif +#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6) + /** - * Task performing curl operations - * @param cls plugin as closure - * @param tc gnunet scheduler task context + * Encapsulation of all of the state of the plugin. */ -static void -client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); +struct Plugin; /** - * Function setting up file descriptors and scheduling task to run - * - * @param plugin plugin as closure - * @param now schedule task in 1ms, regardless of what curl may say - * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok + * Session handle for connections. */ -static int -client_schedule (struct Plugin *plugin, int now) +struct Session { - 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; - struct GNUNET_TIME_Relative timeout; - - /* Cancel previous scheduled task */ - if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->client_perform_task); - plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; - } - max = -1; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - mret = curl_multi_fdset (plugin->client_mh, &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->client_mh, &to); - if (to == -1) - timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1); - else - timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to); - if (now == GNUNET_YES) - timeout = GNUNET_TIME_UNIT_MILLISECONDS; - - 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->client_perform_task = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - timeout, grs, gws, - &client_run, plugin); - GNUNET_NETWORK_fdset_destroy (gws); - GNUNET_NETWORK_fdset_destroy (grs); - return GNUNET_OK; -} - + /** + * To whom are we talking to (set to our identity + * if we are still waiting for the welcome message) + */ + struct GNUNET_PeerIdentity sender; + + /** + * Stored in a linked list. + */ + struct Session *next; + + /** + * Pointer to the global plugin struct. + */ + struct Plugin *plugin; + + /** + * The client (used to identify this connection) + */ + /* void *client; */ + + /** + * 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; + + /** + * At what time did we reset last_received last? + */ + struct GNUNET_TIME_Absolute last_quota_update; + + /** + * How many bytes have we received since the "last_quota_update" + * timestamp? + */ + uint64_t last_received; + + /** + * Number of bytes per ms that this peer is allowed + * to send to us. + */ + uint32_t quota; + +}; -int -client_send (struct Session *s, struct HTTP_Message *msg) +/** + * Encapsulation of all of the state of the plugin. + */ +struct Plugin { - GNUNET_assert (s != NULL); - GNUNET_CONTAINER_DLL_insert_tail (s->msg_head, s->msg_tail, msg); - - if (GNUNET_YES != exist_session(p, s)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (s->client_put_paused == GNUNET_YES) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Client: %p was suspended, unpausing\n", s->client_put); - s->client_put_paused = GNUNET_NO; - curl_easy_pause (s->client_put, CURLPAUSE_CONT); - } - client_schedule (s->plugin, GNUNET_YES); + /** + * Our environment. + */ + struct GNUNET_TRANSPORT_PluginEnvironment *env; - return GNUNET_OK; -} + /** + * List of open sessions. + */ + struct Session *sessions; + +}; /** - * Task performing curl operations + * 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 plugin as closure - * @param tc gnunet scheduler task context + * @param cls closure + * @param session which session must be used + * @param msgbuf the message to transmit + * @param msgbuf_size number of bytes in 'msgbuf' + * @param priority how important is the message (most plugins will + * ignore message priority and just FIFO) + * @param to how long to wait at most for the transmission (does not + * require plugins to discard the message after the timeout, + * just advisory for the desired delay; most plugins will ignore + * this as well) + * @param cont continuation to call once the message has + * been transmitted (or if the transport is ready + * for the next transmission call; or if the + * peer disconnected...); can be NULL + * @param cont_cls closure for cont + * @return number of bytes used (on the physical network, with overheads); + * -1 on hard errors (i.e. address invalid); 0 is a legal value + * and does NOT mean that the message was not transmitted (DV) */ -static void -client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +static ssize_t +http_client_plugin_send (void *cls, + struct Session *session, + const char *msgbuf, size_t msgbuf_size, + unsigned int priority, + struct GNUNET_TIME_Relative to, + GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls) { struct Plugin *plugin = cls; - int running; - CURLMcode mret; - - GNUNET_assert (cls != NULL); - - plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; - - do - { - running = 0; - mret = curl_multi_perform (plugin->client_mh, &running); - - CURLMsg *msg; - int msgs_left; - - while ((msg = curl_multi_info_read (plugin->client_mh, &msgs_left))) - { - CURL *easy_h = msg->easy_handle; - struct Session *s = NULL; - char *d = (char *) s; - - if (easy_h == NULL) - { - GNUNET_break (0); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Client: connection to ended with reason %i: `%s', %i handles running\n", - msg->data.result, - curl_easy_strerror (msg->data.result), running); - continue; - } - - GNUNET_assert (CURLE_OK == - curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d)); - s = (struct Session *) d; - - if (GNUNET_YES != exist_session(plugin, s)) - { - GNUNET_break (0); - return; - } - - GNUNET_assert (s != NULL); - if (msg->msg == CURLMSG_DONE) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Client: %p connection to '%s' %s ended with reason %i: `%s'\n", - msg->easy_handle, GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, - s->addrlen), - msg->data.result, - curl_easy_strerror (msg->data.result)); - - /* Disconnect other transmission direction and tell transport */ - client_disconnect (s); - } - } - } - while (mret == CURLM_CALL_MULTI_PERFORM); - client_schedule (plugin, GNUNET_NO); -} + int bytes_sent = 0; + GNUNET_assert (plugin != NULL); + GNUNET_assert (session != NULL); -int -client_disconnect (struct Session *s) -{ - int res = GNUNET_OK; - CURLMcode mret; - struct Plugin *plugin = s->plugin; - struct HTTP_Message *msg; - struct HTTP_Message *t; - - if (GNUNET_YES != exist_session(plugin, s)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - - if (s->client_put != NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Client: %p / %p Deleting outbound PUT session to peer `%s'\n", - s, s->client_put, GNUNET_i2s (&s->target)); - - /* remove curl handle from multi handle */ - mret = curl_multi_remove_handle (plugin->client_mh, s->client_put); - if (mret != CURLM_OK) - { - /* clean up easy handle, handle is now invalid and free'd */ - res = GNUNET_SYSERR; - GNUNET_break (0); - } - curl_easy_cleanup (s->client_put); - s->client_put = NULL; - } - - - if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (s->recv_wakeup_task); - s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK; - } - - if (s->client_get != NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Client: %p / %p Deleting outbound GET session to peer `%s'\n", - s, - s->client_get, GNUNET_i2s (&s->target)); - - /* remove curl handle from multi handle */ - mret = curl_multi_remove_handle (plugin->client_mh, s->client_get); - if (mret != CURLM_OK) - { - /* clean up easy handle, handle is now invalid and free'd */ - res = GNUNET_SYSERR; - GNUNET_break (0); - } - curl_easy_cleanup (s->client_get); - s->client_get = NULL; - } - - msg = s->msg_head; - while (msg != NULL) - { - t = msg->next; - if (NULL != msg->transmit_cont) - msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR); - GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); - GNUNET_free (msg); - msg = t; - } - - plugin->cur_connections -= 2; - - notify_session_end (plugin, &s->target, s); - - GNUNET_assert (plugin->outbound_sessions > 0); - plugin->outbound_sessions --; - GNUNET_STATISTICS_set (plugin->env->stats, - "# HTTP outbound sessions", - plugin->outbound_sessions, - GNUNET_NO); - - /* Re-schedule since handles have changed */ - if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->client_perform_task); - plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; - } - client_schedule (plugin, GNUNET_YES); - - return res; + /* struct Plugin *plugin = cls; */ + return bytes_sent; } -static int -client_receive_mst_cb (void *cls, void *client, - const struct GNUNET_MessageHeader *message) -{ - struct Session *s = cls; - struct GNUNET_TIME_Relative delay; - - if (GNUNET_YES != exist_session(p, s)) - { - GNUNET_break (0); - return GNUNET_OK; - } - - delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen); - s->next_receive = - GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay); - - if (GNUNET_TIME_absolute_get ().abs_value < s->next_receive.abs_value) - { - struct Plugin *plugin = s->plugin; - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Client: peer `%s' address `%s' next read delayed for %llu ms\n", - GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen), - delay); - } - return GNUNET_OK; -} +/** + * 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 -client_wake_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +http_client_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target) { - struct Session *s = cls; - - if (GNUNET_YES != exist_session(p, s)) - { - GNUNET_break (0); - return; - } - s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Client: %p Waking up receive handle\n", s->client_get); - if (s->client_get != NULL) - curl_easy_pause (s->client_get, CURLPAUSE_CONT); + // struct Plugin *plugin = cls; + // FIXME } /** - * Callback method used with libcurl - * Method is called when libcurl needs to write data during sending + * Convert the transports address to a nice, human-readable + * format. * - * @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 cls destination pointer, passed to the libcurl handle - * @return bytes read from stream + * @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 size_t -client_receive (void *stream, size_t size, size_t nmemb, void *cls) +static void +http_client_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) { - struct Session *s = cls; - struct GNUNET_TIME_Absolute now; - size_t len = size * nmemb; - struct Plugin *plugin = s->plugin; - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Client: Received %u bytes from peer `%s'\n", len, - GNUNET_i2s (&s->target)); - now = GNUNET_TIME_absolute_get (); - if (now.abs_value < s->next_receive.abs_value) - { - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - struct GNUNET_TIME_Relative delta = - GNUNET_TIME_absolute_get_difference (now, s->next_receive); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Client: %p No inbound bandwidth available! Next read was delayed for %llu ms\n", - s->client_get, delta.rel_value); - if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (s->recv_wakeup_task); - s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK; - } - s->recv_wakeup_task = - GNUNET_SCHEDULER_add_delayed (delta, &client_wake_up, s); - return CURLPAUSE_ALL; - } - if (NULL == s->msg_tk) - s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb, s); - GNUNET_SERVER_mst_receive (s->msg_tk, s, stream, len, GNUNET_NO, GNUNET_NO); - return len; + asc (asc_cls, NULL); } + /** - * Callback method used with libcurl - * Method is called when libcurl needs to read data during sending + * 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 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 cls source pointer, passed to the libcurl handle - * @return bytes written to stream, returning 0 will terminate connection! + * @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 size_t -client_send_cb (void *stream, size_t size, size_t nmemb, void *cls) +static int +http_client_plugin_address_suggested (void *cls, const void *addr, size_t addrlen) { - struct Session *s = cls; - struct Plugin *plugin = s->plugin; - struct HTTP_Message *msg = s->msg_head; - size_t len; - - if (GNUNET_YES != exist_session(plugin, s)) - { - GNUNET_break (0); - return 0; - } - if (NULL == msg) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Client: %p Nothing to send! Suspending PUT handle!\n", - s->client_put); - s->client_put_paused = GNUNET_YES; - return CURL_READFUNC_PAUSE; - } - /* data to send */ - GNUNET_assert (msg->pos < msg->size); - /* calculate how much fits in buffer */ - len = GNUNET_MIN (msg->size - msg->pos, - size * nmemb); - memcpy (stream, &msg->buf[msg->pos], len); - msg->pos += len; - if (msg->pos == msg->size) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Client: %p Message with %u bytes sent, removing message from queue\n", - s->client_put, msg->size, msg->pos); - /* Calling transmit continuation */ - GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); - if (NULL != msg->transmit_cont) - msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK); - GNUNET_free (msg); - } - return len; + /* struct Plugin *plugin = cls; */ + + /* check if the address is plausible; if so, + * add it to our list! */ + return GNUNET_OK; } -int -client_connect (struct Session *s) +/** + * 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_client_plugin_address_to_string (void *cls, const void *addr, size_t addrlen) { - struct Plugin *plugin = s->plugin; - int res = GNUNET_OK; - char *url; - CURLMcode mret; - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Initiating outbound session peer `%s'\n", - GNUNET_i2s (&s->target)); - s->inbound = GNUNET_NO; - plugin->last_tag++; - /* create url */ - GNUNET_asprintf (&url, "%s%s;%u", - http_plugin_address_to_string (plugin, s->addr, s->addrlen), - GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey), - plugin->last_tag); -#if 0 - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, "URL `%s'\n", url); -#endif - /* create get connection */ - s->client_get = curl_easy_init (); -#if VERBOSE_CURL - curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L); - curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log); - curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, s->client_get); -#endif -#if BUILD_HTTPS - curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); - curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0); - curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0); -#endif - curl_easy_setopt (s->client_get, CURLOPT_URL, url); - //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb); - //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps); - curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb); - curl_easy_setopt (s->client_get, CURLOPT_READDATA, s); - curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive); - curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s); - curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT_MS, - (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value); - curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s); - curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS, - (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value); - curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE, - 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE); -#if CURL_TCP_NODELAY - curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1); -#endif + GNUNET_break (0); + return NULL; +} - /* create put connection */ - s->client_put = curl_easy_init (); -#if VERBOSE_CURL - curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L); - curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log); - curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, s->client_put); -#endif -#if BUILD_HTTPS - curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); - curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0); - curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0); -#endif - curl_easy_setopt (s->client_put, CURLOPT_URL, url); - curl_easy_setopt (s->client_put, CURLOPT_PUT, 1L); - //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &curl_put_header_cb); - //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps); - curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb); - curl_easy_setopt (s->client_put, CURLOPT_READDATA, s); - curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive); - curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s); - curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT_MS, - (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value); - curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s); - curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS, - (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value); - curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE, - 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE); -#if CURL_TCP_NODELAY - curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1); -#endif - GNUNET_free (url); - - mret = curl_multi_add_handle (plugin->client_mh, s->client_get); - if (mret != CURLM_OK) - { - curl_easy_cleanup (s->client_get); - res = GNUNET_SYSERR; - GNUNET_break (0); - } - - mret = curl_multi_add_handle (plugin->client_mh, s->client_put); - if (mret != CURLM_OK) - { - curl_multi_remove_handle (plugin->client_mh, s->client_get); - curl_easy_cleanup (s->client_get); - curl_easy_cleanup (s->client_put); - res = GNUNET_SYSERR; - GNUNET_break (0); - } - - /* Perform connect */ - plugin->cur_connections += 2; - - plugin->outbound_sessions ++; - GNUNET_STATISTICS_set (plugin->env->stats, - "# HTTP outbound sessions", - plugin->outbound_sessions, - GNUNET_NO); - - /* Re-schedule since handles have changed */ - if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->client_perform_task); - plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; - } - plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin); - - return res; -} -int -client_start (struct Plugin *plugin) +/** + * Entry point for the plugin. + */ +void * +LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls) { - int res = GNUNET_OK; - p = plugin; - - curl_global_init (CURL_GLOBAL_ALL); - plugin->client_mh = curl_multi_init (); - - if (NULL == plugin->client_mh) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - _ - ("Could not initialize curl multi handle, failed to start %s plugin!\n"), - plugin->name); - res = GNUNET_SYSERR; - } - return res; + struct GNUNET_TRANSPORT_PluginEnvironment *env = cls; + struct GNUNET_TRANSPORT_PluginFunctions *api; + struct Plugin *plugin; + + plugin = GNUNET_malloc (sizeof (struct Plugin)); + plugin->env = env; + api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); + api->cls = plugin; + api->send = &http_client_plugin_send; + api->disconnect = &http_client_plugin_disconnect; + api->address_pretty_printer = &http_client_plugin_address_pretty_printer; + api->check_address = &http_client_plugin_address_suggested; + api->address_to_string = &http_client_plugin_address_to_string; + return api; } -void -client_stop (struct Plugin *plugin) +/** + * Exit point from the plugin. + */ +void * +LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) { - p = NULL; - if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->client_perform_task); - plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; - } - - curl_multi_cleanup (plugin->client_mh); - curl_global_cleanup (); -} - + struct GNUNET_TRANSPORT_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; + GNUNET_free (plugin); + GNUNET_free (api); + return NULL; +} /* end of plugin_transport_http_client.c */ diff --git a/src/transport/plugin_transport_http_client_old.c b/src/transport/plugin_transport_http_client_old.c new file mode 100644 index 000000000..ff23da974 --- /dev/null +++ b/src/transport/plugin_transport_http_client_old.c @@ -0,0 +1,645 @@ +/* + This file is part of GNUnet + (C) 2002--2012 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_http_client.c + * @brief http transport service plugin + * @author Matthias Wachs + */ + +#include "plugin_transport_http.h" + +static struct Plugin * p; + +#if VERBOSE_CURL +/** + * Function to log curl debug messages with GNUNET_log + * @param curl handle + * @param type curl_infotype + * @param data data + * @param size size + * @param cls closure + * @return 0 + */ +static int +client_log (CURL * curl, curl_infotype type, char *data, size_t size, void *cls) +{ + if (type == CURLINFO_TEXT) + { + char text[size + 2]; + + memcpy (text, data, size); + if (text[size - 1] == '\n') + text[size] = '\0'; + else + { + text[size] = '\n'; + text[size + 1] = '\0'; + } +#if BUILD_HTTPS + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-https", + "Client: %p - %s", cls, text); +#else + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-http", + "Client: %p - %s", cls, text); +#endif + } + return 0; +} +#endif + +/** + * Task performing curl operations + * @param cls plugin as closure + * @param tc gnunet scheduler task context + */ +static void +client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc); + + +/** + * Function setting up file descriptors and scheduling task to run + * + * @param plugin plugin as closure + * @param now schedule task in 1ms, regardless of what curl may say + * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok + */ +static int +client_schedule (struct Plugin *plugin, int now) +{ + 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; + struct GNUNET_TIME_Relative timeout; + + /* Cancel previous scheduled task */ + if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->client_perform_task); + plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; + } + max = -1; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + mret = curl_multi_fdset (plugin->client_mh, &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->client_mh, &to); + if (to == -1) + timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1); + else + timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to); + if (now == GNUNET_YES) + timeout = GNUNET_TIME_UNIT_MILLISECONDS; + + 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->client_perform_task = + GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + timeout, grs, gws, + &client_run, plugin); + GNUNET_NETWORK_fdset_destroy (gws); + GNUNET_NETWORK_fdset_destroy (grs); + return GNUNET_OK; +} + + +int +client_send (struct Session *s, struct HTTP_Message *msg) +{ + GNUNET_assert (s != NULL); + GNUNET_CONTAINER_DLL_insert_tail (s->msg_head, s->msg_tail, msg); + + if (GNUNET_YES != exist_session(p, s)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (s->client_put_paused == GNUNET_YES) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, + "Client: %p was suspended, unpausing\n", s->client_put); + s->client_put_paused = GNUNET_NO; + curl_easy_pause (s->client_put, CURLPAUSE_CONT); + } + client_schedule (s->plugin, GNUNET_YES); + + return GNUNET_OK; +} + + +/** + * Task performing curl operations + * + * @param cls plugin as closure + * @param tc gnunet scheduler task context + */ +static void +client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Plugin *plugin = cls; + int running; + CURLMcode mret; + + GNUNET_assert (cls != NULL); + + plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + + do + { + running = 0; + mret = curl_multi_perform (plugin->client_mh, &running); + + CURLMsg *msg; + int msgs_left; + + while ((msg = curl_multi_info_read (plugin->client_mh, &msgs_left))) + { + CURL *easy_h = msg->easy_handle; + struct Session *s = NULL; + char *d = (char *) s; + + if (easy_h == NULL) + { + GNUNET_break (0); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Client: connection to ended with reason %i: `%s', %i handles running\n", + msg->data.result, + curl_easy_strerror (msg->data.result), running); + continue; + } + + GNUNET_assert (CURLE_OK == + curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d)); + s = (struct Session *) d; + + if (GNUNET_YES != exist_session(plugin, s)) + { + GNUNET_break (0); + return; + } + + GNUNET_assert (s != NULL); + if (msg->msg == CURLMSG_DONE) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Client: %p connection to '%s' %s ended with reason %i: `%s'\n", + msg->easy_handle, GNUNET_i2s (&s->target), + http_plugin_address_to_string (NULL, s->addr, + s->addrlen), + msg->data.result, + curl_easy_strerror (msg->data.result)); + + /* Disconnect other transmission direction and tell transport */ + client_disconnect (s); + } + } + } + while (mret == CURLM_CALL_MULTI_PERFORM); + client_schedule (plugin, GNUNET_NO); +} + + +int +client_disconnect (struct Session *s) +{ + int res = GNUNET_OK; + CURLMcode mret; + struct Plugin *plugin = s->plugin; + struct HTTP_Message *msg; + struct HTTP_Message *t; + + if (GNUNET_YES != exist_session(plugin, s)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + + if (s->client_put != NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Client: %p / %p Deleting outbound PUT session to peer `%s'\n", + s, s->client_put, GNUNET_i2s (&s->target)); + + /* remove curl handle from multi handle */ + mret = curl_multi_remove_handle (plugin->client_mh, s->client_put); + if (mret != CURLM_OK) + { + /* clean up easy handle, handle is now invalid and free'd */ + res = GNUNET_SYSERR; + GNUNET_break (0); + } + curl_easy_cleanup (s->client_put); + s->client_put = NULL; + } + + + if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (s->recv_wakeup_task); + s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK; + } + + if (s->client_get != NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Client: %p / %p Deleting outbound GET session to peer `%s'\n", + s, + s->client_get, GNUNET_i2s (&s->target)); + + /* remove curl handle from multi handle */ + mret = curl_multi_remove_handle (plugin->client_mh, s->client_get); + if (mret != CURLM_OK) + { + /* clean up easy handle, handle is now invalid and free'd */ + res = GNUNET_SYSERR; + GNUNET_break (0); + } + curl_easy_cleanup (s->client_get); + s->client_get = NULL; + } + + msg = s->msg_head; + while (msg != NULL) + { + t = msg->next; + if (NULL != msg->transmit_cont) + msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR); + GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); + GNUNET_free (msg); + msg = t; + } + + plugin->cur_connections -= 2; + + notify_session_end (plugin, &s->target, s); + + GNUNET_assert (plugin->outbound_sessions > 0); + plugin->outbound_sessions --; + GNUNET_STATISTICS_set (plugin->env->stats, + "# HTTP outbound sessions", + plugin->outbound_sessions, + GNUNET_NO); + + /* Re-schedule since handles have changed */ + if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->client_perform_task); + plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; + } + client_schedule (plugin, GNUNET_YES); + + return res; +} + +static int +client_receive_mst_cb (void *cls, void *client, + const struct GNUNET_MessageHeader *message) +{ + struct Session *s = cls; + struct GNUNET_TIME_Relative delay; + + if (GNUNET_YES != exist_session(p, s)) + { + GNUNET_break (0); + return GNUNET_OK; + } + + delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen); + s->next_receive = + GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay); + + if (GNUNET_TIME_absolute_get ().abs_value < s->next_receive.abs_value) + { + struct Plugin *plugin = s->plugin; + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Client: peer `%s' address `%s' next read delayed for %llu ms\n", + GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen), + delay); + } + return GNUNET_OK; +} + + +static void +client_wake_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Session *s = cls; + + if (GNUNET_YES != exist_session(p, s)) + { + GNUNET_break (0); + return; + } + s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, + "Client: %p Waking up receive handle\n", s->client_get); + if (s->client_get != NULL) + curl_easy_pause (s->client_get, CURLPAUSE_CONT); +} + + +/** + * 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 cls destination pointer, passed to the libcurl handle + * @return bytes read from stream + */ +static size_t +client_receive (void *stream, size_t size, size_t nmemb, void *cls) +{ + struct Session *s = cls; + struct GNUNET_TIME_Absolute now; + size_t len = size * nmemb; + struct Plugin *plugin = s->plugin; + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Client: Received %u bytes from peer `%s'\n", len, + GNUNET_i2s (&s->target)); + now = GNUNET_TIME_absolute_get (); + if (now.abs_value < s->next_receive.abs_value) + { + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + struct GNUNET_TIME_Relative delta = + GNUNET_TIME_absolute_get_difference (now, s->next_receive); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Client: %p No inbound bandwidth available! Next read was delayed for %llu ms\n", + s->client_get, delta.rel_value); + if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (s->recv_wakeup_task); + s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK; + } + s->recv_wakeup_task = + GNUNET_SCHEDULER_add_delayed (delta, &client_wake_up, s); + return CURLPAUSE_ALL; + } + if (NULL == s->msg_tk) + s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb, s); + GNUNET_SERVER_mst_receive (s->msg_tk, s, stream, len, GNUNET_NO, GNUNET_NO); + return len; +} + + +/** + * 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 cls source pointer, passed to the libcurl handle + * @return bytes written to stream, returning 0 will terminate connection! + */ +static size_t +client_send_cb (void *stream, size_t size, size_t nmemb, void *cls) +{ + struct Session *s = cls; + struct Plugin *plugin = s->plugin; + struct HTTP_Message *msg = s->msg_head; + size_t len; + + if (GNUNET_YES != exist_session(plugin, s)) + { + GNUNET_break (0); + return 0; + } + if (NULL == msg) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Client: %p Nothing to send! Suspending PUT handle!\n", + s->client_put); + s->client_put_paused = GNUNET_YES; + return CURL_READFUNC_PAUSE; + } + /* data to send */ + GNUNET_assert (msg->pos < msg->size); + /* calculate how much fits in buffer */ + len = GNUNET_MIN (msg->size - msg->pos, + size * nmemb); + memcpy (stream, &msg->buf[msg->pos], len); + msg->pos += len; + if (msg->pos == msg->size) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Client: %p Message with %u bytes sent, removing message from queue\n", + s->client_put, msg->size, msg->pos); + /* Calling transmit continuation */ + GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); + if (NULL != msg->transmit_cont) + msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK); + GNUNET_free (msg); + } + return len; +} + + +int +client_connect (struct Session *s) +{ + struct Plugin *plugin = s->plugin; + int res = GNUNET_OK; + char *url; + CURLMcode mret; + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Initiating outbound session peer `%s'\n", + GNUNET_i2s (&s->target)); + s->inbound = GNUNET_NO; + plugin->last_tag++; + /* create url */ + GNUNET_asprintf (&url, "%s%s;%u", + http_plugin_address_to_string (plugin, s->addr, s->addrlen), + GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey), + plugin->last_tag); +#if 0 + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, "URL `%s'\n", url); +#endif + /* create get connection */ + s->client_get = curl_easy_init (); +#if VERBOSE_CURL + curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L); + curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log); + curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, s->client_get); +#endif +#if BUILD_HTTPS + curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); + curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0); +#endif + curl_easy_setopt (s->client_get, CURLOPT_URL, url); + //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb); + //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps); + curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb); + curl_easy_setopt (s->client_get, CURLOPT_READDATA, s); + curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive); + curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s); + curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT_MS, + (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value); + curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s); + curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS, + (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value); + curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE, + 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE); +#if CURL_TCP_NODELAY + curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1); +#endif + + /* create put connection */ + s->client_put = curl_easy_init (); +#if VERBOSE_CURL + curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L); + curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log); + curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, s->client_put); +#endif +#if BUILD_HTTPS + curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); + curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0); +#endif + curl_easy_setopt (s->client_put, CURLOPT_URL, url); + curl_easy_setopt (s->client_put, CURLOPT_PUT, 1L); + //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &curl_put_header_cb); + //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps); + curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb); + curl_easy_setopt (s->client_put, CURLOPT_READDATA, s); + curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive); + curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s); + curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT_MS, + (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value); + curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s); + curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS, + (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value); + curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE, + 2 * GNUNET_SERVER_MAX_MESSAGE_SIZE); +#if CURL_TCP_NODELAY + curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1); +#endif + + GNUNET_free (url); + + mret = curl_multi_add_handle (plugin->client_mh, s->client_get); + if (mret != CURLM_OK) + { + curl_easy_cleanup (s->client_get); + res = GNUNET_SYSERR; + GNUNET_break (0); + } + + mret = curl_multi_add_handle (plugin->client_mh, s->client_put); + if (mret != CURLM_OK) + { + curl_multi_remove_handle (plugin->client_mh, s->client_get); + curl_easy_cleanup (s->client_get); + curl_easy_cleanup (s->client_put); + res = GNUNET_SYSERR; + GNUNET_break (0); + } + + /* Perform connect */ + plugin->cur_connections += 2; + + plugin->outbound_sessions ++; + GNUNET_STATISTICS_set (plugin->env->stats, + "# HTTP outbound sessions", + plugin->outbound_sessions, + GNUNET_NO); + + /* Re-schedule since handles have changed */ + if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->client_perform_task); + plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; + } + plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin); + + return res; +} + + +int +client_start (struct Plugin *plugin) +{ + int res = GNUNET_OK; + p = plugin; + + curl_global_init (CURL_GLOBAL_ALL); + plugin->client_mh = curl_multi_init (); + + if (NULL == plugin->client_mh) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + _ + ("Could not initialize curl multi handle, failed to start %s plugin!\n"), + plugin->name); + res = GNUNET_SYSERR; + } + return res; +} + + +void +client_stop (struct Plugin *plugin) +{ + p = NULL; + if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->client_perform_task); + plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK; + } + + curl_multi_cleanup (plugin->client_mh); + curl_global_cleanup (); +} + + + +/* end of plugin_transport_http_client.c */ diff --git a/src/transport/plugin_transport_http_server.c b/src/transport/plugin_transport_http_server.c index f81f5cf6e..da0c11e24 100644 --- a/src/transport/plugin_transport_http_server.c +++ b/src/transport/plugin_transport_http_server.c @@ -19,1318 +19,289 @@ */ /** - * @file transport/plugin_transport_http.c - * @brief http transport service plugin + * @file transport/plugin_transport_http_server.c + * @brief HTTP/S server transport plugin * @author Matthias Wachs */ -#include "plugin_transport_http.h" +#include "platform.h" +#include "gnunet_protocols.h" +#include "gnunet_connection_lib.h" +#include "gnunet_server_lib.h" +#include "gnunet_service_lib.h" +#include "gnunet_statistics_service.h" +#include "gnunet_transport_service.h" +#include "gnunet_transport_plugin.h" + +#if BUILD_HTTPS +#define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_server_init +#define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_server_done +#else +#define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_server_init +#define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_server_done +#endif -#define HTTP_ERROR_RESPONSE "404 Not Found

Not Found

The requested URL was not found on this server.


" -#define _RECEIVE 0 -#define _SEND 1 -static struct Plugin * p; +#define DEBUG_TEMPLATE GNUNET_EXTRA_LOGGING /** - * Function that queries MHD's select sets and - * starts the task waiting for them. - * @param plugin plugin - * @param daemon_handle the MHD daemon handle - * @param now schedule now or with MHD delay - * @return gnunet task identifier + * After how long do we expire an address that we + * learned from another peer if it is not reconfirmed + * by anyone? */ -static GNUNET_SCHEDULER_TaskIdentifier -server_schedule (struct Plugin *plugin, - struct MHD_Daemon *daemon_handle, - int now); +#define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6) -static void -server_log (void *arg, const char *fmt, va_list ap) -{ - char text[1024]; - - vsnprintf (text, sizeof (text), fmt, ap); - va_end (ap); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Server: %s\n", text); -} /** - * Check if incoming connection is accepted. - * NOTE: Here every connection is accepted - * @param cls plugin as closure - * @param addr address of incoming connection - * @param addr_len address length of incoming connection - * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected - * + * Encapsulation of all of the state of the plugin. */ -static int -server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len) -{ - struct Plugin *plugin = cls; - - if (plugin->cur_connections <= plugin->max_connections) - return MHD_YES; - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Server: Cannot accept new connections\n"); - return MHD_NO; - } -} - - -#if BUILD_HTTPS -static char * -server_load_file (const char *file) -{ - struct GNUNET_DISK_FileHandle *gn_file; - uint64_t fsize; - char *text = NULL; - - if (GNUNET_OK != GNUNET_DISK_file_size (file, - &fsize, GNUNET_NO, GNUNET_YES)) - return NULL; - text = GNUNET_malloc (fsize + 1); - gn_file = - GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ, - GNUNET_DISK_PERM_USER_READ); - if (gn_file == NULL) - { - GNUNET_free (text); - return NULL; - } - if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fsize)) - { - GNUNET_free (text); - GNUNET_DISK_file_close (gn_file); - return NULL; - } - text[fsize] = '\0'; - GNUNET_DISK_file_close (gn_file); - return text; -} -#endif - - -#if BUILD_HTTPS - -static int -server_load_certificate (struct Plugin *plugin) -{ - int res = GNUNET_OK; - - char *key_file; - char *cert_file; - - /* Get crypto init string from config - * If not present just use default values */ - - GNUNET_assert (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, - plugin->name, - "CRYPTO_INIT", - &plugin->crypto_init)); - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, - "KEY_FILE", &key_file)) - { - key_file = GNUNET_strdup ("https_key.key"); - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, - "CERT_FILE", &cert_file)) - { - GNUNET_asprintf (&cert_file, "%s", "https_cert.crt"); - } - - /* read key & certificates from file */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Loading TLS certificate from key-file `%s' cert-file`%s'\n", - key_file, cert_file); - - plugin->key = server_load_file (key_file); - plugin->cert = server_load_file (cert_file); - - if ((plugin->key == NULL) || (plugin->cert == NULL)) - { - struct GNUNET_OS_Process *cert_creation; - - GNUNET_free_non_null (plugin->key); - plugin->key = NULL; - GNUNET_free_non_null (plugin->cert); - plugin->cert = NULL; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "No usable TLS certificate found, creating certificate\n"); - errno = 0; - cert_creation = - GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL, - "gnunet-transport-certificate-creation", - "gnunet-transport-certificate-creation", - key_file, cert_file, NULL); - if (cert_creation == NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - _ - ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n")); - GNUNET_free (key_file); - GNUNET_free (cert_file); - - GNUNET_free_non_null (plugin->key); - plugin->key = NULL; - GNUNET_free_non_null (plugin->cert); - plugin->cert = NULL; - GNUNET_free_non_null (plugin->crypto_init); - plugin->crypto_init = NULL; - - return GNUNET_SYSERR; - } - GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation)); - GNUNET_OS_process_destroy (cert_creation); - - plugin->key = server_load_file (key_file); - plugin->cert = server_load_file (cert_file); - } - - if ((plugin->key == NULL) || (plugin->cert == NULL)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - _ - ("No usable TLS certificate found and creating one failed!\n"), - "transport-https"); - GNUNET_free (key_file); - GNUNET_free (cert_file); - - GNUNET_free_non_null (plugin->key); - plugin->key = NULL; - GNUNET_free_non_null (plugin->cert); - plugin->cert = NULL; - GNUNET_free_non_null (plugin->crypto_init); - plugin->crypto_init = NULL; - - return GNUNET_SYSERR; - } - GNUNET_free (key_file); - GNUNET_free (cert_file); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n"); - return res; -} -#endif +struct Plugin; /** - * Reschedule the execution of both IPv4 and IPv6 server - * @param plugin the plugin - * @param server which server to schedule v4 or v6? - * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait - * until timeout + * Session handle for connections. */ -static void -server_reschedule (struct Plugin *plugin, struct MHD_Daemon *server, int now) +struct Session { - if ((server == plugin->server_v4) && (plugin->server_v4 != NULL)) - { - if (GNUNET_YES == plugin->server_v4_immediately) - return; /* No rescheduling, server will run asap */ - - if (GNUNET_YES == now) - plugin->server_v4_immediately = GNUNET_YES; - - if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v4_task); - plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; - } - plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now); - } - - if ((server == plugin->server_v6) && (plugin->server_v6 != NULL)) - { - if (GNUNET_YES == plugin->server_v6_immediately) - return; /* No rescheduling, server will run asap */ - - if (GNUNET_YES == now) - plugin->server_v6_immediately = GNUNET_YES; - - if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v6_task); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; - } - plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now); - } -} + /** + * To whom are we talking to (set to our identity + * if we are still waiting for the welcome message) + */ + struct GNUNET_PeerIdentity sender; + + /** + * Stored in a linked list. + */ + struct Session *next; + + /** + * Pointer to the global plugin struct. + */ + struct Plugin *plugin; + + /** + * The client (used to identify this connection) + */ + /* void *client; */ + + /** + * 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; + + /** + * At what time did we reset last_received last? + */ + struct GNUNET_TIME_Absolute last_quota_update; + + /** + * How many bytes have we received since the "last_quota_update" + * timestamp? + */ + uint64_t last_received; + + /** + * Number of bytes per ms that this peer is allowed + * to send to us. + */ + uint32_t quota; + +}; /** - * Callback called by MessageStreamTokenizer when a message has arrived - * @param cls current session as closure - * @param client clien - * @param message the message to be forwarded to transport service + * Encapsulation of all of the state of the plugin. */ -static int -server_receive_mst_cb (void *cls, void *client, - const struct GNUNET_MessageHeader *message) +struct Plugin { - struct Session *s = cls; - - GNUNET_assert (NULL != p); - if (GNUNET_NO == exist_session(p, s)) - return GNUNET_OK; + /** + * Our environment. + */ + struct GNUNET_TRANSPORT_PluginEnvironment *env; - struct Plugin *plugin = s->plugin; - struct GNUNET_TIME_Relative delay; + /** + * List of open sessions. + */ + struct Session *sessions; - delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen); - - s->next_receive = - GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay); - - if (delay.rel_value > 0) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: peer `%s' address `%s' next read delayed for %llu ms\n", - GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, s->addrlen), - delay); - } - return GNUNET_OK; -} +}; /** - * Callback called by MHD when it needs data to send - * @param cls current session - * @param pos position in buffer - * @param buf the buffer to write data to - * @param max max number of bytes available in buffer - * @return bytes written to buffer + * Function that can be used by the transport service to transmit + * a message using the plugin. Note that in the case of a + * peer disconnecting, the continuation MUST be called + * prior to the disconnect notification itself. This function + * will be called with this peer's HELLO message to initiate + * a fresh connection to another peer. + * + * @param cls closure + * @param session which session must be used + * @param msgbuf the message to transmit + * @param msgbuf_size number of bytes in 'msgbuf' + * @param priority how important is the message (most plugins will + * ignore message priority and just FIFO) + * @param to how long to wait at most for the transmission (does not + * require plugins to discard the message after the timeout, + * just advisory for the desired delay; most plugins will ignore + * this as well) + * @param cont continuation to call once the message has + * been transmitted (or if the transport is ready + * for the next transmission call; or if the + * peer disconnected...); can be NULL + * @param cont_cls closure for cont + * @return number of bytes used (on the physical network, with overheads); + * -1 on hard errors (i.e. address invalid); 0 is a legal value + * and does NOT mean that the message was not transmitted (DV) */ static ssize_t -server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) +http_server_plugin_send (void *cls, + struct Session *session, + const char *msgbuf, size_t msgbuf_size, + unsigned int priority, + struct GNUNET_TIME_Relative to, + GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls) { - struct Session *s = cls; - ssize_t bytes_read = 0; - struct HTTP_Message *msg; - - GNUNET_assert (NULL != p); - if (GNUNET_NO == exist_session(p, s)) - return 0; - msg = s->msg_head; - if (NULL != msg) - { - /* sending */ - bytes_read = GNUNET_MIN (msg->size - msg->pos, - max); - memcpy (buf, &msg->buf[msg->pos], bytes_read); - msg->pos += bytes_read; - - /* removing message */ - if (msg->pos == msg->size) - { - GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); - if (NULL != msg->transmit_cont) - msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK); - GNUNET_free (msg); - } - } - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Server: %p: sent %u bytes\n", s, bytes_read); - return bytes_read; -} - + struct Plugin *plugin = cls; + int bytes_sent = 0; -static struct Session * -server_lookup_session (struct Plugin *plugin, - struct ServerConnection * sc) -{ - struct Session *s; + GNUNET_assert (plugin != NULL); + GNUNET_assert (session != NULL); - for (s = plugin->head; NULL != s; s = s->next) - if ((s->server_recv == sc) || (s->server_send == sc)) - return s; - for (s = plugin->server_semi_head; NULL != s; s = s->next) - if ((s->server_recv == sc) || (s->server_send == sc)) - return s; - return NULL; + /* struct Plugin *plugin = cls; */ + return bytes_sent; } -static struct ServerConnection * -server_lookup_serverconnection (struct Plugin *plugin, - struct MHD_Connection *mhd_connection, const char *url, - const char *method) -{ - struct Session *s = NULL; - struct Session *t; - struct ServerConnection *sc = NULL; - const union MHD_ConnectionInfo *conn_info; - struct GNUNET_ATS_Information ats; - struct IPv4HttpAddress a4; - struct IPv6HttpAddress a6; - struct sockaddr_in *s4; - struct sockaddr_in6 *s6; - void *a; - size_t a_len; - struct GNUNET_PeerIdentity target; - uint32_t tag = 0; - int direction = GNUNET_SYSERR; - - /* url parsing variables */ - size_t url_len; - char *url_end; - char *hash_start; - char *hash_end; - char *tag_start; - char *tag_end; - - conn_info = MHD_get_connection_info (mhd_connection, - MHD_CONNECTION_INFO_CLIENT_ADDRESS); - if ((conn_info->client_addr->sa_family != AF_INET) && - (conn_info->client_addr->sa_family != AF_INET6)) - return NULL; - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "New %s connection from %s\n", - method, url); - /* URL parsing - * URL is valid if it is in the form [peerid[103];tag]*/ - url_len = strlen (url); - url_end = (char *) &url[url_len]; - - if (url_len < 105) - { - goto error; /* too short */ - } - hash_start = strrchr (url, '/'); - if (NULL == hash_start) - { - goto error; /* '/' delimiter not found */ - } - if (hash_start >= url_end) - { - goto error; /* mal formed */ - } - hash_start++; - - hash_end = strrchr (hash_start, ';'); - if (NULL == hash_end) - goto error; /* ';' delimiter not found */ - if (hash_end >= url_end) - { - goto error; /* mal formed */ - } - - if (hash_start >= hash_end) - { - goto error; /* mal formed */ - } - - if ((strlen(hash_start) - strlen(hash_end)) != 103) - { - goto error; /* invalid hash length */ - } - - char hash[104]; - memcpy (hash, hash_start, 103); - hash[103] = '\0'; - if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target.hashPubKey))) - { - goto error; /* mal formed */ - } - - if (hash_end >= url_end) - { - goto error; /* mal formed */ - } - - tag_start = &hash_end[1]; - /* Converting tag */ - tag_end = NULL; - tag = strtoul (tag_start, &tag_end, 10); - if (tag == 0) - { - goto error; /* mal formed */ - } - if (tag_end == NULL) - { - goto error; /* mal formed */ - } - if (tag_end != url_end) - { - goto error; /* mal formed */ - } - - if (0 == strcmp (MHD_HTTP_METHOD_PUT, method)) - direction = _RECEIVE; - else if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) - direction = _SEND; - else - { - goto error; - } - - plugin->cur_connections++; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: New %s connection from %s with tag %u\n", - method, - GNUNET_i2s (&target), tag); - - /* find duplicate session */ - t = plugin->head; - while (t != NULL) - { - if ((t->inbound) && - (0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) - && - /* FIXME add source address comparison */ - (t->tag == tag)) - break; - t = t->next; - } - if (t != NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Duplicate session, dismissing new connection from peer `%s'\n", - GNUNET_i2s (&target)); - goto error; - } - - /* find semi-session */ - t = plugin->server_semi_head; - - while (t != NULL) - { - /* FIXME add source address comparison */ - if ((0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) - && (t->tag == tag)) - { - break; - } - t = t->next; - } - - if (t == NULL) - goto create; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Found existing semi-session for `%s'\n", - GNUNET_i2s (&target)); - - if ((direction == _SEND) && (t->server_send != NULL)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Duplicate GET session, dismissing new connection from peer `%s'\n", - GNUNET_i2s (&target)); - goto error; - } - else - { - s = t; - GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, - plugin->server_semi_tail, s); - GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Found matching semi-session, merging session for peer `%s'\n", - GNUNET_i2s (&target)); - - plugin->inbound_sessions ++; - GNUNET_STATISTICS_set (plugin->env->stats, - "# HTTP inbound sessions", - plugin->inbound_sessions, - GNUNET_NO); - GNUNET_assert (NULL != s); - goto found; - } - if ((direction == _RECEIVE) && (t->server_recv != NULL)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Duplicate PUT session, dismissing new connection from peer `%s'\n", - GNUNET_i2s (&target)); - goto error; - } - else - { - s = t; - GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, - plugin->server_semi_tail, s); - GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Found matching semi-session, merging session for peer `%s'\n", - GNUNET_i2s (&target)); - plugin->inbound_sessions ++; - GNUNET_STATISTICS_set (plugin->env->stats, - "# HTTP inbound sessions", - plugin->inbound_sessions, - GNUNET_NO); - GNUNET_assert (NULL != s); - goto found; - } - -create: -/* create new session */ - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Creating new session for peer `%s' \n", - GNUNET_i2s (&target)); - switch (conn_info->client_addr->sa_family) - { - case (AF_INET): - s4 = ((struct sockaddr_in *) conn_info->client_addr); - a4.u4_port = s4->sin_port; - memcpy (&a4.ipv4_addr, &s4->sin_addr, sizeof (struct in_addr)); - a = &a4; - a_len = sizeof (struct IPv4HttpAddress); - ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) s4, sizeof (struct sockaddr_in)); - break; - case (AF_INET6): - s6 = ((struct sockaddr_in6 *) conn_info->client_addr); - a6.u6_port = s6->sin6_port; - memcpy (&a6.ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr)); - a = &a6; - a_len = sizeof (struct IPv6HttpAddress); - ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) s6, sizeof (struct sockaddr_in6)); - break; - default: - GNUNET_break (0); - goto error; - } - s = create_session (plugin, &target, a, a_len); - GNUNET_assert (NULL != s); - s->ats_address_network_type = ats.value; - s->inbound = GNUNET_YES; - s->next_receive = GNUNET_TIME_UNIT_ZERO_ABS; - s->tag = tag; - s->server_recv = NULL; - s->server_send = NULL; - - GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head, - plugin->server_semi_tail, s); - goto found; - -error: - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Invalid connection request\n"); - return NULL; - -found: - sc = GNUNET_malloc (sizeof (struct ServerConnection)); - sc->mhd_conn = mhd_connection; - sc->direction = direction; - sc->session = s; - if (direction == _SEND) - s->server_send = sc; - if (direction == _RECEIVE) - s->server_recv = sc; - -#if MHD_VERSION >= 0x00090E00 - int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Setting timeout for %p to %u sec.\n", sc, to); - MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to); - - struct MHD_Daemon *d = NULL; - - if (s->addrlen == sizeof (struct IPv6HttpAddress)) - d = plugin->server_v6; - if (s->addrlen == sizeof (struct IPv4HttpAddress)) - d = plugin->server_v4; - - server_reschedule (plugin, d, GNUNET_NO); -#endif - return sc; -} /** - * Process GET or PUT request received via MHD. For - * GET, queue response that will send back our pending - * messages. For PUT, process incoming data and send - * to GNUnet core. In either case, check if a session - * already exists and create a new one if not. + * 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 int -server_access_cb (void *cls, struct MHD_Connection *mhd_connection, - const char *url, const char *method, const char *version, - const char *upload_data, size_t * upload_data_size, - void **httpSessionCache) +static void +http_server_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target) { - struct Plugin *plugin = cls; - struct ServerConnection *sc = *httpSessionCache; - struct Session *s; - struct MHD_Response *response; - int res = MHD_YES; - - GNUNET_assert (cls != NULL); - if (sc == NULL) - { - /* new connection */ - sc = server_lookup_serverconnection (plugin, mhd_connection, url, method); - if (sc != NULL) - (*httpSessionCache) = sc; - else - { - response = - MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE), - HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO); - res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response); - MHD_destroy_response (response); - return res; - } - } - else - { - /* 'old' connection */ - if (NULL == server_lookup_session (plugin, sc)) - { - /* Session was already disconnected */ - return MHD_NO; - } - } - - /* existing connection */ - sc = (*httpSessionCache); - s = sc->session; - - GNUNET_assert (NULL != s); - - /* connection is to be disconnected */ - if (sc->disconnect == GNUNET_YES) - { - /* Sent HTTP/1.1: 200 OK as PUT Response\ */ - response = - MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!", - MHD_NO, MHD_NO); - res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - return MHD_YES; - } - - GNUNET_assert (s != NULL); - /* Check if both directions are connected */ - if ((sc->session->server_recv == NULL) || (sc->session->server_send == NULL)) - { - /* Delayed read from since not both semi-connections are connected */ - return MHD_YES; - } - - if (sc->direction == _SEND) - { - response = - MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, - 32 * 1024, - &server_send_callback, s, - NULL); - MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); - MHD_destroy_response (response); - return MHD_YES; - } - if (sc->direction == _RECEIVE) - { - if (*upload_data_size == 0) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Peer `%s' PUT on address `%s' connected\n", - GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, - s->addrlen)); - return MHD_YES; - } - - /* Receiving data */ - if ((*upload_data_size > 0)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: peer `%s' PUT on address `%s' received %u bytes\n", - GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, - s->addrlen), - *upload_data_size); - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - - if ((s->next_receive.abs_value <= now.abs_value)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: %p: PUT with %u bytes forwarded to MST\n", s, - *upload_data_size); - if (s->msg_tk == NULL) - { - s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s); - } - GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data, - *upload_data_size, GNUNET_NO, GNUNET_NO); - -#if MHD_VERSION >= 0x00090E00 - int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); - struct ServerConnection *t = NULL; - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Received %u bytes\n", *upload_data_size); - /* Setting timeouts for other connections */ - if (s->server_recv != NULL) - { - t = s->server_recv; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Setting timeout for %p to %u sec.\n", t, - to); - MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - to); - } - if (s->server_send != NULL) - { - t = s->server_send; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: Setting timeout for %p to %u sec.\n", t, - to); - MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - to); - } - struct MHD_Daemon *d = NULL; - - if (s->addrlen == sizeof (struct IPv6HttpAddress)) - d = plugin->server_v6; - if (s->addrlen == sizeof (struct IPv4HttpAddress)) - d = plugin->server_v4; - server_reschedule (plugin, d, GNUNET_NO); -#endif - (*upload_data_size) = 0; - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Server: %p no inbound bandwidth available! Next read was delayed by %llu ms\n", - s, now.abs_value - s->next_receive.abs_value); - } - return MHD_YES; - } - else - return MHD_NO; - } - return res; + // struct Plugin *plugin = cls; + // FIXME } + +/** + * 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 -server_disconnect_cb (void *cls, struct MHD_Connection *connection, - void **httpSessionCache) +http_server_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) { - struct ServerConnection *sc = *httpSessionCache; - struct ServerConnection *tc = NULL; - struct Session *s = NULL; - struct Session *t = NULL; - struct Plugin *plugin = NULL; - - if (sc == NULL) - return; - - if (NULL == (s = server_lookup_session (p, sc))) - return; - - GNUNET_assert (NULL != p); - if (GNUNET_NO == exist_session(p, s)) - return; - - plugin = s->plugin; - if (sc->direction == _SEND) - { - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: %p peer `%s' GET on address `%s' disconnected\n", - s->server_send, GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, s->addrlen)); - s->server_send = NULL; - if (NULL != (tc = s->server_recv)) - { - tc->disconnect = GNUNET_YES; - GNUNET_assert (NULL != tc->mhd_conn); -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (tc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - } - } - if (sc->direction == _RECEIVE) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: %p peer `%s' PUT on address `%s' disconnected\n", - s->server_recv, GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, s->addrlen)); - s->server_recv = NULL; - if (NULL != (tc = s->server_send)) - { - tc->disconnect = GNUNET_YES; - GNUNET_assert (NULL != tc->mhd_conn); -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (tc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - } - if (s->msg_tk != NULL) - { - GNUNET_SERVER_mst_destroy (s->msg_tk); - s->msg_tk = NULL; - } - } - - GNUNET_free (sc); - - t = plugin->server_semi_head; - while (t != NULL) - { - if (t == s) - { - GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, - plugin->server_semi_tail, s); - GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); - break; - } - t = t->next; - } - plugin->cur_connections--; - - struct MHD_Daemon *d = NULL; - - if (s->addrlen == sizeof (struct IPv6HttpAddress)) - d = plugin->server_v6; - if (s->addrlen == sizeof (struct IPv4HttpAddress)) - d = plugin->server_v4; - server_reschedule (plugin, d, GNUNET_NO); - - if ((s->server_send == NULL) && (s->server_recv == NULL)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Server: peer `%s' on address `%s' disconnected\n", - GNUNET_i2s (&s->target), - http_plugin_address_to_string (NULL, s->addr, s->addrlen)); - if (s->msg_tk != NULL) - { - GNUNET_SERVER_mst_destroy (s->msg_tk); - s->msg_tk = NULL; - } - - GNUNET_assert (plugin->inbound_sessions > 0); - plugin->inbound_sessions --; - GNUNET_STATISTICS_set (plugin->env->stats, - "# HTTP inbound sessions", - plugin->inbound_sessions, GNUNET_NO); - - notify_session_end (s->plugin, &s->target, s); - } + asc (asc_cls, NULL); } -int -server_disconnect (struct Session *s) -{ - struct ServerConnection * send; - struct ServerConnection * recv; - send = (struct ServerConnection *) s->server_send; - if (s->server_send != NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Server: %p / %p Terminating inbound PUT session to peer `%s'\n", - s, s->server_send, GNUNET_i2s (&s->target)); - send->disconnect = GNUNET_YES; -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - } - - recv = (struct ServerConnection *) s->server_recv; - if (recv != NULL) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, - "Server: %p / %p Terminating inbound GET session to peer `%s'\n", - s, s->server_recv, GNUNET_i2s (&s->target)); - - recv->disconnect = GNUNET_YES; -#if MHD_VERSION >= 0x00090E00 - MHD_set_connection_option (recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, - 1); -#endif - } - - /* Schedule connection immediately */ - if (s->addrlen == sizeof (struct IPv4HttpAddress)) - { - server_reschedule (s->plugin, s->plugin->server_v4, GNUNET_YES); - } - else if (s->addrlen == sizeof (struct IPv6HttpAddress)) - { - server_reschedule (s->plugin, s->plugin->server_v6, GNUNET_YES); - } - return GNUNET_OK; -} - -int -server_send (struct Session *s, struct HTTP_Message *msg) +/** + * 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_server_plugin_address_suggested (void *cls, const void *addr, size_t addrlen) { - GNUNET_CONTAINER_DLL_insert_tail (s->msg_head, s->msg_tail, msg); + /* struct Plugin *plugin = cls; */ - if (s->addrlen == sizeof (struct IPv4HttpAddress)) - { - server_reschedule (s->plugin, s->plugin->server_v4, GNUNET_YES); - } - else if (s->addrlen == sizeof (struct IPv6HttpAddress)) - { - server_reschedule (s->plugin, s->plugin->server_v6, GNUNET_YES); - } - else - return GNUNET_SYSERR; + /* check if the address is plausible; if so, + * add it to our list! */ return GNUNET_OK; } - /** - * Call MHD IPv4 to process pending requests and then go back - * and schedule the next run. - * @param cls plugin as closure - * @param tc task context + * 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 void -server_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +static const char * +http_server_plugin_address_to_string (void *cls, const void *addr, size_t addrlen) { - struct Plugin *plugin = cls; - - GNUNET_assert (cls != NULL); - - plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; -#if 0 - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Running IPv4 server\n"); -#endif - plugin->server_v4_immediately = GNUNET_NO; - GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4)); - server_reschedule (plugin, plugin->server_v4, GNUNET_NO); + GNUNET_break (0); + return NULL; } -/** - * Call MHD IPv6 to process pending requests and then go back - * and schedule the next run. - * @param cls plugin as closure - * @param tc task context - */ -static void -server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct Plugin *plugin = cls; - GNUNET_assert (cls != NULL); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - return; -#if 0 - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Running IPv6 server\n"); -#endif - plugin->server_v6_immediately = GNUNET_NO; - GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6)); - server_reschedule (plugin, plugin->server_v6, GNUNET_NO); -} /** - * Function that queries MHD's select sets and - * starts the task waiting for them. - * @param plugin plugin - * @param daemon_handle the MHD daemon handle - * @return gnunet task identifier + * Entry point for the plugin. */ -static GNUNET_SCHEDULER_TaskIdentifier -server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle, - int now) +void * +LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *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 MHD_LONG_LONG timeout; - static unsigned long long last_timeout = 0; - int haveto; - - struct GNUNET_TIME_Relative tv; - - ret = GNUNET_SCHEDULER_NO_TASK; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - wrs = GNUNET_NETWORK_fdset_create (); - wes = GNUNET_NETWORK_fdset_create (); - wws = GNUNET_NETWORK_fdset_create (); - max = -1; - GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max)); - haveto = MHD_get_timeout (daemon_handle, &timeout); - if (haveto == MHD_YES) - { - if (timeout != last_timeout) - { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "SELECT Timeout changed from %llu to %llu\n", - last_timeout, timeout); -#endif - last_timeout = timeout; - } - tv.rel_value = (uint64_t) timeout; - } - else - tv = GNUNET_TIME_UNIT_SECONDS; - /* Force immediate run, since we have outbound data to send */ - if (now == GNUNET_YES) - tv = GNUNET_TIME_UNIT_MILLISECONDS; - GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1); - GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1); - GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1); - - if (daemon_handle == plugin->server_v4) - { - if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v4_task); - plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; - } -#if 0 - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Scheduling IPv4 server task in %llu ms\n", tv); -#endif - ret = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - tv, wrs, wws, - &server_v4_run, plugin); - } - if (daemon_handle == plugin->server_v6) - { - if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v6_task); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; - } -#if 0 - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Scheduling IPv6 server task in %llu ms\n", tv); -#endif - ret = - GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, - tv, wrs, wws, - &server_v6_run, plugin); - } - GNUNET_NETWORK_fdset_destroy (wrs); - GNUNET_NETWORK_fdset_destroy (wws); - GNUNET_NETWORK_fdset_destroy (wes); - return ret; + struct GNUNET_TRANSPORT_PluginEnvironment *env = cls; + struct GNUNET_TRANSPORT_PluginFunctions *api; + struct Plugin *plugin; + + plugin = GNUNET_malloc (sizeof (struct Plugin)); + plugin->env = env; + api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions)); + api->cls = plugin; + api->send = &http_server_plugin_send; + api->disconnect = &http_server_plugin_disconnect; + api->address_pretty_printer = &http_server_plugin_address_pretty_printer; + api->check_address = &http_server_plugin_address_suggested; + api->address_to_string = &http_server_plugin_address_to_string; + return api; } -int -server_start (struct Plugin *plugin) -{ - int res = GNUNET_OK; - unsigned int timeout; - p = plugin; - GNUNET_assert (NULL != plugin); - -#if BUILD_HTTPS - res = server_load_certificate (plugin); - if (res == GNUNET_SYSERR) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Could not load or create server certificate! Loading plugin failed!\n"); - return res; - } -#endif - -#if MHD_VERSION >= 0x00090E00 - timeout = HTTP_NOT_VALIDATED_TIMEOUT.rel_value / 1000; - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "MHD can set timeout per connection! Default time out %u sec.\n", - timeout); -#else - timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000; - GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name, - "MHD cannot set timeout per connection! Default time out %u sec.\n", - timeout); -#endif - plugin->server_v4 = NULL; - if (plugin->ipv4 == GNUNET_YES) - { - plugin->server_v4 = MHD_start_daemon ( -#if VERBOSE_SERVER - MHD_USE_DEBUG | -#endif -#if BUILD_HTTPS - MHD_USE_SSL | -#endif - MHD_NO_FLAG, plugin->port, - &server_accept_cb, plugin, - &server_access_cb, plugin, - MHD_OPTION_SOCK_ADDR, - (struct sockaddr_in *) - plugin->server_addr_v4, - MHD_OPTION_CONNECTION_LIMIT, - (unsigned int) - plugin->max_connections, -#if BUILD_HTTPS - MHD_OPTION_HTTPS_PRIORITIES, - plugin->crypto_init, - MHD_OPTION_HTTPS_MEM_KEY, - plugin->key, - MHD_OPTION_HTTPS_MEM_CERT, - plugin->cert, -#endif - MHD_OPTION_CONNECTION_TIMEOUT, - timeout, - MHD_OPTION_CONNECTION_MEMORY_LIMIT, - (size_t) (2 * - GNUNET_SERVER_MAX_MESSAGE_SIZE), - MHD_OPTION_NOTIFY_COMPLETED, - &server_disconnect_cb, plugin, - MHD_OPTION_EXTERNAL_LOGGER, - server_log, NULL, MHD_OPTION_END); - } - plugin->server_v6 = NULL; - if (plugin->ipv6 == GNUNET_YES) - { - plugin->server_v6 = MHD_start_daemon ( -#if VERBOSE_SERVER - MHD_USE_DEBUG | -#endif -#if BUILD_HTTPS - MHD_USE_SSL | -#endif - MHD_USE_IPv6, plugin->port, - &server_accept_cb, plugin, - &server_access_cb, plugin, - MHD_OPTION_SOCK_ADDR, - (struct sockaddr_in6 *) - plugin->server_addr_v6, - MHD_OPTION_CONNECTION_LIMIT, - (unsigned int) - plugin->max_connections, -#if BUILD_HTTPS - MHD_OPTION_HTTPS_PRIORITIES, - plugin->crypto_init, - MHD_OPTION_HTTPS_MEM_KEY, - plugin->key, - MHD_OPTION_HTTPS_MEM_CERT, - plugin->cert, -#endif - MHD_OPTION_CONNECTION_TIMEOUT, - timeout, - MHD_OPTION_CONNECTION_MEMORY_LIMIT, - (size_t) (2 * - GNUNET_SERVER_MAX_MESSAGE_SIZE), - MHD_OPTION_NOTIFY_COMPLETED, - &server_disconnect_cb, plugin, - MHD_OPTION_EXTERNAL_LOGGER, - server_log, NULL, MHD_OPTION_END); - - } - - if ((plugin->ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Failed to start %s IPv4 server component on port %u\n", - plugin->name, plugin->port); - return GNUNET_SYSERR; - } - server_reschedule (plugin, plugin->server_v4, GNUNET_NO); - - if ((plugin->ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL)) - { - GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, - "Failed to start %s IPv6 server component on port %u\n", - plugin->name, plugin->port); - return GNUNET_SYSERR; - } - server_reschedule (plugin, plugin->server_v6, GNUNET_NO); - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "%s server component started on port %u\n", plugin->name, - plugin->port); - return res; -} - -void -server_stop (struct Plugin *plugin) +/** + * Exit point from the plugin. + */ +void * +LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls) { - struct Session *s = NULL; - struct Session *t = NULL; - - struct MHD_Daemon *server_v4_tmp = plugin->server_v4; - plugin->server_v4 = NULL; - - struct MHD_Daemon *server_v6_tmp = plugin->server_v6; - plugin->server_v6 = NULL; - - if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v4_task); - plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; - } - - if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) - { - GNUNET_SCHEDULER_cancel (plugin->server_v6_task); - plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; - } - - if (server_v6_tmp != NULL) - { - MHD_stop_daemon (server_v4_tmp); - } - if (server_v6_tmp != NULL) - { - MHD_stop_daemon (server_v6_tmp); - } + struct GNUNET_TRANSPORT_PluginFunctions *api = cls; + struct Plugin *plugin = api->cls; - /* cleaning up semi-sessions never propagated */ - s = plugin->server_semi_head; - while (s != NULL) - { -#if VERBOSE_SERVER - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "Deleting semi-sessions %p\n", s); -#endif - t = s->next; - struct HTTP_Message *msg = s->msg_head; - struct HTTP_Message *tmp = NULL; - - while (msg != NULL) - { - tmp = msg->next; - - GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); - if (msg->transmit_cont != NULL) - { - msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR); - } - GNUNET_free (msg); - msg = tmp; - } - - delete_session (s); - s = t; - } - - p = NULL; - -#if BUILD_HTTPS - GNUNET_free_non_null (plugin->crypto_init); - GNUNET_free_non_null (plugin->cert); - GNUNET_free_non_null (plugin->key); -#endif - - GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, - "%s server component stopped\n", plugin->name); + GNUNET_free (plugin); + GNUNET_free (api); + return NULL; } - - -/* end of plugin_transport_http.c */ +/* end of plugin_transport_http_server.c */ diff --git a/src/transport/plugin_transport_http_server_old.c b/src/transport/plugin_transport_http_server_old.c new file mode 100644 index 000000000..f81f5cf6e --- /dev/null +++ b/src/transport/plugin_transport_http_server_old.c @@ -0,0 +1,1336 @@ +/* + 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_http.c + * @brief http transport service plugin + * @author Matthias Wachs + */ + +#include "plugin_transport_http.h" + +#define HTTP_ERROR_RESPONSE "404 Not Found

Not Found

The requested URL was not found on this server.


" +#define _RECEIVE 0 +#define _SEND 1 + +static struct Plugin * p; + +/** + * Function that queries MHD's select sets and + * starts the task waiting for them. + * @param plugin plugin + * @param daemon_handle the MHD daemon handle + * @param now schedule now or with MHD delay + * @return gnunet task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier +server_schedule (struct Plugin *plugin, + struct MHD_Daemon *daemon_handle, + int now); + +static void +server_log (void *arg, const char *fmt, va_list ap) +{ + char text[1024]; + + vsnprintf (text, sizeof (text), fmt, ap); + va_end (ap); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Server: %s\n", text); +} + +/** + * Check if incoming connection is accepted. + * NOTE: Here every connection is accepted + * @param cls plugin as closure + * @param addr address of incoming connection + * @param addr_len address length of incoming connection + * @return MHD_YES if connection is accepted, MHD_NO if connection is rejected + * + */ +static int +server_accept_cb (void *cls, const struct sockaddr *addr, socklen_t addr_len) +{ + struct Plugin *plugin = cls; + + if (plugin->cur_connections <= plugin->max_connections) + return MHD_YES; + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Server: Cannot accept new connections\n"); + return MHD_NO; + } +} + + +#if BUILD_HTTPS +static char * +server_load_file (const char *file) +{ + struct GNUNET_DISK_FileHandle *gn_file; + uint64_t fsize; + char *text = NULL; + + if (GNUNET_OK != GNUNET_DISK_file_size (file, + &fsize, GNUNET_NO, GNUNET_YES)) + return NULL; + text = GNUNET_malloc (fsize + 1); + gn_file = + GNUNET_DISK_file_open (file, GNUNET_DISK_OPEN_READ, + GNUNET_DISK_PERM_USER_READ); + if (gn_file == NULL) + { + GNUNET_free (text); + return NULL; + } + if (GNUNET_SYSERR == GNUNET_DISK_file_read (gn_file, text, fsize)) + { + GNUNET_free (text); + GNUNET_DISK_file_close (gn_file); + return NULL; + } + text[fsize] = '\0'; + GNUNET_DISK_file_close (gn_file); + return text; +} +#endif + + +#if BUILD_HTTPS + +static int +server_load_certificate (struct Plugin *plugin) +{ + int res = GNUNET_OK; + + char *key_file; + char *cert_file; + + /* Get crypto init string from config + * If not present just use default values */ + + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg, + plugin->name, + "CRYPTO_INIT", + &plugin->crypto_init)); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, + "KEY_FILE", &key_file)) + { + key_file = GNUNET_strdup ("https_key.key"); + } + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (plugin->env->cfg, plugin->name, + "CERT_FILE", &cert_file)) + { + GNUNET_asprintf (&cert_file, "%s", "https_cert.crt"); + } + + /* read key & certificates from file */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Loading TLS certificate from key-file `%s' cert-file`%s'\n", + key_file, cert_file); + + plugin->key = server_load_file (key_file); + plugin->cert = server_load_file (cert_file); + + if ((plugin->key == NULL) || (plugin->cert == NULL)) + { + struct GNUNET_OS_Process *cert_creation; + + GNUNET_free_non_null (plugin->key); + plugin->key = NULL; + GNUNET_free_non_null (plugin->cert); + plugin->cert = NULL; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No usable TLS certificate found, creating certificate\n"); + errno = 0; + cert_creation = + GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_OUT_AND_ERR, NULL, NULL, + "gnunet-transport-certificate-creation", + "gnunet-transport-certificate-creation", + key_file, cert_file, NULL); + if (cert_creation == NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + _ + ("Could not create a new TLS certificate, program `gnunet-transport-certificate-creation' could not be started!\n")); + GNUNET_free (key_file); + GNUNET_free (cert_file); + + GNUNET_free_non_null (plugin->key); + plugin->key = NULL; + GNUNET_free_non_null (plugin->cert); + plugin->cert = NULL; + GNUNET_free_non_null (plugin->crypto_init); + plugin->crypto_init = NULL; + + return GNUNET_SYSERR; + } + GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (cert_creation)); + GNUNET_OS_process_destroy (cert_creation); + + plugin->key = server_load_file (key_file); + plugin->cert = server_load_file (cert_file); + } + + if ((plugin->key == NULL) || (plugin->cert == NULL)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + _ + ("No usable TLS certificate found and creating one failed!\n"), + "transport-https"); + GNUNET_free (key_file); + GNUNET_free (cert_file); + + GNUNET_free_non_null (plugin->key); + plugin->key = NULL; + GNUNET_free_non_null (plugin->cert); + plugin->cert = NULL; + GNUNET_free_non_null (plugin->crypto_init); + plugin->crypto_init = NULL; + + return GNUNET_SYSERR; + } + GNUNET_free (key_file); + GNUNET_free (cert_file); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "TLS certificate loaded\n"); + return res; +} +#endif + + +/** + * Reschedule the execution of both IPv4 and IPv6 server + * @param plugin the plugin + * @param server which server to schedule v4 or v6? + * @param now GNUNET_YES to schedule execution immediately, GNUNET_NO to wait + * until timeout + */ +static void +server_reschedule (struct Plugin *plugin, struct MHD_Daemon *server, int now) +{ + if ((server == plugin->server_v4) && (plugin->server_v4 != NULL)) + { + if (GNUNET_YES == plugin->server_v4_immediately) + return; /* No rescheduling, server will run asap */ + + if (GNUNET_YES == now) + plugin->server_v4_immediately = GNUNET_YES; + + if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->server_v4_task); + plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; + } + plugin->server_v4_task = server_schedule (plugin, plugin->server_v4, now); + } + + if ((server == plugin->server_v6) && (plugin->server_v6 != NULL)) + { + if (GNUNET_YES == plugin->server_v6_immediately) + return; /* No rescheduling, server will run asap */ + + if (GNUNET_YES == now) + plugin->server_v6_immediately = GNUNET_YES; + + if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->server_v6_task); + plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; + } + plugin->server_v6_task = server_schedule (plugin, plugin->server_v6, now); + } +} + +/** + * Callback called by MessageStreamTokenizer when a message has arrived + * @param cls current session as closure + * @param client clien + * @param message the message to be forwarded to transport service + */ +static int +server_receive_mst_cb (void *cls, void *client, + const struct GNUNET_MessageHeader *message) +{ + struct Session *s = cls; + + GNUNET_assert (NULL != p); + if (GNUNET_NO == exist_session(p, s)) + return GNUNET_OK; + + struct Plugin *plugin = s->plugin; + struct GNUNET_TIME_Relative delay; + + delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen); + + s->next_receive = + GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay); + + if (delay.rel_value > 0) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: peer `%s' address `%s' next read delayed for %llu ms\n", + GNUNET_i2s (&s->target), + http_plugin_address_to_string (NULL, s->addr, s->addrlen), + delay); + } + return GNUNET_OK; +} + + +/** + * Callback called by MHD when it needs data to send + * @param cls current session + * @param pos position in buffer + * @param buf the buffer to write data to + * @param max max number of bytes available in buffer + * @return bytes written to buffer + */ +static ssize_t +server_send_callback (void *cls, uint64_t pos, char *buf, size_t max) +{ + struct Session *s = cls; + ssize_t bytes_read = 0; + struct HTTP_Message *msg; + + GNUNET_assert (NULL != p); + if (GNUNET_NO == exist_session(p, s)) + return 0; + msg = s->msg_head; + if (NULL != msg) + { + /* sending */ + bytes_read = GNUNET_MIN (msg->size - msg->pos, + max); + memcpy (buf, &msg->buf[msg->pos], bytes_read); + msg->pos += bytes_read; + + /* removing message */ + if (msg->pos == msg->size) + { + GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); + if (NULL != msg->transmit_cont) + msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK); + GNUNET_free (msg); + } + } + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, + "Server: %p: sent %u bytes\n", s, bytes_read); + return bytes_read; +} + + +static struct Session * +server_lookup_session (struct Plugin *plugin, + struct ServerConnection * sc) +{ + struct Session *s; + + for (s = plugin->head; NULL != s; s = s->next) + if ((s->server_recv == sc) || (s->server_send == sc)) + return s; + for (s = plugin->server_semi_head; NULL != s; s = s->next) + if ((s->server_recv == sc) || (s->server_send == sc)) + return s; + return NULL; +} + + +static struct ServerConnection * +server_lookup_serverconnection (struct Plugin *plugin, + struct MHD_Connection *mhd_connection, const char *url, + const char *method) +{ + struct Session *s = NULL; + struct Session *t; + struct ServerConnection *sc = NULL; + const union MHD_ConnectionInfo *conn_info; + struct GNUNET_ATS_Information ats; + struct IPv4HttpAddress a4; + struct IPv6HttpAddress a6; + struct sockaddr_in *s4; + struct sockaddr_in6 *s6; + void *a; + size_t a_len; + struct GNUNET_PeerIdentity target; + uint32_t tag = 0; + int direction = GNUNET_SYSERR; + + /* url parsing variables */ + size_t url_len; + char *url_end; + char *hash_start; + char *hash_end; + char *tag_start; + char *tag_end; + + conn_info = MHD_get_connection_info (mhd_connection, + MHD_CONNECTION_INFO_CLIENT_ADDRESS); + if ((conn_info->client_addr->sa_family != AF_INET) && + (conn_info->client_addr->sa_family != AF_INET6)) + return NULL; + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "New %s connection from %s\n", + method, url); + /* URL parsing + * URL is valid if it is in the form [peerid[103];tag]*/ + url_len = strlen (url); + url_end = (char *) &url[url_len]; + + if (url_len < 105) + { + goto error; /* too short */ + } + hash_start = strrchr (url, '/'); + if (NULL == hash_start) + { + goto error; /* '/' delimiter not found */ + } + if (hash_start >= url_end) + { + goto error; /* mal formed */ + } + hash_start++; + + hash_end = strrchr (hash_start, ';'); + if (NULL == hash_end) + goto error; /* ';' delimiter not found */ + if (hash_end >= url_end) + { + goto error; /* mal formed */ + } + + if (hash_start >= hash_end) + { + goto error; /* mal formed */ + } + + if ((strlen(hash_start) - strlen(hash_end)) != 103) + { + goto error; /* invalid hash length */ + } + + char hash[104]; + memcpy (hash, hash_start, 103); + hash[103] = '\0'; + if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((const char *) hash, &(target.hashPubKey))) + { + goto error; /* mal formed */ + } + + if (hash_end >= url_end) + { + goto error; /* mal formed */ + } + + tag_start = &hash_end[1]; + /* Converting tag */ + tag_end = NULL; + tag = strtoul (tag_start, &tag_end, 10); + if (tag == 0) + { + goto error; /* mal formed */ + } + if (tag_end == NULL) + { + goto error; /* mal formed */ + } + if (tag_end != url_end) + { + goto error; /* mal formed */ + } + + if (0 == strcmp (MHD_HTTP_METHOD_PUT, method)) + direction = _RECEIVE; + else if (0 == strcmp (MHD_HTTP_METHOD_GET, method)) + direction = _SEND; + else + { + goto error; + } + + plugin->cur_connections++; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: New %s connection from %s with tag %u\n", + method, + GNUNET_i2s (&target), tag); + + /* find duplicate session */ + t = plugin->head; + while (t != NULL) + { + if ((t->inbound) && + (0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) + && + /* FIXME add source address comparison */ + (t->tag == tag)) + break; + t = t->next; + } + if (t != NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Duplicate session, dismissing new connection from peer `%s'\n", + GNUNET_i2s (&target)); + goto error; + } + + /* find semi-session */ + t = plugin->server_semi_head; + + while (t != NULL) + { + /* FIXME add source address comparison */ + if ((0 == memcmp (&t->target, &target, sizeof (struct GNUNET_PeerIdentity))) + && (t->tag == tag)) + { + break; + } + t = t->next; + } + + if (t == NULL) + goto create; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Found existing semi-session for `%s'\n", + GNUNET_i2s (&target)); + + if ((direction == _SEND) && (t->server_send != NULL)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Duplicate GET session, dismissing new connection from peer `%s'\n", + GNUNET_i2s (&target)); + goto error; + } + else + { + s = t; + GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, + plugin->server_semi_tail, s); + GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Found matching semi-session, merging session for peer `%s'\n", + GNUNET_i2s (&target)); + + plugin->inbound_sessions ++; + GNUNET_STATISTICS_set (plugin->env->stats, + "# HTTP inbound sessions", + plugin->inbound_sessions, + GNUNET_NO); + GNUNET_assert (NULL != s); + goto found; + } + if ((direction == _RECEIVE) && (t->server_recv != NULL)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Duplicate PUT session, dismissing new connection from peer `%s'\n", + GNUNET_i2s (&target)); + goto error; + } + else + { + s = t; + GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, + plugin->server_semi_tail, s); + GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Found matching semi-session, merging session for peer `%s'\n", + GNUNET_i2s (&target)); + plugin->inbound_sessions ++; + GNUNET_STATISTICS_set (plugin->env->stats, + "# HTTP inbound sessions", + plugin->inbound_sessions, + GNUNET_NO); + GNUNET_assert (NULL != s); + goto found; + } + +create: +/* create new session */ + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Creating new session for peer `%s' \n", + GNUNET_i2s (&target)); + switch (conn_info->client_addr->sa_family) + { + case (AF_INET): + s4 = ((struct sockaddr_in *) conn_info->client_addr); + a4.u4_port = s4->sin_port; + memcpy (&a4.ipv4_addr, &s4->sin_addr, sizeof (struct in_addr)); + a = &a4; + a_len = sizeof (struct IPv4HttpAddress); + ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) s4, sizeof (struct sockaddr_in)); + break; + case (AF_INET6): + s6 = ((struct sockaddr_in6 *) conn_info->client_addr); + a6.u6_port = s6->sin6_port; + memcpy (&a6.ipv6_addr, &s6->sin6_addr, sizeof (struct in6_addr)); + a = &a6; + a_len = sizeof (struct IPv6HttpAddress); + ats = plugin->env->get_address_type (plugin->env->cls, (const struct sockaddr *) s6, sizeof (struct sockaddr_in6)); + break; + default: + GNUNET_break (0); + goto error; + } + s = create_session (plugin, &target, a, a_len); + GNUNET_assert (NULL != s); + s->ats_address_network_type = ats.value; + s->inbound = GNUNET_YES; + s->next_receive = GNUNET_TIME_UNIT_ZERO_ABS; + s->tag = tag; + s->server_recv = NULL; + s->server_send = NULL; + + GNUNET_CONTAINER_DLL_insert (plugin->server_semi_head, + plugin->server_semi_tail, s); + goto found; + +error: + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Invalid connection request\n"); + return NULL; + +found: + sc = GNUNET_malloc (sizeof (struct ServerConnection)); + sc->mhd_conn = mhd_connection; + sc->direction = direction; + sc->session = s; + if (direction == _SEND) + s->server_send = sc; + if (direction == _RECEIVE) + s->server_recv = sc; + +#if MHD_VERSION >= 0x00090E00 + int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Setting timeout for %p to %u sec.\n", sc, to); + MHD_set_connection_option (mhd_connection, MHD_CONNECTION_OPTION_TIMEOUT, to); + + struct MHD_Daemon *d = NULL; + + if (s->addrlen == sizeof (struct IPv6HttpAddress)) + d = plugin->server_v6; + if (s->addrlen == sizeof (struct IPv4HttpAddress)) + d = plugin->server_v4; + + server_reschedule (plugin, d, GNUNET_NO); +#endif + return sc; +} + +/** + * Process GET or PUT request received via MHD. For + * GET, queue response that will send back our pending + * messages. For PUT, process incoming data and send + * to GNUnet core. In either case, check if a session + * already exists and create a new one if not. + */ +static int +server_access_cb (void *cls, struct MHD_Connection *mhd_connection, + const char *url, const char *method, const char *version, + const char *upload_data, size_t * upload_data_size, + void **httpSessionCache) +{ + struct Plugin *plugin = cls; + struct ServerConnection *sc = *httpSessionCache; + struct Session *s; + struct MHD_Response *response; + int res = MHD_YES; + + GNUNET_assert (cls != NULL); + if (sc == NULL) + { + /* new connection */ + sc = server_lookup_serverconnection (plugin, mhd_connection, url, method); + if (sc != NULL) + (*httpSessionCache) = sc; + else + { + response = + MHD_create_response_from_data (strlen (HTTP_ERROR_RESPONSE), + HTTP_ERROR_RESPONSE, MHD_NO, MHD_NO); + res = MHD_queue_response (mhd_connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response (response); + return res; + } + } + else + { + /* 'old' connection */ + if (NULL == server_lookup_session (plugin, sc)) + { + /* Session was already disconnected */ + return MHD_NO; + } + } + + /* existing connection */ + sc = (*httpSessionCache); + s = sc->session; + + GNUNET_assert (NULL != s); + + /* connection is to be disconnected */ + if (sc->disconnect == GNUNET_YES) + { + /* Sent HTTP/1.1: 200 OK as PUT Response\ */ + response = + MHD_create_response_from_data (strlen ("Thank you!"), "Thank you!", + MHD_NO, MHD_NO); + res = MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + return MHD_YES; + } + + GNUNET_assert (s != NULL); + /* Check if both directions are connected */ + if ((sc->session->server_recv == NULL) || (sc->session->server_send == NULL)) + { + /* Delayed read from since not both semi-connections are connected */ + return MHD_YES; + } + + if (sc->direction == _SEND) + { + response = + MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, + 32 * 1024, + &server_send_callback, s, + NULL); + MHD_queue_response (mhd_connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + return MHD_YES; + } + if (sc->direction == _RECEIVE) + { + if (*upload_data_size == 0) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Peer `%s' PUT on address `%s' connected\n", + GNUNET_i2s (&s->target), + http_plugin_address_to_string (NULL, s->addr, + s->addrlen)); + return MHD_YES; + } + + /* Receiving data */ + if ((*upload_data_size > 0)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: peer `%s' PUT on address `%s' received %u bytes\n", + GNUNET_i2s (&s->target), + http_plugin_address_to_string (NULL, s->addr, + s->addrlen), + *upload_data_size); + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + + if ((s->next_receive.abs_value <= now.abs_value)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: %p: PUT with %u bytes forwarded to MST\n", s, + *upload_data_size); + if (s->msg_tk == NULL) + { + s->msg_tk = GNUNET_SERVER_mst_create (&server_receive_mst_cb, s); + } + GNUNET_SERVER_mst_receive (s->msg_tk, s, upload_data, + *upload_data_size, GNUNET_NO, GNUNET_NO); + +#if MHD_VERSION >= 0x00090E00 + int to = (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000); + struct ServerConnection *t = NULL; + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Received %u bytes\n", *upload_data_size); + /* Setting timeouts for other connections */ + if (s->server_recv != NULL) + { + t = s->server_recv; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Setting timeout for %p to %u sec.\n", t, + to); + MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, + to); + } + if (s->server_send != NULL) + { + t = s->server_send; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: Setting timeout for %p to %u sec.\n", t, + to); + MHD_set_connection_option (t->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, + to); + } + struct MHD_Daemon *d = NULL; + + if (s->addrlen == sizeof (struct IPv6HttpAddress)) + d = plugin->server_v6; + if (s->addrlen == sizeof (struct IPv4HttpAddress)) + d = plugin->server_v4; + server_reschedule (plugin, d, GNUNET_NO); +#endif + (*upload_data_size) = 0; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Server: %p no inbound bandwidth available! Next read was delayed by %llu ms\n", + s, now.abs_value - s->next_receive.abs_value); + } + return MHD_YES; + } + else + return MHD_NO; + } + return res; +} + +static void +server_disconnect_cb (void *cls, struct MHD_Connection *connection, + void **httpSessionCache) +{ + struct ServerConnection *sc = *httpSessionCache; + struct ServerConnection *tc = NULL; + struct Session *s = NULL; + struct Session *t = NULL; + struct Plugin *plugin = NULL; + + if (sc == NULL) + return; + + if (NULL == (s = server_lookup_session (p, sc))) + return; + + GNUNET_assert (NULL != p); + if (GNUNET_NO == exist_session(p, s)) + return; + + plugin = s->plugin; + if (sc->direction == _SEND) + { + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: %p peer `%s' GET on address `%s' disconnected\n", + s->server_send, GNUNET_i2s (&s->target), + http_plugin_address_to_string (NULL, s->addr, s->addrlen)); + s->server_send = NULL; + if (NULL != (tc = s->server_recv)) + { + tc->disconnect = GNUNET_YES; + GNUNET_assert (NULL != tc->mhd_conn); +#if MHD_VERSION >= 0x00090E00 + MHD_set_connection_option (tc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, + 1); +#endif + } + } + if (sc->direction == _RECEIVE) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: %p peer `%s' PUT on address `%s' disconnected\n", + s->server_recv, GNUNET_i2s (&s->target), + http_plugin_address_to_string (NULL, s->addr, s->addrlen)); + s->server_recv = NULL; + if (NULL != (tc = s->server_send)) + { + tc->disconnect = GNUNET_YES; + GNUNET_assert (NULL != tc->mhd_conn); +#if MHD_VERSION >= 0x00090E00 + MHD_set_connection_option (tc->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, + 1); +#endif + } + if (s->msg_tk != NULL) + { + GNUNET_SERVER_mst_destroy (s->msg_tk); + s->msg_tk = NULL; + } + } + + GNUNET_free (sc); + + t = plugin->server_semi_head; + while (t != NULL) + { + if (t == s) + { + GNUNET_CONTAINER_DLL_remove (plugin->server_semi_head, + plugin->server_semi_tail, s); + GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s); + break; + } + t = t->next; + } + plugin->cur_connections--; + + struct MHD_Daemon *d = NULL; + + if (s->addrlen == sizeof (struct IPv6HttpAddress)) + d = plugin->server_v6; + if (s->addrlen == sizeof (struct IPv4HttpAddress)) + d = plugin->server_v4; + server_reschedule (plugin, d, GNUNET_NO); + + if ((s->server_send == NULL) && (s->server_recv == NULL)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Server: peer `%s' on address `%s' disconnected\n", + GNUNET_i2s (&s->target), + http_plugin_address_to_string (NULL, s->addr, s->addrlen)); + if (s->msg_tk != NULL) + { + GNUNET_SERVER_mst_destroy (s->msg_tk); + s->msg_tk = NULL; + } + + GNUNET_assert (plugin->inbound_sessions > 0); + plugin->inbound_sessions --; + GNUNET_STATISTICS_set (plugin->env->stats, + "# HTTP inbound sessions", + plugin->inbound_sessions, GNUNET_NO); + + notify_session_end (s->plugin, &s->target, s); + } +} + +int +server_disconnect (struct Session *s) +{ + struct ServerConnection * send; + struct ServerConnection * recv; + + send = (struct ServerConnection *) s->server_send; + if (s->server_send != NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, + "Server: %p / %p Terminating inbound PUT session to peer `%s'\n", + s, s->server_send, GNUNET_i2s (&s->target)); + + send->disconnect = GNUNET_YES; +#if MHD_VERSION >= 0x00090E00 + MHD_set_connection_option (send->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, + 1); +#endif + } + + recv = (struct ServerConnection *) s->server_recv; + if (recv != NULL) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name, + "Server: %p / %p Terminating inbound GET session to peer `%s'\n", + s, s->server_recv, GNUNET_i2s (&s->target)); + + recv->disconnect = GNUNET_YES; +#if MHD_VERSION >= 0x00090E00 + MHD_set_connection_option (recv->mhd_conn, MHD_CONNECTION_OPTION_TIMEOUT, + 1); +#endif + } + + /* Schedule connection immediately */ + if (s->addrlen == sizeof (struct IPv4HttpAddress)) + { + server_reschedule (s->plugin, s->plugin->server_v4, GNUNET_YES); + } + else if (s->addrlen == sizeof (struct IPv6HttpAddress)) + { + server_reschedule (s->plugin, s->plugin->server_v6, GNUNET_YES); + } + return GNUNET_OK; +} + +int +server_send (struct Session *s, struct HTTP_Message *msg) +{ + GNUNET_CONTAINER_DLL_insert_tail (s->msg_head, s->msg_tail, msg); + + if (s->addrlen == sizeof (struct IPv4HttpAddress)) + { + server_reschedule (s->plugin, s->plugin->server_v4, GNUNET_YES); + } + else if (s->addrlen == sizeof (struct IPv6HttpAddress)) + { + server_reschedule (s->plugin, s->plugin->server_v6, GNUNET_YES); + } + else + return GNUNET_SYSERR; + return GNUNET_OK; +} + + + +/** + * Call MHD IPv4 to process pending requests and then go back + * and schedule the next run. + * @param cls plugin as closure + * @param tc task context + */ +static void +server_v4_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Plugin *plugin = cls; + + GNUNET_assert (cls != NULL); + + plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; +#if 0 + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Running IPv4 server\n"); +#endif + plugin->server_v4_immediately = GNUNET_NO; + GNUNET_assert (MHD_YES == MHD_run (plugin->server_v4)); + server_reschedule (plugin, plugin->server_v4, GNUNET_NO); +} + + +/** + * Call MHD IPv6 to process pending requests and then go back + * and schedule the next run. + * @param cls plugin as closure + * @param tc task context + */ +static void +server_v6_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Plugin *plugin = cls; + + GNUNET_assert (cls != NULL); + plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; + if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + return; +#if 0 + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Running IPv6 server\n"); +#endif + plugin->server_v6_immediately = GNUNET_NO; + GNUNET_assert (MHD_YES == MHD_run (plugin->server_v6)); + server_reschedule (plugin, plugin->server_v6, GNUNET_NO); +} + +/** + * Function that queries MHD's select sets and + * starts the task waiting for them. + * @param plugin plugin + * @param daemon_handle the MHD daemon handle + * @return gnunet task identifier + */ +static GNUNET_SCHEDULER_TaskIdentifier +server_schedule (struct Plugin *plugin, struct MHD_Daemon *daemon_handle, + int now) +{ + GNUNET_SCHEDULER_TaskIdentifier ret; + fd_set rs; + fd_set ws; + fd_set es; + struct GNUNET_NETWORK_FDSet *wrs; + struct GNUNET_NETWORK_FDSet *wws; + struct GNUNET_NETWORK_FDSet *wes; + int max; + unsigned MHD_LONG_LONG timeout; + static unsigned long long last_timeout = 0; + int haveto; + + struct GNUNET_TIME_Relative tv; + + ret = GNUNET_SCHEDULER_NO_TASK; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + wrs = GNUNET_NETWORK_fdset_create (); + wes = GNUNET_NETWORK_fdset_create (); + wws = GNUNET_NETWORK_fdset_create (); + max = -1; + GNUNET_assert (MHD_YES == MHD_get_fdset (daemon_handle, &rs, &ws, &es, &max)); + haveto = MHD_get_timeout (daemon_handle, &timeout); + if (haveto == MHD_YES) + { + if (timeout != last_timeout) + { +#if VERBOSE_SERVER + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "SELECT Timeout changed from %llu to %llu\n", + last_timeout, timeout); +#endif + last_timeout = timeout; + } + tv.rel_value = (uint64_t) timeout; + } + else + tv = GNUNET_TIME_UNIT_SECONDS; + /* Force immediate run, since we have outbound data to send */ + if (now == GNUNET_YES) + tv = GNUNET_TIME_UNIT_MILLISECONDS; + GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1); + GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1); + GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1); + + if (daemon_handle == plugin->server_v4) + { + if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->server_v4_task); + plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; + } +#if 0 + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Scheduling IPv4 server task in %llu ms\n", tv); +#endif + ret = + GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + tv, wrs, wws, + &server_v4_run, plugin); + } + if (daemon_handle == plugin->server_v6) + { + if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->server_v6_task); + plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; + } +#if 0 + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Scheduling IPv6 server task in %llu ms\n", tv); +#endif + ret = + GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, + tv, wrs, wws, + &server_v6_run, plugin); + } + GNUNET_NETWORK_fdset_destroy (wrs); + GNUNET_NETWORK_fdset_destroy (wws); + GNUNET_NETWORK_fdset_destroy (wes); + return ret; +} + +int +server_start (struct Plugin *plugin) +{ + int res = GNUNET_OK; + unsigned int timeout; + p = plugin; + GNUNET_assert (NULL != plugin); + +#if BUILD_HTTPS + res = server_load_certificate (plugin); + if (res == GNUNET_SYSERR) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + "Could not load or create server certificate! Loading plugin failed!\n"); + return res; + } +#endif + + +#if MHD_VERSION >= 0x00090E00 + timeout = HTTP_NOT_VALIDATED_TIMEOUT.rel_value / 1000; + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "MHD can set timeout per connection! Default time out %u sec.\n", + timeout); +#else + timeout = GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value / 1000; + GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name, + "MHD cannot set timeout per connection! Default time out %u sec.\n", + timeout); +#endif + plugin->server_v4 = NULL; + if (plugin->ipv4 == GNUNET_YES) + { + plugin->server_v4 = MHD_start_daemon ( +#if VERBOSE_SERVER + MHD_USE_DEBUG | +#endif +#if BUILD_HTTPS + MHD_USE_SSL | +#endif + MHD_NO_FLAG, plugin->port, + &server_accept_cb, plugin, + &server_access_cb, plugin, + MHD_OPTION_SOCK_ADDR, + (struct sockaddr_in *) + plugin->server_addr_v4, + MHD_OPTION_CONNECTION_LIMIT, + (unsigned int) + plugin->max_connections, +#if BUILD_HTTPS + MHD_OPTION_HTTPS_PRIORITIES, + plugin->crypto_init, + MHD_OPTION_HTTPS_MEM_KEY, + plugin->key, + MHD_OPTION_HTTPS_MEM_CERT, + plugin->cert, +#endif + MHD_OPTION_CONNECTION_TIMEOUT, + timeout, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, + (size_t) (2 * + GNUNET_SERVER_MAX_MESSAGE_SIZE), + MHD_OPTION_NOTIFY_COMPLETED, + &server_disconnect_cb, plugin, + MHD_OPTION_EXTERNAL_LOGGER, + server_log, NULL, MHD_OPTION_END); + } + plugin->server_v6 = NULL; + if (plugin->ipv6 == GNUNET_YES) + { + plugin->server_v6 = MHD_start_daemon ( +#if VERBOSE_SERVER + MHD_USE_DEBUG | +#endif +#if BUILD_HTTPS + MHD_USE_SSL | +#endif + MHD_USE_IPv6, plugin->port, + &server_accept_cb, plugin, + &server_access_cb, plugin, + MHD_OPTION_SOCK_ADDR, + (struct sockaddr_in6 *) + plugin->server_addr_v6, + MHD_OPTION_CONNECTION_LIMIT, + (unsigned int) + plugin->max_connections, +#if BUILD_HTTPS + MHD_OPTION_HTTPS_PRIORITIES, + plugin->crypto_init, + MHD_OPTION_HTTPS_MEM_KEY, + plugin->key, + MHD_OPTION_HTTPS_MEM_CERT, + plugin->cert, +#endif + MHD_OPTION_CONNECTION_TIMEOUT, + timeout, + MHD_OPTION_CONNECTION_MEMORY_LIMIT, + (size_t) (2 * + GNUNET_SERVER_MAX_MESSAGE_SIZE), + MHD_OPTION_NOTIFY_COMPLETED, + &server_disconnect_cb, plugin, + MHD_OPTION_EXTERNAL_LOGGER, + server_log, NULL, MHD_OPTION_END); + + } + + if ((plugin->ipv4 == GNUNET_YES) && (plugin->server_v4 == NULL)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + "Failed to start %s IPv4 server component on port %u\n", + plugin->name, plugin->port); + return GNUNET_SYSERR; + } + server_reschedule (plugin, plugin->server_v4, GNUNET_NO); + + if ((plugin->ipv6 == GNUNET_YES) && (plugin->server_v6 == NULL)) + { + GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, + "Failed to start %s IPv6 server component on port %u\n", + plugin->name, plugin->port); + return GNUNET_SYSERR; + } + server_reschedule (plugin, plugin->server_v6, GNUNET_NO); + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "%s server component started on port %u\n", plugin->name, + plugin->port); + return res; +} + +void +server_stop (struct Plugin *plugin) +{ + struct Session *s = NULL; + struct Session *t = NULL; + + struct MHD_Daemon *server_v4_tmp = plugin->server_v4; + plugin->server_v4 = NULL; + + struct MHD_Daemon *server_v6_tmp = plugin->server_v6; + plugin->server_v6 = NULL; + + if (plugin->server_v4_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->server_v4_task); + plugin->server_v4_task = GNUNET_SCHEDULER_NO_TASK; + } + + if (plugin->server_v6_task != GNUNET_SCHEDULER_NO_TASK) + { + GNUNET_SCHEDULER_cancel (plugin->server_v6_task); + plugin->server_v6_task = GNUNET_SCHEDULER_NO_TASK; + } + + if (server_v6_tmp != NULL) + { + MHD_stop_daemon (server_v4_tmp); + } + if (server_v6_tmp != NULL) + { + MHD_stop_daemon (server_v6_tmp); + } + + /* cleaning up semi-sessions never propagated */ + s = plugin->server_semi_head; + while (s != NULL) + { +#if VERBOSE_SERVER + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "Deleting semi-sessions %p\n", s); +#endif + t = s->next; + struct HTTP_Message *msg = s->msg_head; + struct HTTP_Message *tmp = NULL; + + while (msg != NULL) + { + tmp = msg->next; + + GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg); + if (msg->transmit_cont != NULL) + { + msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR); + } + GNUNET_free (msg); + msg = tmp; + } + + delete_session (s); + s = t; + } + + p = NULL; + +#if BUILD_HTTPS + GNUNET_free_non_null (plugin->crypto_init); + GNUNET_free_non_null (plugin->cert); + GNUNET_free_non_null (plugin->key); +#endif + + GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name, + "%s server component stopped\n", plugin->name); +} + + + +/* end of plugin_transport_http.c */ diff --git a/src/transport/test_transport_api_http_reverse_proxy.conf b/src/transport/test_transport_api_http_reverse_proxy.conf index 49fd9612e..8310ef3dd 100644 --- a/src/transport/test_transport_api_http_reverse_proxy.conf +++ b/src/transport/test_transport_api_http_reverse_proxy.conf @@ -5,7 +5,7 @@ DEFAULTCONFIG = test_transport_api_http_reverse_proxy.conf [transport-http] PORT = 12080 -EXTERNAL_HOSTNAME = fulcrum.net.in.tum.de/peer +EXTERNAL_HOSTNAME = fulcrum.net.in.tum.de:12080/peer [arm] PORT = 12085 -- cgit v1.2.3