diff options
author | Andreas Steffen <andreas.steffen@strongswan.org> | 2013-04-03 12:08:52 +0200 |
---|---|---|
committer | Andreas Steffen <andreas.steffen@strongswan.org> | 2013-04-03 21:38:04 +0200 |
commit | 91503c2112a2987641c51e34074726b45797139c (patch) | |
tree | 9e177a743b9e646c04150f1ee6ee3b6f1b183a6d /src/libcharon/plugins/tnc_ifmap | |
parent | 1044710b04e328ed4b1a7801dbc927b94f41dbe0 (diff) | |
download | strongswan-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.am | 1 | ||||
-rw-r--r-- | src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.c | 244 | ||||
-rw-r--r-- | src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.h | 68 | ||||
-rw-r--r-- | src/libcharon/plugins/tnc_ifmap/tnc_ifmap_soap_msg.c | 197 |
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(¶meter, ':', &line) && eat_whitespace(&line)) + { + if (match("Content-Length", ¶meter)) + { + if (sscanf(line.ptr, "%u", &len) == 1) + { + *content_len = len; + } + } + else if (match("Transfer-Encoding", ¶meter) && + 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(¶meter, ':', &line) && - match("Content-Length", ¶meter) && - 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, ); |