diff options
Diffstat (limited to 'src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.c')
-rw-r--r-- | src/libcharon/plugins/tnc_ifmap/tnc_ifmap_http.c | 244 |
1 files changed, 244 insertions, 0 deletions
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; +} + |