* configure.ac support for --with-gnutls=PFX
* TLS connection handler in daemon.c
* simple TLS echo client server example
This commit is contained in:
lv-426
2008-05-24 13:59:21 +00:00
parent a0cc83b7c2
commit c51d65ffce
6 changed files with 1433 additions and 993 deletions
+31
View File
@@ -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)
+606 -576
View File
@@ -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;
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);
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;
}
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;
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 (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));
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))
{
s = ACCEPT (daemon->socket_fd, addr, &addrlen);
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;
}
}
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 ((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)))
{
#if HAVE_MESSAGES
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);
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));
#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;
}
/**
* 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;
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;
}
}
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;
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;
}
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;
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));
#endif
timeout.tv_sec = 0;
timeout.tv_usec = 0;
if (daemon == NULL)
abort();
if (daemon->shutdown == MHD_YES)
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;
}
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));
#endif
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;
}
/**
* 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;
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;
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)
{
#if HAVE_MESSAGES
if ((options & MHD_USE_DEBUG) != 0)
fprintf (stderr, "Call to socket failed: %s\n", STRERROR (errno));
#endif
/* 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 ((SETSOCKOPT (socket_fd,
SOL_SOCKET,
SO_REUSEADDR,
&on, sizeof (on)) < 0) && (options & MHD_USE_DEBUG) != 0)
{
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
fprintf (stderr, "setsockopt failed: %s\n", STRERROR (errno));
if ((options & MHD_USE_DEBUG) != 0)
fprintf(stderr, "Call to socket 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)
{
return NULL;
}
if ((SETSOCKOPT (socket_fd,
SOL_SOCKET,
SO_REUSEADDR,
&on, sizeof (on)) < 0) && (options & MHD_USE_DEBUG) != 0)
{
#if HAVE_MESSAGES
if ((options & MHD_USE_DEBUG) != 0)
fprintf (stderr,
"Failed to bind to port %u: %s\n", port, STRERROR (errno));
fprintf(stderr, "setsockopt failed: %s\n", STRERROR (errno));
#endif
CLOSE (socket_fd);
return NULL;
}
if (LISTEN (socket_fd, 20) < 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 listen for connections: %s\n", 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;
}
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;
}
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));
#endif
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;
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 (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__ ((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__ ((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 */
+432 -414
View File
@@ -1,21 +1,21 @@
/*
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 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* Set to -1 if size is not known.
*/
size_t total_size;
/**
* Size of data.
*/
size_t data_size;
/**
* Size of data.
*/
size_t data_size;
/**
* Size of the data buffer.
*/
size_t data_buffer_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;
/**
* 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,
{
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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,
/**
* 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;
/**
* This is a linked list.
*/
struct MHD_Connection *next;
/**
* Reference to the MHD_Daemon struct.
*/
struct MHD_Daemon *daemon;
/**
* Reference to the MHD_Daemon struct.
*/
struct MHD_Daemon *daemon;
/**
* Linked list of parsed headers.
*/
struct MHD_HTTP_Header *headers_received;
/**
* Linked list of parsed headers.
*/
struct MHD_HTTP_Header *headers_received;
/**
* Response to transmit (initially NULL).
*/
struct MHD_Response *response;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* Request method. Should be GET/POST/etc. Allocated
* in pool.
*/
char *method;
/**
* Requested URL (everything after "GET" only). Allocated
* in pool.
*/
char *url;
/**
* Requested URL (everything after "GET" only). Allocated
* in pool.
*/
char *url;
/**
* HTTP version string (i.e. http/1.1). Allocated
* in pool.
*/
char *version;
/**
* 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 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* Length of the foreign address.
*/
socklen_t addr_len;
/**
* Last time this connection had any activity
* (reading or writing).
*/
time_t last_activity;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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 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;
/**
* 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 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;
};
/**
* 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;
/**
* Callback function for all requests.
*/
MHD_AccessHandlerCallback default_handler;
/**
* Closure argument to default_handler.
*/
void *default_handler_cls;
/**
* 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.
*/
struct MHD_Connection *connections;
MHD_AcceptPolicyCallback apc;
/**
* Linked list of our current connections.
*/
// TODO switch to a dedicated tls connection struct
struct MHD_Connection *tls_connections;
void *apc_cls;
MHD_AcceptPolicyCallback apc;
MHD_RequestCompletedCallback notify_completed;
void *apc_cls;
void *notify_completed_cls;
MHD_RequestCompletedCallback notify_completed;
/**
* PID of the select thread (if we have internal select)
*/
pthread_t pid;
void *notify_completed_cls;
/**
* Listen socket.
*/
int socket_fd;
/**
* PID of the select thread (if we have internal select)
*/
pthread_t pid;
/**
* Are we shutting down?
*/
int shutdown;
/**
* Listen socket.
*/
int socket_fd;
/**
* Size of the per-connection memory pools.
*/
unsigned int pool_size;
/**
* Are we shutting down?
*/
int shutdown;
/**
* Limit on the number of parallel connections.
*/
unsigned int max_connections;
/**
* Size of the per-connection memory pools.
*/
unsigned int pool_size;
/**
* After how many seconds of inactivity should
* connections time out? Zero for no timeout.
*/
unsigned int connection_timeout;
/**
* Limit on the number of parallel connections.
*/
unsigned int max_connections;
/**
* Maximum number of connections per IP, or 0 for
* unlimited.
*/
unsigned int per_ip_connection_limit;
/**
* After how many seconds of inactivity should
* connections time out? Zero for no timeout.
*/
unsigned int connection_timeout;
/**
* Daemon's options.
*/
enum MHD_OPTION options;
/**
* Maximum number of connections per IP, or 0 for
* unlimited.
*/
unsigned int per_ip_connection_limit;
/**
* Listen port.
*/
unsigned short port;
/**
* Daemon's options.
*/
enum MHD_OPTION options;
};
/**
* Listen port.
*/
unsigned short port;
};
#endif
+29 -3
View File
@@ -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
+153
View File
@@ -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;
}
+182
View File
@@ -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;
}