Annotation of embedaddon/strongswan/src/libstrongswan/plugins/winhttp/winhttp_fetcher.c, revision 1.1.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>