http_server.c
/*
* libsyncml - A syncml protocol implementation
* Copyright (C) 2005 Armin Bauer <armin.bauer@opensync.org>
* Copyright (C) 2007-2009 Michael Bell <michael.bell@opensync.org>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <glib.h>
#include <libsyncml/syncml.h>
#include <libsyncml/syncml_internals.h>
#include <libsyncml/sml_error_internals.h>
#include <libsyncml/sml_transport_internals.h>
#include "http_server_internals.h"
#ifdef HAVE_LIBSOUP22
static void smlTransportHttpServerCallback(
SoupServerContext *context,
SoupMessage *msg,
gpointer data)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, context, msg, data);
SmlMimeType mimetype = SML_MIMETYPE_UNKNOWN;
smlAssert(data);
SmlTransportHttpServerEnv *env = data;
SmlLinkHttpServerEnv *linkenv = NULL;
SmlLink *link_ = NULL;
SmlError *error = NULL;
char *path = soup_uri_to_string (soup_message_get_uri(msg), TRUE);
smlTrace(TRACE_INTERNAL, "%s: %s %s HTTP/1.%d",
__func__, VA_STRING(msg->method), VA_STRING(path), soup_message_get_http_version(msg));
if (g_strcasecmp(path, env->url)) {
/* check that this is a registered session */
smlSafeCFree(&path);
path = soup_uri_to_string (soup_message_get_uri(msg), FALSE);
link_ = g_hash_table_lookup(env->uriToLink, path);
if (!link_)
{
/* The URL was changed and there is no
* key/value pair for this URL available.
*
* SECURITY: This is a potential attack.
* FALSE POSITIVE: Session with a timeout.
*/
smlErrorSet(&error, SML_ERROR_INTERNAL_FILE_NOT_FOUND,
"Not Found (%s).", path);
soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
smlSafeCFree(&path);
goto error;
}
smlLinkRef(link_);
}
smlSafeCFree(&path);
if (soup_message_get_http_version(msg) != 1) {
smlErrorSet(&error, SML_ERROR_NOT_IMPLEMENTED, "Wrong http version");
soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
goto error;
}
if (soup_method_get_id(msg->method) != SOUP_METHOD_ID_POST) {
smlErrorSet(&error, SML_ERROR_NOT_IMPLEMENTED, "Wrong method");
soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
goto error;
}
const char *header = soup_message_get_header(msg->request_headers, "Content-Type");
if (header && !g_strncasecmp(header, SML_ELEMENT_XML, strlen(SML_ELEMENT_XML)))
mimetype = SML_MIMETYPE_XML;
else if(header && !g_strncasecmp(header, SML_ELEMENT_WBXML, strlen(SML_ELEMENT_WBXML)))
mimetype = SML_MIMETYPE_WBXML;
else if(header && !g_strncasecmp(header, SML_ELEMENT_SAN, strlen(SML_ELEMENT_SAN)))
mimetype = SML_MIMETYPE_SAN;
else if (header) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mimetype");
soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
goto error;
} else {
smlErrorSet(&error, SML_ERROR_GENERIC, "Faulty mimetype");
soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
goto error;
}
/* prepare link and linkenv */
if (!link_)
{
/* This is a potentially new session.
* It is not possible to check this here.
* smlTransportGetResponseURI must be used
* to verify this.
*/
linkenv = smlTryMalloc0(sizeof(SmlLinkHttpServerEnv), &error);
if (!linkenv)
goto error;
linkenv->env = env;
link_ = smlLinkNew(env->tsp, linkenv, &error);
if (!link_)
goto error;
linkenv->msg = msg;
/* the link must be cached for disconnect signaling */
linkenv->link = link_;
smlLinkRef(linkenv->link);
/* We think positive here and signal connect.
* the link is now complete, so send the connect event
*/
smlTransportReceiveEvent(env->tsp, link_, SML_TRANSPORT_EVENT_CONNECT_DONE, NULL, NULL);
} else {
linkenv = link_->link_data;
if (linkenv->msg)
smlTrace(TRACE_INTERNAL,
"%s: WARNING: ResponseURI should not be in use - test mode.",
__func__);
linkenv->msg = msg;
}
smlTrace(TRACE_INTERNAL, "%s: The message length is %i.", __func__, msg->request.length);
/* We need one byte more than the request length
* because the data can be a native XML message.
* If the data is a native XML message then it is
* sometimes directly used as string.
*
* The string is automatically NULL terminated
* because smlTryMalloc0 fills the new memory with NULLs.
*/
char *body = smlTryMalloc0(msg->request.length + 1, &error);
if (!body)
goto error;
memcpy(body, msg->request.body, msg->request.length);
SmlTransportData *tspdata = smlTransportDataNew(body, msg->request.length, mimetype, TRUE, &error);
body = NULL;
if (!tspdata)
goto error_unref_msg;
smlTransportReceiveEvent(env->tsp, link_, SML_TRANSPORT_EVENT_DATA, tspdata, NULL);
smlLinkDeref(link_);
smlTransportDataDeref(tspdata);
soup_message_io_pause(msg);
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error_unref_msg:
link_->link_data = NULL;
smlLinkDeref(link_);
linkenv->msg = NULL;
smlSafeFree((gpointer *) &linkenv);
error:
soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (msg), SOUP_TRANSFER_CONTENT_LENGTH);
soup_message_io_unpause(msg);
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
}
#else /* !HAVE_LIBSOUP22 == libsoup 2.4 */
static void smlTransportHttpServerCallback(
SoupServer *server,
SoupMessage *msg,
const char *path,
GHashTable *query,
SoupClientContext *client,
gpointer data)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %s, %p, %p, %p)", __func__, server, msg, VA_STRING(path), query, client, data);
SmlMimeType mimetype = SML_MIMETYPE_UNKNOWN;
smlAssert(data);
SmlTransportHttpServerEnv *env = data;
SmlLinkHttpServerEnv *linkenv = NULL;
SmlLink *link_ = NULL;
SmlError *error = NULL;
smlTrace(TRACE_INTERNAL, "%s: %s %s HTTP/1.%d",
__func__, VA_STRING(msg->method), VA_STRING(path), soup_message_get_http_version(msg));
if (msg->method != SOUP_METHOD_POST) {
smlErrorSet(&error, SML_ERROR_NOT_IMPLEMENTED, "Wrong method");
soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
goto error;
}
if (g_strcasecmp(path, env->url)) {
/* check that this is a registered session */
char *fullpath = soup_uri_to_string (soup_message_get_uri(msg), FALSE);
link_ = g_hash_table_lookup(env->uriToLink, fullpath);
if (!link_)
{
/* The URL was changed and there is no
* key/value pair for this URL available.
*
* SECURITY: This is a potential attack.
* FALSE POSITIVE: Session with a timeout.
*/
smlErrorSet(&error, SML_ERROR_INTERNAL_FILE_NOT_FOUND,
"Not Found (%s).", fullpath);
soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
smlSafeCFree(&fullpath);
goto error;
}
smlLinkRef(link_);
smlSafeCFree(&fullpath);
}
const char *header = soup_message_headers_get(msg->request_headers, "Content-Type");
if (header && !g_strncasecmp(header, SML_ELEMENT_XML, strlen(SML_ELEMENT_XML)))
mimetype = SML_MIMETYPE_XML;
else if(header && !g_strncasecmp(header, SML_ELEMENT_WBXML, strlen(SML_ELEMENT_WBXML)))
mimetype = SML_MIMETYPE_WBXML;
else if(header && !g_strncasecmp(header, SML_ELEMENT_SAN, strlen(SML_ELEMENT_SAN)))
mimetype = SML_MIMETYPE_SAN;
else if (header) {
smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown mimetype");
soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
goto error;
} else {
smlErrorSet(&error, SML_ERROR_GENERIC, "Faulty mimetype");
soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
goto error;
}
/* prepare link and linkenv */
if (!link_)
{
/* This is a potentially new session.
* It is not possible to check this here.
* smlTransportGetResponseURI must be used
* to verify this.
*/
linkenv = smlTryMalloc0(sizeof(SmlLinkHttpServerEnv), &error);
if (!linkenv)
goto error;
linkenv->env = env;
link_ = smlLinkNew(env->tsp, linkenv, &error);
if (!link_)
goto error;
linkenv->msg = msg;
/* the link must be cached for disconnect signaling */
linkenv->link = link_;
smlLinkRef(linkenv->link);
/* We think positive here and signal connect.
* the link is now complete, so send the connect event
*/
smlTransportReceiveEvent(env->tsp, link_, SML_TRANSPORT_EVENT_CONNECT_DONE, NULL, NULL);
} else {
linkenv = link_->link_data;
if (linkenv->msg)
smlTrace(TRACE_INTERNAL,
"%s: WARNING: ResponseURI should not be in use - test mode.",
__func__);
linkenv->msg = msg;
}
if (msg->request_body) {
smlTrace(TRACE_INTERNAL, "%s: The message length is %i.",
__func__, msg->request_body->length);
} else {
smlTrace(TRACE_INTERNAL, "%s: The message has no request body.", __func__);
}
/* We need one byte more than the request length
* because the data can be a native XML message.
* If the data is a native XML message then it is
* sometimes directly used as string.
*
* The string is automatically NULL terminated
* because smlTryMalloc0 fills the new memory with NULLs.
*/
char *body = smlTryMalloc0(msg->request_body->length + 1, &error);
if (!body)
goto error;
memcpy(body, msg->request_body->data, msg->request_body->length);
SmlTransportData *tspdata = smlTransportDataNew(body, msg->request_body->length, mimetype, TRUE, &error);
body = NULL;
if (!tspdata)
goto error_unref_msg;
smlTransportReceiveEvent(env->tsp, link_, SML_TRANSPORT_EVENT_DATA, tspdata, NULL);
smlLinkDeref(link_);
/* If we activate this function call then we get segfaults */
/* smlLinkDeref(link_); */
smlTransportDataDeref(tspdata);
soup_server_pause_message(server, msg);
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error_unref_msg:
smlLinkDeref(link_);
error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(&error));
smlErrorDeref(&error);
}
#endif /* HAVE_LIBSOUP22 */
static SmlBool smlTransportHttpServerSetConfigOption(
SmlTransport *tsp,
const char *name,
const char *value,
SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %s, %s, %p)", __func__, tsp, VA_STRING(name), VA_STRING(value), error);
CHECK_ERROR_REF
smlAssert(tsp);
smlAssert(tsp->transport_data);
SmlTransportHttpServerEnv *env = tsp->transport_data;
if (!strcmp(name, SML_TRANSPORT_CONFIG_PORT)) {
env->port = atoi(value);
if (!(env->port > 0 && env->port < 65535)) {
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "specified port was wrong");
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
smlTrace(TRACE_INTERNAL, "%s: Port %i detected", __func__, env->port);
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_URL)) {
env->url = g_strdup(value);
smlTrace(TRACE_INTERNAL, "%s: URL %s detected", __func__, VA_STRING(env->url));
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_SSL_KEY)) {
env->ssl_key = g_strdup(value);
smlTrace(TRACE_INTERNAL, "%s: SSL key %s detected", __func__, VA_STRING(env->ssl_key));
} else if (!strcmp(name, SML_TRANSPORT_CONFIG_SSL_SERVER_CERT)) {
env->ssl_cert = g_strdup(value);
smlTrace(TRACE_INTERNAL, "%s: SSL server certificate %s detected", __func__, VA_STRING(env->ssl_cert));
} else {
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "Unknown parameter %s found.", name);
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
static SmlBool smlTransportHttpServerInit(SmlTransport *tsp, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
CHECK_ERROR_REF
smlAssert(tsp);
smlAssert(tsp->context);
smlAssert(tsp->transport_data);
SmlTransportHttpServerEnv *env = tsp->transport_data;
if (!env->port)
{
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION,
"The HTTP server needs a port where it has to run.");
goto error_free_env;
}
if (!env->url)
{
/* configure a default location */
env->url = g_strdup("/");
}
smlTrace(TRACE_INTERNAL, "%s: config: port %i, url %s", __func__, env->port, VA_STRING(env->url));
smlTrace(TRACE_INTERNAL, "%s: http server uses context %p.", __func__, tsp->context);
if(!env->ssl_key || !env->ssl_cert) {
env->server = soup_server_new(
SOUP_SERVER_ASYNC_CONTEXT, tsp->context,
SOUP_SERVER_PORT, env->port,
NULL);
} else {
env->server = soup_server_new(
SOUP_SERVER_ASYNC_CONTEXT, tsp->context,
SOUP_SERVER_PORT, env->port,
SOUP_SERVER_SSL_CERT_FILE, env->ssl_cert,
SOUP_SERVER_SSL_KEY_FILE, env->ssl_key,
NULL);
}
if (!env->server) {
smlErrorSet(error, SML_ERROR_INTERNAL_MISCONFIGURATION, "Unable to spawn server");
goto error_free_env;
}
#ifdef HAVE_LIBSOUP22
soup_server_add_handler(env->server, NULL, NULL, smlTransportHttpServerCallback, NULL, env);
#else
soup_server_add_handler(env->server, NULL, smlTransportHttpServerCallback, env, NULL);
#endif
soup_server_run_async(env->server);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error_free_env:
if (env->url)
smlSafeCFree(&(env->url));
smlSafeFree((gpointer *)&env);
// error:
smlTrace(TRACE_EXIT_ERROR, "%s: %s", __func__, smlErrorPrint(error));
return FALSE;
}
static void smlTransportHttpServerDisconnect(void *data, void *linkdata)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, linkdata);
smlAssert(linkdata);
SmlTransportHttpServerEnv *env = data;
SmlLinkHttpServerEnv *linkenv = linkdata;
/* remove session from sessionToUri */
smlTrace(TRACE_INTERNAL, "%s: remove link from session/uri cache", __func__);
if (linkenv->session)
{
char *uri = g_hash_table_lookup(linkenv->env->sessionToUri, linkenv->session);
g_hash_table_remove(linkenv->env->sessionToUri, linkenv->session);
if (uri)
smlSafeCFree(&uri);
/* This is not a bug. We added two references to session
* because the session is used in two locations.
*/
smlSessionUnref(linkenv->session);
smlSessionUnref(linkenv->session);
linkenv->session = NULL;
}
/* remove link from uriToLink */
SmlLink *link_ = NULL;
smlTrace(TRACE_INTERNAL, "%s: remove link from uri/link cache", __func__);
if (linkenv->url)
{
link_ = g_hash_table_lookup(linkenv->env->uriToLink, linkenv->url);
g_hash_table_remove(linkenv->env->uriToLink, linkenv->url);
smlLinkDeref(link_);
smlSafeCFree(&(linkenv->url));
}
/* save link for disconnect event */
link_ = linkenv->link;
/* signal disconnect to socket and potential client */
if (linkenv->msg)
{
smlTrace(TRACE_INTERNAL, "%s: close open message/connection", __func__);
soup_message_set_status (linkenv->msg, SOUP_STATUS_SERVICE_UNAVAILABLE);
#ifdef HAVE_LIBSOUP22
//soup_server_message_set_encoding (
// SOUP_SERVER_MESSAGE (linkenv->msg),
// SOUP_TRANSFER_CONTENT_LENGTH);
soup_message_io_unpause(linkenv->msg);
#else
soup_server_unpause_message(linkenv->env->server, linkenv->msg);
#endif
linkenv->msg = NULL;
}
/* cleanup */
smlTrace(TRACE_INTERNAL, "%s: free memory", __func__);
linkenv->env = NULL;
link_->link_data = NULL;
smlSafeFree((gpointer *)&linkenv);
smlTrace(TRACE_INTERNAL, "%s: signal and unref link", __func__);
smlTransportReceiveEvent(env->tsp, link_, SML_TRANSPORT_EVENT_DISCONNECT_DONE, NULL, NULL);
smlLinkDeref(link_);
smlTrace(TRACE_EXIT, "%s", __func__);
}
static void smlTransportHttpServerFreeResponseURI(gpointer key)
{
smlTrace(TRACE_ENTRY, "%s(%p)", __func__, key);
smlSafeCFree((char **)&key);
smlTrace(TRACE_EXIT, "%s", __func__);
}
static gboolean smlTransportHttpServerFreeUriToLink(
gpointer key,
gpointer value,
gpointer user_data)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, key, value, user_data);
// SmlTransportHttpServerEnv *env = user_data;
char *uri = key;
SmlLink *link_ = value;
smlLinkDeref(link_);
smlSafeCFree(&uri);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
static gboolean smlTransportHttpServerFreeSessionToUri(
gpointer key,
gpointer value,
gpointer user_data)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, key, value, user_data);
// SmlTransportHttpServerEnv *env = user_data;
SmlSession *session = key;
char *uri = value;
smlSafeCFree(&uri);
smlSessionUnref(session);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
static SmlBool smlTransportHttpServerCleanupSocket(
gpointer data,
SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s", __func__);
CHECK_ERROR_REF
smlAssert(data);
SoupServer *server = data;
soup_server_quit(server);
g_object_unref(server);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
static SmlBool smlTransportHttpServerFinalize(void *data, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, data, error);
CHECK_ERROR_REF
smlAssert(data);
SmlTransportHttpServerEnv *env = data;
smlAssert(env->tsp);
if (env->server) {
/* If there was no server init
* then there is no server.
* (e.g. failure after smlTransportHttpServerNew)
*/
if (!smlThreadCallFunction(
env->tsp->thread,
smlTransportHttpServerCleanupSocket,
env->server,
error))
goto error;
env->server = NULL;
}
if (env->url)
smlSafeCFree(&(env->url));
guint count = 0;
guint removed = 0;
/* cleanup hash table uriToLink */
count = g_hash_table_size(env->uriToLink);
removed = g_hash_table_foreach_steal(
env->uriToLink,
smlTransportHttpServerFreeUriToLink,
env);
if (count != removed)
{
smlErrorSet(error, SML_ERROR_GENERIC,
"The glib function g_hash_table_foreach_steal could " \
"not remove all key/value pairs from uriToLink (%u of %u).",
removed, count);
goto error;
} else {
g_hash_table_unref(env->uriToLink);
env->uriToLink = NULL;
}
/* cleanup hash table sessionToUri */
count = g_hash_table_size(env->sessionToUri);
removed = g_hash_table_foreach_steal(
env->sessionToUri,
smlTransportHttpServerFreeSessionToUri,
env);
if (count != removed)
{
smlErrorSet(error, SML_ERROR_GENERIC,
"The glib function g_hash_table_foreach_steal could not " \
"remove all key/value pairs from sessionToUri. (%u of %u)",
removed, count);
goto error;
} else {
g_hash_table_unref(env->sessionToUri);
env->sessionToUri = NULL;
}
smlSafeFree((gpointer *)&env);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
error:
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return FALSE;
}
static void smlTransportHttpServerSend(void *userdata, void *linkdata, SmlTransportData *data, SmlError *error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p, %p)", __func__, userdata, linkdata, data, error);
// smlAssert(userdata);
smlAssert(linkdata);
smlAssert(error || data);
// SmlTransportHttpServerEnv *env = userdata;
SmlLinkHttpServerEnv *linkenv = linkdata;
smlAssert(linkenv->msg);
SoupMessage *msg = linkenv->msg;
linkenv->msg = NULL;
if (error)
goto error_free_message;
soup_message_set_status (msg, SOUP_STATUS_OK);
#ifdef HAVE_LIBSOUP22
soup_server_message_set_encoding (SOUP_SERVER_MESSAGE(msg), SOUP_TRANSFER_CONTENT_LENGTH);
#endif
const char *content_type;
switch (data->type) {
case SML_MIMETYPE_XML:
content_type = SML_ELEMENT_XML;
break;
case SML_MIMETYPE_WBXML:
content_type = SML_ELEMENT_WBXML;
break;
case SML_MIMETYPE_SAN:
content_type = SML_ELEMENT_SAN;
break;
default:
smlErrorSet(&error, SML_ERROR_GENERIC, "Unknown Mimetype");
goto error_free_message;
}
#ifdef HAVE_LIBSOUP22
// duplicate the data because sometimes data is freed to early
char *soupcopy = (char *) smlTryMalloc0(data->size, &error);
if (error)
goto error_free_message;
memcpy(soupcopy, data->data, data->size);
soup_message_set_response (msg, content_type, SOUP_BUFFER_SYSTEM_OWNED,
soupcopy, data->size);
soup_message_io_unpause(msg);
#else
soup_message_set_response(msg, content_type,
SOUP_MEMORY_COPY, data->data, data->size);
soup_server_unpause_message(linkenv->env->server, msg);
#endif
smlTrace(TRACE_EXIT, "%s", __func__);
return;
error_free_message:
if (smlErrorGetClass(&error) <= SML_ERRORCLASS_RETRY)
soup_message_set_status_full(msg, SOUP_STATUS_BAD_REQUEST, smlErrorPrint(&error));
else
soup_message_set_status_full(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, smlErrorPrint(&error));
#ifdef HAVE_LIBSOUP22
soup_server_message_set_encoding (SOUP_SERVER_MESSAGE (msg), SOUP_TRANSFER_CONTENT_LENGTH);
soup_message_io_unpause(msg);
#else
soup_server_unpause_message(linkenv->env->server, msg);
#endif
smlErrorDeref(&error);
smlTrace(TRACE_EXIT, "%s: Sent Error", __func__);
return;
}
char * smlTransportHttpServerGetResponseURI(SmlLink *link_, SmlSession *session, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, link_, session, error);
/* Do not change the assertion to simple return FALSE stuff
* because to many developers incl. me ignore returned FALSE
* if it is only configuration.
*/
CHECK_ERROR_REF
smlAssert(link_);
smlAssert(session);
smlAssert(link_->link_data);
SmlLinkHttpServerEnv *linkenv = link_->link_data;
smlAssert(linkenv->env);
SmlTransportHttpServerEnv *env = linkenv->env;
char *responseURI = NULL;
smlTrace(TRACE_INTERNAL, "%s: params checked", __func__);
/* SECURITY NOTE
* This is the function were we have to create and validate
* the used URLs. If you are not sure about what you are doing
* then do not touch the code and contact the developers.
*/
/* check the session */
if (!g_hash_table_lookup(env->sessionToUri, session))
{
/* This is a potentially new session.
* The URL must be the original URL.
*/
smlTrace(TRACE_INTERNAL, "%s: new session detected", __func__);
char *soupURI = soup_uri_to_string (soup_message_get_uri(linkenv->msg), TRUE);
if (strcmp(env->url, soupURI))
{
/* SECURITY: This can be an attack. */
smlErrorSet(error, SML_ERROR_RETRY_LATER,
"A new session with a configured HTTP session was detected.");
smlSafeCFree(&soupURI);
goto error;
}
smlSafeCFree(&soupURI);
/* create new random response ID
*
* glib only supports 32 bit random numbers.
* Therefore 4 random numbers are created
* and a fixed length is used.
*
* Do not send the response ID as HTTP GET parameter
* because this can cause conflicts
* because SyncML requires HTTP POST and
* libsoup 2.4 filters such parameters
* from the path in the callback interface.
*/
soupURI = soup_uri_to_string (soup_message_get_uri(linkenv->msg), FALSE),
responseURI = g_strdup_printf("%s0X%08X%08X%08X%08X",
soupURI,
g_random_int (),
g_random_int (),
g_random_int (),
g_random_int ());
smlSafeCFree(&soupURI);
/* map URI to link */
if (g_hash_table_lookup(env->uriToLink, responseURI))
{
/* SECURITY: This can be an insecure random number generator. */
smlErrorSet(error, SML_ERROR_RETRY_LATER,
"A fresh random session ID is already in use.");
goto error;
}
smlLinkRef(link_);
g_hash_table_insert(env->uriToLink, g_strdup(responseURI), link_);
smlTrace(TRACE_INTERNAL, "%s: ResponseURI is %s.", __func__, VA_STRING(responseURI));
/* map session to URI */
g_hash_table_insert(env->sessionToUri, session, g_strdup(responseURI));
smlSessionRef(session);
/* cache session and URI in linkenv - necessary for cleanup */
linkenv->session = session;
smlSessionRef(session);
linkenv->url = g_strdup(responseURI);
} else {
/* The session already exists.
* The referenced URL must be checked against the cached URL.
* The used URL must be checked against the cached URL too.
*/
smlTrace(TRACE_INTERNAL, "%s: cached session detected", __func__);
char *soupURI = soup_uri_to_string (soup_message_get_uri(linkenv->msg), FALSE);
const char *sessionURI = g_hash_table_lookup(env->sessionToUri, session);
if (strcmp(soupURI, sessionURI))
{
/* SECURITY: This is a potential attack. */
smlErrorSet(error, SML_ERROR_RETRY_LATER,
"Another session (%s) re-used an already existing HTTP session (%s).",
soupURI, sessionURI);
smlSafeCFree(&soupURI);
goto error;
}
SmlLink *cachedLink = g_hash_table_lookup(env->uriToLink, soupURI);
if (!cachedLink)
{
/* SECURITY: This is an internal bug. */
smlErrorSet(error, SML_ERROR_GENERIC,
"Cannot find link for used URL.");
smlSafeCFree(&soupURI);
goto error;
}
if (cachedLink != link_)
{
/* SECURITY: This is an internal bug. */
smlErrorSet(error, SML_ERROR_GENERIC,
"The link objects mismatch.");
smlSafeCFree(&soupURI);
goto error;
}
if (strcmp(linkenv->url, soupURI))
{
/* SECURITY: This can be an attack. */
smlErrorSet(error, SML_ERROR_GENERIC,
"The URL of the link object is wrong.");
smlSafeCFree(&soupURI);
goto error;
}
responseURI = soupURI;
}
smlTrace(TRACE_EXIT, "%s - %s", __func__, VA_STRING(responseURI));
return responseURI;
error:
smlTrace(TRACE_EXIT_ERROR, "%s - %s", __func__, smlErrorPrint(error));
return NULL;
}
SmlBool smlTransportHttpServerNew(SmlTransport *tsp, SmlError **error)
{
smlTrace(TRACE_ENTRY, "%s(%p, %p)", __func__, tsp, error);
CHECK_ERROR_REF
smlAssert(tsp);
tsp->functions.set_config_option = smlTransportHttpServerSetConfigOption;
tsp->functions.initialize = smlTransportHttpServerInit;
tsp->functions.finalize = smlTransportHttpServerFinalize;
tsp->functions.send = smlTransportHttpServerSend;
tsp->functions.get_response_uri = smlTransportHttpServerGetResponseURI;
tsp->functions.disconnect = smlTransportHttpServerDisconnect;
SmlTransportHttpServerEnv *env = smlTryMalloc0(sizeof(SmlTransportHttpServerEnv), error);
if (!env)
return FALSE;
tsp->transport_data = env;
env->tsp = tsp;
env->sessionToUri = g_hash_table_new(g_direct_hash, g_direct_equal);
env->uriToLink = g_hash_table_new_full(g_str_hash, g_str_equal, smlTransportHttpServerFreeResponseURI, NULL);
smlTrace(TRACE_EXIT, "%s", __func__);
return TRUE;
}
SmlTransportHttpServerEnv
Definition: http_server_internals.h:33
smlErrorPrint
const char * smlErrorPrint(SmlError **error)
Returns the message of the error.
Definition: sml_error.c:299
smlThreadCallFunction
SmlBool smlThreadCallFunction(SmlThread *thread, SmlThreadCallFunctionType func, gpointer data, SmlError **error)
Definition: sml_support.c:561
SmlLinkHttpServerEnv
Definition: http_server_internals.h:51
smlErrorGetClass
SmlErrorClass smlErrorGetClass(SmlError **error)
Gets the error class.
Definition: sml_error.c:382
SmlTransport
Definition: sml_transport_internals.h:69
SmlTransportData
Definition: sml_transport_internals.h:112
SmlSession
Definition: sml_session_internals.h:27
SmlLink
Definition: sml_transport_internals.h:92
smlTrace
void smlTrace(SmlTraceType type, const char *message,...)
Used for tracing the application.
Definition: sml_support.c:120
smlTryMalloc0
void * smlTryMalloc0(long n_bytes, SmlError **error)
Safely mallocs.
Definition: sml_support.c:335
smlErrorSet
void smlErrorSet(SmlError **error, SmlErrorType type, const char *format,...)
Sets the error.
Definition: sml_error.c:355
SmlError
Represent an error.
Definition: sml_error_internals.h:29