diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
commit | 81c63b0eed39432878f78727f60a1e7499645199 (patch) | |
tree | 82387d8fecd1c20788fd8bd784a9b0bde091fb6b /src/libstrongswan/plugins/winhttp/winhttp_fetcher.c | |
parent | c5ebfc7b9c16551fe825dc1d79c3f7e2f096f6c9 (diff) | |
download | vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.tar.gz vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.zip |
Imported Upstream version 5.2.0
Diffstat (limited to 'src/libstrongswan/plugins/winhttp/winhttp_fetcher.c')
-rw-r--r-- | src/libstrongswan/plugins/winhttp/winhttp_fetcher.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c new file mode 100644 index 000000000..5f0b58479 --- /dev/null +++ b/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * 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. + */ + +#include <winsock2.h> +#include <windows.h> +#include <winhttp.h> + +#include "winhttp_fetcher.h" + +#include <library.h> + +/** + * Timeout for DNS resolution, in ms + */ +#define RESOLVE_TIMEOUT 5000 + +/** + * Timeout for TCP connect, in ms + */ +#define CONNECT_TIMEOUT 10000 + +typedef struct private_winhttp_fetcher_t private_winhttp_fetcher_t; + +/** + * Private data of a winhttp_fetcher_t. + */ +struct private_winhttp_fetcher_t { + + /** + * Public interface + */ + winhttp_fetcher_t public; + + /** + * WinHTTP session handle + */ + HINTERNET session; + + /** + * POST request data + */ + chunk_t request; + + /** + * HTTP version string to use + */ + LPWSTR version; + + /** + * Optional HTTP headers, as allocated LPWSTR + */ + linked_list_t *headers; + + /** + * Callback function + */ + fetcher_callback_t cb; + + /** + * Timeout for operations, in ms + */ + u_long timeout; + + /** + * User pointer to store HTTP status code to + */ + u_int *result; +}; + +/** + * Configure and send the HTTP request + */ +static bool send_request(private_winhttp_fetcher_t *this, HINTERNET request) +{ + WCHAR headers[512] = L""; + LPWSTR hdr; + + /* Set timeout. By default, send/receive does not time out */ + if (!WinHttpSetTimeouts(request, RESOLVE_TIMEOUT, CONNECT_TIMEOUT, + this->timeout, this->timeout)) + { + DBG1(DBG_LIB, "opening HTTP request failed: %u", GetLastError()); + return FALSE; + } + while (this->headers->remove_first(this->headers, (void**)&hdr) == SUCCESS) + { + wcsncat(headers, hdr, countof(headers) - wcslen(headers) - 1); + if (this->headers->get_count(this->headers)) + { + wcsncat(headers, L"\r\n", countof(headers) - wcslen(headers) - 1); + } + free(hdr); + } + if (!WinHttpSendRequest(request, headers, wcslen(headers), + this->request.ptr, this->request.len, this->request.len, 0)) + { + DBG1(DBG_LIB, "sending HTTP request failed: %u", GetLastError()); + return FALSE; + } + return TRUE; +} + +/** + * Read back result and invoke receive callback + */ +static bool read_result(private_winhttp_fetcher_t *this, HINTERNET request, + void *user) +{ + DWORD received; + char buf[1024]; + u_int32_t code; + DWORD codelen = sizeof(code); + + if (!WinHttpReceiveResponse(request, NULL)) + { + DBG1(DBG_LIB, "reading HTTP response header failed: %u", GetLastError()); + return FALSE; + } + if (!WinHttpQueryHeaders(request, + WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + NULL, &code, &codelen, NULL)) + { + DBG1(DBG_LIB, "reading HTTP status code failed: %u", GetLastError()); + return FALSE; + } + if (this->result) + { + *this->result = code; + } + if (code < 200 || code >= 300) + { /* non-successful HTTP status code */ + if (!this->result) + { + DBG1(DBG_LIB, "HTTP request failed with status %u", code); + } + return FALSE; + } + if (this->cb == fetcher_default_callback) + { + *(chunk_t*)user = chunk_empty; + } + while (TRUE) + { + if (!WinHttpReadData(request, buf, sizeof(buf), &received)) + { + DBG1(DBG_LIB, "reading HTTP response failed: %u", GetLastError()); + return FALSE; + } + if (received == 0) + { + /* end of response */ + break; + } + if (!this->cb(user, chunk_create(buf, received))) + { + DBG1(DBG_LIB, "processing response failed or cancelled"); + return FALSE; + } + } + return TRUE; +} + +/** + * Parse an uri to wide string host and path, optionally set flags and port + */ +static bool parse_uri(private_winhttp_fetcher_t *this, char *uri, + LPWSTR host, int hostlen, LPWSTR path, int pathlen, + LPWSTR user, int userlen, LPWSTR pass, int passlen, + DWORD *flags, INTERNET_PORT *port) +{ + WCHAR wuri[512], extra[256]; + URL_COMPONENTS comps = { + .dwStructSize = sizeof(URL_COMPONENTS), + .lpszHostName = host, + .dwHostNameLength = hostlen, + .lpszUrlPath = path, + .dwUrlPathLength = pathlen, + .lpszUserName = user, + .dwUserNameLength = userlen, + .lpszPassword = pass, + .dwPasswordLength = passlen, + .lpszExtraInfo = extra, + .dwExtraInfoLength = countof(extra), + }; + + if (!MultiByteToWideChar(CP_THREAD_ACP, 0, uri, -1, wuri, countof(wuri))) + { + DBG1(DBG_LIB, "converting URI failed: %u", GetLastError()); + return FALSE; + } + if (!WinHttpCrackUrl(wuri, 0, ICU_ESCAPE, &comps)) + { + DBG1(DBG_LIB, "cracking URI failed: %u", GetLastError()); + return FALSE; + } + if (comps.nScheme == INTERNET_SCHEME_HTTPS) + { + *flags |= WINHTTP_FLAG_SECURE; + } + if (comps.dwExtraInfoLength) + { + wcsncat(path, extra, pathlen - comps.dwUrlPathLength - 1); + } + if (comps.nPort) + { + *port = comps.nPort; + } + return TRUE; +} + +/** + * Set credentials for basic authentication, if given + */ +static bool set_credentials(private_winhttp_fetcher_t *this, + HINTERNET *request, LPWSTR user, LPWSTR pass) +{ + if (!wcslen(user) && !wcslen(pass)) + { /* skip */ + return TRUE; + } + return WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_SERVER, + WINHTTP_AUTH_SCHEME_BASIC, user, pass, NULL); +} + +METHOD(fetcher_t, fetch, status_t, + private_winhttp_fetcher_t *this, char *uri, void *userdata) +{ + INTERNET_PORT port = INTERNET_DEFAULT_PORT; + status_t status = FAILED; + DWORD flags = 0; + HINTERNET connection, request; + WCHAR host[256], path[512], user[256], pass[256], *method; + + if (this->request.len) + { + method = L"POST"; + } + else + { + method = L"GET"; + } + + if (this->result) + { /* zero-initialize for early failures */ + *this->result = 0; + } + + if (parse_uri(this, uri, host, countof(host), path, countof(path), + user, countof(user), pass, countof(pass), &flags, &port)) + { + connection = WinHttpConnect(this->session, host, port, 0); + if (connection) + { + request = WinHttpOpenRequest(connection, method, path, this->version, + WINHTTP_NO_REFERER, + WINHTTP_DEFAULT_ACCEPT_TYPES, flags); + if (request) + { + if (set_credentials(this, request, user, pass) && + send_request(this, request) && + read_result(this, request, userdata)) + { + status = SUCCESS; + } + WinHttpCloseHandle(request); + } + else + { + DBG1(DBG_LIB, "opening request failed: %u", GetLastError()); + } + WinHttpCloseHandle(connection); + } + else + { + DBG1(DBG_LIB, "connection failed: %u", GetLastError()); + } + } + return status; +} + +/** + * Append an header as wide string + */ +static bool append_header(private_winhttp_fetcher_t *this, char *name) +{ + int len; + LPWSTR buf; + + len = MultiByteToWideChar(CP_THREAD_ACP, 0, name, -1, NULL, 0); + if (!len) + { + return FALSE; + } + buf = calloc(len, sizeof(WCHAR)); + if (!MultiByteToWideChar(CP_THREAD_ACP, 0, name, -1, buf, len)) + { + free(buf); + return FALSE; + } + this->headers->insert_last(this->headers, buf); + return TRUE; +} + +METHOD(fetcher_t, set_option, bool, + private_winhttp_fetcher_t *this, fetcher_option_t option, ...) +{ + bool supported = TRUE; + char buf[128]; + va_list args; + + va_start(args, option); + switch (option) + { + case FETCH_REQUEST_DATA: + this->request = va_arg(args, chunk_t); + break; + case FETCH_REQUEST_TYPE: + snprintf(buf, sizeof(buf), "Content-Type: %s", va_arg(args, char*)); + supported = append_header(this, buf); + break; + case FETCH_REQUEST_HEADER: + supported = append_header(this, va_arg(args, char*)); + break; + case FETCH_HTTP_VERSION_1_0: + this->version = L"HTTP/1.0"; + break; + case FETCH_TIMEOUT: + this->timeout = va_arg(args, u_int) * 1000; + break; + case FETCH_CALLBACK: + this->cb = va_arg(args, fetcher_callback_t); + break; + case FETCH_RESPONSE_CODE: + this->result = va_arg(args, u_int*); + break; + case FETCH_SOURCEIP: + /* not supported, FALL */ + default: + supported = FALSE; + break; + } + va_end(args); + return supported; +} + +METHOD(fetcher_t, destroy, void, + private_winhttp_fetcher_t *this) +{ + WinHttpCloseHandle(this->session); + this->headers->destroy_function(this->headers, free); + free(this); +} +/* + * Described in header. + */ +winhttp_fetcher_t *winhttp_fetcher_create() +{ + private_winhttp_fetcher_t *this; + + INIT(this, + .public = { + .interface = { + .fetch = _fetch, + .set_option = _set_option, + .destroy = _destroy, + }, + }, + .version = L"HTTP/1.1", + .cb = fetcher_default_callback, + .headers = linked_list_create(), + .session = WinHttpOpen(L"strongSwan WinHTTP fetcher", + WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0), + ); + + if (!this->session) + { + free(this); + return NULL; + } + + return &this->public; +} |