Changeset 57794 in vbox
- Timestamp:
- Sep 16, 2015 7:39:06 PM (10 years ago)
- svn:sync-xref-src-repo-rev:
- 102736
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/generic/http-curl.cpp
r57777 r57794 34 34 #include <iprt/asm.h> 35 35 #include <iprt/assert.h> 36 #include <iprt/cidr.h> 36 37 #include <iprt/crypto/store.h> 37 38 #include <iprt/ctype.h> … … 39 40 #include <iprt/err.h> 40 41 #include <iprt/file.h> 41 #ifdef RT_OS_WINDOWS 42 # include <iprt/ldr.h> 43 #endif 42 #include <iprt/ldr.h> 44 43 #include <iprt/mem.h> 44 #include <iprt/net.h> 45 45 #include <iprt/once.h> 46 46 #include <iprt/path.h> 47 47 #include <iprt/stream.h> 48 48 #include <iprt/string.h> 49 #include <iprt/uni.h> 49 50 #include <iprt/uri.h> 50 51 … … 551 552 552 553 /** 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 */ 562 static 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 /** 553 644 * Consults enviornment variables that cURL/lynx/wget/lynx uses for figuring out 554 645 * the proxy config. … … 619 710 if (cchValue != 0) 620 711 { 621 /* Add a http:// prefix so RTUriParse groks it . */712 /* Add a http:// prefix so RTUriParse groks it (cheaper to do it here). */ 622 713 if (!strstr(szTmp, "://")) 623 714 { … … 626 717 } 627 718 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; 690 722 } 691 723 /* … … 741 773 742 774 /** 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 */ 781 static 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 */ 886 static 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 /** 743 996 * Reconfigures the cURL proxy settings for the given URL. 744 997 * … … 747 1000 * @param pszUrl The URL. 748 1001 */ 749 static int rtHttp ConfigureProxyForUrlWindows(PRTHTTPINTERNAL pThis, const char *pszUrl)1002 static int rtHttpWinConfigureProxyForUrl(PRTHTTPINTERNAL pThis, const char *pszUrl) 750 1003 { 751 1004 int rcRet = VINF_NOT_SUPPORTED; … … 880 1133 881 1134 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); 884 1139 break; 885 1140 … … 913 1168 { 914 1169 #ifdef RT_OS_WINDOWS 915 int rc = rtHttp ConfigureProxyForUrlWindows(pThis, pszUrl);1170 int rc = rtHttpWinConfigureProxyForUrl(pThis, pszUrl); 916 1171 if (rc == VINF_SUCCESS || RT_FAILURE(rc)) 917 1172 return rc;
Note:
See TracChangeset
for help on using the changeset viewer.