Annotation of embedaddon/strongswan/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (C) 2014 Martin Willi
! 3: * Copyright (C) 2014 revosec AG
! 4: *
! 5: * This program is free software; you can redistribute it and/or modify it
! 6: * under the terms of the GNU General Public License as published by the
! 7: * Free Software Foundation; either version 2 of the License, or (at your
! 8: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
! 9: *
! 10: * This program is distributed in the hope that it will be useful, but
! 11: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
! 12: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
! 13: * for more details.
! 14: */
! 15:
! 16: #include <winsock2.h>
! 17: #include <windows.h>
! 18: #include <winhttp.h>
! 19:
! 20: #include "winhttp_fetcher.h"
! 21:
! 22: #include <library.h>
! 23:
! 24: /**
! 25: * Timeout for DNS resolution, in ms
! 26: */
! 27: #define RESOLVE_TIMEOUT 5000
! 28:
! 29: /**
! 30: * Timeout for TCP connect, in ms
! 31: */
! 32: #define CONNECT_TIMEOUT 10000
! 33:
! 34: typedef struct private_winhttp_fetcher_t private_winhttp_fetcher_t;
! 35:
! 36: /**
! 37: * Private data of a winhttp_fetcher_t.
! 38: */
! 39: struct private_winhttp_fetcher_t {
! 40:
! 41: /**
! 42: * Public interface
! 43: */
! 44: winhttp_fetcher_t public;
! 45:
! 46: /**
! 47: * WinHTTP session handle
! 48: */
! 49: HINTERNET session;
! 50:
! 51: /**
! 52: * POST request data
! 53: */
! 54: chunk_t request;
! 55:
! 56: /**
! 57: * HTTP version string to use
! 58: */
! 59: LPWSTR version;
! 60:
! 61: /**
! 62: * Optional HTTP headers, as allocated LPWSTR
! 63: */
! 64: linked_list_t *headers;
! 65:
! 66: /**
! 67: * Callback function
! 68: */
! 69: fetcher_callback_t cb;
! 70:
! 71: /**
! 72: * Timeout for operations, in ms
! 73: */
! 74: u_long timeout;
! 75:
! 76: /**
! 77: * User pointer to store HTTP status code to
! 78: */
! 79: u_int *result;
! 80: };
! 81:
! 82: /**
! 83: * Configure and send the HTTP request
! 84: */
! 85: static bool send_request(private_winhttp_fetcher_t *this, HINTERNET request)
! 86: {
! 87: WCHAR headers[512] = L"";
! 88: LPWSTR hdr;
! 89:
! 90: /* Set timeout. By default, send/receive does not time out */
! 91: if (!WinHttpSetTimeouts(request, RESOLVE_TIMEOUT, CONNECT_TIMEOUT,
! 92: this->timeout, this->timeout))
! 93: {
! 94: DBG1(DBG_LIB, "opening HTTP request failed: %u", GetLastError());
! 95: return FALSE;
! 96: }
! 97: while (this->headers->remove_first(this->headers, (void**)&hdr) == SUCCESS)
! 98: {
! 99: wcsncat(headers, hdr, countof(headers) - wcslen(headers) - 1);
! 100: if (this->headers->get_count(this->headers))
! 101: {
! 102: wcsncat(headers, L"\r\n", countof(headers) - wcslen(headers) - 1);
! 103: }
! 104: free(hdr);
! 105: }
! 106: if (!WinHttpSendRequest(request, headers, wcslen(headers),
! 107: this->request.ptr, this->request.len, this->request.len, 0))
! 108: {
! 109: DBG1(DBG_LIB, "sending HTTP request failed: %u", GetLastError());
! 110: return FALSE;
! 111: }
! 112: return TRUE;
! 113: }
! 114:
! 115: /**
! 116: * Read back result and invoke receive callback
! 117: */
! 118: static bool read_result(private_winhttp_fetcher_t *this, HINTERNET request,
! 119: void *user)
! 120: {
! 121: DWORD received;
! 122: char buf[1024];
! 123: uint32_t code;
! 124: DWORD codelen = sizeof(code);
! 125:
! 126: if (!WinHttpReceiveResponse(request, NULL))
! 127: {
! 128: DBG1(DBG_LIB, "reading HTTP response header failed: %u", GetLastError());
! 129: return FALSE;
! 130: }
! 131: if (!WinHttpQueryHeaders(request,
! 132: WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
! 133: NULL, &code, &codelen, NULL))
! 134: {
! 135: DBG1(DBG_LIB, "reading HTTP status code failed: %u", GetLastError());
! 136: return FALSE;
! 137: }
! 138: if (this->result)
! 139: {
! 140: *this->result = code;
! 141: }
! 142: if (code < 200 || code >= 300)
! 143: { /* non-successful HTTP status code */
! 144: if (!this->result)
! 145: {
! 146: DBG1(DBG_LIB, "HTTP request failed with status %u", code);
! 147: }
! 148: return FALSE;
! 149: }
! 150: if (this->cb == fetcher_default_callback)
! 151: {
! 152: *(chunk_t*)user = chunk_empty;
! 153: }
! 154: while (TRUE)
! 155: {
! 156: if (!WinHttpReadData(request, buf, sizeof(buf), &received))
! 157: {
! 158: DBG1(DBG_LIB, "reading HTTP response failed: %u", GetLastError());
! 159: return FALSE;
! 160: }
! 161: if (received == 0)
! 162: {
! 163: /* end of response */
! 164: break;
! 165: }
! 166: if (!this->cb(user, chunk_create(buf, received)))
! 167: {
! 168: DBG1(DBG_LIB, "processing response failed or cancelled");
! 169: return FALSE;
! 170: }
! 171: }
! 172: return TRUE;
! 173: }
! 174:
! 175: /**
! 176: * Parse an uri to wide string host and path, optionally set flags and port
! 177: */
! 178: static bool parse_uri(private_winhttp_fetcher_t *this, char *uri,
! 179: LPWSTR host, int hostlen, LPWSTR path, int pathlen,
! 180: LPWSTR user, int userlen, LPWSTR pass, int passlen,
! 181: DWORD *flags, INTERNET_PORT *port)
! 182: {
! 183: WCHAR wuri[512], extra[256];
! 184: URL_COMPONENTS comps = {
! 185: .dwStructSize = sizeof(URL_COMPONENTS),
! 186: .lpszHostName = host,
! 187: .dwHostNameLength = hostlen,
! 188: .lpszUrlPath = path,
! 189: .dwUrlPathLength = pathlen,
! 190: .lpszUserName = user,
! 191: .dwUserNameLength = userlen,
! 192: .lpszPassword = pass,
! 193: .dwPasswordLength = passlen,
! 194: .lpszExtraInfo = extra,
! 195: .dwExtraInfoLength = countof(extra),
! 196: };
! 197:
! 198: if (!MultiByteToWideChar(CP_THREAD_ACP, 0, uri, -1, wuri, countof(wuri)))
! 199: {
! 200: DBG1(DBG_LIB, "converting URI failed: %u", GetLastError());
! 201: return FALSE;
! 202: }
! 203: if (!WinHttpCrackUrl(wuri, 0, ICU_ESCAPE, &comps))
! 204: {
! 205: DBG1(DBG_LIB, "cracking URI failed: %u", GetLastError());
! 206: return FALSE;
! 207: }
! 208: if (comps.nScheme == INTERNET_SCHEME_HTTPS)
! 209: {
! 210: *flags |= WINHTTP_FLAG_SECURE;
! 211: }
! 212: if (comps.dwExtraInfoLength)
! 213: {
! 214: wcsncat(path, extra, pathlen - comps.dwUrlPathLength - 1);
! 215: }
! 216: if (comps.nPort)
! 217: {
! 218: *port = comps.nPort;
! 219: }
! 220: return TRUE;
! 221: }
! 222:
! 223: /**
! 224: * Set credentials for basic authentication, if given
! 225: */
! 226: static bool set_credentials(private_winhttp_fetcher_t *this,
! 227: HINTERNET *request, LPWSTR user, LPWSTR pass)
! 228: {
! 229: if (!wcslen(user) && !wcslen(pass))
! 230: { /* skip */
! 231: return TRUE;
! 232: }
! 233: return WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_SERVER,
! 234: WINHTTP_AUTH_SCHEME_BASIC, user, pass, NULL);
! 235: }
! 236:
! 237: METHOD(fetcher_t, fetch, status_t,
! 238: private_winhttp_fetcher_t *this, char *uri, void *userdata)
! 239: {
! 240: INTERNET_PORT port = INTERNET_DEFAULT_PORT;
! 241: status_t status = FAILED;
! 242: DWORD flags = 0;
! 243: HINTERNET connection, request;
! 244: WCHAR host[256], path[512], user[256], pass[256], *method;
! 245:
! 246: if (this->request.len)
! 247: {
! 248: method = L"POST";
! 249: }
! 250: else
! 251: {
! 252: method = L"GET";
! 253: }
! 254:
! 255: if (this->result)
! 256: { /* zero-initialize for early failures */
! 257: *this->result = 0;
! 258: }
! 259:
! 260: if (parse_uri(this, uri, host, countof(host), path, countof(path),
! 261: user, countof(user), pass, countof(pass), &flags, &port))
! 262: {
! 263: connection = WinHttpConnect(this->session, host, port, 0);
! 264: if (connection)
! 265: {
! 266: request = WinHttpOpenRequest(connection, method, path, this->version,
! 267: WINHTTP_NO_REFERER,
! 268: WINHTTP_DEFAULT_ACCEPT_TYPES, flags);
! 269: if (request)
! 270: {
! 271: if (set_credentials(this, request, user, pass) &&
! 272: send_request(this, request) &&
! 273: read_result(this, request, userdata))
! 274: {
! 275: status = SUCCESS;
! 276: }
! 277: WinHttpCloseHandle(request);
! 278: }
! 279: else
! 280: {
! 281: DBG1(DBG_LIB, "opening request failed: %u", GetLastError());
! 282: }
! 283: WinHttpCloseHandle(connection);
! 284: }
! 285: else
! 286: {
! 287: DBG1(DBG_LIB, "connection failed: %u", GetLastError());
! 288: }
! 289: }
! 290: return status;
! 291: }
! 292:
! 293: /**
! 294: * Append an header as wide string
! 295: */
! 296: static bool append_header(private_winhttp_fetcher_t *this, char *name)
! 297: {
! 298: int len;
! 299: LPWSTR buf;
! 300:
! 301: len = MultiByteToWideChar(CP_THREAD_ACP, 0, name, -1, NULL, 0);
! 302: if (!len)
! 303: {
! 304: return FALSE;
! 305: }
! 306: buf = calloc(len, sizeof(WCHAR));
! 307: if (!MultiByteToWideChar(CP_THREAD_ACP, 0, name, -1, buf, len))
! 308: {
! 309: free(buf);
! 310: return FALSE;
! 311: }
! 312: this->headers->insert_last(this->headers, buf);
! 313: return TRUE;
! 314: }
! 315:
! 316: METHOD(fetcher_t, set_option, bool,
! 317: private_winhttp_fetcher_t *this, fetcher_option_t option, ...)
! 318: {
! 319: bool supported = TRUE;
! 320: char buf[128];
! 321: va_list args;
! 322:
! 323: va_start(args, option);
! 324: switch (option)
! 325: {
! 326: case FETCH_REQUEST_DATA:
! 327: this->request = va_arg(args, chunk_t);
! 328: break;
! 329: case FETCH_REQUEST_TYPE:
! 330: snprintf(buf, sizeof(buf), "Content-Type: %s", va_arg(args, char*));
! 331: supported = append_header(this, buf);
! 332: break;
! 333: case FETCH_REQUEST_HEADER:
! 334: supported = append_header(this, va_arg(args, char*));
! 335: break;
! 336: case FETCH_HTTP_VERSION_1_0:
! 337: this->version = L"HTTP/1.0";
! 338: break;
! 339: case FETCH_TIMEOUT:
! 340: this->timeout = va_arg(args, u_int) * 1000;
! 341: break;
! 342: case FETCH_CALLBACK:
! 343: this->cb = va_arg(args, fetcher_callback_t);
! 344: break;
! 345: case FETCH_RESPONSE_CODE:
! 346: this->result = va_arg(args, u_int*);
! 347: break;
! 348: case FETCH_SOURCEIP:
! 349: /* not supported, FALL */
! 350: default:
! 351: supported = FALSE;
! 352: break;
! 353: }
! 354: va_end(args);
! 355: return supported;
! 356: }
! 357:
! 358: METHOD(fetcher_t, destroy, void,
! 359: private_winhttp_fetcher_t *this)
! 360: {
! 361: WinHttpCloseHandle(this->session);
! 362: this->headers->destroy_function(this->headers, free);
! 363: free(this);
! 364: }
! 365: /*
! 366: * Described in header.
! 367: */
! 368: winhttp_fetcher_t *winhttp_fetcher_create()
! 369: {
! 370: private_winhttp_fetcher_t *this;
! 371:
! 372: INIT(this,
! 373: .public = {
! 374: .interface = {
! 375: .fetch = _fetch,
! 376: .set_option = _set_option,
! 377: .destroy = _destroy,
! 378: },
! 379: },
! 380: .version = L"HTTP/1.1",
! 381: .cb = fetcher_default_callback,
! 382: .headers = linked_list_create(),
! 383: .session = WinHttpOpen(L"strongSwan WinHTTP fetcher",
! 384: WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
! 385: WINHTTP_NO_PROXY_NAME,
! 386: WINHTTP_NO_PROXY_BYPASS, 0),
! 387: );
! 388:
! 389: if (!this->session)
! 390: {
! 391: free(this);
! 392: return NULL;
! 393: }
! 394:
! 395: return &this->public;
! 396: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>