Return to winhttp_fetcher.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / libstrongswan / plugins / winhttp |
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: }