VirtualBox

Changeset 57794 in vbox


Ignore:
Timestamp:
Sep 16, 2015 7:39:06 PM (10 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
102736
Message:

http-curl.cpp: Implementing parsing the windows proxy server list as well as the by-pass list.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/generic/http-curl.cpp

    r57777 r57794  
    3434#include <iprt/asm.h>
    3535#include <iprt/assert.h>
     36#include <iprt/cidr.h>
    3637#include <iprt/crypto/store.h>
    3738#include <iprt/ctype.h>
     
    3940#include <iprt/err.h>
    4041#include <iprt/file.h>
    41 #ifdef RT_OS_WINDOWS
    42 # include <iprt/ldr.h>
    43 #endif
     42#include <iprt/ldr.h>
    4443#include <iprt/mem.h>
     44#include <iprt/net.h>
    4545#include <iprt/once.h>
    4646#include <iprt/path.h>
    4747#include <iprt/stream.h>
    4848#include <iprt/string.h>
     49#include <iprt/uni.h>
    4950#include <iprt/uri.h>
    5051
     
    551552
    552553/**
     554 * Configures a proxy given a "URL" like specification.
     555 *
     556 * Format is [<scheme>"://"][<userid>[@<password>]:]<server>[":"<port>].
     557 *
     558 * @returns IPRT status code.
     559 * @param   pThis               The HTTP client instance.
     560 * @param   pszProxyUrl         The proxy server "URL".
     561 */
     562static int rtHttpConfigureProxyFromUrl(PRTHTTPINTERNAL pThis, const char *pszProxyUrl)
     563{
     564    /*
     565     * Make sure it can be parsed as an URL.
     566     */
     567    char *pszFreeMe = NULL;
     568    if (!strstr(pszProxyUrl, "://"))
     569    {
     570        static const char s_szPrefix[] = "http://";
     571        size_t cchProxyUrl = strlen(pszProxyUrl);
     572        pszFreeMe = (char *)RTMemTmpAlloc(sizeof(s_szPrefix) + cchProxyUrl);
     573        if (pszFreeMe)
     574        {
     575            memcpy(pszFreeMe, s_szPrefix, sizeof(s_szPrefix) - 1);
     576            memcpy(&pszFreeMe[sizeof(s_szPrefix) - 1], pszProxyUrl, cchProxyUrl);
     577            pszFreeMe[sizeof(s_szPrefix) - 1 + cchProxyUrl] = '\0';
     578            pszProxyUrl = pszFreeMe;
     579        }
     580        else
     581            return VERR_NO_TMP_MEMORY;
     582    }
     583
     584    RTURIPARSED Parsed;
     585    int rc = RTUriParse(pszProxyUrl, &Parsed);
     586    if (RT_SUCCESS(rc))
     587    {
     588        bool fDone = false;
     589        char *pszHost = RTUriParsedAuthorityHost(pszProxyUrl, &Parsed);
     590        if (pszHost)
     591        {
     592            /*
     593             * We've got a host name, try get the rest.
     594             */
     595            char    *pszUsername = RTUriParsedAuthorityUsername(pszProxyUrl, &Parsed);
     596            char    *pszPassword = RTUriParsedAuthorityPassword(pszProxyUrl, &Parsed);
     597            uint32_t uProxyPort  = RTUriParsedAuthorityPort(pszProxyUrl, &Parsed);
     598            curl_proxytype enmProxyType;
     599            if (RTUriIsSchemeMatch(pszProxyUrl, "http"))
     600            {
     601                enmProxyType  = CURLPROXY_HTTP;
     602                if (uProxyPort == UINT32_MAX)
     603                    uProxyPort = 80;
     604            }
     605            else if (   RTUriIsSchemeMatch(pszProxyUrl, "socks4")
     606                     || RTUriIsSchemeMatch(pszProxyUrl, "socks"))
     607                enmProxyType = CURLPROXY_SOCKS4;
     608            else if (RTUriIsSchemeMatch(pszProxyUrl, "socks4a"))
     609                enmProxyType = CURLPROXY_SOCKS4A;
     610            else if (RTUriIsSchemeMatch(pszProxyUrl, "socks5"))
     611                enmProxyType = CURLPROXY_SOCKS5;
     612            else if (RTUriIsSchemeMatch(pszProxyUrl, "socks5h"))
     613                enmProxyType = CURLPROXY_SOCKS5_HOSTNAME;
     614            else
     615            {
     616                enmProxyType = CURLPROXY_HTTP;
     617                if (uProxyPort == UINT32_MAX)
     618                    uProxyPort = 8080;
     619            }
     620
     621            /* Guess the port from the proxy type if not given. */
     622            if (uProxyPort == UINT32_MAX)
     623                uProxyPort = 1080; /* CURL_DEFAULT_PROXY_PORT */
     624
     625            rc = rtHttpUpdateProxyConfig(pThis, enmProxyType, pszHost, uProxyPort, pszUsername, pszPassword);
     626
     627            RTStrFree(pszUsername);
     628            RTStrFree(pszPassword);
     629            RTStrFree(pszHost);
     630        }
     631        else
     632            AssertMsgFailed(("RTUriParsedAuthorityHost('%s',) -> NULL\n", pszProxyUrl));
     633    }
     634    else
     635        AssertMsgFailed(("RTUriParse('%s',) -> %Rrc\n", pszProxyUrl, rc));
     636
     637    if (pszFreeMe)
     638        RTMemTmpFree(pszFreeMe);
     639    return rc;
     640}
     641
     642
     643/**
    553644 * Consults enviornment variables that cURL/lynx/wget/lynx uses for figuring out
    554645 * the proxy config.
     
    619710                if (cchValue != 0)
    620711                {
    621                     /* Add a http:// prefix so RTUriParse groks it. */
     712                    /* Add a http:// prefix so RTUriParse groks it (cheaper to do it here). */
    622713                    if (!strstr(szTmp, "://"))
    623714                    {
     
    626717                    }
    627718
    628                     RTURIPARSED Parsed;
    629                     rc2 = RTUriParse(szTmp, &Parsed);
    630                     if (RT_SUCCESS(rc))
    631                     {
    632                         bool fDone = false;
    633                         char *pszHost = RTUriParsedAuthorityHost(szTmp, &Parsed);
    634                         if (pszHost)
    635                         {
    636                             /*
    637                              * We've got a host name, try get the rest.
    638                              */
    639                             char    *pszUsername = RTUriParsedAuthorityUsername(szTmp, &Parsed);
    640                             char    *pszPassword = RTUriParsedAuthorityPassword(szTmp, &Parsed);
    641                             uint32_t uProxyPort  = RTUriParsedAuthorityPort(szTmp, &Parsed);
    642                             curl_proxytype enmProxyType;
    643                             if (RTUriIsSchemeMatch(szTmp, "http"))
    644                                 enmProxyType  = CURLPROXY_HTTP;
    645                             else if (   RTUriIsSchemeMatch(szTmp, "socks4")
    646                                      || RTUriIsSchemeMatch(szTmp, "socks"))
    647                                 enmProxyType = CURLPROXY_SOCKS4;
    648                             else if (RTUriIsSchemeMatch(szTmp, "socks4a"))
    649                                 enmProxyType = CURLPROXY_SOCKS4A;
    650                             else if (RTUriIsSchemeMatch(szTmp, "socks5"))
    651                                 enmProxyType = CURLPROXY_SOCKS5;
    652                             else if (RTUriIsSchemeMatch(szTmp, "socks5h"))
    653                                 enmProxyType = CURLPROXY_SOCKS5_HOSTNAME;
    654                             else
    655                                 enmProxyType = CURLPROXY_HTTP;
    656 
    657                             /* Guess the port from the proxy type if not given. */
    658                             if (uProxyPort == UINT32_MAX)
    659                                 switch (enmProxyType)
    660                                 {
    661                                     case CURLPROXY_HTTP: uProxyPort = 80; break;
    662                                     default:             uProxyPort = 1080 /* CURL_DEFAULT_PROXY_PORT */; break;
    663                                 }
    664 
    665                             rc2 = rtHttpUpdateProxyConfig(pThis, enmProxyType, pszHost, uProxyPort, pszUsername, pszPassword);
    666 
    667                             RTStrFree(pszUsername);
    668                             RTStrFree(pszPassword);
    669                             RTStrFree(pszHost);
    670 
    671                             /* If that succeeded we're done. */
    672                             if (RT_SUCCESS(rc2))
    673                             {
    674                                 rc = rc2;
    675                                 break;
    676                             }
    677 
    678                             if (RT_SUCCESS(rc))
    679                                 rc = rc2;
    680                         }
    681                         else
    682                             AssertMsgFailed(("RTUriParsedAuthorityHost('%s',) -> NULL\n", szTmp));
    683                     }
    684                     else
    685                     {
    686                         AssertMsgFailed(("RTUriParse('%s',) -> %Rrc\n", szTmp, rc2));
    687                         if (RT_SUCCESS(rc))
    688                             rc = rc2;
    689                     }
     719                    rc2 = rtHttpConfigureProxyFromUrl(pThis, szTmp);
     720                    if (RT_SUCCESS(rc2))
     721                        rc = rc2;
    690722                }
    691723                /*
     
    741773
    742774/**
     775 * Matches the URL against the given Windows by-pass list.
     776 *
     777 * @returns true if we should by-pass the proxy for this URL, false if not.
     778 * @param   pszUrl              The URL.
     779 * @param   pwszBypass          The Windows by-pass list.
     780 */
     781static bool rtHttpWinIsUrlInBypassList(const char *pszUrl, PCRTUTF16 pwszBypass)
     782{
     783    /*
     784     * Don't bother parsing the URL if we've actually got nothing to work with
     785     * in the by-pass list.
     786     */
     787    if (!pwszBypass)
     788        return false;
     789
     790    RTUTF16 wc;
     791    while (   (wc = *pwszBypass) != '\0'
     792           && (   RTUniCpIsSpace(wc)
     793               || wc == ';') )
     794        pwszBypass++;
     795    if (wc == '\0')
     796        return false;
     797
     798    /*
     799     * We now need to parse the URL and extract the host name.
     800     */
     801    RTURIPARSED Parsed;
     802    int rc = RTUriParse(pszUrl, &Parsed);
     803    AssertRCReturn(rc, false);
     804    char *pszHost = RTUriParsedAuthorityHost(pszUrl, &Parsed);
     805    if (!pszHost) /* Don't assert, in case of file:///xxx or similar blunder. */
     806        return false;
     807
     808    bool  fRet = false;
     809    char *pszBypassFree;
     810    rc = RTUtf16ToUtf8(pwszBypass, &pszBypassFree);
     811    if (RT_SUCCESS(rc))
     812    {
     813        /*
     814         * Walk the by-pass list.
     815         *
     816         * According to https://msdn.microsoft.com/en-us/library/aa384098(v=vs.85).aspx
     817         * a by-pass list is semicolon delimited list.  The entries are either host
     818         * names or IP addresses, and may use wildcard ('*', '?', I guess).  There
     819         * special "<local>" entry matches anything without a dot.
     820         */
     821        RTNETADDRU  HostAddr;
     822        int         fIsHostIpv4Address = -1;
     823        char *pszEntry = pszBypassFree;
     824        while (*pszEntry != '\0')
     825        {
     826            /*
     827             * Find end of entry.
     828             */
     829            char   ch;
     830            size_t cchEntry = 1;
     831            while (   (ch = pszEntry[cchEntry]) != '\0'
     832                   && ch != ';'
     833                   && !RT_C_IS_SPACE(ch))
     834                cchEntry++;
     835
     836            char chSaved = pszEntry[cchEntry];
     837            pszEntry[cchEntry] = '\0';
     838            if (   cchEntry == sizeof("<local>")  - 1
     839                && memcmp(pszEntry, RT_STR_TUPLE("<local>")) == 0)
     840                fRet = strchr(pszHost, '.') == NULL;
     841            else if (   memchr(pszEntry, '*', cchEntry) != NULL
     842                     || memchr(pszEntry, '?', cchEntry) != NULL)
     843                fRet = RTStrSimplePatternMatch(pszEntry, pszHost);
     844            else
     845            {
     846                if (fIsHostIpv4Address == -1)
     847                    fIsHostIpv4Address = RT_SUCCESS(RTNetStrToIPv4Addr(pszHost, &HostAddr.IPv4));
     848                RTNETADDRIPV4 Network, Netmask;
     849                if (   fIsHostIpv4Address
     850                    && RT_SUCCESS(RTCidrStrToIPv4(pszEntry, &Network, &Netmask)) )
     851                    fRet = (HostAddr.IPv4.u & Netmask.u) == Network.u;
     852                else
     853                    fRet = RTStrICmp(pszEntry, pszHost) == 0;
     854            }
     855            pszEntry[cchEntry] = chSaved;
     856            if (fRet)
     857                break;
     858
     859            /*
     860             * Next entry.
     861             */
     862            pszEntry += cchEntry;
     863            while (   (ch = *pszEntry) != '\0'
     864                   && (   ch == ';'
     865                       || RT_C_IS_SPACE(ch)) )
     866                pszEntry++;
     867        }
     868
     869        RTStrFree(pszBypassFree);
     870    }
     871
     872    RTStrFree(pszHost);
     873    return false;
     874}
     875
     876
     877/**
     878 * Searches a Windows proxy server list for the best fitting proxy to use, then
     879 * reconfigures the HTTP client instance to use it.
     880 *
     881 * @returns IPRT status code, VINF_NOT_SUPPORTED if we need to consult fallback.
     882 * @param   pThis               The HTTP client instance.
     883 * @param   pszUrl              The URL needing proxying.
     884 * @param   pwszProxies         The list of proxy servers to choose from.
     885 */
     886static int rtHttpWinSelectProxyFromList(PRTHTTPINTERNAL pThis, const char *pszUrl, PCRTUTF16 pwszProxies)
     887{
     888    /*
     889     * Fend off empty strings (very unlikely, but just in case).
     890     */
     891    if (!pwszProxies)
     892        return VINF_NOT_SUPPORTED;
     893
     894    RTUTF16 wc;
     895    while (   (wc = *pwszProxies) != '\0'
     896           && (   RTUniCpIsSpace(wc)
     897               || wc == ';') )
     898        pwszProxies++;
     899    if (wc == '\0')
     900        return VINF_NOT_SUPPORTED;
     901
     902    /*
     903     * We now need to parse the URL and extract the scheme.
     904     */
     905    RTURIPARSED Parsed;
     906    int rc = RTUriParse(pszUrl, &Parsed);
     907    AssertRCReturn(rc, false);
     908    char *pszUrlScheme = RTUriParsedScheme(pszUrl, &Parsed);
     909    AssertReturn(pszUrlScheme, VERR_NO_STR_MEMORY);
     910    size_t const cchUrlScheme = strlen(pszUrlScheme);
     911
     912    int rcRet = VINF_NOT_SUPPORTED;
     913    char *pszProxiesFree;
     914    rc = RTUtf16ToUtf8(pwszProxies, &pszProxiesFree);
     915    if (RT_SUCCESS(rc))
     916    {
     917        /*
     918         * Walk the server list.
     919         *
     920         * According to https://msdn.microsoft.com/en-us/library/aa383912(v=vs.85).aspx
     921         * this is also a semicolon delimited list.  The entries are on the form:
     922         *      [<scheme>=][<scheme>"://"]<server>[":"<port>]
     923         */
     924        bool        fBestEntryHasSameScheme = false;
     925        const char *pszBestEntry = NULL;
     926        char       *pszEntry = pszProxiesFree;
     927        while (*pszEntry != '\0')
     928        {
     929            /*
     930             * Find end of entry.  We include spaces here in addition to ';'.
     931             */
     932            char   ch;
     933            size_t cchEntry = 1;
     934            while (   (ch = pszEntry[cchEntry]) != '\0'
     935                   && ch != ';'
     936                   && !RT_C_IS_SPACE(ch))
     937                cchEntry++;
     938
     939            char const chSaved = pszEntry[cchEntry];
     940            pszEntry[cchEntry] = '\0';
     941
     942            /* Parse the entry. */
     943            const char *pszEndOfScheme = strstr(pszEntry, "://");
     944            const char *pszEqual       = (const char *)memchr(pszEntry, '=',
     945                                                              pszEndOfScheme ? pszEndOfScheme - pszEntry : cchEntry);
     946            if (pszEqual)
     947            {
     948                if (   pszEqual - pszEntry == cchUrlScheme
     949                    && RTStrNICmp(pszEntry, pszUrlScheme, cchUrlScheme) == 0)
     950                {
     951                    pszBestEntry = pszEqual + 1;
     952                    break;
     953                }
     954            }
     955            else
     956            {
     957                bool fSchemeMatch = pszEndOfScheme
     958                                 && pszEndOfScheme - pszEntry == cchUrlScheme
     959                                 && RTStrNICmp(pszEntry, pszUrlScheme, cchUrlScheme) == 0;
     960                if (   !pszBestEntry
     961                    || (   !fBestEntryHasSameScheme
     962                        && fSchemeMatch) )
     963                {
     964                    pszBestEntry = pszEntry;
     965                    fBestEntryHasSameScheme = fSchemeMatch;
     966                }
     967            }
     968
     969            /*
     970             * Next entry.
     971             */
     972            if (!chSaved)
     973                break;
     974            pszEntry += cchEntry + 1;
     975            while (   (ch = *pszEntry) != '\0'
     976                   && (   ch == ';'
     977                       || RT_C_IS_SPACE(ch)) )
     978                pszEntry++;
     979        }
     980
     981        /*
     982         * If we found something, try use it.
     983         */
     984        if (pszBestEntry)
     985            rcRet = rtHttpConfigureProxyFromUrl(pThis, pszBestEntry);
     986
     987        RTStrFree(pszProxiesFree);
     988    }
     989
     990    RTStrFree(pszUrlScheme);
     991    return rc;
     992}
     993
     994
     995/**
    743996 * Reconfigures the cURL proxy settings for the given URL.
    744997 *
     
    7471000 * @param   pszUrl      The URL.
    7481001 */
    749 static int rtHttpConfigureProxyForUrlWindows(PRTHTTPINTERNAL pThis, const char *pszUrl)
     1002static int rtHttpWinConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl)
    7501003{
    7511004    int rcRet = VINF_NOT_SUPPORTED;
     
    8801133
    8811134            case WINHTTP_ACCESS_TYPE_NAMED_PROXY:
    882 /** @todo Continue here: parse the proxy thingy. */
    883 //AssertMsgFailed(("lpszProxy='%ls'\n", ProxyInfo.lpszProxy));
     1135                if (!rtHttpWinIsUrlInBypassList(pszUrl, ProxyInfo.lpszProxyBypass))
     1136                    rcRet = rtHttpWinSelectProxyFromList(pThis, pszUrl, ProxyInfo.lpszProxy);
     1137                else
     1138                    rcRet = rtHttpUpdateAutomaticProxyDisable(pThis);
    8841139                break;
    8851140
     
    9131168    {
    9141169#ifdef RT_OS_WINDOWS
    915         int rc = rtHttpConfigureProxyForUrlWindows(pThis, pszUrl);
     1170        int rc = rtHttpWinConfigureProxyForUrl(pThis, pszUrl);
    9161171        if (rc == VINF_SUCCESS || RT_FAILURE(rc))
    9171172            return rc;
Note: See TracChangeset for help on using the changeset viewer.

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