VirtualBox

source: vbox/trunk/src/VBox/Runtime/generic/http-curl.cpp@ 73699

Last change on this file since 73699 was 73699, checked in by vboxsync, 6 years ago

http-curl.cpp: add a few thin wrappers for some curl options.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 94.1 KB
Line 
1/* $Id: http-curl.cpp 73699 2018-08-15 20:26:03Z vboxsync $ */
2/** @file
3 * IPRT - HTTP client API, cURL based.
4 */
5
6/*
7 * Copyright (C) 2012-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_HTTP
32#include <iprt/http.h>
33#include "internal/iprt.h"
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/cidr.h>
38#include <iprt/crypto/store.h>
39#include <iprt/ctype.h>
40#include <iprt/env.h>
41#include <iprt/err.h>
42#include <iprt/file.h>
43#include <iprt/ldr.h>
44#include <iprt/log.h>
45#include <iprt/mem.h>
46#include <iprt/net.h>
47#include <iprt/once.h>
48#include <iprt/path.h>
49#include <iprt/stream.h>
50#include <iprt/string.h>
51#include <iprt/uni.h>
52#include <iprt/uri.h>
53
54#include "internal/magics.h"
55
56#ifdef RT_OS_WINDOWS /* curl.h drags in windows.h which isn't necessarily -Wall clean. */
57# include <iprt/win/windows.h>
58#endif
59#include <curl/curl.h>
60
61#ifdef RT_OS_DARWIN
62# include <CoreFoundation/CoreFoundation.h>
63# include <SystemConfiguration/SystemConfiguration.h>
64# include <CoreServices/CoreServices.h>
65#endif
66#ifdef RT_OS_WINDOWS
67# include <Winhttp.h>
68# include "../r3/win/internal-r3-win.h"
69#endif
70
71#ifdef RT_OS_LINUX
72//# define IPRT_USE_LIBPROXY
73#endif
74#ifdef IPRT_USE_LIBPROXY
75# include <stdlib.h> /* free */
76#endif
77
78
79/*********************************************************************************************************************************
80* Structures and Typedefs *
81*********************************************************************************************************************************/
82/**
83 * Internal HTTP client instance.
84 */
85typedef struct RTHTTPINTERNAL
86{
87 /** Magic value. */
88 uint32_t u32Magic;
89 /** cURL handle. */
90 CURL *pCurl;
91 /** The last response code. */
92 long lLastResp;
93 /** Custom headers/ */
94 struct curl_slist *pHeaders;
95 /** CA certificate file for HTTPS authentication. */
96 char *pszCaFile;
97 /** Whether to delete the CA on destruction. */
98 bool fDeleteCaFile;
99
100 /** Set if we've applied a CURLOTP_USERAGENT already. */
101 bool fHaveSetUserAgent;
102 /** Set if we've got a user agent header, otherwise clear. */
103 bool fHaveUserAgentHeader;
104
105 /** @name Proxy settings.
106 * When fUseSystemProxySettings is set, the other members will be updated each
107 * time we're presented with a new URL. The members reflect the cURL
108 * configuration.
109 *
110 * @{ */
111 /** Set if we should use the system proxy settings for a URL.
112 * This means reconfiguring cURL for each request. */
113 bool fUseSystemProxySettings;
114 /** Set if we've detected no proxy necessary. */
115 bool fNoProxy;
116 /** Proxy host name (RTStrFree). */
117 char *pszProxyHost;
118 /** Proxy port number (UINT32_MAX if not specified). */
119 uint32_t uProxyPort;
120 /** The proxy type (CURLPROXY_HTTP, CURLPROXY_SOCKS5, ++). */
121 curl_proxytype enmProxyType;
122 /** Proxy username (RTStrFree). */
123 char *pszProxyUsername;
124 /** Proxy password (RTStrFree). */
125 char *pszProxyPassword;
126 /** @} */
127
128 /** @name Cached settings.
129 * @{ */
130 /** Maximum number of redirects to follow.
131 * Zero if not automatically following (default). */
132 uint32_t cMaxRedirects;
133 /** @} */
134
135 /** Abort the current HTTP request if true. */
136 bool volatile fAbort;
137 /** Set if someone is preforming an HTTP operation. */
138 bool volatile fBusy;
139 /** The location field for 301 responses. */
140 char *pszRedirLocation;
141
142 /** Output callback data. */
143 union
144 {
145 /** For file destination. */
146 RTFILE hFile;
147 /** For memory destination. */
148 struct
149 {
150 /** The current size (sans terminator char). */
151 size_t cb;
152 /** The currently allocated size. */
153 size_t cbAllocated;
154 /** Pointer to the buffer. */
155 uint8_t *pb;
156 } Mem;
157 } Output;
158 /** Output callback status. */
159 int rcOutput;
160 /** Download size hint set by the progress callback. */
161 uint64_t cbDownloadHint;
162 /** Callback called during download. */
163 PRTHTTPDOWNLDPROGRCALLBACK pfnDownloadProgress;
164 /** User pointer parameter for pfnDownloadProgress. */
165 void *pvDownloadProgressUser;
166} RTHTTPINTERNAL;
167/** Pointer to an internal HTTP client instance. */
168typedef RTHTTPINTERNAL *PRTHTTPINTERNAL;
169
170
171#ifdef RT_OS_WINDOWS
172/** @name Windows: Types for dynamically resolved APIs
173 * @{ */
174typedef HINTERNET (WINAPI * PFNWINHTTPOPEN)(LPCWSTR, DWORD, LPCWSTR, LPCWSTR, DWORD);
175typedef BOOL (WINAPI * PFNWINHTTPCLOSEHANDLE)(HINTERNET);
176typedef BOOL (WINAPI * PFNWINHTTPGETPROXYFORURL)(HINTERNET, LPCWSTR, WINHTTP_AUTOPROXY_OPTIONS *, WINHTTP_PROXY_INFO *);
177typedef BOOL (WINAPI * PFNWINHTTPGETDEFAULTPROXYCONFIGURATION)(WINHTTP_PROXY_INFO *);
178typedef BOOL (WINAPI * PFNWINHTTPGETIEPROXYCONFIGFORCURRENTUSER)(WINHTTP_CURRENT_USER_IE_PROXY_CONFIG *);
179/** @} */
180#endif
181
182#ifdef IPRT_USE_LIBPROXY
183typedef struct px_proxy_factory *PLIBPROXYFACTORY;
184typedef PLIBPROXYFACTORY (* PFNLIBPROXYFACTORYCTOR)(void);
185typedef void (* PFNLIBPROXYFACTORYDTOR)(PLIBPROXYFACTORY);
186typedef char ** (* PFNLIBPROXYFACTORYGETPROXIES)(PLIBPROXYFACTORY, const char *);
187#endif
188
189
190/*********************************************************************************************************************************
191* Defined Constants And Macros *
192*********************************************************************************************************************************/
193/** @def RTHTTP_MAX_MEM_DOWNLOAD_SIZE
194 * The max size we are allowed to download to a memory buffer.
195 *
196 * @remarks The minus 1 is for the trailing zero terminator we always add.
197 */
198#if ARCH_BITS == 64
199# define RTHTTP_MAX_MEM_DOWNLOAD_SIZE (UINT32_C(64)*_1M - 1)
200#else
201# define RTHTTP_MAX_MEM_DOWNLOAD_SIZE (UINT32_C(32)*_1M - 1)
202#endif
203
204/** Checks whether a cURL return code indicates success. */
205#define CURL_SUCCESS(rcCurl) RT_LIKELY(rcCurl == CURLE_OK)
206/** Checks whether a cURL return code indicates failure. */
207#define CURL_FAILURE(rcCurl) RT_UNLIKELY(rcCurl != CURLE_OK)
208
209/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
210#define RTHTTP_VALID_RETURN_RC(hHttp, rcCurl) \
211 do { \
212 AssertPtrReturn((hHttp), (rcCurl)); \
213 AssertReturn((hHttp)->u32Magic == RTHTTP_MAGIC, (rcCurl)); \
214 } while (0)
215
216/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
217#define RTHTTP_VALID_RETURN(hHTTP) RTHTTP_VALID_RETURN_RC((hHttp), VERR_INVALID_HANDLE)
218
219/** Validates a handle and returns (void) if not valid. */
220#define RTHTTP_VALID_RETURN_VOID(hHttp) \
221 do { \
222 AssertPtrReturnVoid(hHttp); \
223 AssertReturnVoid((hHttp)->u32Magic == RTHTTP_MAGIC); \
224 } while (0)
225
226
227/*********************************************************************************************************************************
228* Global Variables *
229*********************************************************************************************************************************/
230#ifdef RT_OS_WINDOWS
231/** @name Windows: Dynamically resolved APIs
232 * @{ */
233static RTONCE g_WinResolveImportsOnce = RTONCE_INITIALIZER;
234static PFNWINHTTPOPEN g_pfnWinHttpOpen = NULL;
235static PFNWINHTTPCLOSEHANDLE g_pfnWinHttpCloseHandle = NULL;
236static PFNWINHTTPGETPROXYFORURL g_pfnWinHttpGetProxyForUrl = NULL;
237static PFNWINHTTPGETDEFAULTPROXYCONFIGURATION g_pfnWinHttpGetDefaultProxyConfiguration = NULL;
238static PFNWINHTTPGETIEPROXYCONFIGFORCURRENTUSER g_pfnWinHttpGetIEProxyConfigForCurrentUser = NULL;
239/** @} */
240#endif
241
242#ifdef IPRT_USE_LIBPROXY
243/** @name Dynamaically resolved libproxy APIs.
244 * @{ */
245static RTONCE g_LibProxyResolveImportsOnce = RTONCE_INITIALIZER;
246static RTLDRMOD g_hLdrLibProxy = NIL_RTLDRMOD;
247static PFNLIBPROXYFACTORYCTOR g_pfnLibProxyFactoryCtor = NULL;
248static PFNLIBPROXYFACTORYDTOR g_pfnLibProxyFactoryDtor = NULL;
249static PFNLIBPROXYFACTORYGETPROXIES g_pfnLibProxyFactoryGetProxies = NULL;
250/** @} */
251#endif
252
253
254/*********************************************************************************************************************************
255* Internal Functions *
256*********************************************************************************************************************************/
257static void rtHttpUnsetCaFile(PRTHTTPINTERNAL pThis);
258#ifdef RT_OS_DARWIN
259static int rtHttpDarwinTryConfigProxies(PRTHTTPINTERNAL pThis, CFArrayRef hArrayProxies, CFURLRef hUrlTarget, bool fIgnorePacType);
260#endif
261
262
263RTR3DECL(int) RTHttpCreate(PRTHTTP phHttp)
264{
265 AssertPtrReturn(phHttp, VERR_INVALID_PARAMETER);
266
267 /** @todo r=bird: rainy day: curl_global_init is not thread safe, only a
268 * problem if multiple threads get here at the same time. */
269 int rc = VERR_HTTP_INIT_FAILED;
270 CURLcode rcCurl = curl_global_init(CURL_GLOBAL_ALL);
271 if (!CURL_FAILURE(rcCurl))
272 {
273 CURL *pCurl = curl_easy_init();
274 if (pCurl)
275 {
276 PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)RTMemAllocZ(sizeof(RTHTTPINTERNAL));
277 if (pThis)
278 {
279 pThis->u32Magic = RTHTTP_MAGIC;
280 pThis->pCurl = pCurl;
281 pThis->fUseSystemProxySettings = true;
282 pThis->cMaxRedirects = 0; /* no automatic redir following */
283
284 *phHttp = (RTHTTP)pThis;
285
286 return VINF_SUCCESS;
287 }
288 rc = VERR_NO_MEMORY;
289 }
290 else
291 rc = VERR_HTTP_INIT_FAILED;
292 }
293 curl_global_cleanup();
294 return rc;
295}
296
297
298RTR3DECL(int) RTHttpReset(RTHTTP hHttp)
299{
300 if (hHttp == NIL_RTHTTP)
301 return VERR_INVALID_HANDLE;
302
303 PRTHTTPINTERNAL pThis = hHttp;
304 RTHTTP_VALID_RETURN(pThis);
305
306 AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
307
308 /* This resets options, but keeps open connections, cookies, etc. */
309 curl_easy_reset(pThis->pCurl);
310 return VINF_SUCCESS;
311}
312
313
314RTR3DECL(void) RTHttpDestroy(RTHTTP hHttp)
315{
316 if (hHttp == NIL_RTHTTP)
317 return;
318
319 PRTHTTPINTERNAL pThis = hHttp;
320 RTHTTP_VALID_RETURN_VOID(pThis);
321
322 Assert(!pThis->fBusy);
323
324 pThis->u32Magic = RTHTTP_MAGIC_DEAD;
325
326 curl_easy_cleanup(pThis->pCurl);
327 pThis->pCurl = NULL;
328
329 if (pThis->pHeaders)
330 curl_slist_free_all(pThis->pHeaders);
331
332 rtHttpUnsetCaFile(pThis);
333 Assert(!pThis->pszCaFile);
334
335 if (pThis->pszRedirLocation)
336 RTStrFree(pThis->pszRedirLocation);
337
338 RTStrFree(pThis->pszProxyHost);
339 RTStrFree(pThis->pszProxyUsername);
340 if (pThis->pszProxyPassword)
341 {
342 RTMemWipeThoroughly(pThis->pszProxyPassword, strlen(pThis->pszProxyPassword), 2);
343 RTStrFree(pThis->pszProxyPassword);
344 }
345
346 RTMemFree(pThis);
347
348 curl_global_cleanup();
349}
350
351
352RTR3DECL(int) RTHttpAbort(RTHTTP hHttp)
353{
354 PRTHTTPINTERNAL pThis = hHttp;
355 RTHTTP_VALID_RETURN(pThis);
356
357 pThis->fAbort = true;
358
359 return VINF_SUCCESS;
360}
361
362
363RTR3DECL(int) RTHttpGetRedirLocation(RTHTTP hHttp, char **ppszRedirLocation)
364{
365 PRTHTTPINTERNAL pThis = hHttp;
366 RTHTTP_VALID_RETURN(pThis);
367 Assert(!pThis->fBusy);
368
369 if (!pThis->pszRedirLocation)
370 return VERR_HTTP_NOT_FOUND;
371
372 return RTStrDupEx(ppszRedirLocation, pThis->pszRedirLocation);
373}
374
375
376RTR3DECL(int) RTHttpUseSystemProxySettings(RTHTTP hHttp)
377{
378 PRTHTTPINTERNAL pThis = hHttp;
379 RTHTTP_VALID_RETURN(pThis);
380 AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
381
382 /*
383 * Change the settings.
384 */
385 pThis->fUseSystemProxySettings = true;
386 return VINF_SUCCESS;
387}
388
389
390/**
391 * rtHttpConfigureProxyForUrl: Update cURL proxy settings as needed.
392 *
393 * @returns IPRT status code.
394 * @param pThis The HTTP client instance.
395 * @param enmProxyType The proxy type.
396 * @param pszHost The proxy host name.
397 * @param uPort The proxy port number.
398 * @param pszUsername The proxy username, or NULL if none.
399 * @param pszPassword The proxy password, or NULL if none.
400 */
401static int rtHttpUpdateProxyConfig(PRTHTTPINTERNAL pThis, curl_proxytype enmProxyType, const char *pszHost,
402 uint32_t uPort, const char *pszUsername, const char *pszPassword)
403{
404 int rcCurl;
405 AssertReturn(pszHost, VERR_INVALID_PARAMETER);
406 Log(("rtHttpUpdateProxyConfig: pThis=%p type=%d host='%s' port=%u user='%s'%s\n",
407 pThis, enmProxyType, pszHost, uPort, pszUsername, pszPassword ? " with password" : " without password"));
408
409#ifdef CURLOPT_NOPROXY
410 if (pThis->fNoProxy)
411 {
412 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOPROXY, (const char *)NULL);
413 AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_NOPROXY=NULL: %d (%#x)\n", rcCurl, rcCurl),
414 VERR_HTTP_CURL_PROXY_CONFIG);
415 pThis->fNoProxy = false;
416 }
417#endif
418
419 if (enmProxyType != pThis->enmProxyType)
420 {
421 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYTYPE, (long)enmProxyType);
422 AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_PROXYTYPE=%d: %d (%#x)\n", enmProxyType, rcCurl, rcCurl),
423 VERR_HTTP_CURL_PROXY_CONFIG);
424 pThis->enmProxyType = CURLPROXY_HTTP;
425 }
426
427 if (uPort != pThis->uProxyPort)
428 {
429 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, (long)uPort);
430 AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_PROXYPORT=%d: %d (%#x)\n", uPort, rcCurl, rcCurl),
431 VERR_HTTP_CURL_PROXY_CONFIG);
432 pThis->uProxyPort = uPort;
433 }
434
435 if ( pszUsername != pThis->pszProxyUsername
436 || RTStrCmp(pszUsername, pThis->pszProxyUsername))
437 {
438 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYUSERNAME, pszUsername);
439 AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_PROXYUSERNAME=%s: %d (%#x)\n", pszUsername, rcCurl, rcCurl),
440 VERR_HTTP_CURL_PROXY_CONFIG);
441 if (pThis->pszProxyUsername)
442 {
443 RTStrFree(pThis->pszProxyUsername);
444 pThis->pszProxyUsername = NULL;
445 }
446 if (pszUsername)
447 {
448 pThis->pszProxyUsername = RTStrDup(pszUsername);
449 AssertReturn(pThis->pszProxyUsername, VERR_NO_STR_MEMORY);
450 }
451 }
452
453 if ( pszPassword != pThis->pszProxyPassword
454 || RTStrCmp(pszPassword, pThis->pszProxyPassword))
455 {
456 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPASSWORD, pszPassword);
457 AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_PROXYPASSWORD=%s: %d (%#x)\n", pszPassword ? "xxx" : NULL, rcCurl, rcCurl),
458 VERR_HTTP_CURL_PROXY_CONFIG);
459 if (pThis->pszProxyPassword)
460 {
461 RTMemWipeThoroughly(pThis->pszProxyPassword, strlen(pThis->pszProxyPassword), 2);
462 RTStrFree(pThis->pszProxyPassword);
463 pThis->pszProxyPassword = NULL;
464 }
465 if (pszPassword)
466 {
467 pThis->pszProxyPassword = RTStrDup(pszPassword);
468 AssertReturn(pThis->pszProxyPassword, VERR_NO_STR_MEMORY);
469 }
470 }
471
472 if ( pszHost != pThis->pszProxyHost
473 || RTStrCmp(pszHost, pThis->pszProxyHost))
474 {
475 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, pszHost);
476 AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_PROXY=%s: %d (%#x)\n", pszHost, rcCurl, rcCurl),
477 VERR_HTTP_CURL_PROXY_CONFIG);
478 if (pThis->pszProxyHost)
479 {
480 RTStrFree(pThis->pszProxyHost);
481 pThis->pszProxyHost = NULL;
482 }
483 if (pszHost)
484 {
485 pThis->pszProxyHost = RTStrDup(pszHost);
486 AssertReturn(pThis->pszProxyHost, VERR_NO_STR_MEMORY);
487 }
488 }
489
490 return VINF_SUCCESS;
491}
492
493
494/**
495 * rtHttpConfigureProxyForUrl: Disables proxying.
496 *
497 * @returns IPRT status code.
498 * @param pThis The HTTP client instance.
499 */
500static int rtHttpUpdateAutomaticProxyDisable(PRTHTTPINTERNAL pThis)
501{
502 Log(("rtHttpUpdateAutomaticProxyDisable: pThis=%p\n", pThis));
503
504 AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTP) == CURLE_OK, VERR_INTERNAL_ERROR_2);
505 pThis->enmProxyType = CURLPROXY_HTTP;
506
507 AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, (long)1080) == CURLE_OK, VERR_INTERNAL_ERROR_2);
508 pThis->uProxyPort = 1080;
509
510 AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYUSERNAME, (const char *)NULL) == CURLE_OK, VERR_INTERNAL_ERROR_2);
511 if (pThis->pszProxyUsername)
512 {
513 RTStrFree(pThis->pszProxyUsername);
514 pThis->pszProxyUsername = NULL;
515 }
516
517 AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPASSWORD, (const char *)NULL) == CURLE_OK, VERR_INTERNAL_ERROR_2);
518 if (pThis->pszProxyPassword)
519 {
520 RTStrFree(pThis->pszProxyPassword);
521 pThis->pszProxyPassword = NULL;
522 }
523
524 AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, (const char *)NULL) == CURLE_OK, VERR_INTERNAL_ERROR_2);
525 if (pThis->pszProxyHost)
526 {
527 RTStrFree(pThis->pszProxyHost);
528 pThis->pszProxyHost = NULL;
529 }
530
531#ifdef CURLOPT_NOPROXY
532 /* No proxy for everything! */
533 AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_NOPROXY, "*") == CURLE_OK, CURLOPT_PROXY);
534 pThis->fNoProxy = true;
535#endif
536
537 return VINF_SUCCESS;
538}
539
540
541/**
542 * See if the host name of the URL is included in the stripped no_proxy list.
543 *
544 * The no_proxy list is a colon or space separated list of domain names for
545 * which there should be no proxying. Given "no_proxy=oracle.com" neither the
546 * URL "http://www.oracle.com" nor "http://oracle.com" will not be proxied, but
547 * "http://notoracle.com" will be.
548 *
549 * @returns true if the URL is in the no_proxy list, otherwise false.
550 * @param pszUrl The URL.
551 * @param pszNoProxyList The stripped no_proxy list.
552 */
553static bool rtHttpUrlInNoProxyList(const char *pszUrl, const char *pszNoProxyList)
554{
555 /*
556 * Check for just '*', diabling proxying for everything.
557 * (Caller stripped pszNoProxyList.)
558 */
559 if (*pszNoProxyList == '*' && pszNoProxyList[1] == '\0')
560 return true;
561
562 /*
563 * Empty list? (Caller stripped it, remember).
564 */
565 if (!*pszNoProxyList)
566 return false;
567
568 /*
569 * We now need to parse the URL and extract the host name.
570 */
571 RTURIPARSED Parsed;
572 int rc = RTUriParse(pszUrl, &Parsed);
573 AssertRCReturn(rc, false);
574 char *pszHost = RTUriParsedAuthorityHost(pszUrl, &Parsed);
575 if (!pszHost) /* Don't assert, in case of file:///xxx or similar blunder. */
576 return false;
577
578 bool fRet = false;
579 size_t const cchHost = strlen(pszHost);
580 if (cchHost)
581 {
582 /*
583 * The list is comma or space separated, walk it and match host names.
584 */
585 while (*pszNoProxyList != '\0')
586 {
587 /* Strip leading slashes, commas and dots. */
588 char ch;
589 while ( (ch = *pszNoProxyList) == ','
590 || ch == '.'
591 || RT_C_IS_SPACE(ch))
592 pszNoProxyList++;
593
594 /* Find the end. */
595 size_t cch = RTStrOffCharOrTerm(pszNoProxyList, ',');
596 size_t offNext = RTStrOffCharOrTerm(pszNoProxyList, ' ');
597 cch = RT_MIN(cch, offNext);
598 offNext = cch;
599
600 /* Trip trailing spaces, well tabs and stuff. */
601 while (cch > 0 && RT_C_IS_SPACE(pszNoProxyList[cch - 1]))
602 cch--;
603
604 /* Do the matching, if we have anything to work with. */
605 if (cch > 0)
606 {
607 if ( ( cch == cchHost
608 && RTStrNICmp(pszNoProxyList, pszHost, cch) == 0)
609 || ( cch < cchHost
610 && pszHost[cchHost - cch - 1] == '.'
611 && RTStrNICmp(pszNoProxyList, &pszHost[cchHost - cch], cch) == 0) )
612 {
613 fRet = true;
614 break;
615 }
616 }
617
618 /* Next. */
619 pszNoProxyList += offNext;
620 }
621 }
622
623 RTStrFree(pszHost);
624 return fRet;
625}
626
627
628/**
629 * Configures a proxy given a "URL" like specification.
630 *
631 * The format is:
632 * @verbatim
633 * [<scheme>"://"][<userid>[@<password>]:]<server>[":"<port>]
634 * @endverbatim
635 *
636 * Where the scheme gives the type of proxy server we're dealing with rather
637 * than the protocol of the external server we wish to talk to.
638 *
639 * @returns IPRT status code.
640 * @param pThis The HTTP client instance.
641 * @param pszProxyUrl The proxy server "URL".
642 */
643static int rtHttpConfigureProxyFromUrl(PRTHTTPINTERNAL pThis, const char *pszProxyUrl)
644{
645 /*
646 * Make sure it can be parsed as an URL.
647 */
648 char *pszFreeMe = NULL;
649 if (!strstr(pszProxyUrl, "://"))
650 {
651 static const char s_szPrefix[] = "http://";
652 size_t cchProxyUrl = strlen(pszProxyUrl);
653 pszFreeMe = (char *)RTMemTmpAlloc(sizeof(s_szPrefix) + cchProxyUrl);
654 if (pszFreeMe)
655 {
656 memcpy(pszFreeMe, s_szPrefix, sizeof(s_szPrefix) - 1);
657 memcpy(&pszFreeMe[sizeof(s_szPrefix) - 1], pszProxyUrl, cchProxyUrl);
658 pszFreeMe[sizeof(s_szPrefix) - 1 + cchProxyUrl] = '\0';
659 pszProxyUrl = pszFreeMe;
660 }
661 else
662 return VERR_NO_TMP_MEMORY;
663 }
664
665 RTURIPARSED Parsed;
666 int rc = RTUriParse(pszProxyUrl, &Parsed);
667 if (RT_SUCCESS(rc))
668 {
669 char *pszHost = RTUriParsedAuthorityHost(pszProxyUrl, &Parsed);
670 if (pszHost)
671 {
672 /*
673 * We've got a host name, try get the rest.
674 */
675 char *pszUsername = RTUriParsedAuthorityUsername(pszProxyUrl, &Parsed);
676 char *pszPassword = RTUriParsedAuthorityPassword(pszProxyUrl, &Parsed);
677 uint32_t uProxyPort = RTUriParsedAuthorityPort(pszProxyUrl, &Parsed);
678 curl_proxytype enmProxyType;
679 if (RTUriIsSchemeMatch(pszProxyUrl, "http"))
680 {
681 enmProxyType = CURLPROXY_HTTP;
682 if (uProxyPort == UINT32_MAX)
683 uProxyPort = 80;
684 }
685 else if ( RTUriIsSchemeMatch(pszProxyUrl, "socks4")
686 || RTUriIsSchemeMatch(pszProxyUrl, "socks"))
687 enmProxyType = CURLPROXY_SOCKS4;
688 else if (RTUriIsSchemeMatch(pszProxyUrl, "socks4a"))
689 enmProxyType = CURLPROXY_SOCKS4A;
690 else if (RTUriIsSchemeMatch(pszProxyUrl, "socks5"))
691 enmProxyType = CURLPROXY_SOCKS5;
692 else if (RTUriIsSchemeMatch(pszProxyUrl, "socks5h"))
693 enmProxyType = CURLPROXY_SOCKS5_HOSTNAME;
694 else
695 {
696 enmProxyType = CURLPROXY_HTTP;
697 if (uProxyPort == UINT32_MAX)
698 uProxyPort = 8080;
699 }
700
701 /* Guess the port from the proxy type if not given. */
702 if (uProxyPort == UINT32_MAX)
703 uProxyPort = 1080; /* CURL_DEFAULT_PROXY_PORT */
704
705 rc = rtHttpUpdateProxyConfig(pThis, enmProxyType, pszHost, uProxyPort, pszUsername, pszPassword);
706
707 RTStrFree(pszUsername);
708 RTStrFree(pszPassword);
709 RTStrFree(pszHost);
710 }
711 else
712 AssertMsgFailed(("RTUriParsedAuthorityHost('%s',) -> NULL\n", pszProxyUrl));
713 }
714 else
715 AssertMsgFailed(("RTUriParse('%s',) -> %Rrc\n", pszProxyUrl, rc));
716
717 if (pszFreeMe)
718 RTMemTmpFree(pszFreeMe);
719 return rc;
720}
721
722
723/**
724 * Consults enviornment variables that cURL/lynx/wget/lynx uses for figuring out
725 * the proxy config.
726 *
727 * @returns IPRT status code.
728 * @param pThis The HTTP client instance.
729 * @param pszUrl The URL to configure a proxy for.
730 */
731static int rtHttpConfigureProxyForUrlFromEnv(PRTHTTPINTERNAL pThis, const char *pszUrl)
732{
733 char szTmp[_1K];
734
735 /*
736 * First we consult the "no_proxy" / "NO_PROXY" environment variable.
737 */
738 const char *pszNoProxyVar;
739 size_t cchActual;
740 char *pszNoProxyFree = NULL;
741 char *pszNoProxy = szTmp;
742 int rc = RTEnvGetEx(RTENV_DEFAULT, pszNoProxyVar = "no_proxy", szTmp, sizeof(szTmp), &cchActual);
743 if (rc == VERR_ENV_VAR_NOT_FOUND)
744 rc = RTEnvGetEx(RTENV_DEFAULT, pszNoProxyVar = "NO_PROXY", szTmp, sizeof(szTmp), &cchActual);
745 if (rc == VERR_BUFFER_OVERFLOW)
746 {
747 pszNoProxyFree = pszNoProxy = (char *)RTMemTmpAlloc(cchActual + _1K);
748 AssertReturn(pszNoProxy, VERR_NO_TMP_MEMORY);
749 rc = RTEnvGetEx(RTENV_DEFAULT, pszNoProxyVar, pszNoProxy, cchActual + _1K, NULL);
750 }
751 AssertMsg(rc == VINF_SUCCESS || rc == VERR_ENV_VAR_NOT_FOUND, ("rc=%Rrc\n", rc));
752 bool fNoProxy = false;
753 if (RT_SUCCESS(rc))
754 fNoProxy = rtHttpUrlInNoProxyList(pszUrl, RTStrStrip(pszNoProxy));
755 RTMemTmpFree(pszNoProxyFree);
756 if (!fNoProxy)
757 {
758 /*
759 * Get the schema specific specific env var, falling back on the
760 * generic all_proxy if not found.
761 */
762 const char *apszEnvVars[4];
763 unsigned cEnvVars = 0;
764 if (!RTStrNICmp(pszUrl, RT_STR_TUPLE("http:")))
765 apszEnvVars[cEnvVars++] = "http_proxy"; /* Skip HTTP_PROXY because of cgi paranoia */
766 else if (!RTStrNICmp(pszUrl, RT_STR_TUPLE("https:")))
767 {
768 apszEnvVars[cEnvVars++] = "https_proxy";
769 apszEnvVars[cEnvVars++] = "HTTPS_PROXY";
770 }
771 else if (!RTStrNICmp(pszUrl, RT_STR_TUPLE("ftp:")))
772 {
773 apszEnvVars[cEnvVars++] = "ftp_proxy";
774 apszEnvVars[cEnvVars++] = "FTP_PROXY";
775 }
776 else
777 AssertMsgFailedReturn(("Unknown/unsupported schema in URL: '%s'\n", pszUrl), VERR_NOT_SUPPORTED);
778 apszEnvVars[cEnvVars++] = "all_proxy";
779 apszEnvVars[cEnvVars++] = "ALL_PROXY";
780
781 /*
782 * We try the env vars out and goes with the first one we can make sense out of.
783 * If we cannot make sense of any, we return the first unexpected rc we got.
784 */
785 rc = VINF_SUCCESS;
786 for (uint32_t i = 0; i < cEnvVars; i++)
787 {
788 size_t cchValue;
789 int rc2 = RTEnvGetEx(RTENV_DEFAULT, apszEnvVars[i], szTmp, sizeof(szTmp) - sizeof("http://"), &cchValue);
790 if (RT_SUCCESS(rc2))
791 {
792 if (cchValue != 0)
793 {
794 /* Add a http:// prefix so RTUriParse groks it (cheaper to do it here). */
795 if (!strstr(szTmp, "://"))
796 {
797 memmove(&szTmp[sizeof("http://") - 1], szTmp, cchValue + 1);
798 memcpy(szTmp, RT_STR_TUPLE("http://"));
799 }
800
801 rc2 = rtHttpConfigureProxyFromUrl(pThis, szTmp);
802 if (RT_SUCCESS(rc2))
803 rc = rc2;
804 }
805 /*
806 * The variable is empty. Guess that means no proxying wanted.
807 */
808 else
809 {
810 rc = rtHttpUpdateAutomaticProxyDisable(pThis);
811 break;
812 }
813 }
814 else
815 AssertMsgStmt(rc2 == VERR_ENV_VAR_NOT_FOUND, ("%Rrc\n", rc2), if (RT_SUCCESS(rc)) rc = rc2);
816 }
817 }
818 /*
819 * The host is the no-proxy list, it seems.
820 */
821 else
822 rc = rtHttpUpdateAutomaticProxyDisable(pThis);
823
824 return rc;
825}
826
827#ifdef IPRT_USE_LIBPROXY
828
829/**
830 * @callback_method_impl{FNRTONCE,
831 * Attempts to load libproxy.so.1 and resolves APIs}
832 */
833static DECLCALLBACK(int) rtHttpLibProxyResolveImports(void *pvUser)
834{
835 RTLDRMOD hMod;
836 int rc = RTLdrLoad("/usr/lib/libproxy.so.1", &hMod);
837 if (RT_SUCCESS(rc))
838 {
839 rc = RTLdrGetSymbol(hMod, "px_proxy_factory_new", (void **)&g_pfnLibProxyFactoryCtor);
840 if (RT_SUCCESS(rc))
841 rc = RTLdrGetSymbol(hMod, "px_proxy_factory_free", (void **)&g_pfnLibProxyFactoryDtor);
842 if (RT_SUCCESS(rc))
843 rc = RTLdrGetSymbol(hMod, "px_proxy_factory_get_proxies", (void **)&g_pfnLibProxyFactoryGetProxies);
844 if (RT_SUCCESS(rc))
845 g_hLdrLibProxy = hMod;
846 else
847 RTLdrClose(hMod);
848 AssertRC(rc);
849 }
850
851 NOREF(pvUser);
852 return rc;
853}
854
855/**
856 * Reconfigures the cURL proxy settings for the given URL, libproxy style.
857 *
858 * @returns IPRT status code. VINF_NOT_SUPPORTED if we should try fallback.
859 * @param pThis The HTTP client instance.
860 * @param pszUrl The URL.
861 */
862static int rtHttpLibProxyConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
863{
864 int rcRet = VINF_NOT_SUPPORTED;
865
866 int rc = RTOnce(&g_LibProxyResolveImportsOnce, rtHttpLibProxyResolveImports, NULL);
867 if (RT_SUCCESS(rc))
868 {
869 /*
870 * Instance the factory and ask for a list of proxies.
871 */
872 PLIBPROXYFACTORY pFactory = g_pfnLibProxyFactoryCtor();
873 if (pFactory)
874 {
875 char **papszProxies = g_pfnLibProxyFactoryGetProxies(pFactory, pszUrl);
876 g_pfnLibProxyFactoryDtor(pFactory);
877 if (papszProxies)
878 {
879 /*
880 * Look for something we can use.
881 */
882 for (unsigned i = 0; papszProxies[i]; i++)
883 {
884 if (strncmp(papszProxies[i], RT_STR_TUPLE("direct://")) == 0)
885 rcRet = rtHttpUpdateAutomaticProxyDisable(pThis);
886 else if ( strncmp(papszProxies[i], RT_STR_TUPLE("http://")) == 0
887 || strncmp(papszProxies[i], RT_STR_TUPLE("socks5://")) == 0
888 || strncmp(papszProxies[i], RT_STR_TUPLE("socks4://")) == 0
889 || strncmp(papszProxies[i], RT_STR_TUPLE("socks://")) == 0 /** @todo same problem as on OS X. */
890 )
891 rcRet = rtHttpConfigureProxyFromUrl(pThis, papszProxies[i]);
892 else
893 continue;
894 if (rcRet != VINF_NOT_SUPPORTED)
895 break;
896 }
897
898 /* free the result. */
899 for (unsigned i = 0; papszProxies[i]; i++)
900 free(papszProxies[i]);
901 free(papszProxies);
902 }
903 }
904 }
905
906 return rcRet;
907}
908
909#endif /* IPRT_USE_LIBPROXY */
910
911#ifdef RT_OS_DARWIN
912
913/**
914 * Get a boolean like integer value from a dictionary.
915 *
916 * @returns true / false.
917 * @param hDict The dictionary.
918 * @param pvKey The dictionary value key.
919 */
920static bool rtHttpDarwinGetBooleanFromDict(CFDictionaryRef hDict, void const *pvKey, bool fDefault)
921{
922 CFNumberRef hNum = (CFNumberRef)CFDictionaryGetValue(hDict, pvKey);
923 if (hNum)
924 {
925 int fEnabled;
926 if (!CFNumberGetValue(hNum, kCFNumberIntType, &fEnabled))
927 return fDefault;
928 return fEnabled != 0;
929 }
930 return fDefault;
931}
932
933
934/**
935 * Creates a CFURL object for an URL.
936 *
937 * @returns CFURL object reference.
938 * @param pszUrl The URL.
939 */
940static CFURLRef rtHttpDarwinUrlToCFURL(const char *pszUrl)
941{
942 CFURLRef hUrl = NULL;
943 CFStringRef hStrUrl = CFStringCreateWithCString(kCFAllocatorDefault, pszUrl, kCFStringEncodingUTF8);
944 if (hStrUrl)
945 {
946 CFStringRef hStrUrlEscaped = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, hStrUrl,
947 NULL /*charactersToLeaveUnescaped*/,
948 NULL /*legalURLCharactersToBeEscaped*/,
949 kCFStringEncodingUTF8);
950 if (hStrUrlEscaped)
951 {
952 hUrl = CFURLCreateWithString(kCFAllocatorDefault, hStrUrlEscaped, NULL /*baseURL*/);
953 Assert(hUrl);
954 CFRelease(hStrUrlEscaped);
955 }
956 else
957 AssertFailed();
958 CFRelease(hStrUrl);
959 }
960 else
961 AssertFailed();
962 return hUrl;
963}
964
965
966/**
967 * For passing results from rtHttpDarwinPacCallback to
968 * rtHttpDarwinExecuteProxyAutoConfigurationUrl.
969 */
970typedef struct RTHTTPDARWINPACRESULT
971{
972 CFArrayRef hArrayProxies;
973 CFErrorRef hError;
974} RTHTTPDARWINPACRESULT;
975typedef RTHTTPDARWINPACRESULT *PRTHTTPDARWINPACRESULT;
976
977/**
978 * Stupid callback for getting the result from
979 * CFNetworkExecuteProxyAutoConfigurationURL.
980 *
981 * @param pvUser Pointer to a RTHTTPDARWINPACRESULT on the stack of
982 * rtHttpDarwinExecuteProxyAutoConfigurationUrl.
983 * @param hArrayProxies The result array.
984 * @param hError Errors, if any.
985 */
986static void rtHttpDarwinPacCallback(void *pvUser, CFArrayRef hArrayProxies, CFErrorRef hError)
987{
988 PRTHTTPDARWINPACRESULT pResult = (PRTHTTPDARWINPACRESULT)pvUser;
989
990 Assert(pResult->hArrayProxies == NULL);
991 if (hArrayProxies)
992 pResult->hArrayProxies = (CFArrayRef)CFRetain(hArrayProxies);
993
994 Assert(pResult->hError == NULL);
995 if (hError)
996 pResult->hError = (CFErrorRef)CFRetain(hError);
997
998 CFRunLoopStop(CFRunLoopGetCurrent());
999}
1000
1001
1002/**
1003 * Executes a PAC script and returning the proxies it suggests.
1004 *
1005 * @returns Array of proxy configs (CFProxySupport.h style).
1006 * @param hUrlTarget The URL we're about to use.
1007 * @param hUrlScript The PAC script URL.
1008 */
1009static CFArrayRef rtHttpDarwinExecuteProxyAutoConfigurationUrl(CFURLRef hUrlTarget, CFURLRef hUrlScript)
1010{
1011 char szTmp[256];
1012 if (LogIsFlowEnabled())
1013 {
1014 szTmp[0] = '\0';
1015 CFStringGetCString(CFURLGetString(hUrlScript), szTmp, sizeof(szTmp), kCFStringEncodingUTF8);
1016 LogFlow(("rtHttpDarwinExecuteProxyAutoConfigurationUrl: hUrlScript=%p:%s\n", hUrlScript, szTmp));
1017 }
1018
1019 /*
1020 * Use CFNetworkExecuteProxyAutoConfigurationURL here so we don't have to
1021 * download the script ourselves and mess around with too many CF APIs.
1022 */
1023 CFRunLoopRef hRunLoop = CFRunLoopGetCurrent();
1024 AssertReturn(hRunLoop, NULL);
1025
1026 RTHTTPDARWINPACRESULT Result = { NULL, NULL };
1027 CFStreamClientContext Ctx = { 0, &Result, NULL, NULL, NULL };
1028 CFRunLoopSourceRef hRunLoopSrc = CFNetworkExecuteProxyAutoConfigurationURL(hUrlScript, hUrlTarget,
1029 rtHttpDarwinPacCallback, &Ctx);
1030 AssertReturn(hRunLoopSrc, NULL);
1031
1032 CFStringRef kMode = CFSTR("com.apple.dts.CFProxySupportTool");
1033 CFRunLoopAddSource(hRunLoop, hRunLoopSrc, kMode);
1034 CFRunLoopRunInMode(kMode, 1.0e10, false); /* callback will force a return. */
1035 CFRunLoopRemoveSource(hRunLoop, hRunLoopSrc, kMode);
1036
1037 /** @todo convert errors, maybe even fail. */
1038
1039 /*
1040 * Autoconfig (or missing wpad server) typically results in:
1041 * domain:kCFErrorDomainCFNetwork; code=kCFHostErrorUnknown (2).
1042 *
1043 * In the autoconfig case, it looks like we're getting two entries, first
1044 * one that's http://wpad/wpad.dat and a noproxy entry. So, no reason to
1045 * be very upset if this fails, just continue trying alternatives.
1046 */
1047 if (Result.hError)
1048 {
1049 if (LogIsEnabled())
1050 {
1051 szTmp[0] = '\0';
1052 CFStringGetCString(CFErrorCopyDescription(Result.hError), szTmp, sizeof(szTmp), kCFStringEncodingUTF8);
1053 Log(("rtHttpDarwinExecuteProxyAutoConfigurationUrl: error! code=%ld desc='%s'\n", (long)CFErrorGetCode(Result.hError), szTmp));
1054 }
1055 CFRelease(Result.hError);
1056 }
1057 return Result.hArrayProxies;
1058}
1059
1060
1061/**
1062 * Attempt to configure the proxy according to @a hDictProxy.
1063 *
1064 * @returns IPRT status code. VINF_NOT_SUPPORTED if not able to configure it and
1065 * the caller should try out alternative proxy configs and fallbacks.
1066 * @param pThis The HTTP client instance.
1067 * @param hDictProxy The proxy configuration (see CFProxySupport.h).
1068 * @param hUrlTarget The URL we're about to use.
1069 * @param fIgnorePacType Whether to ignore PAC type proxy entries (i.e.
1070 * javascript URL). This is set when we're processing
1071 * the output from a PAC script.
1072 */
1073static int rtHttpDarwinTryConfigProxy(PRTHTTPINTERNAL pThis, CFDictionaryRef hDictProxy, CFURLRef hUrlTarget, bool fIgnorePacType)
1074{
1075 CFStringRef hStrProxyType = (CFStringRef)CFDictionaryGetValue(hDictProxy, kCFProxyTypeKey);
1076 AssertReturn(hStrProxyType, VINF_NOT_SUPPORTED);
1077
1078 /*
1079 * No proxy is fairly simple and common.
1080 */
1081 if (CFEqual(hStrProxyType, kCFProxyTypeNone))
1082 return rtHttpUpdateAutomaticProxyDisable(pThis);
1083
1084 /*
1085 * PAC URL means recursion, however we only do one level.
1086 */
1087 if (CFEqual(hStrProxyType, kCFProxyTypeAutoConfigurationURL))
1088 {
1089 AssertReturn(!fIgnorePacType, VINF_NOT_SUPPORTED);
1090
1091 CFURLRef hUrlScript = (CFURLRef)CFDictionaryGetValue(hDictProxy, kCFProxyAutoConfigurationURLKey);
1092 AssertReturn(hUrlScript, VINF_NOT_SUPPORTED);
1093
1094 int rcRet = VINF_NOT_SUPPORTED;
1095 CFArrayRef hArray = rtHttpDarwinExecuteProxyAutoConfigurationUrl(hUrlTarget, hUrlScript);
1096 if (hArray)
1097 {
1098 rcRet = rtHttpDarwinTryConfigProxies(pThis, hArray, hUrlTarget, true /*fIgnorePacType*/);
1099 CFRelease(hArray);
1100 }
1101 return rcRet;
1102 }
1103
1104 /*
1105 * Determine the proxy type (not entirely sure about type == proxy type and
1106 * not scheme/protocol)...
1107 */
1108 curl_proxytype enmProxyType = CURLPROXY_HTTP;
1109 uint32_t uDefaultProxyPort = 8080;
1110 if ( CFEqual(hStrProxyType, kCFProxyTypeHTTP)
1111 || CFEqual(hStrProxyType, kCFProxyTypeHTTPS))
1112 { /* defaults */ }
1113 else if (CFEqual(hStrProxyType, kCFProxyTypeSOCKS))
1114 {
1115 /** @todo All we get from darwin is 'SOCKS', no idea whether it's SOCK4 or
1116 * SOCK5 on the other side... Selecting SOCKS5 for now. */
1117 enmProxyType = CURLPROXY_SOCKS5;
1118 uDefaultProxyPort = 1080;
1119 }
1120 /* Unknown proxy type. */
1121 else
1122 return VINF_NOT_SUPPORTED;
1123
1124 /*
1125 * Extract the proxy configuration.
1126 */
1127 /* The proxy host name. */
1128 char szHostname[_1K];
1129 CFStringRef hStr = (CFStringRef)CFDictionaryGetValue(hDictProxy, kCFProxyHostNameKey);
1130 AssertReturn(hStr, VINF_NOT_SUPPORTED);
1131 AssertReturn(CFStringGetCString(hStr, szHostname, sizeof(szHostname), kCFStringEncodingUTF8), VINF_NOT_SUPPORTED);
1132
1133 /* Get the port number (optional). */
1134 SInt32 iProxyPort;
1135 CFNumberRef hNum = (CFNumberRef)CFDictionaryGetValue(hDictProxy, kCFProxyPortNumberKey);
1136 if (hNum && CFNumberGetValue(hNum, kCFNumberSInt32Type, &iProxyPort))
1137 AssertMsgStmt(iProxyPort > 0 && iProxyPort < _64K, ("%d\n", iProxyPort), iProxyPort = uDefaultProxyPort);
1138 else
1139 iProxyPort = uDefaultProxyPort;
1140
1141 /* The proxy username. */
1142 char szUsername[256];
1143 hStr = (CFStringRef)CFDictionaryGetValue(hDictProxy, kCFProxyUsernameKey);
1144 if (hStr)
1145 AssertReturn(CFStringGetCString(hStr, szUsername, sizeof(szUsername), kCFStringEncodingUTF8), VINF_NOT_SUPPORTED);
1146 else
1147 szUsername[0] = '\0';
1148
1149 /* The proxy password. */
1150 char szPassword[384];
1151 hStr = (CFStringRef)CFDictionaryGetValue(hDictProxy, kCFProxyPasswordKey);
1152 if (hStr)
1153 AssertReturn(CFStringGetCString(hStr, szPassword, sizeof(szPassword), kCFStringEncodingUTF8), VINF_NOT_SUPPORTED);
1154 else
1155 szPassword[0] = '\0';
1156
1157 /*
1158 * Apply the proxy config.
1159 */
1160 return rtHttpUpdateProxyConfig(pThis, enmProxyType, szHostname, iProxyPort,
1161 szUsername[0] ? szUsername : NULL, szPassword[0] ? szPassword : NULL);
1162}
1163
1164
1165/**
1166 * Try do proxy config for our HTTP client instance given an array of proxies.
1167 *
1168 * This is used with the output from a CFProxySupport.h API.
1169 *
1170 * @returns IPRT status code. VINF_NOT_SUPPORTED if not able to configure it and
1171 * we might want to try out fallbacks.
1172 * @param pThis The HTTP client instance.
1173 * @param hArrayProxies The proxies CFPRoxySupport have given us.
1174 * @param hUrlTarget The URL we're about to use.
1175 * @param fIgnorePacType Whether to ignore PAC type proxy entries (i.e.
1176 * javascript URL). This is set when we're processing
1177 * the output from a PAC script.
1178 */
1179static int rtHttpDarwinTryConfigProxies(PRTHTTPINTERNAL pThis, CFArrayRef hArrayProxies, CFURLRef hUrlTarget, bool fIgnorePacType)
1180{
1181 int rcRet = VINF_NOT_SUPPORTED;
1182 CFIndex const cEntries = CFArrayGetCount(hArrayProxies);
1183 LogFlow(("rtHttpDarwinTryConfigProxies: cEntries=%d\n", cEntries));
1184 for (CFIndex i = 0; i < cEntries; i++)
1185 {
1186 CFDictionaryRef hDictProxy = (CFDictionaryRef)CFArrayGetValueAtIndex(hArrayProxies, i);
1187 AssertContinue(hDictProxy);
1188
1189 rcRet = rtHttpDarwinTryConfigProxy(pThis, hDictProxy, hUrlTarget, fIgnorePacType);
1190 if (rcRet != VINF_NOT_SUPPORTED)
1191 break;
1192 }
1193 return rcRet;
1194}
1195
1196
1197/**
1198 * Inner worker for rtHttpWinConfigureProxyForUrl.
1199 *
1200 * @returns IPRT status code. VINF_NOT_SUPPORTED if we should try fallback.
1201 * @param pThis The HTTP client instance.
1202 * @param pszUrl The URL.
1203 */
1204static int rtHttpDarwinConfigureProxyForUrlWorker(PRTHTTPINTERNAL pThis, CFDictionaryRef hDictProxies,
1205 const char *pszUrl, const char *pszHost)
1206{
1207 CFArrayRef hArray;
1208
1209 /*
1210 * From what I can tell, the CFNetworkCopyProxiesForURL API doesn't apply
1211 * proxy exclusion rules (tested on 10.9). So, do that manually.
1212 */
1213 RTNETADDRU HostAddr;
1214 int fIsHostIpv4Address = -1;
1215 char szTmp[_4K];
1216
1217 /* If we've got a simple hostname, something containing no dots, we must check
1218 whether such simple hostnames are excluded from proxying by default or not. */
1219 if (strchr(pszHost, '.') == NULL)
1220 {
1221 if (rtHttpDarwinGetBooleanFromDict(hDictProxies, kSCPropNetProxiesExcludeSimpleHostnames, false))
1222 return rtHttpUpdateAutomaticProxyDisable(pThis);
1223 fIsHostIpv4Address = false;
1224 }
1225
1226 /* Consult the exclusion list. This is an array of strings.
1227 This is very similar to what we do on windows. */
1228 hArray = (CFArrayRef)CFDictionaryGetValue(hDictProxies, kSCPropNetProxiesExceptionsList);
1229 if (hArray)
1230 {
1231 CFIndex const cEntries = CFArrayGetCount(hArray);
1232 for (CFIndex i = 0; i < cEntries; i++)
1233 {
1234 CFStringRef hStr = (CFStringRef)CFArrayGetValueAtIndex(hArray, i);
1235 AssertContinue(hStr);
1236 AssertContinue(CFStringGetCString(hStr, szTmp, sizeof(szTmp), kCFStringEncodingUTF8));
1237 RTStrToLower(szTmp);
1238
1239 bool fRet;
1240 if ( strchr(szTmp, '*')
1241 || strchr(szTmp, '?'))
1242 fRet = RTStrSimplePatternMatch(szTmp, pszHost);
1243 else
1244 {
1245 if (fIsHostIpv4Address == -1)
1246 fIsHostIpv4Address = RT_SUCCESS(RTNetStrToIPv4Addr(pszHost, &HostAddr.IPv4));
1247 RTNETADDRIPV4 Network, Netmask;
1248 if ( fIsHostIpv4Address
1249 && RT_SUCCESS(RTCidrStrToIPv4(szTmp, &Network, &Netmask)) )
1250 fRet = (HostAddr.IPv4.u & Netmask.u) == Network.u;
1251 else
1252 fRet = strcmp(szTmp, pszHost) == 0;
1253 }
1254 if (fRet)
1255 return rtHttpUpdateAutomaticProxyDisable(pThis);
1256 }
1257 }
1258
1259#if 0 /* The start of a manual alternative to CFNetworkCopyProxiesForURL below, hopefully we won't need this. */
1260 /*
1261 * Is proxy auto config (PAC) enabled? If so, we must consult it first.
1262 */
1263 if (rtHttpDarwinGetBooleanFromDict(hDictProxies, kSCPropNetProxiesProxyAutoConfigEnable, false))
1264 {
1265 /* Convert the auto config url string to a CFURL object. */
1266 CFStringRef hStrAutoConfigUrl = (CFStringRef)CFDictionaryGetValue(hDictProxies, kSCPropNetProxiesProxyAutoConfigURLString);
1267 if (hStrAutoConfigUrl)
1268 {
1269 if (CFStringGetCString(hStrAutoConfigUrl, szTmp, sizeof(szTmp), kCFStringEncodingUTF8))
1270 {
1271 CFURLRef hUrlScript = rtHttpDarwinUrlToCFURL(szTmp);
1272 if (hUrlScript)
1273 {
1274 int rcRet = VINF_NOT_SUPPORTED;
1275 CFURLRef hUrlTarget = rtHttpDarwinUrlToCFURL(pszUrl);
1276 if (hUrlTarget)
1277 {
1278 /* Work around for <rdar://problem/5530166>, whatever that is. Initializes
1279 some internal CFNetwork state, they say. See CFPRoxySupportTool example. */
1280 hArray = CFNetworkCopyProxiesForURL(hUrlTarget, NULL);
1281 if (hArray)
1282 CFRelease(hArray);
1283
1284 hArray = rtHttpDarwinExecuteProxyAutoConfigurationUrl(hUrlTarget, hUrlScript);
1285 if (hArray)
1286 {
1287 rcRet = rtHttpDarwinTryConfigProxies(pThis, hArray, hUrlTarget, true /*fIgnorePacType*/);
1288 CFRelease(hArray);
1289 }
1290 }
1291 CFRelease(hUrlScript);
1292 if (rcRet != VINF_NOT_SUPPORTED)
1293 return rcRet;
1294 }
1295 }
1296 }
1297 }
1298
1299 /*
1300 * Try static proxy configs.
1301 */
1302 /** @todo later if needed. */
1303 return VERR_NOT_SUPPORTED;
1304
1305#else
1306 /*
1307 * Simple solution - "just" use CFNetworkCopyProxiesForURL.
1308 */
1309 CFURLRef hUrlTarget = rtHttpDarwinUrlToCFURL(pszUrl);
1310 AssertReturn(hUrlTarget, VERR_INTERNAL_ERROR);
1311 int rcRet = VINF_NOT_SUPPORTED;
1312
1313 /* Work around for <rdar://problem/5530166>, whatever that is. Initializes
1314 some internal CFNetwork state, they say. See CFPRoxySupportTool example. */
1315 hArray = CFNetworkCopyProxiesForURL(hUrlTarget, NULL);
1316 if (hArray)
1317 CFRelease(hArray);
1318
1319 /* The actual run. */
1320 hArray = CFNetworkCopyProxiesForURL(hUrlTarget, hDictProxies);
1321 if (hArray)
1322 {
1323 rcRet = rtHttpDarwinTryConfigProxies(pThis, hArray, hUrlTarget, false /*fIgnorePacType*/);
1324 CFRelease(hArray);
1325 }
1326 CFRelease(hUrlTarget);
1327
1328 return rcRet;
1329#endif
1330}
1331
1332/**
1333 * Reconfigures the cURL proxy settings for the given URL, OS X style.
1334 *
1335 * @returns IPRT status code. VINF_NOT_SUPPORTED if we should try fallback.
1336 * @param pThis The HTTP client instance.
1337 * @param pszUrl The URL.
1338 */
1339static int rtHttpDarwinConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
1340{
1341 /*
1342 * Parse the URL, if there isn't any host name (like for file:///xxx.txt)
1343 * we don't need to run thru proxy settings to know what to do.
1344 */
1345 RTURIPARSED Parsed;
1346 int rc = RTUriParse(pszUrl, &Parsed);
1347 AssertRCReturn(rc, false);
1348 if (Parsed.cchAuthorityHost == 0)
1349 return rtHttpUpdateAutomaticProxyDisable(pThis);
1350 char *pszHost = RTUriParsedAuthorityHost(pszUrl, &Parsed);
1351 AssertReturn(pszHost, VERR_NO_STR_MEMORY);
1352 RTStrToLower(pszHost);
1353
1354 /*
1355 * Get a copy of the proxy settings (10.6 API).
1356 */
1357 CFDictionaryRef hDictProxies = CFNetworkCopySystemProxySettings(); /* Alt for 10.5: SCDynamicStoreCopyProxies(NULL); */
1358 if (hDictProxies)
1359 rc = rtHttpDarwinConfigureProxyForUrlWorker(pThis, hDictProxies, pszUrl, pszHost);
1360 else
1361 rc = VINF_NOT_SUPPORTED;
1362 CFRelease(hDictProxies);
1363
1364 RTStrFree(pszHost);
1365 return rc;
1366}
1367
1368#endif /* RT_OS_DARWIN */
1369
1370#ifdef RT_OS_WINDOWS
1371
1372/**
1373 * @callback_method_impl{FNRTONCE, Loads WinHttp.dll and resolves APIs}
1374 */
1375static DECLCALLBACK(int) rtHttpWinResolveImports(void *pvUser)
1376{
1377 /*
1378 * winhttp.dll is not present on NT4 and probably was first introduced with XP.
1379 */
1380 RTLDRMOD hMod;
1381 int rc = RTLdrLoadSystem("winhttp.dll", true /*fNoUnload*/, &hMod);
1382 if (RT_SUCCESS(rc))
1383 {
1384 rc = RTLdrGetSymbol(hMod, "WinHttpOpen", (void **)&g_pfnWinHttpOpen);
1385 if (RT_SUCCESS(rc))
1386 rc = RTLdrGetSymbol(hMod, "WinHttpCloseHandle", (void **)&g_pfnWinHttpCloseHandle);
1387 if (RT_SUCCESS(rc))
1388 rc = RTLdrGetSymbol(hMod, "WinHttpGetProxyForUrl", (void **)&g_pfnWinHttpGetProxyForUrl);
1389 if (RT_SUCCESS(rc))
1390 rc = RTLdrGetSymbol(hMod, "WinHttpGetDefaultProxyConfiguration", (void **)&g_pfnWinHttpGetDefaultProxyConfiguration);
1391 if (RT_SUCCESS(rc))
1392 rc = RTLdrGetSymbol(hMod, "WinHttpGetIEProxyConfigForCurrentUser", (void **)&g_pfnWinHttpGetIEProxyConfigForCurrentUser);
1393 RTLdrClose(hMod);
1394 AssertRC(rc);
1395 }
1396 else
1397 AssertMsg(g_enmWinVer < kRTWinOSType_XP, ("%Rrc\n", rc));
1398
1399 NOREF(pvUser);
1400 return rc;
1401}
1402
1403
1404/**
1405 * Matches the URL against the given Windows by-pass list.
1406 *
1407 * @returns true if we should by-pass the proxy for this URL, false if not.
1408 * @param pszUrl The URL.
1409 * @param pwszBypass The Windows by-pass list.
1410 */
1411static bool rtHttpWinIsUrlInBypassList(const char *pszUrl, PCRTUTF16 pwszBypass)
1412{
1413 /*
1414 * Don't bother parsing the URL if we've actually got nothing to work with
1415 * in the by-pass list.
1416 */
1417 if (!pwszBypass)
1418 return false;
1419
1420 RTUTF16 wc;
1421 while ( (wc = *pwszBypass) != '\0'
1422 && ( RTUniCpIsSpace(wc)
1423 || wc == ';') )
1424 pwszBypass++;
1425 if (wc == '\0')
1426 return false;
1427
1428 /*
1429 * We now need to parse the URL and extract the host name.
1430 */
1431 RTURIPARSED Parsed;
1432 int rc = RTUriParse(pszUrl, &Parsed);
1433 AssertRCReturn(rc, false);
1434 char *pszHost = RTUriParsedAuthorityHost(pszUrl, &Parsed);
1435 if (!pszHost) /* Don't assert, in case of file:///xxx or similar blunder. */
1436 return false;
1437 RTStrToLower(pszHost);
1438
1439 bool fRet = false;
1440 char *pszBypassFree;
1441 rc = RTUtf16ToUtf8(pwszBypass, &pszBypassFree);
1442 if (RT_SUCCESS(rc))
1443 {
1444 /*
1445 * Walk the by-pass list.
1446 *
1447 * According to https://msdn.microsoft.com/en-us/library/aa384098(v=vs.85).aspx
1448 * a by-pass list is semicolon delimited list. The entries are either host
1449 * names or IP addresses, and may use wildcard ('*', '?', I guess). There
1450 * special "<local>" entry matches anything without a dot.
1451 */
1452 RTNETADDRU HostAddr = { 0, 0 };
1453 int fIsHostIpv4Address = -1;
1454 char *pszEntry = pszBypassFree;
1455 while (*pszEntry != '\0')
1456 {
1457 /*
1458 * Find end of entry.
1459 */
1460 char ch;
1461 size_t cchEntry = 1;
1462 while ( (ch = pszEntry[cchEntry]) != '\0'
1463 && ch != ';'
1464 && !RT_C_IS_SPACE(ch))
1465 cchEntry++;
1466
1467 char chSaved = pszEntry[cchEntry];
1468 pszEntry[cchEntry] = '\0';
1469 RTStrToLower(pszEntry);
1470
1471 if ( cchEntry == sizeof("<local>") - 1
1472 && memcmp(pszEntry, RT_STR_TUPLE("<local>")) == 0)
1473 fRet = strchr(pszHost, '.') == NULL;
1474 else if ( memchr(pszEntry, '*', cchEntry) != NULL
1475 || memchr(pszEntry, '?', cchEntry) != NULL)
1476 fRet = RTStrSimplePatternMatch(pszEntry, pszHost);
1477 else
1478 {
1479 if (fIsHostIpv4Address == -1)
1480 fIsHostIpv4Address = RT_SUCCESS(RTNetStrToIPv4Addr(pszHost, &HostAddr.IPv4));
1481 RTNETADDRIPV4 Network, Netmask;
1482 if ( fIsHostIpv4Address
1483 && RT_SUCCESS(RTCidrStrToIPv4(pszEntry, &Network, &Netmask)) )
1484 fRet = (HostAddr.IPv4.u & Netmask.u) == Network.u;
1485 else
1486 fRet = strcmp(pszEntry, pszHost) == 0;
1487 }
1488
1489 pszEntry[cchEntry] = chSaved;
1490 if (fRet)
1491 break;
1492
1493 /*
1494 * Next entry.
1495 */
1496 pszEntry += cchEntry;
1497 while ( (ch = *pszEntry) != '\0'
1498 && ( ch == ';'
1499 || RT_C_IS_SPACE(ch)) )
1500 pszEntry++;
1501 }
1502
1503 RTStrFree(pszBypassFree);
1504 }
1505
1506 RTStrFree(pszHost);
1507 return false;
1508}
1509
1510
1511/**
1512 * Searches a Windows proxy server list for the best fitting proxy to use, then
1513 * reconfigures the HTTP client instance to use it.
1514 *
1515 * @returns IPRT status code, VINF_NOT_SUPPORTED if we need to consult fallback.
1516 * @param pThis The HTTP client instance.
1517 * @param pszUrl The URL needing proxying.
1518 * @param pwszProxies The list of proxy servers to choose from.
1519 */
1520static int rtHttpWinSelectProxyFromList(PRTHTTPINTERNAL pThis, const char *pszUrl, PCRTUTF16 pwszProxies)
1521{
1522 /*
1523 * Fend off empty strings (very unlikely, but just in case).
1524 */
1525 if (!pwszProxies)
1526 return VINF_NOT_SUPPORTED;
1527
1528 RTUTF16 wc;
1529 while ( (wc = *pwszProxies) != '\0'
1530 && ( RTUniCpIsSpace(wc)
1531 || wc == ';') )
1532 pwszProxies++;
1533 if (wc == '\0')
1534 return VINF_NOT_SUPPORTED;
1535
1536 /*
1537 * We now need to parse the URL and extract the scheme.
1538 */
1539 RTURIPARSED Parsed;
1540 int rc = RTUriParse(pszUrl, &Parsed);
1541 AssertRCReturn(rc, false);
1542 char *pszUrlScheme = RTUriParsedScheme(pszUrl, &Parsed);
1543 AssertReturn(pszUrlScheme, VERR_NO_STR_MEMORY);
1544 size_t const cchUrlScheme = strlen(pszUrlScheme);
1545
1546 int rcRet = VINF_NOT_SUPPORTED;
1547 char *pszProxiesFree;
1548 rc = RTUtf16ToUtf8(pwszProxies, &pszProxiesFree);
1549 if (RT_SUCCESS(rc))
1550 {
1551 /*
1552 * Walk the server list.
1553 *
1554 * According to https://msdn.microsoft.com/en-us/library/aa383912(v=vs.85).aspx
1555 * this is also a semicolon delimited list. The entries are on the form:
1556 * [<scheme>=][<scheme>"://"]<server>[":"<port>]
1557 */
1558 bool fBestEntryHasSameScheme = false;
1559 const char *pszBestEntry = NULL;
1560 char *pszEntry = pszProxiesFree;
1561 while (*pszEntry != '\0')
1562 {
1563 /*
1564 * Find end of entry. We include spaces here in addition to ';'.
1565 */
1566 char ch;
1567 size_t cchEntry = 1;
1568 while ( (ch = pszEntry[cchEntry]) != '\0'
1569 && ch != ';'
1570 && !RT_C_IS_SPACE(ch))
1571 cchEntry++;
1572
1573 char const chSaved = pszEntry[cchEntry];
1574 pszEntry[cchEntry] = '\0';
1575
1576 /* Parse the entry. */
1577 const char *pszEndOfScheme = strstr(pszEntry, "://");
1578 const char *pszEqual = (const char *)memchr(pszEntry, '=',
1579 pszEndOfScheme ? pszEndOfScheme - pszEntry : cchEntry);
1580 if (pszEqual)
1581 {
1582 if ( (uintptr_t)(pszEqual - pszEntry) == cchUrlScheme
1583 && RTStrNICmp(pszEntry, pszUrlScheme, cchUrlScheme) == 0)
1584 {
1585 pszBestEntry = pszEqual + 1;
1586 break;
1587 }
1588 }
1589 else
1590 {
1591 bool fSchemeMatch = pszEndOfScheme
1592 && (uintptr_t)(pszEndOfScheme - pszEntry) == cchUrlScheme
1593 && RTStrNICmp(pszEntry, pszUrlScheme, cchUrlScheme) == 0;
1594 if ( !pszBestEntry
1595 || ( !fBestEntryHasSameScheme
1596 && fSchemeMatch) )
1597 {
1598 pszBestEntry = pszEntry;
1599 fBestEntryHasSameScheme = fSchemeMatch;
1600 }
1601 }
1602
1603 /*
1604 * Next entry.
1605 */
1606 if (!chSaved)
1607 break;
1608 pszEntry += cchEntry + 1;
1609 while ( (ch = *pszEntry) != '\0'
1610 && ( ch == ';'
1611 || RT_C_IS_SPACE(ch)) )
1612 pszEntry++;
1613 }
1614
1615 /*
1616 * If we found something, try use it.
1617 */
1618 if (pszBestEntry)
1619 rcRet = rtHttpConfigureProxyFromUrl(pThis, pszBestEntry);
1620
1621 RTStrFree(pszProxiesFree);
1622 }
1623
1624 RTStrFree(pszUrlScheme);
1625 return rc;
1626}
1627
1628
1629/**
1630 * Reconfigures the cURL proxy settings for the given URL, Windows style.
1631 *
1632 * @returns IPRT status code. VINF_NOT_SUPPORTED if we should try fallback.
1633 * @param pThis The HTTP client instance.
1634 * @param pszUrl The URL.
1635 */
1636static int rtHttpWinConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
1637{
1638 int rcRet = VINF_NOT_SUPPORTED;
1639
1640 int rc = RTOnce(&g_WinResolveImportsOnce, rtHttpWinResolveImports, NULL);
1641 if (RT_SUCCESS(rc))
1642 {
1643 /*
1644 * Try get some proxy info for the URL. We first try getting the IE
1645 * config and seeing if we can use WinHttpGetIEProxyConfigForCurrentUser
1646 * in some way, if we can we prepare ProxyOptions with a non-zero dwFlags.
1647 */
1648 WINHTTP_PROXY_INFO ProxyInfo;
1649 WINHTTP_AUTOPROXY_OPTIONS AutoProxyOptions;
1650 RT_ZERO(AutoProxyOptions);
1651 RT_ZERO(ProxyInfo);
1652
1653 WINHTTP_CURRENT_USER_IE_PROXY_CONFIG IeProxyConfig;
1654 if (g_pfnWinHttpGetIEProxyConfigForCurrentUser(&IeProxyConfig))
1655 {
1656 AutoProxyOptions.fAutoLogonIfChallenged = FALSE;
1657 AutoProxyOptions.lpszAutoConfigUrl = IeProxyConfig.lpszAutoConfigUrl;
1658 if (IeProxyConfig.fAutoDetect)
1659 {
1660 AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT | WINHTTP_AUTOPROXY_RUN_INPROCESS;
1661 AutoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
1662 }
1663 else if (AutoProxyOptions.lpszAutoConfigUrl)
1664 AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
1665 else if (ProxyInfo.lpszProxy)
1666 ProxyInfo.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
1667 ProxyInfo.lpszProxy = IeProxyConfig.lpszProxy;
1668 ProxyInfo.lpszProxyBypass = IeProxyConfig.lpszProxyBypass;
1669 }
1670 else
1671 {
1672 AssertMsgFailed(("WinHttpGetIEProxyConfigForCurrentUser -> %u\n", GetLastError()));
1673 if (!g_pfnWinHttpGetDefaultProxyConfiguration(&ProxyInfo))
1674 {
1675 AssertMsgFailed(("WinHttpGetDefaultProxyConfiguration -> %u\n", GetLastError()));
1676 RT_ZERO(ProxyInfo);
1677 }
1678 }
1679
1680 /*
1681 * Should we try WinHttGetProxyForUrl?
1682 */
1683 if (AutoProxyOptions.dwFlags != 0)
1684 {
1685 HINTERNET hSession = g_pfnWinHttpOpen(NULL /*pwszUserAgent*/, WINHTTP_ACCESS_TYPE_NO_PROXY,
1686 WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 /*dwFlags*/ );
1687 if (hSession != NULL)
1688 {
1689 PRTUTF16 pwszUrl;
1690 rc = RTStrToUtf16(pszUrl, &pwszUrl);
1691 if (RT_SUCCESS(rc))
1692 {
1693 /*
1694 * Try autodetect first, then fall back on the config URL if there is one.
1695 *
1696 * Also, we first try without auto authentication, then with. This will according
1697 * to http://msdn.microsoft.com/en-us/library/aa383153%28v=VS.85%29.aspx help with
1698 * caching the result when it's processed out-of-process (seems default here on W10).
1699 */
1700 WINHTTP_PROXY_INFO TmpProxyInfo;
1701 BOOL fRc = g_pfnWinHttpGetProxyForUrl(hSession, pwszUrl, &AutoProxyOptions, &TmpProxyInfo);
1702 if ( !fRc
1703 && GetLastError() == ERROR_WINHTTP_LOGIN_FAILURE)
1704 {
1705 AutoProxyOptions.fAutoLogonIfChallenged = TRUE;
1706 fRc = g_pfnWinHttpGetProxyForUrl(hSession, pwszUrl, &AutoProxyOptions, &TmpProxyInfo);
1707 }
1708
1709 if ( !fRc
1710 && AutoProxyOptions.dwFlags != WINHTTP_AUTOPROXY_CONFIG_URL
1711 && AutoProxyOptions.lpszAutoConfigUrl)
1712 {
1713 AutoProxyOptions.fAutoLogonIfChallenged = FALSE;
1714 AutoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_CONFIG_URL;
1715 AutoProxyOptions.dwAutoDetectFlags = 0;
1716 fRc = g_pfnWinHttpGetProxyForUrl(hSession, pwszUrl, &AutoProxyOptions, &TmpProxyInfo);
1717 if ( !fRc
1718 && GetLastError() == ERROR_WINHTTP_LOGIN_FAILURE)
1719 {
1720 AutoProxyOptions.fAutoLogonIfChallenged = TRUE;
1721 fRc = g_pfnWinHttpGetProxyForUrl(hSession, pwszUrl, &AutoProxyOptions, &TmpProxyInfo);
1722 }
1723 }
1724
1725 if (fRc)
1726 {
1727 if (ProxyInfo.lpszProxy)
1728 GlobalFree(ProxyInfo.lpszProxy);
1729 if (ProxyInfo.lpszProxyBypass)
1730 GlobalFree(ProxyInfo.lpszProxyBypass);
1731 ProxyInfo = TmpProxyInfo;
1732 }
1733 /*
1734 * If the autodetection failed, assume no proxy.
1735 */
1736 else
1737 {
1738 DWORD dwErr = GetLastError();
1739 if ( dwErr == ERROR_WINHTTP_AUTODETECTION_FAILED
1740 || dwErr == ERROR_WINHTTP_UNABLE_TO_DOWNLOAD_SCRIPT
1741 || ( dwErr == ERROR_WINHTTP_UNRECOGNIZED_SCHEME
1742 && ( RTStrNICmp(pszUrl, RT_STR_TUPLE("https://")) == 0
1743 || RTStrNICmp(pszUrl, RT_STR_TUPLE("http://")) == 0) ) )
1744 rcRet = rtHttpUpdateAutomaticProxyDisable(pThis);
1745 else
1746 AssertMsgFailed(("g_pfnWinHttpGetProxyForUrl(%s) -> %u; lpszAutoConfigUrl=%sx\n",
1747 pszUrl, dwErr, AutoProxyOptions.lpszAutoConfigUrl));
1748 }
1749 RTUtf16Free(pwszUrl);
1750 }
1751 else
1752 {
1753 AssertMsgFailed(("RTStrToUtf16(%s,) -> %Rrc\n", pszUrl, rc));
1754 rcRet = rc;
1755 }
1756 g_pfnWinHttpCloseHandle(hSession);
1757 }
1758 else
1759 AssertMsgFailed(("g_pfnWinHttpOpen -> %u\n", GetLastError()));
1760 }
1761
1762 /*
1763 * Try use the proxy info we've found.
1764 */
1765 switch (ProxyInfo.dwAccessType)
1766 {
1767 case WINHTTP_ACCESS_TYPE_NO_PROXY:
1768 rcRet = rtHttpUpdateAutomaticProxyDisable(pThis);
1769 break;
1770
1771 case WINHTTP_ACCESS_TYPE_NAMED_PROXY:
1772 if (!rtHttpWinIsUrlInBypassList(pszUrl, ProxyInfo.lpszProxyBypass))
1773 rcRet = rtHttpWinSelectProxyFromList(pThis, pszUrl, ProxyInfo.lpszProxy);
1774 else
1775 rcRet = rtHttpUpdateAutomaticProxyDisable(pThis);
1776 break;
1777
1778 case 0:
1779 break;
1780
1781 default:
1782 AssertMsgFailed(("%#x\n", ProxyInfo.dwAccessType));
1783 }
1784
1785 /*
1786 * Cleanup.
1787 */
1788 if (ProxyInfo.lpszProxy)
1789 GlobalFree(ProxyInfo.lpszProxy);
1790 if (ProxyInfo.lpszProxyBypass)
1791 GlobalFree(ProxyInfo.lpszProxyBypass);
1792 if (AutoProxyOptions.lpszAutoConfigUrl)
1793 GlobalFree((PRTUTF16)AutoProxyOptions.lpszAutoConfigUrl);
1794 }
1795
1796 return rcRet;
1797}
1798
1799#endif /* RT_OS_WINDOWS */
1800
1801
1802static int rtHttpConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
1803{
1804 if (pThis->fUseSystemProxySettings)
1805 {
1806#ifdef IPRT_USE_LIBPROXY
1807 int rc = rtHttpLibProxyConfigureProxyForUrl(pThis, pszUrl);
1808 if (rc == VINF_SUCCESS || RT_FAILURE(rc))
1809 return rc;
1810 Assert(rc == VINF_NOT_SUPPORTED);
1811#endif
1812#ifdef RT_OS_DARWIN
1813 int rc = rtHttpDarwinConfigureProxyForUrl(pThis, pszUrl);
1814 if (rc == VINF_SUCCESS || RT_FAILURE(rc))
1815 return rc;
1816 Assert(rc == VINF_NOT_SUPPORTED);
1817#endif
1818#ifdef RT_OS_WINDOWS
1819 int rc = rtHttpWinConfigureProxyForUrl(pThis, pszUrl);
1820 if (rc == VINF_SUCCESS || RT_FAILURE(rc))
1821 return rc;
1822 Assert(rc == VINF_NOT_SUPPORTED);
1823#endif
1824/** @todo system specific class here, fall back on env vars if necessary. */
1825 return rtHttpConfigureProxyForUrlFromEnv(pThis, pszUrl);
1826 }
1827
1828 return VINF_SUCCESS;
1829}
1830
1831
1832RTR3DECL(int) RTHttpSetProxy(RTHTTP hHttp, const char *pcszProxy, uint32_t uPort,
1833 const char *pcszProxyUser, const char *pcszProxyPwd)
1834{
1835 PRTHTTPINTERNAL pThis = hHttp;
1836 RTHTTP_VALID_RETURN(pThis);
1837 AssertPtrReturn(pcszProxy, VERR_INVALID_PARAMETER);
1838 AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
1839
1840 /*
1841 * Update the settings.
1842 *
1843 * Currently, we don't make alot of effort parsing or checking the input, we
1844 * leave that to cURL. (A bit afraid of breaking user settings.)
1845 */
1846 pThis->fUseSystemProxySettings = false;
1847 return rtHttpUpdateProxyConfig(pThis, CURLPROXY_HTTP, pcszProxy, uPort ? uPort : 1080, pcszProxyUser, pcszProxyPwd);
1848}
1849
1850
1851RTR3DECL(int) RTHttpSetFollowRedirects(RTHTTP hHttp, uint32_t cMaxRedirects)
1852{
1853 PRTHTTPINTERNAL pThis = hHttp;
1854 RTHTTP_VALID_RETURN(pThis);
1855 AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
1856
1857 /*
1858 * Update the redirection settings.
1859 */
1860 if (pThis->cMaxRedirects != cMaxRedirects)
1861 {
1862 int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_MAXREDIRS, (long)cMaxRedirects);
1863 AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_MAXREDIRS=%u: %d (%#x)\n", cMaxRedirects, rcCurl, rcCurl),
1864 VERR_HTTP_CURL_ERROR);
1865
1866 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_FOLLOWLOCATION, (long)(cMaxRedirects > 0));
1867 AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_FOLLOWLOCATION=%d: %d (%#x)\n", cMaxRedirects > 0, rcCurl, rcCurl),
1868 VERR_HTTP_CURL_ERROR);
1869
1870 pThis->cMaxRedirects = cMaxRedirects;
1871 }
1872 return VINF_SUCCESS;
1873}
1874
1875
1876RTR3DECL(int) RTHttpSetHeaders(RTHTTP hHttp, size_t cHeaders, const char * const *papszHeaders)
1877{
1878 PRTHTTPINTERNAL pThis = hHttp;
1879 RTHTTP_VALID_RETURN(pThis);
1880
1881 pThis->fHaveUserAgentHeader = false;
1882 if (!cHeaders)
1883 {
1884 if (pThis->pHeaders)
1885 curl_slist_free_all(pThis->pHeaders);
1886 pThis->pHeaders = 0;
1887 return VINF_SUCCESS;
1888 }
1889
1890 struct curl_slist *pHeaders = NULL;
1891 for (size_t i = 0; i < cHeaders; i++)
1892 {
1893 pHeaders = curl_slist_append(pHeaders, papszHeaders[i]);
1894 if (strncmp(papszHeaders[i], RT_STR_TUPLE("User-Agent:")) == 0)
1895 pThis->fHaveUserAgentHeader = true;
1896 }
1897
1898 pThis->pHeaders = pHeaders;
1899 int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPHEADER, pHeaders);
1900 if (CURL_FAILURE(rcCurl))
1901 return VERR_INVALID_PARAMETER;
1902
1903 /*
1904 * Unset the user agent if it's in one of the headers.
1905 */
1906 if ( pThis->fHaveUserAgentHeader
1907 && pThis->fHaveSetUserAgent)
1908 {
1909 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_USERAGENT, (char *)NULL);
1910 Assert(CURL_SUCCESS(rcCurl));
1911 pThis->fHaveSetUserAgent = false;
1912 }
1913
1914 return VINF_SUCCESS;
1915}
1916
1917
1918/**
1919 * Set the CA file to NULL, deleting any temporary file if necessary.
1920 *
1921 * @param pThis The HTTP/HTTPS client instance.
1922 */
1923static void rtHttpUnsetCaFile(PRTHTTPINTERNAL pThis)
1924{
1925 if (pThis->pszCaFile)
1926 {
1927 if (pThis->fDeleteCaFile)
1928 {
1929 int rc2 = RTFileDelete(pThis->pszCaFile); RT_NOREF_PV(rc2);
1930 AssertMsg(RT_SUCCESS(rc2) || !RTFileExists(pThis->pszCaFile), ("rc=%Rrc '%s'\n", rc2, pThis->pszCaFile));
1931 }
1932 RTStrFree(pThis->pszCaFile);
1933 pThis->pszCaFile = NULL;
1934 }
1935}
1936
1937
1938RTR3DECL(int) RTHttpSetCAFile(RTHTTP hHttp, const char *pszCaFile)
1939{
1940 PRTHTTPINTERNAL pThis = hHttp;
1941 RTHTTP_VALID_RETURN(pThis);
1942
1943 rtHttpUnsetCaFile(pThis);
1944
1945 pThis->fDeleteCaFile = false;
1946 if (pszCaFile)
1947 return RTStrDupEx(&pThis->pszCaFile, pszCaFile);
1948 return VINF_SUCCESS;
1949}
1950
1951
1952RTR3DECL(int) RTHttpUseTemporaryCaFile(RTHTTP hHttp, PRTERRINFO pErrInfo)
1953{
1954 PRTHTTPINTERNAL pThis = hHttp;
1955 RTHTTP_VALID_RETURN(pThis);
1956
1957 /*
1958 * Create a temporary file.
1959 */
1960 int rc = VERR_NO_STR_MEMORY;
1961 char *pszCaFile = RTStrAlloc(RTPATH_MAX);
1962 if (pszCaFile)
1963 {
1964 RTFILE hFile;
1965 rc = RTFileOpenTemp(&hFile, pszCaFile, RTPATH_MAX,
1966 RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE | (0600 << RTFILE_O_CREATE_MODE_SHIFT));
1967 if (RT_SUCCESS(rc))
1968 {
1969 /*
1970 * Gather certificates into a temporary store and export them to the temporary file.
1971 */
1972 RTCRSTORE hStore;
1973 rc = RTCrStoreCreateInMem(&hStore, 256);
1974 if (RT_SUCCESS(rc))
1975 {
1976 rc = RTHttpGatherCaCertsInStore(hStore, 0 /*fFlags*/, pErrInfo);
1977 if (RT_SUCCESS(rc))
1978 /** @todo Consider adding an API for exporting to a RTFILE... */
1979 rc = RTCrStoreCertExportAsPem(hStore, 0 /*fFlags*/, pszCaFile);
1980 RTCrStoreRelease(hStore);
1981 }
1982 RTFileClose(hFile);
1983 if (RT_SUCCESS(rc))
1984 {
1985 /*
1986 * Set the CA file for the instance.
1987 */
1988 rtHttpUnsetCaFile(pThis);
1989
1990 pThis->fDeleteCaFile = true;
1991 pThis->pszCaFile = pszCaFile;
1992 return VINF_SUCCESS;
1993 }
1994
1995 int rc2 = RTFileDelete(pszCaFile);
1996 AssertRC(rc2);
1997 }
1998 else
1999 RTErrInfoAddF(pErrInfo, rc, "Error creating temorary file: %Rrc", rc);
2000
2001 RTStrFree(pszCaFile);
2002 }
2003 return rc;
2004}
2005
2006
2007RTR3DECL(int) RTHttpGatherCaCertsInStore(RTCRSTORE hStore, uint32_t fFlags, PRTERRINFO pErrInfo)
2008{
2009 uint32_t const cBefore = RTCrStoreCertCount(hStore);
2010 AssertReturn(cBefore != UINT32_MAX, VERR_INVALID_HANDLE);
2011 RT_NOREF_PV(fFlags);
2012
2013
2014 /*
2015 * Add the user store, quitely ignoring any errors.
2016 */
2017 RTCRSTORE hSrcStore;
2018 int rcUser = RTCrStoreCreateSnapshotById(&hSrcStore, RTCRSTOREID_USER_TRUSTED_CAS_AND_CERTIFICATES, pErrInfo);
2019 if (RT_SUCCESS(rcUser))
2020 {
2021 rcUser = RTCrStoreCertAddFromStore(hStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
2022 hSrcStore);
2023 RTCrStoreRelease(hSrcStore);
2024 }
2025
2026 /*
2027 * Ditto for the system store.
2028 */
2029 int rcSystem = RTCrStoreCreateSnapshotById(&hSrcStore, RTCRSTOREID_SYSTEM_TRUSTED_CAS_AND_CERTIFICATES, pErrInfo);
2030 if (RT_SUCCESS(rcSystem))
2031 {
2032 rcSystem = RTCrStoreCertAddFromStore(hStore, RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR,
2033 hSrcStore);
2034 RTCrStoreRelease(hSrcStore);
2035 }
2036
2037 /*
2038 * If the number of certificates increased, we consider it a success.
2039 */
2040 if (RTCrStoreCertCount(hStore) > cBefore)
2041 {
2042 if (RT_FAILURE(rcSystem))
2043 return -rcSystem;
2044 if (RT_FAILURE(rcUser))
2045 return -rcUser;
2046 return rcSystem != VINF_SUCCESS ? rcSystem : rcUser;
2047 }
2048
2049 if (RT_FAILURE(rcSystem))
2050 return rcSystem;
2051 if (RT_FAILURE(rcUser))
2052 return rcUser;
2053 return VERR_NOT_FOUND;
2054}
2055
2056
2057RTR3DECL(int) RTHttpGatherCaCertsInFile(const char *pszCaFile, uint32_t fFlags, PRTERRINFO pErrInfo)
2058{
2059 RTCRSTORE hStore;
2060 int rc = RTCrStoreCreateInMem(&hStore, 256);
2061 if (RT_SUCCESS(rc))
2062 {
2063 rc = RTHttpGatherCaCertsInStore(hStore, fFlags, pErrInfo);
2064 if (RT_SUCCESS(rc))
2065 rc = RTCrStoreCertExportAsPem(hStore, 0 /*fFlags*/, pszCaFile);
2066 RTCrStoreRelease(hStore);
2067 }
2068 return rc;
2069}
2070
2071
2072
2073/**
2074 * Figures out the IPRT status code for a GET.
2075 *
2076 * @returns IPRT status code.
2077 * @param pThis The HTTP/HTTPS client instance.
2078 * @param rcCurl What curl returned.
2079 */
2080static int rtHttpGetCalcStatus(PRTHTTPINTERNAL pThis, int rcCurl)
2081{
2082 int rc = VERR_HTTP_CURL_ERROR;
2083
2084 if (pThis->pszRedirLocation)
2085 {
2086 RTStrFree(pThis->pszRedirLocation);
2087 pThis->pszRedirLocation = NULL;
2088 }
2089 if (rcCurl == CURLE_OK)
2090 {
2091 curl_easy_getinfo(pThis->pCurl, CURLINFO_RESPONSE_CODE, &pThis->lLastResp);
2092 switch (pThis->lLastResp)
2093 {
2094 case 200:
2095 /* OK, request was fulfilled */
2096 case 204:
2097 /* empty response */
2098 rc = VINF_SUCCESS;
2099 break;
2100 case 301: /* Moved permantently. */
2101 case 302: /* Found / Moved temporarily. */
2102 case 303: /* See Other. */
2103 case 307: /* Temporary redirect. */
2104 case 308: /* Permanent redirect. */
2105 {
2106 const char *pszRedirect = NULL;
2107 curl_easy_getinfo(pThis->pCurl, CURLINFO_REDIRECT_URL, &pszRedirect);
2108 size_t cb = pszRedirect ? strlen(pszRedirect) : 0;
2109 if (cb > 0 && cb < 2048)
2110 pThis->pszRedirLocation = RTStrDup(pszRedirect);
2111 rc = VERR_HTTP_REDIRECTED;
2112 break;
2113 }
2114 case 400:
2115 /* bad request */
2116 rc = VERR_HTTP_BAD_REQUEST;
2117 break;
2118 case 403:
2119 /* forbidden, authorization will not help */
2120 rc = VERR_HTTP_ACCESS_DENIED;
2121 break;
2122 case 404:
2123 /* URL not found */
2124 rc = VERR_HTTP_NOT_FOUND;
2125 break;
2126 }
2127
2128 if (pThis->pszRedirLocation)
2129 Log(("rtHttpGetCalcStatus: rc=%Rrc lastResp=%lu redir='%s'\n", rc, pThis->lLastResp, pThis->pszRedirLocation));
2130 else
2131 Log(("rtHttpGetCalcStatus: rc=%Rrc lastResp=%lu\n", rc, pThis->lLastResp));
2132 }
2133 else
2134 {
2135 switch (rcCurl)
2136 {
2137 case CURLE_URL_MALFORMAT:
2138 case CURLE_COULDNT_RESOLVE_HOST:
2139 rc = VERR_HTTP_HOST_NOT_FOUND;
2140 break;
2141 case CURLE_COULDNT_CONNECT:
2142 rc = VERR_HTTP_COULDNT_CONNECT;
2143 break;
2144 case CURLE_SSL_CONNECT_ERROR:
2145 rc = VERR_HTTP_SSL_CONNECT_ERROR;
2146 break;
2147 case CURLE_SSL_CACERT:
2148 /* The peer certificate cannot be authenticated with the CA certificates
2149 * set by RTHttpSetCAFile(). We need other or additional CA certificates. */
2150 rc = VERR_HTTP_CACERT_CANNOT_AUTHENTICATE;
2151 break;
2152 case CURLE_SSL_CACERT_BADFILE:
2153 /* CAcert file (see RTHttpSetCAFile()) has wrong format */
2154 rc = VERR_HTTP_CACERT_WRONG_FORMAT;
2155 break;
2156 case CURLE_ABORTED_BY_CALLBACK:
2157 /* forcefully aborted */
2158 rc = VERR_HTTP_ABORTED;
2159 break;
2160 case CURLE_COULDNT_RESOLVE_PROXY:
2161 rc = VERR_HTTP_PROXY_NOT_FOUND;
2162 break;
2163 case CURLE_WRITE_ERROR:
2164 rc = RT_FAILURE_NP(pThis->rcOutput) ? pThis->rcOutput : VERR_WRITE_ERROR;
2165 break;
2166 //case CURLE_READ_ERROR
2167
2168 default:
2169 break;
2170 }
2171 Log(("rtHttpGetCalcStatus: rc=%Rrc rcCurl=%u\n", rc, rcCurl));
2172 }
2173
2174 return rc;
2175}
2176
2177
2178/**
2179 * cURL callback for reporting progress, we use it for checking for abort.
2180 */
2181static int rtHttpProgress(void *pData, double rdTotalDownload, double rdDownloaded, double rdTotalUpload, double rdUploaded)
2182{
2183 PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pData;
2184 AssertReturn(pThis->u32Magic == RTHTTP_MAGIC, 1);
2185 RT_NOREF_PV(rdTotalUpload);
2186 RT_NOREF_PV(rdUploaded);
2187
2188 pThis->cbDownloadHint = (uint64_t)rdTotalDownload;
2189
2190 if (pThis->pfnDownloadProgress)
2191 pThis->pfnDownloadProgress(pThis, pThis->pvDownloadProgressUser, (uint64_t)rdTotalDownload, (uint64_t)rdDownloaded);
2192
2193 return pThis->fAbort ? 1 : 0;
2194}
2195
2196
2197/**
2198 * Whether we're likely to need SSL to handle the give URL.
2199 *
2200 * @returns true if we need, false if we probably don't.
2201 * @param pszUrl The URL.
2202 */
2203static bool rtHttpNeedSsl(const char *pszUrl)
2204{
2205 return RTStrNICmp(pszUrl, RT_STR_TUPLE("https:")) == 0;
2206}
2207
2208
2209/**
2210 * Applies recoded settings to the cURL instance before doing work.
2211 *
2212 * @returns IPRT status code.
2213 * @param pThis The HTTP/HTTPS client instance.
2214 * @param pszUrl The URL.
2215 */
2216static int rtHttpApplySettings(PRTHTTPINTERNAL pThis, const char *pszUrl)
2217{
2218 /*
2219 * The URL.
2220 */
2221 int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_URL, pszUrl);
2222 if (CURL_FAILURE(rcCurl))
2223 return VERR_INVALID_PARAMETER;
2224
2225 /*
2226 * Proxy config.
2227 */
2228 int rc = rtHttpConfigureProxyForUrl(pThis, pszUrl);
2229 if (RT_FAILURE(rc))
2230 return rc;
2231
2232 /*
2233 * Setup SSL. Can be a bit of work.
2234 */
2235 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_SSLVERSION, (long)CURL_SSLVERSION_TLSv1);
2236 if (CURL_FAILURE(rcCurl))
2237 return VERR_INVALID_PARAMETER;
2238
2239 const char *pszCaFile = pThis->pszCaFile;
2240 if ( !pszCaFile
2241 && rtHttpNeedSsl(pszUrl))
2242 {
2243 rc = RTHttpUseTemporaryCaFile(pThis, NULL);
2244 if (RT_SUCCESS(rc))
2245 pszCaFile = pThis->pszCaFile;
2246 else
2247 return rc; /* Non-portable alternative: pszCaFile = "/etc/ssl/certs/ca-certificates.crt"; */
2248 }
2249 if (pszCaFile)
2250 {
2251 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CAINFO, pszCaFile);
2252 if (CURL_FAILURE(rcCurl))
2253 return VERR_HTTP_CURL_ERROR;
2254 }
2255
2256 /*
2257 * Progress/abort.
2258 */
2259 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROGRESSFUNCTION, &rtHttpProgress);
2260 if (CURL_FAILURE(rcCurl))
2261 return VERR_HTTP_CURL_ERROR;
2262 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROGRESSDATA, (void *)pThis);
2263 if (CURL_FAILURE(rcCurl))
2264 return VERR_HTTP_CURL_ERROR;
2265 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOPROGRESS, (long)0);
2266 if (CURL_FAILURE(rcCurl))
2267 return VERR_HTTP_CURL_ERROR;
2268
2269 /*
2270 * Set default user agent string if necessary. Some websites take offence
2271 * if we don't set it.
2272 */
2273 if ( !pThis->fHaveSetUserAgent
2274 && !pThis->fHaveUserAgentHeader)
2275 {
2276 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_USERAGENT, "Mozilla/5.0 (AgnosticOS; Blend) IPRT/64.42");
2277 if (CURL_FAILURE(rcCurl))
2278 return VERR_HTTP_CURL_ERROR;
2279 pThis->fHaveSetUserAgent = true;
2280 }
2281
2282 /*
2283 * Use GET by default.
2284 */
2285 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOBODY, 0L);
2286 if (CURL_FAILURE(rcCurl))
2287 return VERR_HTTP_CURL_ERROR;
2288 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HEADER, 0L);
2289 if (CURL_FAILURE(rcCurl))
2290 return VERR_HTTP_CURL_ERROR;
2291
2292 return VINF_SUCCESS;
2293}
2294
2295
2296/**
2297 * cURL callback for writing data.
2298 */
2299static size_t rtHttpWriteData(void *pvBuf, size_t cbUnit, size_t cUnits, void *pvUser)
2300{
2301 PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pvUser;
2302
2303 /*
2304 * Do max size and overflow checks.
2305 */
2306 size_t const cbToAppend = cbUnit * cUnits;
2307 size_t const cbCurSize = pThis->Output.Mem.cb;
2308 size_t const cbNewSize = cbCurSize + cbToAppend;
2309 if ( cbToAppend < RTHTTP_MAX_MEM_DOWNLOAD_SIZE
2310 && cbNewSize < RTHTTP_MAX_MEM_DOWNLOAD_SIZE)
2311 {
2312 if (cbNewSize + 1 <= pThis->Output.Mem.cbAllocated)
2313 {
2314 memcpy(&pThis->Output.Mem.pb[cbCurSize], pvBuf, cbToAppend);
2315 pThis->Output.Mem.cb = cbNewSize;
2316 pThis->Output.Mem.pb[cbNewSize] = '\0';
2317 return cbToAppend;
2318 }
2319
2320 /*
2321 * We need to reallocate the output buffer.
2322 */
2323 /** @todo this could do with a better strategy wrt growth. */
2324 size_t cbAlloc = RT_ALIGN_Z(cbNewSize + 1, 64);
2325 if ( cbAlloc <= pThis->cbDownloadHint
2326 && pThis->cbDownloadHint < RTHTTP_MAX_MEM_DOWNLOAD_SIZE)
2327 cbAlloc = RT_ALIGN_Z(pThis->cbDownloadHint + 1, 64);
2328
2329 uint8_t *pbNew = (uint8_t *)RTMemRealloc(pThis->Output.Mem.pb, cbAlloc);
2330 if (pbNew)
2331 {
2332 memcpy(&pbNew[cbCurSize], pvBuf, cbToAppend);
2333 pbNew[cbNewSize] = '\0';
2334
2335 pThis->Output.Mem.cbAllocated = cbAlloc;
2336 pThis->Output.Mem.pb = pbNew;
2337 pThis->Output.Mem.cb = cbNewSize;
2338 return cbToAppend;
2339 }
2340
2341 pThis->rcOutput = VERR_NO_MEMORY;
2342 }
2343 else
2344 pThis->rcOutput = VERR_TOO_MUCH_DATA;
2345
2346 /*
2347 * Failure - abort.
2348 */
2349 RTMemFree(pThis->Output.Mem.pb);
2350 pThis->Output.Mem.pb = NULL;
2351 pThis->Output.Mem.cb = RTHTTP_MAX_MEM_DOWNLOAD_SIZE;
2352 pThis->fAbort = true;
2353 return 0;
2354}
2355
2356
2357/**
2358 * Internal worker that performs a HTTP GET.
2359 *
2360 * @returns IPRT status code.
2361 * @param hHttp The HTTP/HTTPS client instance.
2362 * @param pszUrl The URL.
2363 * @param fNoBody Set to suppress the body.
2364 * @param ppvResponse Where to return the pointer to the allocated
2365 * response data (RTMemFree). There will always be
2366 * an zero terminator char after the response, that
2367 * is not part of the size returned via @a pcb.
2368 * @param pcb The size of the response data.
2369 *
2370 * @remarks We ASSUME the API user doesn't do concurrent GETs in different
2371 * threads, because that will probably blow up!
2372 */
2373static int rtHttpGetToMem(RTHTTP hHttp, const char *pszUrl, bool fNoBody, uint8_t **ppvResponse, size_t *pcb)
2374{
2375 PRTHTTPINTERNAL pThis = hHttp;
2376 RTHTTP_VALID_RETURN(pThis);
2377
2378 /*
2379 * Reset the return values in case of more "GUI programming" on the client
2380 * side (i.e. a programming style not bothering checking return codes).
2381 */
2382 *ppvResponse = NULL;
2383 *pcb = 0;
2384
2385 /*
2386 * Set the busy flag (paranoia).
2387 */
2388 bool fBusy = ASMAtomicXchgBool(&pThis->fBusy, true);
2389 AssertReturn(!fBusy, VERR_WRONG_ORDER);
2390
2391 /*
2392 * Reset the state and apply settings.
2393 */
2394 pThis->fAbort = false;
2395 pThis->rcOutput = VINF_SUCCESS;
2396 pThis->cbDownloadHint = 0;
2397
2398 int rc = rtHttpApplySettings(hHttp, pszUrl);
2399 if (RT_SUCCESS(rc))
2400 {
2401 RT_ZERO(pThis->Output.Mem);
2402 int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEFUNCTION, &rtHttpWriteData);
2403 if (!CURL_FAILURE(rcCurl))
2404 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEDATA, (void *)pThis);
2405 if (fNoBody)
2406 {
2407 if (!CURL_FAILURE(rcCurl))
2408 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOBODY, 1L);
2409 if (!CURL_FAILURE(rcCurl))
2410 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HEADER, 1L);
2411 }
2412 if (!CURL_FAILURE(rcCurl))
2413 {
2414 /*
2415 * Perform the HTTP operation.
2416 */
2417 rcCurl = curl_easy_perform(pThis->pCurl);
2418 rc = rtHttpGetCalcStatus(pThis, rcCurl);
2419 if (RT_SUCCESS(rc))
2420 rc = pThis->rcOutput;
2421 if (RT_SUCCESS(rc))
2422 {
2423 *ppvResponse = pThis->Output.Mem.pb;
2424 *pcb = pThis->Output.Mem.cb;
2425 Log(("rtHttpGetToMem: %zx bytes (allocated %zx)\n", pThis->Output.Mem.cb, pThis->Output.Mem.cbAllocated));
2426 }
2427 else if (pThis->Output.Mem.pb)
2428 RTMemFree(pThis->Output.Mem.pb);
2429 RT_ZERO(pThis->Output.Mem);
2430 }
2431 else
2432 rc = VERR_HTTP_CURL_ERROR;
2433 }
2434
2435 ASMAtomicWriteBool(&pThis->fBusy, false);
2436 return rc;
2437}
2438
2439
2440RTR3DECL(int) RTHttpGetText(RTHTTP hHttp, const char *pszUrl, char **ppszNotUtf8)
2441{
2442 Log(("RTHttpGetText: hHttp=%p pszUrl=%s\n", hHttp, pszUrl));
2443 uint8_t *pv;
2444 size_t cb;
2445 int rc = rtHttpGetToMem(hHttp, pszUrl, false /*fNoBody*/, &pv, &cb);
2446 if (RT_SUCCESS(rc))
2447 {
2448 if (pv) /* paranoia */
2449 *ppszNotUtf8 = (char *)pv;
2450 else
2451 *ppszNotUtf8 = (char *)RTMemDup("", 1);
2452 }
2453 else
2454 *ppszNotUtf8 = NULL;
2455 return rc;
2456}
2457
2458
2459RTR3DECL(int) RTHttpGetHeaderText(RTHTTP hHttp, const char *pszUrl, char **ppszNotUtf8)
2460{
2461 Log(("RTHttpGetText: hHttp=%p pszUrl=%s\n", hHttp, pszUrl));
2462 uint8_t *pv;
2463 size_t cb;
2464 int rc = rtHttpGetToMem(hHttp, pszUrl, true /*fNoBody*/, &pv, &cb);
2465 if (RT_SUCCESS(rc))
2466 {
2467 if (pv) /* paranoia */
2468 *ppszNotUtf8 = (char *)pv;
2469 else
2470 *ppszNotUtf8 = (char *)RTMemDup("", 1);
2471 }
2472 else
2473 *ppszNotUtf8 = NULL;
2474 return rc;
2475
2476}
2477
2478
2479RTR3DECL(void) RTHttpFreeResponseText(char *pszNotUtf8)
2480{
2481 RTMemFree(pszNotUtf8);
2482}
2483
2484
2485RTR3DECL(int) RTHttpGetBinary(RTHTTP hHttp, const char *pszUrl, void **ppvResponse, size_t *pcb)
2486{
2487 Log(("RTHttpGetBinary: hHttp=%p pszUrl=%s\n", hHttp, pszUrl));
2488 return rtHttpGetToMem(hHttp, pszUrl, false /*fNoBody*/, (uint8_t **)ppvResponse, pcb);
2489}
2490
2491
2492RTR3DECL(int) RTHttpGetHeaderBinary(RTHTTP hHttp, const char *pszUrl, void **ppvResponse, size_t *pcb)
2493{
2494 Log(("RTHttpGetBinary: hHttp=%p pszUrl=%s\n", hHttp, pszUrl));
2495 return rtHttpGetToMem(hHttp, pszUrl, true /*fNoBody*/, (uint8_t **)ppvResponse, pcb);
2496}
2497
2498
2499RTR3DECL(void) RTHttpFreeResponse(void *pvResponse)
2500{
2501 RTMemFree(pvResponse);
2502}
2503
2504
2505/**
2506 * cURL callback for writing data to a file.
2507 */
2508static size_t rtHttpWriteDataToFile(void *pvBuf, size_t cbUnit, size_t cUnits, void *pvUser)
2509{
2510 PRTHTTPINTERNAL pThis = (PRTHTTPINTERNAL)pvUser;
2511 size_t cbWritten = 0;
2512 int rc = RTFileWrite(pThis->Output.hFile, pvBuf, cbUnit * cUnits, &cbWritten);
2513 if (RT_SUCCESS(rc))
2514 return cbWritten;
2515 Log(("rtHttpWriteDataToFile: rc=%Rrc cbUnit=%zd cUnits=%zu\n", rc, cbUnit, cUnits));
2516 pThis->rcOutput = rc;
2517 return 0;
2518}
2519
2520
2521RTR3DECL(int) RTHttpGetFile(RTHTTP hHttp, const char *pszUrl, const char *pszDstFile)
2522{
2523 Log(("RTHttpGetBinary: hHttp=%p pszUrl=%s pszDstFile=%s\n", hHttp, pszUrl, pszDstFile));
2524 PRTHTTPINTERNAL pThis = hHttp;
2525 RTHTTP_VALID_RETURN(pThis);
2526
2527 /*
2528 * Set the busy flag (paranoia).
2529 */
2530 bool fBusy = ASMAtomicXchgBool(&pThis->fBusy, true);
2531 AssertReturn(!fBusy, VERR_WRONG_ORDER);
2532
2533 /*
2534 * Reset the state and apply settings.
2535 */
2536 pThis->fAbort = false;
2537 pThis->rcOutput = VINF_SUCCESS;
2538 pThis->cbDownloadHint = 0;
2539
2540 int rc = rtHttpApplySettings(hHttp, pszUrl);
2541 if (RT_SUCCESS(rc))
2542 {
2543 pThis->Output.hFile = NIL_RTFILE;
2544 int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEFUNCTION, &rtHttpWriteDataToFile);
2545 if (!CURL_FAILURE(rcCurl))
2546 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEDATA, (void *)pThis);
2547 if (!CURL_FAILURE(rcCurl))
2548 {
2549 /*
2550 * Open the output file.
2551 */
2552 rc = RTFileOpen(&pThis->Output.hFile, pszDstFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_READWRITE);
2553 if (RT_SUCCESS(rc))
2554 {
2555 /*
2556 * Perform the HTTP operation.
2557 */
2558 rcCurl = curl_easy_perform(pThis->pCurl);
2559 rc = rtHttpGetCalcStatus(pThis, rcCurl);
2560 if (RT_SUCCESS(rc))
2561 rc = pThis->rcOutput;
2562
2563 int rc2 = RTFileClose(pThis->Output.hFile);
2564 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
2565 rc = rc2;
2566 }
2567 pThis->Output.hFile = NIL_RTFILE;
2568 }
2569 else
2570 rc = VERR_HTTP_CURL_ERROR;
2571 }
2572
2573 ASMAtomicWriteBool(&pThis->fBusy, false);
2574 return rc;
2575}
2576
2577
2578RTR3DECL(int) RTHttpSetDownloadProgressCallback(RTHTTP hHttp, PRTHTTPDOWNLDPROGRCALLBACK pfnDownloadProgress, void *pvUser)
2579{
2580 PRTHTTPINTERNAL pThis = hHttp;
2581 RTHTTP_VALID_RETURN(pThis);
2582
2583 pThis->pfnDownloadProgress = pfnDownloadProgress;
2584 pThis->pvDownloadProgressUser = pvUser;
2585 return VINF_SUCCESS;
2586}
2587
2588
2589
2590RTR3DECL(int) RTHttpSetReadCallback(RTHTTP hHttp, PRTHTTPREADCALLBACK pfnRead, void *pvUser)
2591{
2592 CURLcode rcCurl;
2593
2594 PRTHTTPINTERNAL pThis = hHttp;
2595 RTHTTP_VALID_RETURN(pThis);
2596
2597 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_READFUNCTION, pfnRead);
2598 if (rcCurl != CURLE_OK)
2599 return VERR_HTTP_CURL_ERROR;
2600
2601 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_READDATA, pvUser);
2602 if (rcCurl != CURLE_OK)
2603 return VERR_HTTP_CURL_ERROR;
2604
2605 return VINF_SUCCESS;
2606}
2607
2608
2609RTR3DECL(int) RTHttpSetWriteCallback(RTHTTP hHttp, PRTHTTPWRITECALLBACK pfnWrite, void *pvUser)
2610{
2611 CURLcode rcCurl;
2612
2613 PRTHTTPINTERNAL pThis = hHttp;
2614 RTHTTP_VALID_RETURN(pThis);
2615
2616 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEFUNCTION, pfnWrite);
2617 if (rcCurl != CURLE_OK)
2618 return VERR_HTTP_CURL_ERROR;
2619
2620 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_WRITEDATA, pvUser);
2621 if (rcCurl != CURLE_OK)
2622 return VERR_HTTP_CURL_ERROR;
2623
2624 return VINF_SUCCESS;
2625}
2626
2627
2628RTR3DECL(int) RTHttpSetWriteHeaderCallback(RTHTTP hHttp, PRTHTTPWRITECALLBACK pfnWrite, void *pvUser)
2629{
2630 CURLcode rcCurl;
2631
2632 PRTHTTPINTERNAL pThis = hHttp;
2633 RTHTTP_VALID_RETURN(pThis);
2634
2635 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HEADERFUNCTION, pfnWrite);
2636 if (rcCurl != CURLE_OK)
2637 return VERR_HTTP_CURL_ERROR;
2638
2639 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HEADERDATA, pvUser);
2640 if (rcCurl != CURLE_OK)
2641 return VERR_HTTP_CURL_ERROR;
2642
2643 return VINF_SUCCESS;
2644}
2645
2646
2647RTR3DECL(int) RTHttpRawSetUrl(RTHTTP hHttp, const char *pszUrl)
2648{
2649 CURLcode rcCurl;
2650
2651 PRTHTTPINTERNAL pThis = hHttp;
2652 RTHTTP_VALID_RETURN(pThis);
2653
2654 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_URL, pszUrl);
2655 if (rcCurl != CURLE_OK)
2656 return VERR_HTTP_CURL_ERROR;
2657
2658 return VINF_SUCCESS;
2659}
2660
2661
2662RTR3DECL(int) RTHttpRawSetGet(RTHTTP hHttp)
2663{
2664 CURLcode rcCurl;
2665
2666 PRTHTTPINTERNAL pThis = hHttp;
2667 RTHTTP_VALID_RETURN(pThis);
2668
2669 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPGET, 1L);
2670 if (rcCurl != CURLE_OK)
2671 return VERR_HTTP_CURL_ERROR;
2672
2673 return VINF_SUCCESS;
2674}
2675
2676
2677RTR3DECL(int) RTHttpRawSetHead(RTHTTP hHttp)
2678{
2679 CURLcode rcCurl;
2680
2681 PRTHTTPINTERNAL pThis = hHttp;
2682 RTHTTP_VALID_RETURN(pThis);
2683
2684 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_HTTPGET, 1L);
2685 if (rcCurl != CURLE_OK)
2686 return VERR_HTTP_CURL_ERROR;
2687
2688 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOBODY, 1L);
2689 if (rcCurl != CURLE_OK)
2690 return VERR_HTTP_CURL_ERROR;
2691
2692 return VINF_SUCCESS;
2693}
2694
2695
2696RTR3DECL(int) RTHttpRawSetPost(RTHTTP hHttp)
2697{
2698 CURLcode rcCurl;
2699
2700 PRTHTTPINTERNAL pThis = hHttp;
2701 RTHTTP_VALID_RETURN(pThis);
2702
2703 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_POST, 1L);
2704 if (rcCurl != CURLE_OK)
2705 return VERR_HTTP_CURL_ERROR;
2706
2707 return VINF_SUCCESS;
2708}
2709
2710
2711RTR3DECL(int) RTHttpRawSetPut(RTHTTP hHttp)
2712{
2713 CURLcode rcCurl;
2714
2715 PRTHTTPINTERNAL pThis = hHttp;
2716 RTHTTP_VALID_RETURN(pThis);
2717
2718 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PUT, 1L);
2719 if (rcCurl != CURLE_OK)
2720 return VERR_HTTP_CURL_ERROR;
2721
2722 return VINF_SUCCESS;
2723}
2724
2725
2726RTR3DECL(int) RTHttpRawSetDelete(RTHTTP hHttp)
2727{
2728 /* curl doesn't provide an option for this */
2729 return RTHttpRawSetCustomRequest(hHttp, "DELETE");
2730}
2731
2732
2733RTR3DECL(int) RTHttpRawSetCustomRequest(RTHTTP hHttp, const char *pszVerb)
2734{
2735 CURLcode rcCurl;
2736
2737 PRTHTTPINTERNAL pThis = hHttp;
2738 RTHTTP_VALID_RETURN(pThis);
2739
2740 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CUSTOMREQUEST, pszVerb);
2741 if (rcCurl != CURLE_OK)
2742 return VERR_HTTP_CURL_ERROR;
2743
2744 return VINF_SUCCESS;
2745}
2746
2747
2748RTR3DECL(int) RTHttpRawSetPostFields(RTHTTP hHttp, const void *pv, size_t cb)
2749{
2750 CURLcode rcCurl;
2751
2752 PRTHTTPINTERNAL pThis = hHttp;
2753 RTHTTP_VALID_RETURN(pThis);
2754
2755 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_POSTFIELDSIZE, cb);
2756 if (rcCurl != CURLE_OK)
2757 return VERR_HTTP_CURL_ERROR;
2758
2759 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_POSTFIELDS, pv);
2760 if (rcCurl != CURLE_OK)
2761 return VERR_HTTP_CURL_ERROR;
2762
2763 return VINF_SUCCESS;
2764}
2765
2766RTR3DECL(int) RTHttpRawSetInfileSize(RTHTTP hHttp, RTFOFF cb)
2767{
2768 CURLcode rcCurl;
2769
2770 PRTHTTPINTERNAL pThis = hHttp;
2771 RTHTTP_VALID_RETURN(pThis);
2772
2773 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_INFILESIZE_LARGE, cb);
2774 if (rcCurl != CURLE_OK)
2775 return VERR_HTTP_CURL_ERROR;
2776
2777 return VINF_SUCCESS;
2778}
2779
2780
2781RTR3DECL(int) RTHttpRawSetVerbose(RTHTTP hHttp, bool fValue)
2782{
2783 CURLcode rcCurl;
2784
2785 PRTHTTPINTERNAL pThis = hHttp;
2786 RTHTTP_VALID_RETURN(pThis);
2787
2788 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_VERBOSE, fValue ? 1L : 0L);
2789 if (rcCurl != CURLE_OK)
2790 return VERR_HTTP_CURL_ERROR;
2791
2792 return VINF_SUCCESS;
2793}
2794
2795
2796RTR3DECL(int) RTHttpRawSetTimeout(RTHTTP hHttp, long sec)
2797{
2798 CURLcode rcCurl;
2799
2800 PRTHTTPINTERNAL pThis = hHttp;
2801 RTHTTP_VALID_RETURN(pThis);
2802
2803 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_TIMEOUT, sec);
2804 if (rcCurl != CURLE_OK)
2805 return VERR_HTTP_CURL_ERROR;
2806
2807 return VINF_SUCCESS;
2808}
2809
2810
2811RTR3DECL(int) RTHttpRawPerform(RTHTTP hHttp)
2812{
2813 CURLcode rcCurl;
2814
2815 PRTHTTPINTERNAL pThis = hHttp;
2816 RTHTTP_VALID_RETURN(pThis);
2817
2818 rcCurl = curl_easy_perform(pThis->pCurl);
2819 if (rcCurl != CURLE_OK)
2820 return VERR_HTTP_CURL_ERROR;
2821
2822 return VINF_SUCCESS;
2823}
2824
2825
2826RTR3DECL(int) RTHttpRawGetResponseCode(RTHTTP hHttp, long *plCode)
2827{
2828 CURLcode rcCurl;
2829
2830 PRTHTTPINTERNAL pThis = hHttp;
2831 RTHTTP_VALID_RETURN(pThis);
2832 AssertPtrReturn(plCode, VERR_INVALID_PARAMETER);
2833
2834 rcCurl = curl_easy_getinfo(pThis->pCurl, CURLINFO_RESPONSE_CODE, plCode);
2835 if (rcCurl != CURLE_OK)
2836 return VERR_HTTP_CURL_ERROR;
2837
2838 return VINF_SUCCESS;
2839}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette