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>