aboutsummaryrefslogtreecommitdiffstats
path: root/src/libcharon/plugins/tnc_ifmap
diff options
context:
space:
mode:
authorAndreas Steffen <andreas.steffen@strongswan.org>2013-04-03 12:08:52 +0200
committerAndreas Steffen <andreas.steffen@strongswan.org>2013-04-03 21:38:04 +0200
commit91503c2112a2987641c51e34074726b45797139c (patch)
tree9e177a743b9e646c04150f1ee6ee3b6f1b183a6d /src/libcharon/plugins/tnc_ifmap
parent1044710b04e328ed4b1a7801dbc927b94f41dbe0 (diff)
downloadstrongswan-91503c2112a2987641c51e34074726b45797139c.tar.bz2
strongswan-91503c2112a2987641c51e34074726b45797139c.tar.xz
support chunked HTTP responses
Diffstat (limited to 'src/libcharon/plugins/tnc_ifmap')
-rw-r--r--src/libcharon/plugins/tnc_ifmap/Makefile.am1
-rw-r--r--src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.c244
-rw-r--r--src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.h68
-rw-r--r--src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap_msg.c197
4 files changed, 370 insertions, 140 deletions
diff --git a/src/libcharon/plugins/tnc_ifmap/Makefile.am b/src/libcharon/plugins/tnc_ifmap/Makefile.am
index 7d8b0f5b7..36d9316d7 100644
--- a/src/libcharon/plugins/tnc_ifmap/Makefile.am
+++ b/src/libcharon/plugins/tnc_ifmap/Makefile.am
@@ -22,6 +22,7 @@ libstrongswan_tnc_ifmap_la_SOURCES = \
tnc_ifmap_listener.h tnc_ifmap_listener.c \
tnc_ifmap_soap.h tnc_ifmap_soap.c \
tnc_ifmap_soap_msg.h tnc_ifmap_soap_msg.c \
+ tnc_ifmap_http.h tnc_ifmap_http.c \
tnc_ifmap_renew_session_job.h tnc_ifmap_renew_session_job.c
libstrongswan_tnc_ifmap_la_LDFLAGS = -module -avoid-version
diff --git a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.c b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.c
new file mode 100644
index 000000000..e2a15828d
--- /dev/null
+++ b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2013 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program 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 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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.
+ */
+
+#define _GNU_SOURCE /* for asprintf() */
+
+#include "tnc_ifmap_http.h"
+
+#include <utils/debug.h>
+#include <utils/lexparser.h>
+
+#include <stdio.h>
+
+typedef struct private_tnc_ifmap_http_t private_tnc_ifmap_http_t;
+
+/**
+ * Private data of an tnc_ifmap_http_t object.
+ */
+struct private_tnc_ifmap_http_t {
+
+ /**
+ * Public tnc_ifmap_http_t interface.
+ */
+ tnc_ifmap_http_t public;
+
+ /**
+ * HTTPS Server URI with https:// prefix removed
+ */
+ char *uri;
+
+ /**
+ * Optional base64-encoded username:password for HTTP Basic Authentication
+ */
+ chunk_t user_pass;
+
+ /**
+ * HTTP chunked mode
+ */
+ bool chunked;
+
+};
+
+METHOD(tnc_ifmap_http_t, build, status_t,
+ private_tnc_ifmap_http_t *this, chunk_t *in, chunk_t *out)
+{
+ char *host, *path, *request, auth[128];
+ int len;
+
+ /* Duplicate host[/path] string since we are going to manipulate it */
+ len = strlen(this->uri) + 2;
+ host = malloc(len);
+ memset(host, '\0', len);
+ strcpy(host, this->uri);
+
+ /* Extract appended path or set to root */
+ path = strchr(host, '/');
+ if (!path)
+ {
+ path = host + len - 2;
+ *path = '/';
+ }
+
+ /* Use Basic Authentication? */
+ if (this->user_pass.len)
+ {
+ snprintf(auth, sizeof(auth), "Authorization: Basic %.*s\r\n",
+ this->user_pass.len, this->user_pass.ptr);
+ }
+ else
+ {
+ *auth = '\0';
+ }
+
+ /* Write HTTP POST request, TODO break up into chunks */
+ len = asprintf(&request,
+ "POST %s HTTP/1.1\r\n"
+ "Host: %.*s\r\n"
+ "%s"
+ "Content-Type: application/soap+xml;charset=utf-8\r\n"
+ "Content-Length: %d\r\n"
+ "\r\n"
+ "%.*s", path, (path-host), host, auth, in->len, in->len, in->ptr);
+ free(host);
+
+ if (len == -1)
+ {
+ return FAILED;
+ }
+ *out = chunk_create(request, len);
+ DBG3(DBG_TLS, "sending HTML POST request %B", out);
+
+ return SUCCESS;
+}
+
+static bool process_header(chunk_t *in, bool *chunked, u_int *content_len)
+{
+ chunk_t line, version, parameter;
+ int code;
+ u_int len;
+
+ /* Process HTTP protocol version */
+ if (!fetchline(in, &line) || !extract_token(&version, ' ', &line) ||
+ !match("HTTP/1.1", &version) || sscanf(line.ptr, "%d", &code) != 1)
+ {
+ DBG1(DBG_TNC, "malformed http response header");
+ return FALSE;
+ }
+ if (code != 200)
+ {
+ DBG1(DBG_TNC, "http response returns error code %d", code);
+ return FALSE;
+ }
+
+ *content_len = 0;
+ *chunked = FALSE;
+
+ /* Process HTTP header line by line until the HTTP body is reached */
+ while (fetchline(in, &line))
+ {
+ if (line.len == 0)
+ {
+ break;
+ }
+ if (extract_token(&parameter, ':', &line) && eat_whitespace(&line))
+ {
+ if (match("Content-Length", &parameter))
+ {
+ if (sscanf(line.ptr, "%u", &len) == 1)
+ {
+ *content_len = len;
+ }
+ }
+ else if (match("Transfer-Encoding", &parameter) &&
+ match("chunked", &line))
+ {
+ *chunked = TRUE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+METHOD(tnc_ifmap_http_t, process, status_t,
+ private_tnc_ifmap_http_t *this, chunk_t *in, chunk_t *out)
+{
+ u_int len = 0;
+ chunk_t line, out_chunk;
+
+ DBG3(DBG_TLS, "receiving HTTP response %B", in);
+
+ if (!this->chunked)
+ {
+ if (!process_header(in, &this->chunked, &len))
+ {
+ return FAILED;
+ }
+ }
+
+ while (in->len)
+ {
+ if (this->chunked)
+ {
+ if (!fetchline(in, &line) || sscanf(line.ptr, "%x", &len) != 1)
+ {
+ return FAILED;
+ }
+ DBG3(DBG_TLS, "received HTTP response is chunked (%u bytes)", len);
+
+ /* Received last chunk? */
+ if (len == 0)
+ {
+ return SUCCESS;
+ }
+ }
+
+ /* Check size of of remaining HTTP body */
+ if (len > in->len)
+ {
+ DBG1(DBG_TNC, "insufficient data in HTTP body");
+ return FAILED;
+ }
+
+ if (this->chunked)
+ {
+ out_chunk = *in;
+ out_chunk.len = len;
+ *out = chunk_cat("mc", *out, out_chunk);
+ *in = chunk_skip(*in, len);
+ if (!fetchline(in, &line) || line.len > 0)
+ {
+ return FAILED;
+ }
+ }
+ else
+ {
+ if (len)
+ {
+ in->len = len;
+ }
+ *out = chunk_clone(*in);
+ return SUCCESS;
+ }
+ }
+ return NEED_MORE;
+}
+
+METHOD(tnc_ifmap_http_t, destroy, void,
+ private_tnc_ifmap_http_t *this)
+{
+ free(this);
+}
+
+/**
+ * See header
+ */
+tnc_ifmap_http_t *tnc_ifmap_http_create(char *uri, chunk_t user_pass)
+{
+ private_tnc_ifmap_http_t *this;
+
+ INIT(this,
+ .public = {
+ .build = _build,
+ .process = _process,
+ .destroy = _destroy,
+ },
+ .uri = uri,
+ .user_pass = user_pass,
+ );
+
+ return &this->public;
+}
+
diff --git a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.h b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.h
new file mode 100644
index 000000000..3d3084744
--- /dev/null
+++ b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2013 Andreas Steffen
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * This program 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 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program 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.
+ */
+
+/**
+ * @defgroup tnc_ifmap_http tnc_ifmap_http
+ * @{ @ingroup tnc_ifmap
+ */
+
+#ifndef TNC_IFMAP_HTTP_H_
+#define TNC_IFMAP_HTTP_H_
+
+#include <library.h>
+#include <tls_socket.h>
+
+#include <libxml/parser.h>
+
+typedef struct tnc_ifmap_http_t tnc_ifmap_http_t;
+
+/**
+ * Interface for building and processing HTTP messages
+ */
+struct tnc_ifmap_http_t {
+
+ /**
+ * Build a HTTP POST message
+ *
+ * @param in input data
+ * @param out HTTP POST request
+ * @result status return code
+ */
+ status_t (*build)(tnc_ifmap_http_t *this, chunk_t *in, chunk_t *out);
+
+ /**
+ * Receive a HTTP [chunked] response
+ *
+ * @param in [chunked] HTTP response
+ * @param out output data
+ * @result status return code
+ */
+ status_t (*process)(tnc_ifmap_http_t *this, chunk_t *in, chunk_t *out);
+
+ /**
+ * Destroy a tnc_ifmap_http_t object.
+ */
+ void (*destroy)(tnc_ifmap_http_t *this);
+};
+
+/**
+ * Create a tnc_ifmap_http instance.
+ *
+ * @param uri HTTPS URI with https:// prefix removed
+ * @param user_pass Optional username:password for HTTP Basic Authentication
+ */
+tnc_ifmap_http_t *tnc_ifmap_http_create(char *uri, chunk_t user_pass);
+
+#endif /** TNC_IFMAP_HTTP_H_ @}*/
diff --git a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap_msg.c b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap_msg.c
index eaa835134..b86288683 100644
--- a/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap_msg.c
+++ b/src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap_msg.c
@@ -13,14 +13,10 @@
* for more details.
*/
-#define _GNU_SOURCE /* for asprintf() */
-
#include "tnc_ifmap_soap_msg.h"
+#include "tnc_ifmap_http.h"
#include <utils/debug.h>
-#include <utils/lexparser.h>
-
-#include <stdio.h>
#define SOAP_NS "http://www.w3.org/2003/05/soap-envelope"
@@ -37,17 +33,12 @@ struct private_tnc_ifmap_soap_msg_t {
tnc_ifmap_soap_msg_t public;
/**
- * HTTPS Server URI with https:// prefix removed
- */
- char *uri;
-
- /**
- * Optional base64-encoded username:password for HTTP Basic Authentication
+ * HTTP POST request builder and response processing
*/
- chunk_t user_pass;
+ tnc_ifmap_http_t *http;
/**
- * TLS Socket
+ * TLS socket
*/
tls_socket_t *tls;
@@ -59,118 +50,6 @@ struct private_tnc_ifmap_soap_msg_t {
};
/**
- * Send HTTP POST request and receive HTTP response
- */
-static bool http_post(private_tnc_ifmap_soap_msg_t *this, chunk_t out,
- chunk_t *in)
-{
- char *host, *path, *request, buf[2048];
- chunk_t line, http, parameter;
- int len, written, code, content_len = 0;
-
- /* Duplicate host[/path] string since we are going to manipulate it */
- len = strlen(this->uri) + 2;
- host = malloc(len);
- memset(host, '\0', len);
- strcpy(host, this->uri);
-
- /* Extract appended path or set to root */
- path = strchr(host, '/');
- if (!path)
- {
- path = host + len - 2;
- *path = '/';
- }
-
- /* Use Basic Authentication? */
- if (this->user_pass.len)
- {
- snprintf(buf, sizeof(buf), "Authorization: Basic %.*s\r\n",
- this->user_pass.len, this->user_pass.ptr);
- }
- else
- {
- *buf = '\0';
- }
-
- /* Write HTTP POST request */
- len = asprintf(&request,
- "POST %s HTTP/1.1\r\n"
- "Host: %.*s\r\n"
- "%s"
- "Content-Type: application/soap+xml;charset=utf-8\r\n"
- "Content-Length: %d\r\n"
- "\r\n"
- "%.*s", path, (path-host), host, buf, out.len, out.len, out.ptr);
- free(host);
-
- if (len == -1)
- {
- return FALSE;
- }
- http = chunk_create(request, len);
- DBG3(DBG_TLS, "%B", &http);
-
- written = this->tls->write(this->tls, request, len);
- free(request);
- if (written != len)
- {
- return FALSE;
- }
-
- /* Read HTTP response */
- len = this->tls->read(this->tls, buf, sizeof(buf), TRUE);
- if (len <= 0)
- {
- return FALSE;
- }
- *in = chunk_create(buf, len);
-
- /* Process HTTP protocol version */
- if (!fetchline(in, &line) || !extract_token(&http, ' ', &line) ||
- !match("HTTP/1.1", &http) || sscanf(line.ptr, "%d", &code) != 1)
- {
- DBG1(DBG_TNC, "malformed http response header");
- return FALSE;
- }
- if (code != 200)
- {
- DBG1(DBG_TNC, "http response returns error code %d", code);
- return FALSE;
- }
-
- /* Process HTTP header line by line until the HTTP body is reached */
- while (fetchline(in, &line))
- {
- if (line.len == 0)
- {
- break;
- }
-
- if (extract_token(&parameter, ':', &line) &&
- match("Content-Length", &parameter) &&
- sscanf(line.ptr, "%d", &len) == 1)
- {
- content_len = len;
- }
- }
-
- /* Found Content-Length parameter and check size of HTTP body */
- if (content_len)
- {
- if (content_len > in->len)
- {
- DBG1(DBG_TNC, "http body is smaller than content length");
- return FALSE;
- }
- in->len = content_len;
- }
- *in = chunk_clone(*in);
-
- return TRUE;
-}
-
-/**
* Find a child node with a given name
*/
static xmlNodePtr find_child(xmlNodePtr parent, const xmlChar* name)
@@ -198,9 +77,11 @@ METHOD(tnc_ifmap_soap_msg_t, post, bool,
xmlDocPtr doc;
xmlNodePtr env, body, cur, response;
xmlNsPtr ns;
- xmlChar *xml, *errorCode, *errorString;
- int len;
- chunk_t in, out;
+ xmlChar *xml_str, *errorCode, *errorString;
+ int xml_len, len, written;
+ chunk_t xml, http;
+ char buf[4096];
+ status_t status;
DBG2(DBG_TNC, "sending ifmap %s", request->name);
@@ -217,22 +98,58 @@ METHOD(tnc_ifmap_soap_msg_t, post, bool,
xmlAddChild(env, body);
/* Convert XML Document into a character string */
- xmlDocDumpFormatMemory(doc, &xml, &len, 1);
+ xmlDocDumpFormatMemory(doc, &xml_str, &xml_len, 1);
xmlFreeDoc(doc);
- DBG3(DBG_TNC, "%.*s", len, xml);
- out = chunk_create(xml, len);
+ DBG3(DBG_TNC, "%.*s", xml_len, xml_str);
+ xml = chunk_create(xml_str, xml_len);
- /* Send SOAP-XML request via HTTP POST */
- if (!http_post(this, out, &in))
+ /* Send SOAP-XML request via HTTPS POST */
+ do
+ {
+ status = this->http->build(this->http, &xml, &http);
+ if (status == FAILED)
+ {
+ break;
+ }
+ written = this->tls->write(this->tls, http.ptr, http.len);
+ free(http.ptr);
+ if (written != http.len)
+ {
+ status = FAILED;
+ break;
+ }
+ }
+ while (status == NEED_MORE);
+
+ xmlFree(xml_str);
+ if (status != SUCCESS)
{
- xmlFree(xml);
return FALSE;
}
- xmlFree(xml);
- DBG3(DBG_TNC, "%B", &in);
- this->doc = xmlParseMemory(in.ptr, in.len);
- free(in.ptr);
+ /* Receive SOAP-XML response via [chunked] HTTPS */
+ xml = chunk_empty;
+ do
+ {
+ len = this->tls->read(this->tls, buf, sizeof(buf), TRUE);
+ if (len <= 0)
+ {
+ return FALSE;
+ }
+ http = chunk_create(buf, len);
+
+ status = this->http->process(this->http, &http, &xml);
+ if (status == FAILED)
+ {
+ free(xml.ptr);
+ return FALSE;
+ }
+ }
+ while (status == NEED_MORE);
+
+ DBG3(DBG_TNC, "parsing XML message %B", &xml);
+ this->doc = xmlParseMemory(xml.ptr, xml.len);
+ free(xml.ptr);
if (!this->doc)
{
@@ -309,6 +226,7 @@ METHOD(tnc_ifmap_soap_msg_t, post, bool,
METHOD(tnc_ifmap_soap_msg_t, destroy, void,
private_tnc_ifmap_soap_msg_t *this)
{
+ this->http->destroy(this->http);
if (this->doc)
{
xmlFreeDoc(this->doc);
@@ -329,8 +247,7 @@ tnc_ifmap_soap_msg_t *tnc_ifmap_soap_msg_create(char *uri, chunk_t user_pass,
.post = _post,
.destroy = _destroy,
},
- .uri = uri,
- .user_pass = user_pass,
+ .http = tnc_ifmap_http_create(uri, user_pass),
.tls = tls,
);