libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit c51d65ffce7b0542a15afa4d7bb252df6a970b5c
parent a0cc83b7c2325dfceec0851c2046148059c84619
Author: lv-426 <oxcafebaby@yahoo.com>
Date:   Sat, 24 May 2008 13:59:21 +0000

added :
* configure.ac support for --with-gnutls=PFX
* TLS connection handler in daemon.c
* simple TLS echo client server example

Diffstat:
Mconfigure.ac | 31+++++++++++++++++++++++++++++++
Msrc/daemon/daemon.c | 1194++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/daemon/internal.h | 1024++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/examples/Makefile.am | 32+++++++++++++++++++++++++++++---
Asrc/examples/https_echo_client_example.c | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/examples/https_server_example.c | 182+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 1528 insertions(+), 1088 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -141,6 +141,37 @@ AC_CHECK_PROG([HAVE_SOCAT],[socat], 1, 0) AM_CONDITIONAL(HAVE_ZZUF, test 0 != $HAVE_ZZUF) AM_CONDITIONAL(HAVE_SOCAT, test 0 != $HAVE_SOCAT) +# GNUtls linkage +AC_ARG_WITH(gnutls, + [ --with-gnutls=PFX Base of GNU TLS installation], + [AC_MSG_RESULT("$with_gnutls") + case $with_gnutls in + no) + ;; + yes) + AC_CHECK_HEADERS(gnutls.h,gnutls=true) + ;; + *) + LDFLAGS="-L$with_gnutls/lib $LDFLAGS" + CPPFLAGS="-I$with_gnutls/include $CPPFLAGS" + AC_CHECK_HEADERS(gnutls/gnutls.h, + # check for 'gnutls_global_init' in gnutls.so + AC_CHECK_LIB(gnutls,gnutls_global_init, + GNUTLS_LIB_PATH="$with_gnutls/lib" + GNUTLS_LDFLAGS="-L$with_gnutls/lib" + GNUTLS_CPPFLAGS="-I$with_gnutls/include" + gnutls=true)) + LDFLAGS=$SAVE_LDFLAGS + CPPFLAGS=$SAVE_CPPFLAGS + ;; + esac + ], + [AC_MSG_RESULT([--with-gnutls not specified])]) + +AC_SUBST(GNUTLS_LIB_PATH) +AC_SUBST(GNUTLS_LDFLAGS) +AC_SUBST(GNUTLS_CPPFLAGS) + AC_SUBST(CPPFLAGS) AC_SUBST(LIBS) AC_SUBST(LDFLAGS) diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c @@ -1,22 +1,22 @@ /* - This file is part of libmicrohttpd - (C) 2007 Daniel Pittman and Christian Grothoff + This file is part of libmicrohttpd + (C) 2007 Daniel Pittman and Christian Grothoff - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library 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 - Lesser General Public License for more details. + This library 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 + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ + */ /** * @file daemon.c @@ -52,7 +52,6 @@ */ #define DEBUG_CONNECT MHD_NO - /** * Obtain the select sets for this daemon. * @@ -60,307 +59,331 @@ * daemon was not started with the right * options for this call. */ -int -MHD_get_fdset (struct MHD_Daemon *daemon, - fd_set * read_fd_set, - fd_set * write_fd_set, fd_set * except_fd_set, int *max_fd) -{ - struct MHD_Connection *pos; - int fd; - - if ((daemon == NULL) || - (read_fd_set == NULL) || - (write_fd_set == NULL) || - (except_fd_set == NULL) || - (max_fd == NULL) || - (-1 == (fd = daemon->socket_fd)) || - (daemon->shutdown == MHD_YES) || - ((daemon->options & MHD_USE_THREAD_PER_CONNECTION) != 0)) - return MHD_NO; - FD_SET (fd, read_fd_set); - if ((*max_fd) < fd) - *max_fd = fd; - pos = daemon->connections; - while (pos != NULL) - { - if (MHD_YES != MHD_connection_get_fdset (pos, - read_fd_set, - write_fd_set, - except_fd_set, max_fd)) - return MHD_NO; - pos = pos->next; - } +int MHD_get_fdset(struct MHD_Daemon *daemon, + fd_set * read_fd_set, + fd_set * write_fd_set, + fd_set * except_fd_set, + int *max_fd) + { + struct MHD_Connection *con_itr; + int fd; + + if ((daemon == NULL) || (read_fd_set == NULL) || (write_fd_set == NULL) + || (except_fd_set == NULL) || (max_fd == NULL) || (-1 == (fd = daemon-> + socket_fd)) || (daemon->shutdown == MHD_YES) || ((daemon->options + & MHD_USE_THREAD_PER_CONNECTION) != 0)) + return MHD_NO; + + __FD_SET (fd, read_fd_set); + + /* update max file descriptor */ + if ((*max_fd) < fd) + *max_fd = fd; + + con_itr = daemon->connections; + while (con_itr != NULL) + { + if (MHD_YES != MHD_connection_get_fdset (con_itr, + read_fd_set, + write_fd_set, + except_fd_set, max_fd)) + return MHD_NO; + con_itr = con_itr->next; + } #if DEBUG_CONNECT - MHD_DLOG (daemon, "Maximum socket in select set: %d\n", *max_fd); + MHD_DLOG (daemon, "Maximum socket in select set: %d\n", *max_fd); #endif - return MHD_YES; -} + return MHD_YES; + } /** * Main function of the thread that handles an individual * connection. */ -static void * -MHD_handle_connection (void *data) -{ - struct MHD_Connection *con = data; - int num_ready; - fd_set rs; - fd_set ws; - fd_set es; - int max; - struct timeval tv; - unsigned int timeout; - unsigned int now; - - if (con == NULL) - abort (); - timeout = con->daemon->connection_timeout; - while ((!con->daemon->shutdown) && (con->socket_fd != -1)) - { - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - max = 0; - MHD_connection_get_fdset (con, &rs, &ws, &es, &max); - now = time (NULL); - tv.tv_usec = 0; - if (timeout > (now - con->last_activity)) - tv.tv_sec = timeout - (now - con->last_activity); - else - tv.tv_sec = 0; - num_ready = SELECT (max + 1, - &rs, &ws, &es, (timeout != 0) ? &tv : NULL); - if (num_ready < 0) - { - if (errno == EINTR) - continue; +static void * MHD_handle_connection(void *data) + { + struct MHD_Connection *con = data; + int num_ready; + fd_set rs; + fd_set ws; + fd_set es; + int max; + struct timeval tv; + unsigned int timeout; + unsigned int now; + + if (con == NULL) + abort(); + timeout = con->daemon->connection_timeout; + while ((!con->daemon->shutdown) && (con->socket_fd != -1)) + { + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + max = 0; + MHD_connection_get_fdset(con, &rs, &ws, &es, &max); + now = time(NULL); + tv.tv_usec = 0; + if (timeout > (now - con->last_activity)) + tv.tv_sec = timeout - (now - con->last_activity); + else + tv.tv_sec = 0; + num_ready = SELECT (max + 1, + &rs, &ws, &es, (timeout != 0) ? &tv : NULL); + if (num_ready < 0) + { + if (errno == EINTR) + continue; #if HAVE_MESSAGES - MHD_DLOG (con->daemon, "Error during select (%d): `%s'\n", - max, STRERROR (errno)); + MHD_DLOG(con->daemon, "Error during select (%d): `%s'\n", max, + STRERROR (errno)); #endif - break; - } - if (FD_ISSET (con->socket_fd, &rs)) - MHD_connection_handle_read (con); - if ((con->socket_fd != -1) && (FD_ISSET (con->socket_fd, &ws))) - MHD_connection_handle_write (con); - if (con->socket_fd != -1) - MHD_connection_handle_idle (con); - } - if (con->socket_fd != -1) - { + break; + } + if (FD_ISSET (con->socket_fd, &rs)) + MHD_connection_handle_read(con); + if ((con->socket_fd != -1) && (FD_ISSET (con->socket_fd, &ws))) + MHD_connection_handle_write(con); + if (con->socket_fd != -1) + MHD_connection_handle_idle(con); + } + if (con->socket_fd != -1) + { #if DEBUG_CLOSE #if HAVE_MESSAGES - MHD_DLOG (con->daemon, - "Processing thread terminating, closing connection\n"); + MHD_DLOG (con->daemon, + "Processing thread terminating, closing connection\n"); #endif #endif - SHUTDOWN (con->socket_fd, SHUT_RDWR); - CLOSE (con->socket_fd); - con->socket_fd = -1; - } - return NULL; -} + SHUTDOWN (con->socket_fd, SHUT_RDWR); + CLOSE (con->socket_fd); + con->socket_fd = -1; + } + return NULL; + } + +/** + * Handle an individual TLS connection. + */ +static void * MHDS_handle_connection(void *data) + { + struct MHD_Connection *con = data; + + if (con == NULL) + abort(); + + /* forward call to handler */ + con->daemon->default_handler(NULL, con, NULL, NULL, NULL, NULL, NULL, + NULL); + return NULL; + } /** * Accept an incoming connection and create the MHD_Connection object for * it. This function also enforces policy by way of checking with the * accept policy callback. */ -static int -MHD_accept_connection (struct MHD_Daemon *daemon) -{ - struct MHD_Connection *pos; - struct MHD_Connection *connection; - struct sockaddr_in6 addr6; - struct sockaddr *addr = (struct sockaddr *) &addr6; - socklen_t addrlen; - unsigned int have; - int s; +static int MHD_accept_connection(struct MHD_Daemon *daemon) + { + struct MHD_Connection *pos; + struct MHD_Connection *connection; + struct sockaddr_in6 addr6; + struct sockaddr *addr = (struct sockaddr *) &addr6; + socklen_t addrlen; + unsigned int have; + int s, res_thread_create; #if OSX - static int on = 1; + static int on = 1; #endif + if (sizeof(struct sockaddr) > sizeof(struct sockaddr_in6)) + abort(); /* fatal, serious error */ + addrlen = sizeof(struct sockaddr_in6); + memset(addr, 0, sizeof(struct sockaddr_in6)); + + s = ACCEPT (daemon->socket_fd, addr, &addrlen); - if (sizeof (struct sockaddr) > sizeof (struct sockaddr_in6)) - abort (); /* fatal, serious error */ - addrlen = sizeof (struct sockaddr_in6); - memset (addr, 0, sizeof (struct sockaddr_in6)); - s = ACCEPT (daemon->socket_fd, addr, &addrlen); - if ((s < 0) || (addrlen <= 0)) - { + if ((s < 0) || (addrlen <= 0)) + { #if HAVE_MESSAGES - MHD_DLOG (daemon, "Error accepting connection: %s\n", STRERROR (errno)); + MHD_DLOG(daemon, "Error accepting connection: %s\n", STRERROR (errno)); #endif - if (s != -1) - { - SHUTDOWN (s, SHUT_RDWR); - CLOSE (s); /* just in case */ - } - return MHD_NO; - } + if (s != -1) + { + SHUTDOWN (s, SHUT_RDWR); + CLOSE (s); + /* just in case */ + } + return MHD_NO; + } #if DEBUG_CONNECT - MHD_DLOG (daemon, "Accepted connection on socket %d\n", s); + MHD_DLOG (daemon, "Accepted connection on socket %d\n", s); #endif - have = 0; - if ((daemon->per_ip_connection_limit != 0) && (daemon->max_connections > 0)) - { - pos = daemon->connections; - while (pos != NULL) - { - if ((pos->addr != NULL) && (pos->addr_len == addrlen)) - { - if (addrlen == sizeof (struct sockaddr_in)) - { - const struct sockaddr_in *a1 = - (const struct sockaddr_in *) &addr; - const struct sockaddr_in *a2 = - (const struct sockaddr_in *) pos->addr; - if (0 == - memcmp (&a1->sin_addr, &a2->sin_addr, - sizeof (struct in_addr))) - have++; - } - if (addrlen == sizeof (struct sockaddr_in6)) - { - const struct sockaddr_in6 *a1 = - (const struct sockaddr_in6 *) &addr; - const struct sockaddr_in6 *a2 = - (const struct sockaddr_in6 *) pos->addr; - if (0 == - memcmp (&a1->sin6_addr, &a2->sin6_addr, - sizeof (struct in6_addr))) - have++; - } - } - pos = pos->next; - } - } - - if ((daemon->max_connections == 0) || - ((daemon->per_ip_connection_limit != 0) && - (daemon->per_ip_connection_limit <= have))) - { - /* above connection limit - reject */ + have = 0; + if ((daemon->per_ip_connection_limit != 0) && (daemon->max_connections > 0)) + { + pos = daemon->connections; + while (pos != NULL) + { + if ((pos->addr != NULL) && (pos->addr_len == addrlen)) + { + if (addrlen == sizeof(struct sockaddr_in)) + { + const struct sockaddr_in *a1 = + (const struct sockaddr_in *) &addr; + const struct sockaddr_in *a2 = + (const struct sockaddr_in *) pos->addr; + if (0 == memcmp(&a1->sin_addr, &a2->sin_addr, + sizeof(struct in_addr))) + have++; + } + if (addrlen == sizeof(struct sockaddr_in6)) + { + const struct sockaddr_in6 *a1 = + (const struct sockaddr_in6 *) &addr; + const struct sockaddr_in6 *a2 = + (const struct sockaddr_in6 *) pos->addr; + if (0 == memcmp(&a1->sin6_addr, &a2->sin6_addr, + sizeof(struct in6_addr))) + have++; + } + } + pos = pos->next; + } + } + + if ((daemon->max_connections == 0) || ((daemon->per_ip_connection_limit + != 0) && (daemon-> + per_ip_connection_limit <= have))) + { + /* above connection limit - reject */ #if HAVE_MESSAGES - MHD_DLOG (daemon, - "Server reached connection limit (closing inbound connection)\n"); + MHD_DLOG(daemon, + "Server reached connection limit (closing inbound connection)\n"); #endif - SHUTDOWN (s, SHUT_RDWR); - CLOSE (s); - return MHD_NO; - } - if ((daemon->apc != NULL) && - (MHD_NO == daemon->apc (daemon->apc_cls, addr, addrlen))) - { + SHUTDOWN (s, SHUT_RDWR); + CLOSE (s); + return MHD_NO; + } + if ((daemon->apc != NULL) && (MHD_NO == daemon->apc (daemon->apc_cls, addr, addrlen))) + { #if DEBUG_CLOSE #if HAVE_MESSAGES - MHD_DLOG (daemon, "Connection rejected, closing connection\n"); + MHD_DLOG (daemon, "Connection rejected, closing connection\n"); #endif #endif - SHUTDOWN (s, SHUT_RDWR); - CLOSE (s); - return MHD_YES; - } + SHUTDOWN (s, SHUT_RDWR); + CLOSE (s); + return MHD_YES; + } #if OSX #ifdef SOL_SOCKET #ifdef SO_NOSIGPIPE - setsockopt (s, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof (on)); + setsockopt (s, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof (on)); #endif #endif #endif - connection = malloc (sizeof (struct MHD_Connection)); - if (connection == NULL) - { + connection = malloc(sizeof(struct MHD_Connection)); + if (connection == NULL) + { #if HAVE_MESSAGES - MHD_DLOG (daemon, "Error allocating memory: %s\n", STRERROR (errno)); + MHD_DLOG(daemon, "Error allocating memory: %s\n", STRERROR (errno)); #endif - SHUTDOWN (s, SHUT_RDWR); - CLOSE (s); - return MHD_NO; - } - memset (connection, 0, sizeof (struct MHD_Connection)); - connection->pool = NULL; - connection->addr = malloc (addrlen); - if (connection->addr == NULL) - { + SHUTDOWN (s, SHUT_RDWR); + CLOSE (s); + return MHD_NO; + } + memset(connection, 0, sizeof(struct MHD_Connection)); + connection->pool = NULL; + connection->addr = malloc(addrlen); + if (connection->addr == NULL) + { #if HAVE_MESSAGES - MHD_DLOG (daemon, "Error allocating memory: %s\n", STRERROR (errno)); + MHD_DLOG(daemon, "Error allocating memory: %s\n", STRERROR (errno)); #endif - SHUTDOWN (s, SHUT_RDWR); - CLOSE (s); - free (connection); - return MHD_NO; - } - memcpy (connection->addr, addr, addrlen); - connection->addr_len = addrlen; - connection->socket_fd = s; - connection->daemon = daemon; - if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && - (0 != pthread_create (&connection->pid, - NULL, &MHD_handle_connection, connection))) - { + SHUTDOWN (s, SHUT_RDWR); + CLOSE (s); + free(connection); + return MHD_NO; + } + memcpy(connection->addr, addr, addrlen); + connection->addr_len = addrlen; + connection->socket_fd = s; + connection->daemon = daemon; + + /* attempt to create handler thread */ + if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) + { + if (daemon->options & MHD_USE_SSL) + res_thread_create= pthread_create(&connection->pid, NULL, + &MHDS_handle_connection, connection); + else + { + res_thread_create= pthread_create(&connection->pid, NULL, + &MHD_handle_connection, connection); + } + if (res_thread_create != 0) + { #if HAVE_MESSAGES - MHD_DLOG (daemon, "Failed to create a thread: %s\n", STRERROR (errno)); + MHD_DLOG(daemon, "Failed to create a thread: %s\n", STRERROR (errno)); #endif - SHUTDOWN (s, SHUT_RDWR); - CLOSE (s); - free (connection->addr); - free (connection); - return MHD_NO; - } - connection->last_activity = time (NULL); - connection->next = daemon->connections; - daemon->connections = connection; - daemon->max_connections--; - return MHD_YES; -} - + SHUTDOWN (s, SHUT_RDWR); + CLOSE (s); + free(connection->addr); + free(connection); + return MHD_NO; + } + } + + connection->last_activity = time(NULL); + connection->next = daemon->connections; + daemon->connections = connection; + daemon->max_connections--; + return MHD_YES; + } /** * Free resources associated with all closed connections. * (destroy responses, free buffers, etc.). A connection * is known to be closed if the socket_fd is -1. */ -static void -MHD_cleanup_connections (struct MHD_Daemon *daemon) -{ - struct MHD_Connection *pos; - struct MHD_Connection *prev; - void *unused; - - pos = daemon->connections; - prev = NULL; - while (pos != NULL) - { - if (pos->socket_fd == -1) - { - if (prev == NULL) - daemon->connections = pos->next; - else - prev->next = pos->next; - if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) - { - pthread_kill (pos->pid, SIGALRM); - pthread_join (pos->pid, &unused); - } - MHD_destroy_response (pos->response); - MHD_pool_destroy (pos->pool); - free (pos->addr); - free (pos); - daemon->max_connections++; - if (prev == NULL) - pos = daemon->connections; - else - pos = prev->next; - continue; - } - prev = pos; - pos = pos->next; - } -} +static void MHD_cleanup_connections(struct MHD_Daemon *daemon) + { + struct MHD_Connection *pos; + struct MHD_Connection *prev; + void *unused; + + pos = daemon->connections; + prev = NULL; + while (pos != NULL) + { + if (pos->socket_fd == -1) + { + if (prev == NULL) + daemon->connections = pos->next; + else + prev->next = pos->next; + if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) + { + pthread_kill(pos->pid, SIGALRM); + pthread_join(pos->pid, &unused); + } + MHD_destroy_response(pos->response); + MHD_pool_destroy(pos->pool); + free(pos->addr); + free(pos); + daemon->max_connections++; + if (prev == NULL) + pos = daemon->connections; + else + pos = prev->next; + continue; + } + prev = pos; + pos = pos->next; + } + } /** * Obtain timeout value for select for this daemon @@ -373,35 +396,34 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon) * not used (or no connections exist that would * necessiate the use of a timeout right now). */ -int -MHD_get_timeout (struct MHD_Daemon *daemon, unsigned long long *timeout) -{ - time_t earliest_deadline; - time_t now; - struct MHD_Connection *pos; - unsigned int dto; - - dto = daemon->connection_timeout; - if (0 == dto) - return MHD_NO; - pos = daemon->connections; - if (pos == NULL) - return MHD_NO; /* no connections */ - now = time (NULL); - /* start with conservative estimate */ - earliest_deadline = now + dto; - while (pos != NULL) - { - if (earliest_deadline > pos->last_activity + dto) - earliest_deadline = pos->last_activity + dto; - pos = pos->next; - } - if (earliest_deadline < now) - *timeout = 0; - else - *timeout = (earliest_deadline - now); - return MHD_YES; -} +int MHD_get_timeout(struct MHD_Daemon *daemon, unsigned long long *timeout) + { + time_t earliest_deadline; + time_t now; + struct MHD_Connection *pos; + unsigned int dto; + + dto = daemon->connection_timeout; + if (0 == dto) + return MHD_NO; + pos = daemon->connections; + if (pos == NULL) + return MHD_NO; /* no connections */ + now = time(NULL); + /* start with conservative estimate */ + earliest_deadline = now + dto; + while (pos != NULL) + { + if (earliest_deadline > pos->last_activity + dto) + earliest_deadline = pos->last_activity + dto; + pos = pos->next; + } + if (earliest_deadline < now) + *timeout = 0; + else + *timeout = (earliest_deadline - now); + return MHD_YES; + } /** * Main select call. @@ -409,101 +431,102 @@ MHD_get_timeout (struct MHD_Daemon *daemon, unsigned long long *timeout) * @param may_block YES if blocking, NO if non-blocking * @return MHD_NO on serious errors, MHD_YES on success */ -static int -MHD_select (struct MHD_Daemon *daemon, int may_block) -{ - struct MHD_Connection *pos; - int num_ready; - fd_set rs; - fd_set ws; - fd_set es; - int max; - struct timeval timeout; - unsigned long long ltimeout; - int ds; - time_t now; - - timeout.tv_sec = 0; - timeout.tv_usec = 0; - if (daemon == NULL) - abort (); - if (daemon->shutdown == MHD_YES) - return MHD_NO; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - max = 0; - - if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) - { - /* single-threaded, go over everything */ - if (MHD_NO == MHD_get_fdset (daemon, &rs, &ws, &es, &max)) - return MHD_NO; - } - else - { - /* accept only, have one thread per connection */ - max = daemon->socket_fd; - if (max == -1) - return MHD_NO; - FD_SET (max, &rs); - } - if (may_block == MHD_NO) - { - timeout.tv_usec = 0; - timeout.tv_sec = 0; - } - else - { - /* ltimeout is in ms */ - if (MHD_YES == MHD_get_timeout (daemon, &ltimeout)) - { - timeout.tv_usec = (ltimeout % 1000) * 1000; - timeout.tv_sec = ltimeout / 1000; - may_block = MHD_NO; - } - } - num_ready = SELECT (max + 1, - &rs, &ws, &es, may_block == MHD_NO ? &timeout : NULL); - if (daemon->shutdown == MHD_YES) - return MHD_NO; - if (num_ready < 0) - { - if (errno == EINTR) - return MHD_YES; +static int MHD_select(struct MHD_Daemon *daemon, int may_block) + { + struct MHD_Connection *pos; + int num_ready; + fd_set rs; + fd_set ws; + fd_set es; + int max; + struct timeval timeout; + unsigned long long ltimeout; + int ds; + time_t now; + + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (daemon == NULL) + abort(); + if (daemon->shutdown == MHD_YES) + return MHD_NO; + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + max = 0; + + if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) + { + /* single-threaded, go over everything */ + if (MHD_NO == MHD_get_fdset (daemon, &rs, &ws, &es, &max)) + return MHD_NO; + } + else + { + /* accept only, have one thread per connection */ + max = daemon->socket_fd; + if (max == -1) + return MHD_NO; + FD_SET (max, &rs); + } + + if (may_block == MHD_NO) + { + timeout.tv_usec = 0; + timeout.tv_sec = 0; + } + else + { + /* ltimeout is in ms */ + if (MHD_YES == MHD_get_timeout (daemon, &ltimeout)) + { + timeout.tv_usec = (ltimeout % 1000) * 1000; + timeout.tv_sec = ltimeout / 1000; + may_block = MHD_NO; + } + } + + num_ready = select(max + 1, &rs, &ws, &es, may_block == MHD_NO ? &timeout + : NULL); + + if (daemon->shutdown == MHD_YES) + return MHD_NO; + if (num_ready < 0) + { + if (errno == EINTR) + return MHD_YES; #if HAVE_MESSAGES - MHD_DLOG (daemon, "Select failed: %s\n", STRERROR (errno)); + MHD_DLOG(daemon, "Select failed: %s\n", STRERROR (errno)); #endif - return MHD_NO; - } - ds = daemon->socket_fd; - if (ds == -1) + return MHD_NO; + } + ds = daemon->socket_fd; + if (ds == -1) + return MHD_YES; + if (__FD_ISSET (ds, &rs)) + MHD_accept_connection(daemon); + if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) + { + /* do not have a thread per connection, process all connections now */ + now = time(NULL); + pos = daemon->connections; + while (pos != NULL) + { + ds = pos->socket_fd; + if (ds != -1) + { + if (FD_ISSET (ds, &rs)) + MHD_connection_handle_read(pos); + if ((pos->socket_fd != -1) && (FD_ISSET (ds, &ws))) + MHD_connection_handle_write(pos); + if (pos->socket_fd != -1) + MHD_connection_handle_idle(pos); + } + pos = pos->next; + } + } return MHD_YES; - if (FD_ISSET (ds, &rs)) - MHD_accept_connection (daemon); - if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) - { - /* do not have a thread per connection, process all connections now */ - now = time (NULL); - pos = daemon->connections; - while (pos != NULL) - { - ds = pos->socket_fd; - if (ds != -1) - { - if (FD_ISSET (ds, &rs)) - MHD_connection_handle_read (pos); - if ((pos->socket_fd != -1) && (FD_ISSET (ds, &ws))) - MHD_connection_handle_write (pos); - if (pos->socket_fd != -1) - MHD_connection_handle_idle (pos); - } - pos = pos->next; - } - } - return MHD_YES; -} - + } /** * Run webserver operations (without blocking unless @@ -515,35 +538,31 @@ MHD_select (struct MHD_Daemon *daemon, int may_block) * daemon was not started with the right * options for this call. */ -int -MHD_run (struct MHD_Daemon *daemon) -{ - if ((daemon->shutdown != MHD_NO) || - (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || - (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY))) - return MHD_NO; - MHD_select (daemon, MHD_NO); - MHD_cleanup_connections (daemon); - return MHD_YES; -} - +int MHD_run(struct MHD_Daemon *daemon) + { + if ((daemon->shutdown != MHD_NO) || (0 != (daemon->options + & MHD_USE_THREAD_PER_CONNECTION)) || (0 != (daemon->options + & MHD_USE_SELECT_INTERNALLY))) + return MHD_NO; + MHD_select(daemon, MHD_NO); + MHD_cleanup_connections(daemon); + return MHD_YES; + } /** * Thread that runs the select loop until the daemon * is explicitly shut down. */ -static void * -MHD_select_thread (void *cls) -{ - struct MHD_Daemon *daemon = cls; - while (daemon->shutdown == MHD_NO) - { - MHD_select (daemon, MHD_YES); - MHD_cleanup_connections (daemon); - } - return NULL; -} - +static void * MHD_select_thread(void *cls) + { + struct MHD_Daemon *daemon = cls; + while (daemon->shutdown == MHD_NO) + { + MHD_select(daemon, MHD_YES); + MHD_cleanup_connections(daemon); + } + return NULL; + } /** * Start a webserver on the given port. @@ -556,106 +575,119 @@ MHD_select_thread (void *cls) * @param dh_cls extra argument to dh * @return NULL on error, handle to daemon on success */ -struct MHD_Daemon * -MHD_start_daemon (unsigned int options, - unsigned short port, - MHD_AcceptPolicyCallback apc, - void *apc_cls, - MHD_AccessHandlerCallback dh, void *dh_cls, ...) -{ - const int on = 1; - struct MHD_Daemon *retVal; - int socket_fd; - struct sockaddr_in servaddr4; - struct sockaddr_in6 servaddr6; - const struct sockaddr *servaddr; - socklen_t addrlen; - va_list ap; - enum MHD_OPTION opt; - - if ((options & MHD_USE_SSL) != 0) - return NULL; - if ((port == 0) || (dh == NULL)) - return NULL; - if ((options & MHD_USE_IPv6) != 0) - socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0); - else - socket_fd = SOCKET (PF_INET, SOCK_STREAM, 0); - if (socket_fd < 0) - { +struct MHD_Daemon * MHD_start_daemon(unsigned int options, + unsigned short port, + MHD_AcceptPolicyCallback apc, + void *apc_cls, + MHD_AccessHandlerCallback dh, + void *dh_cls, + ...) + { + const int on = 1; + struct MHD_Daemon *retVal; + + /* listeningss sockets used by the daemon */ + int socket_fd; + + struct sockaddr_in servaddr4; + struct sockaddr_in6 servaddr6; + const struct sockaddr *servaddr; + socklen_t addrlen; + va_list ap; + enum MHD_OPTION opt; + + if ((port == 0) || (dh == NULL)) + return NULL; + if ((options & MHD_USE_IPv6) != 0) + socket_fd = SOCKET (PF_INET6, SOCK_STREAM, 0); + else + socket_fd = SOCKET (PF_INET, SOCK_STREAM, 0); + if (socket_fd < 0) + { #if HAVE_MESSAGES - if ((options & MHD_USE_DEBUG) != 0) - fprintf (stderr, "Call to socket failed: %s\n", STRERROR (errno)); + if ((options & MHD_USE_DEBUG) != 0) + fprintf(stderr, "Call to socket failed: %s\n", STRERROR (errno)); #endif - return NULL; - } - if ((SETSOCKOPT (socket_fd, - SOL_SOCKET, - SO_REUSEADDR, - &on, sizeof (on)) < 0) && (options & MHD_USE_DEBUG) != 0) - { + return NULL; + } + if ((SETSOCKOPT (socket_fd, + SOL_SOCKET, + SO_REUSEADDR, + &on, sizeof (on)) < 0) && (options & MHD_USE_DEBUG) != 0) + { #if HAVE_MESSAGES - fprintf (stderr, "setsockopt failed: %s\n", STRERROR (errno)); + fprintf(stderr, "setsockopt failed: %s\n", STRERROR (errno)); #endif - } - if ((options & MHD_USE_IPv6) != 0) - { - memset (&servaddr6, 0, sizeof (struct sockaddr_in6)); - servaddr6.sin6_family = AF_INET6; - servaddr6.sin6_port = htons (port); - servaddr = (struct sockaddr *) &servaddr6; - addrlen = sizeof (struct sockaddr_in6); - } - else - { - memset (&servaddr4, 0, sizeof (struct sockaddr_in)); - servaddr4.sin_family = AF_INET; - servaddr4.sin_port = htons (port); - servaddr = (struct sockaddr *) &servaddr4; - addrlen = sizeof (struct sockaddr_in); - } - if (BIND (socket_fd, servaddr, addrlen) < 0) - { + } + if ((options & MHD_USE_IPv6) != 0) + { + memset(&servaddr6, 0, sizeof(struct sockaddr_in6)); + servaddr6.sin6_family = AF_INET6; + servaddr6.sin6_port = htons(port); + servaddr = (struct sockaddr *) &servaddr6; + addrlen = sizeof(struct sockaddr_in6); + } + else + { + memset(&servaddr4, 0, sizeof(struct sockaddr_in)); + servaddr4.sin_family = AF_INET; + servaddr4.sin_port = htons(port); + servaddr = (struct sockaddr *) &servaddr4; + addrlen = sizeof(struct sockaddr_in); + } + if (BIND (socket_fd, servaddr, addrlen) < 0) + { #if HAVE_MESSAGES - if ((options & MHD_USE_DEBUG) != 0) - fprintf (stderr, - "Failed to bind to port %u: %s\n", port, STRERROR (errno)); + if ((options & MHD_USE_DEBUG) != 0) + fprintf(stderr, + "Failed to bind to port %u: %s\n", port, STRERROR (errno)); #endif - CLOSE (socket_fd); - return NULL; - } - if (LISTEN (socket_fd, 20) < 0) - { + CLOSE (socket_fd); + return NULL; + } + if (LISTEN (socket_fd, 20) < 0) + { #if HAVE_MESSAGES - if ((options & MHD_USE_DEBUG) != 0) - fprintf (stderr, - "Failed to listen for connections: %s\n", STRERROR (errno)); + if ((options & MHD_USE_DEBUG) != 0) + fprintf(stderr, + "Failed to listen for connections: %s\n", STRERROR (errno)); #endif - CLOSE (socket_fd); - return NULL; - } - retVal = malloc (sizeof (struct MHD_Daemon)); - if (retVal == NULL) - { - CLOSE (socket_fd); - return NULL; - } - memset (retVal, 0, sizeof (struct MHD_Daemon)); - retVal->options = options; - retVal->port = port; - retVal->apc = apc; - retVal->apc_cls = apc_cls; - retVal->socket_fd = socket_fd; - retVal->default_handler = dh; - retVal->default_handler_cls = dh_cls; - retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT; - retVal->pool_size = MHD_POOL_SIZE_DEFAULT; - retVal->connection_timeout = 0; /* no timeout */ - va_start (ap, dh_cls); - while (MHD_OPTION_END != (opt = va_arg (ap, enum MHD_OPTION))) - { - switch (opt) - { + CLOSE (socket_fd); + return NULL; + } + + /* allocate the mhd daemon */ + + retVal = malloc(sizeof(struct MHD_Daemon)); + + if (retVal == NULL) + { + CLOSE (socket_fd); + return NULL; + } + + memset(retVal, 0, sizeof(struct MHD_Daemon)); + retVal->options = options; + retVal->port = port; + retVal->apc = apc; + retVal->apc_cls = apc_cls; + retVal->socket_fd = socket_fd; + retVal->default_handler = dh; + retVal->default_handler_cls = dh_cls; + retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT; + retVal->pool_size = MHD_POOL_SIZE_DEFAULT; + retVal->connection_timeout = 0; /* no timeout */ + + /* initializes the argument pointer variable */ + va_start (ap, dh_cls); + + /* + * loop through daemon options + */ + while (MHD_OPTION_END != (opt = va_arg (ap, enum MHD_OPTION))) + { + switch (opt) + { case MHD_OPTION_CONNECTION_MEMORY_LIMIT: retVal->pool_size = va_arg (ap, unsigned int); break; @@ -666,8 +698,7 @@ MHD_start_daemon (unsigned int options, retVal->connection_timeout = va_arg (ap, unsigned int); break; case MHD_OPTION_NOTIFY_COMPLETED: - retVal->notify_completed = - va_arg (ap, MHD_RequestCompletedCallback); + retVal->notify_completed = va_arg (ap, MHD_RequestCompletedCallback); retVal->notify_completed_cls = va_arg (ap, void *); break; case MHD_OPTION_PER_IP_CONNECTION_LIMIT: @@ -675,76 +706,76 @@ MHD_start_daemon (unsigned int options, break; default: #if HAVE_MESSAGES - fprintf (stderr, - "Invalid MHD_OPTION argument! (Did you terminate the list with MHD_OPTION_END?)\n"); + fprintf(stderr, + "Invalid MHD_OPTION argument! (Did you terminate the list with MHD_OPTION_END?)\n"); #endif - abort (); - } - } - va_end (ap); - if (((0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || - (0 != (options & MHD_USE_SELECT_INTERNALLY))) && - (0 != pthread_create (&retVal->pid, NULL, &MHD_select_thread, retVal))) - { + abort(); + } + } + va_end (ap); + if (((0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || (0 != (options + & MHD_USE_SELECT_INTERNALLY))) && (0 != pthread_create(&retVal->pid, + NULL, &MHD_select_thread, retVal))) + { #if HAVE_MESSAGES - MHD_DLOG (retVal, - "Failed to create listen thread: %s\n", STRERROR (errno)); + MHD_DLOG(retVal, "Failed to create listen thread: %s\n", + STRERROR (errno)); #endif - free (retVal); - CLOSE (socket_fd); - return NULL; - } - return retVal; -} + free(retVal); + CLOSE (socket_fd); + return NULL; + } + + return retVal; + } /** * Shutdown an http daemon. */ -void -MHD_stop_daemon (struct MHD_Daemon *daemon) -{ - void *unused; - int fd; - - if (daemon == NULL) - return; - daemon->shutdown = MHD_YES; - fd = daemon->socket_fd; - daemon->socket_fd = -1; +void MHD_stop_daemon(struct MHD_Daemon *daemon) + { + void *unused; + int fd; + + if (daemon == NULL) + return; + daemon->shutdown = MHD_YES; + fd = daemon->socket_fd; + daemon->socket_fd = -1; #if DEBUG_CLOSE #if HAVE_MESSAGES - MHD_DLOG (daemon, "MHD shutdown, closing listen socket\n"); + MHD_DLOG (daemon, "MHD shutdown, closing listen socket\n"); #endif #endif - CLOSE (fd); - if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || - (0 != (daemon->options & MHD_USE_SELECT_INTERNALLY))) - { - pthread_kill (daemon->pid, SIGALRM); - pthread_join (daemon->pid, &unused); - } - while (daemon->connections != NULL) - { - if (-1 != daemon->connections->socket_fd) - { + CLOSE (fd); + if ((0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) || (0 + != (daemon-> + options & MHD_USE_SELECT_INTERNALLY))) + { + pthread_kill(daemon->pid, SIGALRM); + pthread_join(daemon->pid, &unused); + } + while (daemon->connections != NULL) + { + if (-1 != daemon->connections->socket_fd) + { #if DEBUG_CLOSE #if HAVE_MESSAGES - MHD_DLOG (daemon, "MHD shutdown, closing active connections\n"); + MHD_DLOG (daemon, "MHD shutdown, closing active connections\n"); #endif #endif - if (daemon->notify_completed != NULL) - daemon->notify_completed (daemon->notify_completed_cls, - daemon->connections, - &daemon->connections->client_context, - MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN); - SHUTDOWN (daemon->connections->socket_fd, SHUT_RDWR); - CLOSE (daemon->connections->socket_fd); - daemon->connections->socket_fd = -1; - } - MHD_cleanup_connections (daemon); - } - free (daemon); -} + if (daemon->notify_completed != NULL) + daemon->notify_completed(daemon->notify_completed_cls, + daemon->connections, &daemon->connections->client_context, + MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN); + SHUTDOWN (daemon->connections->socket_fd, SHUT_RDWR); + CLOSE (daemon->connections->socket_fd); + daemon->connections->socket_fd = -1; + } + MHD_cleanup_connections(daemon); + } + free(daemon); + } #ifndef WINDOWS @@ -752,38 +783,37 @@ static struct sigaction sig; static struct sigaction old; -static void -sigalrmHandler (int sig) -{ -} +static void sigalrmHandler(int sig) + { + } /** * Initialize the signal handler for SIGALRM. */ -void __attribute__ ((constructor)) MHD_pthread_handlers_ltdl_init () -{ - /* make sure SIGALRM does not kill us */ - memset (&sig, 0, sizeof (struct sigaction)); - memset (&old, 0, sizeof (struct sigaction)); - sig.sa_flags = SA_NODEFER; - sig.sa_handler = &sigalrmHandler; - sigaction (SIGALRM, &sig, &old); -} - -void __attribute__ ((destructor)) MHD_pthread_handlers_ltdl_fini () -{ - sigaction (SIGALRM, &old, &sig); -} +void __attribute__ ((constructor)) MHD_pthread_handlers_ltdl_init() + { + /* make sure SIGALRM does not kill us */ + memset(&sig, 0, sizeof(struct sigaction)); + memset(&old, 0, sizeof(struct sigaction)); + sig.sa_flags = SA_NODEFER; + sig.sa_handler = &sigalrmHandler; + sigaction(SIGALRM, &sig, &old); + } + +void __attribute__ ((destructor)) MHD_pthread_handlers_ltdl_fini() + { + sigaction(SIGALRM, &old, &sig); + } #else void __attribute__ ((constructor)) MHD_win_ltdl_init () -{ - plibc_init ("CRISP", "libmicrohttpd"); -} + { + plibc_init ("CRISP", "libmicrohttpd"); + } void __attribute__ ((destructor)) MHD_win_ltdl_fini () -{ - plibc_shutdown (); -} + { + plibc_shutdown (); + } #endif /* end of daemon.c */ diff --git a/src/daemon/internal.h b/src/daemon/internal.h @@ -1,21 +1,21 @@ /* - This file is part of libmicrohttpd - (C) 2007 Daniel Pittman and Christian Grothoff - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -*/ + This file is part of libmicrohttpd + (C) 2007 Daniel Pittman and Christian Grothoff + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ /** * @file internal.h @@ -27,7 +27,6 @@ #ifndef INTERNAL_H #define INTERNAL_H - #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -64,101 +63,101 @@ * fprintf-like helper function for logging debug * messages. */ -void MHD_DLOG (const struct MHD_Daemon *daemon, const char *format, ...); +void MHD_DLOG(const struct MHD_Daemon *daemon, const char *format, ...); #endif /** * Process escape sequences ('+'=space, %HH). * Updates val in place. */ -void MHD_http_unescape (char *val); +void MHD_http_unescape(char *val); /** * Header or cookie in HTTP request or response. */ struct MHD_HTTP_Header -{ - struct MHD_HTTP_Header *next; + { + struct MHD_HTTP_Header *next; - char *header; + char *header; - char *value; + char *value; - enum MHD_ValueKind kind; + enum MHD_ValueKind kind; -}; + }; /** * Representation of a response. */ struct MHD_Response -{ - - /** - * Headers to send for the response. Initially - * the linked list is created in inverse order; - * the order should be inverted before sending! - */ - struct MHD_HTTP_Header *first_header; - - /** - * Buffer pointing to data that we are supposed - * to send as a response. - */ - char *data; - - /** - * Closure to give to the content reader - * free callback. - */ - void *crc_cls; - - /** - * How do we get more data? NULL if we are - * given all of the data up front. - */ - MHD_ContentReaderCallback crc; - - /** - * NULL if data must not be freed, otherwise - * either user-specified callback or "&free". - */ - MHD_ContentReaderFreeCallback crfc; - - /** - * Mutex to synchronize access to data/size and - * reference counts. - */ - pthread_mutex_t mutex; - - /** - * Reference count for this response. Free - * once the counter hits zero. - */ - unsigned int reference_count; - - /** - * Set to -1 if size is not known. - */ - size_t total_size; - - /** - * Size of data. - */ - size_t data_size; - - /** - * Size of the data buffer. - */ - size_t data_buffer_size; - - /** - * At what offset in the stream is the - * beginning of data located? - */ - size_t data_start; - -}; + { + + /** + * Headers to send for the response. Initially + * the linked list is created in inverse order; + * the order should be inverted before sending! + */ + struct MHD_HTTP_Header *first_header; + + /** + * Buffer pointing to data that we are supposed + * to send as a response. + */ + char *data; + + /** + * Closure to give to the content reader + * free callback. + */ + void *crc_cls; + + /** + * How do we get more data? NULL if we are + * given all of the data up front. + */ + MHD_ContentReaderCallback crc; + + /** + * NULL if data must not be freed, otherwise + * either user-specified callback or "&free". + */ + MHD_ContentReaderFreeCallback crfc; + + /** + * Mutex to synchronize access to data/size and + * reference counts. + */ + pthread_mutex_t mutex; + + /** + * Reference count for this response. Free + * once the counter hits zero. + */ + unsigned int reference_count; + + /** + * Set to -1 if size is not known. + */ + size_t total_size; + + /** + * Size of data. + */ + size_t data_size; + + /** + * Size of the data buffer. + */ + size_t data_buffer_size; + + /** + * At what offset in the stream is the + * beginning of data located? + */ + size_t data_start; + + }; /** * States in a state machine for a connection. @@ -175,419 +174,438 @@ struct MHD_Response * requires the write to be complete. */ enum MHD_CONNECTION_STATE -{ - /** - * Connection just started (no headers received). - * Waiting for the line with the request type, URL and version. - */ - MHD_CONNECTION_INIT = 0, - - /** - * 1: We got the URL (and request type and version). Wait for a header line. - */ - MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1, - - /** - * 2: We got part of a multi-line request header. Wait for the rest. - */ - MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1, - - /** - * 3: We got the request headers. Process them. - */ - MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1, - - /** - * 4: We have processed the request headers. Send 100 continue. - */ - MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1, - - /** - * 5: We have processed the headers and need to send 100 CONTINUE. - */ - MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1, - - /** - * 6: We have sent 100 CONTINUE (or do not need to). Read the message body. - */ - MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1, - - /** - * 7: We got the request body. Wait for a line of the footer. - */ - MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1, - - /** - * 8: We got part of a line of the footer. Wait for the - * rest. - */ - MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1, - - /** - * 9: We received the entire footer. Wait for a response to be queued - * and prepare the response headers. - */ - MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1, - - /** - * 10: We have prepared the response headers in the writ buffer. - * Send the response headers. - */ - MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1, - - /** - * 11: We have sent the response headers. Get ready to send the body. - */ - MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1, - - /** - * 12: We are ready to send a part of a non-chunked body. Send it. - */ - MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1, - - /** - * 13: We are waiting for the client to provide more - * data of a non-chunked body. - */ - MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1, - - /** - * 14: We are ready to send a chunk. - */ - MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1, - - /** - * 15: We are waiting for the client to provide a chunk of the body. - */ - MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1, - - /** - * 16: We have sent the response body. Prepare the footers. - */ - MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1, - - /** - * 17: We have prepared the response footer. Send it. - */ - MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1, - - /** - * 18: We have sent the response footer. Shutdown or restart. - */ - MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1, - - /** - * 19: This connection is closed (no more activity - * allowed). - */ - MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1, - -}; + { + /** + * Connection just started (no headers received). + * Waiting for the line with the request type, URL and version. + */ + MHD_CONNECTION_INIT = 0, + + /** + * 1: We got the URL (and request type and version). Wait for a header line. + */ + MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1, + + /** + * 2: We got part of a multi-line request header. Wait for the rest. + */ + MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1, + + /** + * 3: We got the request headers. Process them. + */ + MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1, + + /** + * 4: We have processed the request headers. Send 100 continue. + */ + MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1, + + /** + * 5: We have processed the headers and need to send 100 CONTINUE. + */ + MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1, + + /** + * 6: We have sent 100 CONTINUE (or do not need to). Read the message body. + */ + MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1, + + /** + * 7: We got the request body. Wait for a line of the footer. + */ + MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1, + + /** + * 8: We got part of a line of the footer. Wait for the + * rest. + */ + MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1, + + /** + * 9: We received the entire footer. Wait for a response to be queued + * and prepare the response headers. + */ + MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1, + + /** + * 10: We have prepared the response headers in the writ buffer. + * Send the response headers. + */ + MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1, + + /** + * 11: We have sent the response headers. Get ready to send the body. + */ + MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1, + + /** + * 12: We are ready to send a part of a non-chunked body. Send it. + */ + MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1, + + /** + * 13: We are waiting for the client to provide more + * data of a non-chunked body. + */ + MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1, + + /** + * 14: We are ready to send a chunk. + */ + MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1, + + /** + * 15: We are waiting for the client to provide a chunk of the body. + */ + MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1, + + /** + * 16: We have sent the response body. Prepare the footers. + */ + MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1, + + /** + * 17: We have prepared the response footer. Send it. + */ + MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1, + + /** + * 18: We have sent the response footer. Shutdown or restart. + */ + MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1, + + /** + * 19: This connection is closed (no more activity + * allowed). + */ + MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1, + + }; + +enum MHDS_CONNECTION_STATE + { + MHDS_CONNECTION_INIT = 0, + + /** + * 1: We got the URL (and request type and version). Wait for a header line. + */ + MHDS_HANDSHAKE_COMPLETE = MHDS_CONNECTION_INIT + 1, + + MHDS_CONNECTION_CONTINUE_SENDING = MHDS_HANDSHAKE_COMPLETE + 1, + + MHDS_CONNECTION_CLOSED = MHDS_CONNECTION_CONTINUE_SENDING + 1 + }; struct MHD_Connection -{ - - /** - * This is a linked list. - */ - struct MHD_Connection *next; - - /** - * Reference to the MHD_Daemon struct. - */ - struct MHD_Daemon *daemon; - - /** - * Linked list of parsed headers. - */ - struct MHD_HTTP_Header *headers_received; - - /** - * Response to transmit (initially NULL). - */ - struct MHD_Response *response; - - /** - * The memory pool is created whenever we first read - * from the TCP stream and destroyed at the end of - * each request (and re-created for the next request). - * In the meantime, this pointer is NULL. The - * pool is used for all connection-related data - * except for the response (which maybe shared between - * connections) and the IP address (which persists - * across individual requests). - */ - struct MemoryPool *pool; - - /** - * We allow the main application to associate some - * pointer with the connection. Here is where we - * store it. (MHD does not know or care what it - * is). - */ - void *client_context; - - /** - * Request method. Should be GET/POST/etc. Allocated - * in pool. - */ - char *method; - - /** - * Requested URL (everything after "GET" only). Allocated - * in pool. - */ - char *url; - - /** - * HTTP version string (i.e. http/1.1). Allocated - * in pool. - */ - char *version; - - /** - * Buffer for reading requests. Allocated - * in pool. Actually one byte larger than - * read_buffer_size (if non-NULL) to allow for - * 0-termination. - */ - char *read_buffer; - - /** - * Buffer for writing response (headers only). Allocated - * in pool. - */ - char *write_buffer; - - /** - * Last incomplete header line during parsing of headers. - * Allocated in pool. Only valid if state is - * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. - */ - char *last; - - /** - * Position after the colon on the last incomplete header - * line during parsing of headers. - * Allocated in pool. Only valid if state is - * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. - */ - char *colon; - - /** - * Foreign address (of length addr_len). MALLOCED (not - * in pool!). - */ - struct sockaddr_in *addr; - - /** - * Thread for this connection (if we are using - * one thread per connection). - */ - pthread_t pid; - - /** - * Size of read_buffer (in bytes). This value indicates - * how many bytes we're willing to read into the buffer; - * the real buffer is one byte longer to allow for - * adding zero-termination (when needed). - */ - size_t read_buffer_size; - - /** - * Position where we currently append data in - * read_buffer (last valid position). - */ - size_t read_buffer_offset; - - /** - * Size of write_buffer (in bytes). - */ - size_t write_buffer_size; - - /** - * Offset where we are with sending from write_buffer. - */ - size_t write_buffer_send_offset; - - /** - * Last valid location in write_buffer (where do we - * append and up to where is it safe to send?) - */ - size_t write_buffer_append_offset; - - /** - * How many more bytes of the body do we expect - * to read? "-1" for unknown. - */ - size_t remaining_upload_size; - - /** - * Current write position in the actual response - * (excluding headers, content only; should be 0 - * while sending headers). - */ - size_t response_write_position; - - /** - * Position in the 100 CONTINUE message that - * we need to send when receiving http 1.1 requests. - */ - size_t continue_message_write_offset; - - /** - * Length of the foreign address. - */ - socklen_t addr_len; - - /** - * Last time this connection had any activity - * (reading or writing). - */ - time_t last_activity; - - /** - * Socket for this connection. Set to -1 if - * this connection has died (daemon should clean - * up in that case). - */ - int socket_fd; - - /** - * Has this socket been closed for reading (i.e. - * other side closed the connection)? If so, - * we must completely close the connection once - * we are done sending our response (and stop - * trying to read from this socket). - */ - int read_closed; - - /** - * State in the FSM for this connection. - */ - enum MHD_CONNECTION_STATE state; - - /** - * HTTP response code. Only valid if response object - * is already set. - */ - unsigned int responseCode; - - /** - * Set to MHD_YES if the response's content reader - * callback failed to provide data the last time - * we tried to read from it. In that case, the - * write socket should be marked as unready until - * the CRC call succeeds. - */ - int response_unready; - - /** - * Are we sending with chunked encoding? - */ - int have_chunked_response; - - /** - * Are we receiving with chunked encoding? This will be set to - * MHD_YES after we parse the headers and are processing the body - * with chunks. After we are done with the body and we are - * processing the footers; once the footers are also done, this will - * be set to MHD_NO again (before the final call to the handler). - */ - int have_chunked_upload; - - /** - * If we are receiving with chunked encoding, where are we right - * now? Set to 0 if we are waiting to receive the chunk size; - * otherwise, this is the size of the current chunk. A value of - * zero is also used when we're at the end of the chunks. - */ - unsigned int current_chunk_size; - - /** - * If we are receiving with chunked encoding, where are we currently - * with respect to the current chunk (at what offset / position)? - */ - unsigned int current_chunk_offset; - -}; - - + { + + /** + * This is a linked list. + */ + struct MHD_Connection *next; + + /** + * Reference to the MHD_Daemon struct. + */ + struct MHD_Daemon *daemon; + + /** + * Linked list of parsed headers. + */ + struct MHD_HTTP_Header *headers_received; + + /** + * Response to transmit (initially NULL). + */ + struct MHD_Response *response; + + /** + * The memory pool is created whenever we first read + * from the TCP stream and destroyed at the end of + * each request (and re-created for the next request). + * In the meantime, this pointer is NULL. The + * pool is used for all connection-related data + * except for the response (which maybe shared between + * connections) and the IP address (which persists + * across individual requests). + */ + struct MemoryPool *pool; + + /** + * We allow the main application to associate some + * pointer with the connection. Here is where we + * store it. (MHD does not know or care what it + * is). + */ + void *client_context; + + /** + * Request method. Should be GET/POST/etc. Allocated + * in pool. + */ + char *method; + + /** + * Requested URL (everything after "GET" only). Allocated + * in pool. + */ + char *url; + + /** + * HTTP version string (i.e. http/1.1). Allocated + * in pool. + */ + char *version; + + /** + * Buffer for reading requests. Allocated + * in pool. Actually one byte larger than + * read_buffer_size (if non-NULL) to allow for + * 0-termination. + */ + char *read_buffer; + + /** + * Buffer for writing response (headers only). Allocated + * in pool. + */ + char *write_buffer; + + /** + * Last incomplete header line during parsing of headers. + * Allocated in pool. Only valid if state is + * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. + */ + char *last; + + /** + * Position after the colon on the last incomplete header + * line during parsing of headers. + * Allocated in pool. Only valid if state is + * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. + */ + char *colon; + + /** + * Foreign address (of length addr_len). MALLOCED (not + * in pool!). + */ + struct sockaddr_in *addr; + + /** + * Thread for this connection (if we are using + * one thread per connection). + */ + pthread_t pid; + + /** + * Size of read_buffer (in bytes). This value indicates + * how many bytes we're willing to read into the buffer; + * the real buffer is one byte longer to allow for + * adding zero-termination (when needed). + */ + size_t read_buffer_size; + + /** + * Position where we currently append data in + * read_buffer (last valid position). + */ + size_t read_buffer_offset; + + /** + * Size of write_buffer (in bytes). + */ + size_t write_buffer_size; + + /** + * Offset where we are with sending from write_buffer. + */ + size_t write_buffer_send_offset; + + /** + * Last valid location in write_buffer (where do we + * append and up to where is it safe to send?) + */ + size_t write_buffer_append_offset; + + /** + * How many more bytes of the body do we expect + * to read? "-1" for unknown. + */ + size_t remaining_upload_size; + + /** + * Current write position in the actual response + * (excluding headers, content only; should be 0 + * while sending headers). + */ + size_t response_write_position; + + /** + * Position in the 100 CONTINUE message that + * we need to send when receiving http 1.1 requests. + */ + size_t continue_message_write_offset; + + /** + * Length of the foreign address. + */ + socklen_t addr_len; + + /** + * Last time this connection had any activity + * (reading or writing). + */ + time_t last_activity; + + /** + * Socket for this connection. Set to -1 if + * this connection has died (daemon should clean + * up in that case). + */ + int socket_fd; + + /** + * Has this socket been closed for reading (i.e. + * other side closed the connection)? If so, + * we must completely close the connection once + * we are done sending our response (and stop + * trying to read from this socket). + */ + int read_closed; + + /** + * State in the FSM for this connection. + */ + enum MHD_CONNECTION_STATE state; + + /** + * HTTP response code. Only valid if response object + * is already set. + */ + unsigned int responseCode; + + /** + * Set to MHD_YES if the response's content reader + * callback failed to provide data the last time + * we tried to read from it. In that case, the + * write socket should be marked as unready until + * the CRC call succeeds. + */ + int response_unready; + + /** + * Are we sending with chunked encoding? + */ + int have_chunked_response; + + /** + * Are we receiving with chunked encoding? This will be set to + * MHD_YES after we parse the headers and are processing the body + * with chunks. After we are done with the body and we are + * processing the footers; once the footers are also done, this will + * be set to MHD_NO again (before the final call to the handler). + */ + int have_chunked_upload; + + /** + * If we are receiving with chunked encoding, where are we right + * now? Set to 0 if we are waiting to receive the chunk size; + * otherwise, this is the size of the current chunk. A value of + * zero is also used when we're at the end of the chunks. + */ + unsigned int current_chunk_size; + + /** + * If we are receiving with chunked encoding, where are we currently + * with respect to the current chunk (at what offset / position)? + */ + unsigned int current_chunk_offset; + + }; + +typedef struct MHD_Connection MHD_Connection_t; struct MHD_Daemon -{ - - /** - * Callback function for all requests. - */ - MHD_AccessHandlerCallback default_handler; - - /** - * Closure argument to default_handler. - */ - void *default_handler_cls; - - /** - * Linked list of our current connections. - */ - struct MHD_Connection *connections; - - MHD_AcceptPolicyCallback apc; - - void *apc_cls; - - MHD_RequestCompletedCallback notify_completed; - - void *notify_completed_cls; - - /** - * PID of the select thread (if we have internal select) - */ - pthread_t pid; - - /** - * Listen socket. - */ - int socket_fd; - - /** - * Are we shutting down? - */ - int shutdown; - - /** - * Size of the per-connection memory pools. - */ - unsigned int pool_size; - - /** - * Limit on the number of parallel connections. - */ - unsigned int max_connections; - - /** - * After how many seconds of inactivity should - * connections time out? Zero for no timeout. - */ - unsigned int connection_timeout; - - /** - * Maximum number of connections per IP, or 0 for - * unlimited. - */ - unsigned int per_ip_connection_limit; - - /** - * Daemon's options. - */ - enum MHD_OPTION options; - - /** - * Listen port. - */ - unsigned short port; - -}; - + { + + /** + * Callback function for all requests. + */ + MHD_AccessHandlerCallback default_handler; + + /** + * Closure argument to default_handler. + */ + void *default_handler_cls; + + /** + * Linked list of our current connections. + */ + struct MHD_Connection *connections; + + /** + * Linked list of our current connections. + */ + // TODO switch to a dedicated tls connection struct + struct MHD_Connection *tls_connections; + + MHD_AcceptPolicyCallback apc; + + void *apc_cls; + + MHD_RequestCompletedCallback notify_completed; + + void *notify_completed_cls; + + /** + * PID of the select thread (if we have internal select) + */ + pthread_t pid; + + /** + * Listen socket. + */ + int socket_fd; + + /** + * Are we shutting down? + */ + int shutdown; + + /** + * Size of the per-connection memory pools. + */ + unsigned int pool_size; + + /** + * Limit on the number of parallel connections. + */ + unsigned int max_connections; + + /** + * After how many seconds of inactivity should + * connections time out? Zero for no timeout. + */ + unsigned int connection_timeout; + + /** + * Maximum number of connections per IP, or 0 for + * unlimited. + */ + unsigned int per_ip_connection_limit; + + /** + * Daemon's options. + */ + enum MHD_OPTION options; + + /** + * Listen port. + */ + unsigned short port; + + }; #endif diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am @@ -1,10 +1,16 @@ SUBDIRS = . -INCLUDES = -I$(top_srcdir)/src/include +AM_CPPFLAGS = -I$(top_srcdir)/src/include # example programs -noinst_PROGRAMS = minimal_example querystring_example fileserver_example fileserver_example_external_select +noinst_PROGRAMS = \ +https_server_example \ +https_echo_client_example \ +minimal_example \ +querystring_example \ +fileserver_example \ +fileserver_example_external_select minimal_example_SOURCES = \ minimal_example.c @@ -21,9 +27,29 @@ fileserver_example_SOURCES = \ fileserver_example_LDADD = \ $(top_builddir)/src/daemon/libmicrohttpd.la - fileserver_example_external_select_SOURCES = \ fileserver_example_external_select.c fileserver_example_external_select_LDADD = \ $(top_builddir)/src/daemon/libmicrohttpd.la +https_server_example_CPPFLAGS = \ + $(GNUTLS_CPPFLAGS) \ + -I$(top_srcdir)/src/daemon +https_server_example_SOURCES = \ + https_server_example.c +https_server_example_LDADD = \ + $(top_builddir)/src/daemon/libmicrohttpd.la +https_server_example_LDFLAGS = \ + -L$(GNUTLS_LIB_PATH) \ + -lgnutls + +https_echo_client_example_CPPFLAGS = \ + $(GNUTLS_CPPFLAGS) \ + -I$(top_srcdir)/src/daemon +https_echo_client_example_SOURCES = \ + https_echo_client_example.c +https_echo_client_example_LDADD = \ + $(top_builddir)/src/daemon/libmicrohttpd.la +https_echo_client_example_LDFLAGS = \ + -L$(GNUTLS_LIB_PATH) \ + -lgnutls diff --git a/src/examples/https_echo_client_example.c b/src/examples/https_echo_client_example.c @@ -0,0 +1,153 @@ +/* + This file is part of libmicrohttpd + (C) 2007 Christian Grothoff + + libmicrohttpd 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 2, or (at your + option) any later version. + + libmicrohttpd 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 libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file https_echo_client.c + * @brief a simple echo client to use in conjuction with the echo TLS server. + * @author LV-426 + */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <gnutls/gnutls.h> + +#define MAX_BUF 1024 +#define SA struct sockaddr +#define MSG "GET / HTTP/1.0\r\n\r\n" + +extern int tcp_connect (void); +extern void tcp_close (int sd); + +int +main (int argc, char **argv) +{ + int ret, sd, ii, err; + gnutls_session_t session; + char buffer[MAX_BUF + 1]; + gnutls_anon_client_credentials_t anoncred; + + struct sockaddr_in servaddr4; + const struct sockaddr *servaddr; + struct sockaddr_in sa; + socklen_t addrlen; + + if (argc < 2) + { + printf ("Usage : %s SERVER-PORT\n", argv[0]); + return 1; + } + + gnutls_global_init (); + + gnutls_anon_allocate_client_credentials (&anoncred); + + /* Initialize TLS session */ + gnutls_init (&session, GNUTLS_CLIENT); + + /* Use default priorities */ + gnutls_priority_set_direct (session, "PERFORMANCE:+ANON-DH:!ARCFOUR-128", + NULL); + + /* put the anonymous credentials to the current session */ + gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred); + + sd = socket (AF_INET, SOCK_STREAM, 0); + memset (&sa, '\0', sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (atoi (argv[1])); + inet_pton (AF_INET, "127.0.0.1", &sa.sin_addr); + + /* connect to the peer */ + err = connect (sd, (struct sockaddr *) &sa, sizeof (sa)); + if (err < 0) + { + fprintf (stderr, "Connect error\n"); + exit (1); + } + + gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd); + + /* Perform the TLS handshake */ + ret = gnutls_handshake (session); + + if (ret < 0) + { + fprintf (stderr, "*** Handshake failed\n"); + gnutls_perror (ret); + goto end; + } + else + { + printf ("- Handshake was completed\n"); + } + + for (;;) + { + /**/ scanf ("%s", buffer); + + if (strcmp (buffer, "exit") == 0) + { + gnutls_record_send (session, buffer, strlen (MSG)); + break; + } + gnutls_record_send (session, buffer, strlen (MSG)); + + ret = gnutls_record_recv (session, buffer, MAX_BUF); + if (ret == 0) + { + printf ("- Peer has closed the TLS connection\n"); + goto end; + } + else if (ret < 0) + { + fprintf (stderr, "*** Error: %s\n", gnutls_strerror (ret)); + break; + } + + printf ("- Received %d bytes: ", ret); + for (ii = 0; ii < ret; ii++) + { + fputc (buffer[ii], stdout); + } + fputs ("\n", stdout); + } + +end: + + shutdown (sd, SHUT_RDWR); + close (sd); + + gnutls_deinit (session); + + gnutls_anon_free_client_credentials (anoncred); + + gnutls_global_deinit (); + + return 0; +} diff --git a/src/examples/https_server_example.c b/src/examples/https_server_example.c @@ -0,0 +1,182 @@ +/* + This file is part of libmicrohttpd + (C) 2007 Christian Grothoff + + libmicrohttpd 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 2, or (at your + option) any later version. + + libmicrohttpd 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 libmicrohttpd; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ + +/** + * @file https_server_example.c + * @brief a simple echo server using TLS. echo input from client until 'exit' message is received. + * @author LV-426 + */ + +#include "config.h" +#include <microhttpd.h> +#include "internal.h" + +#include <stdlib.h> +#ifndef MINGW +#include <unistd.h> +#endif +#include <string.h> +#include <stdio.h> +#include <gnutls/gnutls.h> + +#define DH_BITS 1024 +#define MAX_BUF 1024 +/* server credintials */ +gnutls_anon_server_credentials_t anoncred; + +/* server Diffie-Hellman parameters */ +static gnutls_dh_params_t dh_params; + + +/* Generate Diffie Hellman parameters - for use with DHE kx algorithms. */ +static int +generate_dh_params (void) +{ + + gnutls_dh_params_init (&dh_params); + gnutls_dh_params_generate2 (dh_params, DH_BITS); + return 0; +} + +gnutls_session_t +initialize_tls_session (void) +{ + gnutls_session_t session; + + gnutls_init (&session, GNUTLS_SERVER); + + gnutls_priority_set_direct (session, "NORMAL:+ANON-DH", NULL); + + gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred); + + gnutls_dh_set_prime_bits (session, DH_BITS); + + return session; +} + +/* Accept Policy Callback */ +static int +TLS_echo (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *upload_data, + const char *version, unsigned int *upload_data_size, void **ptr) +{ + gnutls_session_t session; + static int aptr; + struct MHD_Response *response; + char buffer[MAX_BUF + 1]; + int ret; + + printf ("accepted connection from %d\n", connection->addr->sin_addr); + + session = initialize_tls_session (); + + gnutls_transport_set_ptr (session, connection->socket_fd); + + ret = gnutls_handshake (session); + if (ret < 0) + { + /* set connection as closed */ + connection->socket_fd = 1; + gnutls_deinit (session); + fprintf (stderr, "*** Handshake has failed (%s)\n\n", + gnutls_strerror (ret)); + return MHD_NO; + } + + printf ("TLS Handshake completed\n"); + connection->state = MHDS_HANDSHAKE_COMPLETE; + + /* simple echo loop. message encryption/decryption is acheived through 'gnutls_record_send' + * & gnutls_record_recv calls. */ + for (;;) + { + memset (buffer, 0, MAX_BUF + 1); + ret = gnutls_record_recv (session, buffer, MAX_BUF); + + if (ret < 0) + { + fprintf (stderr, "\n*** Received corrupted " + "data(%d). Closing the connection.\n\n", ret); + break; + } + else if (ret >= 0) + { + if (strcmp (buffer, "exit") == 0) + { + printf ("\n- Peer has closed the GNUTLS connection\n"); + break; + } + else + { + /* echo data back to the client */ + gnutls_record_send (session, buffer, strlen (buffer)); + } + } + } + printf ("\n"); + + /* mark connection as closed */ + connection->socket_fd = -1; + + gnutls_deinit (session); + + return ret; +} + +int +main (int argc, char *const *argv) +{ + struct MHD_Daemon *daemon; + struct MHD_Daemon *TLS_daemon; + + /* look for HTTPS port argument */ + if (argc < 4) + { + printf ("Usage : %s HTTP-PORT SECONDS-TO-RUN HTTPS-PORT\n", argv[0]); + return 1; + } + + gnutls_global_init (); + + gnutls_anon_allocate_server_credentials (&anoncred); + + generate_dh_params (); + + gnutls_anon_set_server_dh_params (anoncred, dh_params); + + TLS_daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION + | MHD_USE_DEBUG | MHD_USE_SSL, + atoi (argv[3]), NULL, NULL, &TLS_echo, NULL, + MHD_OPTION_END); + + if (TLS_daemon == NULL) + return 1; + sleep (atoi (argv[2])); + + MHD_stop_daemon (daemon); + + gnutls_anon_free_server_credentials (anoncred); + + gnutls_global_deinit (); + return 0; +}