VirtualBox

Ignore:
Timestamp:
Sep 12, 2015 12:01:23 AM (9 years ago)
Author:
vboxsync
Message:

RTHttp: RTHttpUseSystemProxySettings improvements wrt env vars.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/common/misc/http.cpp

    r57720 r57726  
    4242#include <iprt/stream.h>
    4343#include <iprt/string.h>
     44#include <iprt/uri.h>
    4445
    4546#include "internal/magics.h"
     
    6869    /** Whether to delete the CA on destruction. */
    6970    bool                fDeleteCaFile;
     71
     72    /** @name Proxy settings.
     73     * When fUseSystemProxySettings is set, the other members will be updated each
     74     * time we're presented with a new URL.  The members reflect the cURL
     75     * configuration.
     76     *
     77     * @{ */
    7078    /** Set if we should use the system proxy settings for a URL.
    7179     * This means reconfiguring cURL for each request.  */
    7280    bool                fUseSystemProxySettings;
     81    /** Set if we've detected no proxy necessary. */
     82    bool                fNoProxy;
     83    /** Proxy host name (RTStrFree). */
     84    char               *pszProxyHost;
     85    /** Proxy port number (UINT32_MAX if not specified). */
     86    uint32_t            uProxyPort;
     87    /** The proxy type (CURLPROXY_HTTP, CURLPROXY_SOCKS5, ++). */
     88    curl_proxytype      enmProxyType;
     89    /** Proxy username (RTStrFree). */
     90    char               *pszProxyUsername;
     91    /** Proxy password (RTStrFree). */
     92    char               *pszProxyPassword;
     93    /** @} */
     94
    7395    /** Abort the current HTTP request if true. */
    7496    bool volatile       fAbort;
     
    238260    PRTHTTPINTERNAL pThis = hHttp;
    239261    RTHTTP_VALID_RETURN(pThis);
    240 
    241     /*
    242      * Very limited right now, just enought to make it work for ourselves.
    243      */
    244     char szProxy[_1K];
    245     int rc = RTEnvGetEx(RTENV_DEFAULT, "http_proxy", szProxy, sizeof(szProxy), NULL);
    246     if (RT_SUCCESS(rc))
    247     {
    248         int rcCurl;
    249         if (!strncmp(szProxy, RT_STR_TUPLE("http://")))
    250         {
    251             rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);
    252             if (CURL_FAILURE(rcCurl))
    253                 return VERR_INVALID_PARAMETER;
    254             rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, 80);
    255             if (CURL_FAILURE(rcCurl))
    256                 return VERR_INVALID_PARAMETER;
    257         }
    258         else
    259         {
    260             rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);
    261             if (CURL_FAILURE(rcCurl))
    262                 return VERR_INVALID_PARAMETER;
    263         }
    264     }
    265     else if (rc == VERR_ENV_VAR_NOT_FOUND)
    266         rc = VINF_SUCCESS;
    267 
    268     return rc;
     262    AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
     263
     264    /*
     265     * Change the settings.
     266     */
     267    pThis->fUseSystemProxySettings = true;
     268    return VINF_SUCCESS;
     269}
     270
     271
     272/**
     273 * rtHttpConfigureProxyForUrl: Update cURL proxy settings as needed.
     274 *
     275 * @returns IPRT status code.
     276 * @param   pThis           The HTTP client instance.
     277 * @param   enmProxyType    The proxy type.
     278 * @param   pszHost         The proxy host name.  If NULL, the proxying will be
     279 *                          disabled.
     280 * @param   uPort           The proxy port number.
     281 * @param   pszUsername     The proxy username, or NULL if none.
     282 * @param   pszPassword     The proxy password, or NULL if none.
     283 */
     284static int rtHttpUpdateProxyConfig(PRTHTTPINTERNAL pThis, curl_proxytype enmProxyType, const char *pszHost,
     285                                   uint32_t uPort, const char *pszUsername, const char *pszPassword)
     286{
     287    int rcCurl;
     288
     289    if (enmProxyType != pThis->enmProxyType)
     290    {
     291        rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYTYPE, (long)enmProxyType);
     292        AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_PROXYTYPE=%d: %d (%#x)\n", enmProxyType, rcCurl, rcCurl),
     293                        VERR_HTTP_CURL_PROXY_CONFIG);
     294        pThis->enmProxyType = CURLPROXY_HTTP;
     295    }
     296
     297    if (uPort != pThis->uProxyPort)
     298    {
     299        rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, (long)uPort);
     300        AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_PROXYPORT=%d: %d (%#x)\n", uPort, rcCurl, rcCurl),
     301                        VERR_HTTP_CURL_PROXY_CONFIG);
     302        pThis->uProxyPort = uPort;
     303    }
     304
     305    if (   pszUsername != pThis->pszProxyUsername
     306        || RTStrCmp(pszUsername, pThis->pszProxyUsername))
     307    {
     308        rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYUSERNAME, pszUsername);
     309        AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_PROXYUSERNAME=%s: %d (%#x)\n", pszUsername, rcCurl, rcCurl),
     310                        VERR_HTTP_CURL_PROXY_CONFIG);
     311        if (pThis->pszProxyUsername)
     312        {
     313            RTStrFree(pThis->pszProxyUsername);
     314            pThis->pszProxyUsername = NULL;
     315        }
     316        if (pszUsername)
     317        {
     318            pThis->pszProxyUsername = RTStrDup(pszUsername);
     319            AssertReturn(pThis->pszProxyUsername, VERR_NO_STR_MEMORY);
     320        }
     321    }
     322
     323    if (   pszPassword != pThis->pszProxyPassword
     324        || RTStrCmp(pszPassword, pThis->pszProxyPassword))
     325    {
     326        rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPASSWORD, pszPassword);
     327        AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_PROXYPASSWORD=%s: %d (%#x)\n", pszPassword ? "xxx" : NULL, rcCurl, rcCurl),
     328                        VERR_HTTP_CURL_PROXY_CONFIG);
     329        if (pThis->pszProxyPassword)
     330        {
     331            RTMemWipeThoroughly(pThis->pszProxyPassword, strlen(pThis->pszProxyPassword), 2);
     332            RTStrFree(pThis->pszProxyPassword);
     333            pThis->pszProxyPassword = NULL;
     334        }
     335        if (pszPassword)
     336        {
     337            pThis->pszProxyPassword = RTStrDup(pszPassword);
     338            AssertReturn(pThis->pszProxyPassword, VERR_NO_STR_MEMORY);
     339        }
     340    }
     341
     342    if (   pszHost != pThis->pszProxyHost
     343        || RTStrCmp(pszHost, pThis->pszProxyHost))
     344    {
     345        rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, pszHost);
     346        AssertMsgReturn(rcCurl == CURLE_OK, ("CURLOPT_PROXY=%s: %d (%#x)\n", pszHost, rcCurl, rcCurl),
     347                        VERR_HTTP_CURL_PROXY_CONFIG);
     348        if (pThis->pszProxyHost)
     349        {
     350            RTStrFree(pThis->pszProxyHost);
     351            pThis->pszProxyHost = NULL;
     352        }
     353        if (pszHost)
     354        {
     355            pThis->pszProxyHost = RTStrDup(pszHost);
     356            AssertReturn(pThis->pszProxyHost, VERR_NO_STR_MEMORY);
     357        }
     358    }
     359
     360    return VINF_SUCCESS;
     361}
     362
     363
     364/**
     365 * rtHttpConfigureProxyForUrl: Disables proxying.
     366 *
     367 * @returns IPRT status code.
     368 * @param   pThis               The HTTP client instance.
     369 */
     370static int rtHttpUpdateAutomaticProxyDisable(PRTHTTPINTERNAL pThis)
     371{
     372    AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYTYPE,   (long)CURLPROXY_HTTP) == CURLE_OK, VERR_INTERNAL_ERROR_2);
     373    pThis->enmProxyType = CURLPROXY_HTTP;
     374
     375    AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT,             (long)1080) == CURLE_OK, VERR_INTERNAL_ERROR_2);
     376    pThis->uProxyPort = 1080;
     377
     378    AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYUSERNAME, (const char *)NULL) == CURLE_OK, VERR_INTERNAL_ERROR_2);
     379    if (pThis->pszProxyUsername)
     380    {
     381        RTStrFree(pThis->pszProxyUsername);
     382        pThis->pszProxyUsername = NULL;
     383    }
     384
     385    AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPASSWORD, (const char *)NULL) == CURLE_OK, VERR_INTERNAL_ERROR_2);
     386    if (pThis->pszProxyPassword)
     387    {
     388        RTStrFree(pThis->pszProxyPassword);
     389        pThis->pszProxyPassword = NULL;
     390    }
     391
     392    AssertReturn(curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY,         (const char *)NULL) == CURLE_OK, VERR_INTERNAL_ERROR_2);
     393    if (pThis->pszProxyHost)
     394    {
     395        RTStrFree(pThis->pszProxyHost);
     396        pThis->pszProxyHost = NULL;
     397    }
     398    return VINF_SUCCESS;
    269399}
    270400
     
    277407
    278408
    279 static int rtHttpConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
    280 {
    281     const char *pszProxy    = NULL;
    282     long        uPort       = 0;
    283     const char *pszUser     = NULL;
    284     const char *pszPassword = NULL;
    285     bool        fNoProxy    = true;
    286 
    287 
     409
     410/**
     411 * Consults enviornment variables that cURL/lynx/wget/lynx uses for figuring out
     412 * the proxy config.
     413 *
     414 * @returns IPRT status code.
     415 * @param   pThis               The HTTP client instance.
     416 * @param   pszUrl              The URL to configure a proxy for.
     417 */
     418static int rtHttpConfigureProxyForUrlFromEnv(PRTHTTPINTERNAL pThis, const char *pszUrl)
     419{
    288420    char szTmp[_1K];
    289421
     
    305437    }
    306438    AssertMsg(rc == VINF_SUCCESS || rc == VERR_ENV_VAR_NOT_FOUND, ("rc=%Rrc\n", rc));
    307     fNoProxy = rtHttpUrlInNoProxyList(pszUrl, pszNoProxy);
    308     RTMemTmpFree(pszNoProxy);
     439    bool fNoProxy = rtHttpUrlInNoProxyList(pszUrl, pszNoProxy);
     440    RTMemTmpFree(pszNoProxyFree);
    309441    if (!fNoProxy)
    310442    {
     
    332464        apszEnvVars[cEnvVars++] = "ALL_PROXY";
    333465
     466        /*
     467         * We try the env vars out and goes with the first one we can make sense out of.
     468         * If we cannot make sense of any, we return the first unexpected rc we got.
     469         */
     470        rc = VINF_SUCCESS;
    334471        for (uint32_t i = 0; i < cEnvVars; i++)
    335472        {
    336473            size_t cchValue;
    337             rc = RTEnvGetEx(RTENV_DEFAULT, apszEnvVars[i], szTmp, sizeof(szTmp) - sizeof("http://"), &cchValue);
    338             if (RT_SUCCESS(rc))
     474            int rc2 = RTEnvGetEx(RTENV_DEFAULT, apszEnvVars[i], szTmp, sizeof(szTmp) - sizeof("http://"), &cchValue);
     475            if (RT_SUCCESS(rc2))
    339476            {
    340                 if (!strstr(szTmp, "://"))
     477                if (cchValue != 0)
    341478                {
    342                     memmove(&szTmp[sizeof("http://") - 1], szTmp, cchValue + 1);
    343                     memcpy(szTmp, RT_STR_TUPLE("http://"));
     479                    /* Add a http:// prefix so RTUriParse groks it. */
     480                    if (!strstr(szTmp, "://"))
     481                    {
     482                        memmove(&szTmp[sizeof("http://") - 1], szTmp, cchValue + 1);
     483                        memcpy(szTmp, RT_STR_TUPLE("http://"));
     484                    }
     485
     486                    RTURIPARSED Parsed;
     487                    rc2 = RTUriParse(szTmp, &Parsed);
     488                    if (RT_SUCCESS(rc))
     489                    {
     490                        bool fDone = false;
     491                        char *pszHost = RTUriParsedAuthorityHost(szTmp, &Parsed);
     492                        if (pszHost)
     493                        {
     494                            /*
     495                             * We've got a host name, try get the rest.
     496                             */
     497                            char    *pszUsername = RTUriParsedAuthorityUsername(szTmp, &Parsed);
     498                            char    *pszPassword = RTUriParsedAuthorityPassword(szTmp, &Parsed);
     499                            uint32_t uProxyPort  = RTUriParsedAuthorityPort(szTmp, &Parsed);
     500                            curl_proxytype enmProxyType;
     501                            if (RTUriIsSchemeMatch(szTmp, "http"))
     502                                enmProxyType  = CURLPROXY_HTTP;
     503                            else if (   RTUriIsSchemeMatch(szTmp, "socks4")
     504                                     || RTUriIsSchemeMatch(szTmp, "socks"))
     505                                enmProxyType = CURLPROXY_SOCKS4;
     506                            else if (RTUriIsSchemeMatch(szTmp, "socks4a"))
     507                                enmProxyType = CURLPROXY_SOCKS4A;
     508                            else if (RTUriIsSchemeMatch(szTmp, "socks5"))
     509                                enmProxyType = CURLPROXY_SOCKS5;
     510                            else if (RTUriIsSchemeMatch(szTmp, "socks5h"))
     511                                enmProxyType = CURLPROXY_SOCKS5_HOSTNAME;
     512                            else
     513                                enmProxyType = CURLPROXY_HTTP;
     514
     515                            /* Guess the port from the proxy type if not given. */
     516                            if (uProxyPort == UINT32_MAX)
     517                                switch (enmProxyType)
     518                                {
     519                                    case CURLPROXY_HTTP: uProxyPort = 80; break;
     520                                    default:             uProxyPort = 1080 /* CURL_DEFAULT_PROXY_PORT */; break;
     521                                }
     522
     523                            rc2 = rtHttpUpdateProxyConfig(pThis, enmProxyType, pszHost, uProxyPort, pszUsername, pszPassword);
     524
     525                            RTStrFree(pszUsername);
     526                            RTStrFree(pszPassword);
     527                            RTStrFree(pszHost);
     528
     529                            /* If that succeeded we're done. */
     530                            if (RT_SUCCESS(rc2))
     531                            {
     532                                rc = rc2;
     533                                break;
     534                            }
     535
     536                            if (RT_SUCCESS(rc))
     537                                rc = rc2;
     538                        }
     539                        else
     540                            AssertMsgFailed(("RTUriParsedAuthorityHost('%s',) -> NULL\n", szTmp));
     541                    }
     542                    else
     543                    {
     544                        AssertMsgFailed(("RTUriParse('%s',) -> %Rrc\n", szTmp, rc2));
     545                        if (RT_SUCCESS(rc))
     546                            rc = rc2;
     547                    }
    344548                }
    345                 /** @todo continue where using RTUriParse... */
     549                /*
     550                 * The variable is empty.  Guess that means no proxying wanted.
     551                 */
     552                else
     553                {
     554                    rc = rtHttpUpdateAutomaticProxyDisable(pThis);
     555                    break;
     556                }
    346557            }
    347558            else
    348                 AssertMsg(rc == VERR_ENV_VAR_NOT_FOUND, ("%Rrc\n"));
    349         }
    350 
    351 #if 0
    352         if (RT_SUCCESS(rc))
    353         {
    354             int rcCurl;
    355             if (!strncmp(szProxy, RT_STR_TUPLE("http://")))
    356             {
    357                 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);
    358                 if (CURL_FAILURE(rcCurl))
    359                     return VERR_INVALID_PARAMETER;
    360                 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, 80);
    361                 if (CURL_FAILURE(rcCurl))
    362                     return VERR_INVALID_PARAMETER;
    363             }
    364             else
    365             {
    366                 rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, &szProxy[sizeof("http://") - 1]);
    367                 if (CURL_FAILURE(rcCurl))
    368                     return VERR_INVALID_PARAMETER;
    369             }
    370         }
    371         else if (rc == VERR_ENV_VAR_NOT_FOUND)
    372             rc = VINF_SUCCESS;
    373 #endif
    374     }
     559                AssertMsgStmt(rc2 == VERR_ENV_VAR_NOT_FOUND, ("%Rrc\n", rc2), if (RT_SUCCESS(rc)) rc = rc2);
     560        }
     561    }
     562    /*
     563     * The host is the no-proxy list, it seems.
     564     */
     565    else
     566        rc = rtHttpUpdateAutomaticProxyDisable(pThis);
    375567
    376568    return rc;
     569}
     570
     571
     572static int rtHttpConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
     573{
     574    if (pThis->fUseSystemProxySettings)
     575    {
     576/** @todo system specific class here, fall back on env vars if necessary. */
     577        return rtHttpConfigureProxyForUrlFromEnv(pThis, pszUrl);
     578    }
     579
     580    return VINF_SUCCESS;
    377581}
    378582
     
    384588    RTHTTP_VALID_RETURN(pThis);
    385589    AssertPtrReturn(pcszProxy, VERR_INVALID_PARAMETER);
    386 
    387     int rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXY, pcszProxy);
    388     if (CURL_FAILURE(rcCurl))
    389         return VERR_INVALID_PARAMETER;
    390 
    391     if (uPort != 0)
    392     {
    393         rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPORT, (long)uPort);
    394         if (CURL_FAILURE(rcCurl))
    395             return VERR_INVALID_PARAMETER;
    396     }
    397 
    398     if (pcszProxyUser && pcszProxyPwd)
    399     {
    400         rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYUSERNAME, pcszProxyUser);
    401         if (CURL_FAILURE(rcCurl))
    402             return VERR_INVALID_PARAMETER;
    403 
    404         rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROXYPASSWORD, pcszProxyPwd);
    405         if (CURL_FAILURE(rcCurl))
    406             return VERR_INVALID_PARAMETER;
    407     }
    408 
    409     return VINF_SUCCESS;
     590    AssertReturn(!pThis->fBusy, VERR_WRONG_ORDER);
     591
     592    /*
     593     * Update the settings.
     594     *
     595     * Currently, we don't make alot of effort parsing or checking the input, we
     596     * leave that to cURL.  (A bit afraid of breaking user settings.)
     597     */
     598    pThis->fUseSystemProxySettings = false;
     599    return rtHttpUpdateProxyConfig(pThis, CURLPROXY_HTTP, pcszProxy, uPort ? uPort : 1080, pcszProxyUser, pcszProxyPwd);
    410600}
    411601
     
    599789static int rtHttpGetCalcStatus(PRTHTTPINTERNAL pThis, int rcCurl)
    600790{
    601     int rc = VERR_INTERNAL_ERROR;
     791    int rc = VERR_HTTP_CURL_ERROR;
    602792
    603793    if (pThis->pszRedirLocation)
     
    728918
    729919    /*
     920     * Proxy config.
     921     */
     922    int rc = rtHttpConfigureProxyForUrl(pThis, pszUrl);
     923    if (RT_FAILURE(rc))
     924        return rc;
     925
     926    /*
    730927     * Setup SSL.  Can be a bit of work.
    731928     */
     
    738935        && rtHttpNeedSsl(pszUrl))
    739936    {
    740         int rc = RTHttpUseTemporaryCaFile(pThis, NULL);
     937        rc = RTHttpUseTemporaryCaFile(pThis, NULL);
    741938        if (RT_SUCCESS(rc))
    742939            pszCaFile = pThis->pszCaFile;
     
    748945        rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_CAINFO, pszCaFile);
    749946        if (CURL_FAILURE(rcCurl))
    750             return VERR_INTERNAL_ERROR;
     947            return VERR_HTTP_CURL_ERROR;
    751948    }
    752949
     
    756953    rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROGRESSFUNCTION, &rtHttpProgress);
    757954    if (CURL_FAILURE(rcCurl))
    758         return VERR_INTERNAL_ERROR;
     955        return VERR_HTTP_CURL_ERROR;
    759956    rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_PROGRESSDATA, (void *)pThis);
    760957    if (CURL_FAILURE(rcCurl))
    761         return VERR_INTERNAL_ERROR;
     958        return VERR_HTTP_CURL_ERROR;
    762959    rcCurl = curl_easy_setopt(pThis->pCurl, CURLOPT_NOPROGRESS, (long)0);
    763960    if (CURL_FAILURE(rcCurl))
    764         return VERR_INTERNAL_ERROR;
     961        return VERR_HTTP_CURL_ERROR;
    765962
    766963    return VINF_SUCCESS;
     
    8951092        }
    8961093        else
    897             rc = VERR_INTERNAL_ERROR_3;
     1094            rc = VERR_HTTP_CURL_ERROR;
    8981095    }
    8991096
     
    10021199        }
    10031200        else
    1004             rc = VERR_INTERNAL_ERROR_3;
     1201            rc = VERR_HTTP_CURL_ERROR;
    10051202    }
    10061203
Note: See TracChangeset for help on using the changeset viewer.

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