HTTP proxy discovery for Windows

This was SVN commit r8969.
This commit is contained in:
Ykkrosh 2011-02-21 21:54:47 +00:00
parent 25039d3aff
commit 9bbda8b80b
4 changed files with 140 additions and 3 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2010 Wildfire Games
/* Copyright (c) 2011 Wildfire Games
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -393,6 +393,11 @@ LibError sys_generate_random_bytes(u8* buf, size_t count)
return INFO::OK;
}
LibError sys_get_proxy_config(const std::string& UNUSED(url), std::string& UNUSED(proxy))
{
return INFO::SKIPPED;
}
LibError sys_open_url(const std::string& url)
{
pid_t pid = fork();

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2010 Wildfire Games
/* Copyright (c) 2011 Wildfire Games
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -32,15 +32,18 @@
#include <shellapi.h> // open_url
#include <Wincrypt.h>
#include <WindowsX.h> // message crackers
#include <winhttp.h>
#include "lib/sysdep/clipboard.h"
#include "lib/sysdep/os/win/error_dialog.h"
#include "lib/sysdep/os/win/wutil.h"
#include <boost/algorithm/string.hpp>
#if MSC_VERSION
#pragma comment(lib, "shell32.lib") // for sys_pick_directory SH* calls
#pragma comment(lib, "winhttp.lib")
#endif
@ -469,3 +472,112 @@ LibError sys_generate_random_bytes(u8* buffer, size_t size)
return INFO::OK;
}
/*
* Given a string of the form
* "example.com:80"
* or
* "ftp=ftp.example.com:80;http=example.com:80;https=example.com:80"
* separated by semicolons or whitespace,
* return the string "example.com:80".
*/
static std::wstring parse_proxy(const std::wstring& input)
{
if(input.find('=') == input.npos)
return input;
std::vector<std::wstring> parts;
split(parts, input, boost::algorithm::is_any_of("; \t\r\n"), boost::algorithm::token_compress_on);
for(size_t i = 0; i < parts.size(); ++i)
if(boost::algorithm::starts_with(parts[i], "http="))
return parts[i].substr(5);
// If we got this far, proxies were only set for non-HTTP protocols
return L"";
}
LibError sys_get_proxy_config(const std::wstring& url, std::wstring& proxy)
{
WINHTTP_AUTOPROXY_OPTIONS autoProxyOptions;
memset(&autoProxyOptions, 0, sizeof(autoProxyOptions));
autoProxyOptions.dwFlags = WINHTTP_AUTOPROXY_AUTO_DETECT;
autoProxyOptions.dwAutoDetectFlags = WINHTTP_AUTO_DETECT_TYPE_DHCP | WINHTTP_AUTO_DETECT_TYPE_DNS_A;
autoProxyOptions.fAutoLogonIfChallenged = TRUE;
WINHTTP_PROXY_INFO proxyInfo;
memset(&proxyInfo, 0, sizeof(proxyInfo));
WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieConfig;
memset(&ieConfig, 0, sizeof(ieConfig));
HINTERNET hSession = NULL;
LibError err = INFO::SKIPPED;
bool useAutoDetect;
if(WinHttpGetIEProxyConfigForCurrentUser(&ieConfig))
{
if(ieConfig.lpszAutoConfigUrl)
{
// Use explicit auto-config script if specified
useAutoDetect = true;
autoProxyOptions.dwFlags |= WINHTTP_AUTOPROXY_CONFIG_URL;
autoProxyOptions.lpszAutoConfigUrl = ieConfig.lpszAutoConfigUrl;
}
else
{
// Use auto-discovery if enabled
useAutoDetect = (ieConfig.fAutoDetect == TRUE);
}
}
else
{
// Can't find IE config settings - fall back to auto-discovery
useAutoDetect = true;
}
if(useAutoDetect)
{
hSession = WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if(hSession && WinHttpGetProxyForUrl(hSession, url.c_str(), &autoProxyOptions, &proxyInfo))
{
proxy = parse_proxy(proxyInfo.lpszProxy);
if(!proxy.empty())
{
err = INFO::OK;
goto done;
}
}
}
// No valid auto-config; try explicit proxy instead
if(ieConfig.lpszProxy)
{
proxy = parse_proxy(ieConfig.lpszProxy);
if(!proxy.empty())
{
err = INFO::OK;
goto done;
}
}
done:
if(ieConfig.lpszProxy)
GlobalFree(ieConfig.lpszProxy);
if(ieConfig.lpszProxyBypass)
GlobalFree(ieConfig.lpszProxyBypass);
if(ieConfig.lpszAutoConfigUrl)
GlobalFree(ieConfig.lpszAutoConfigUrl);
if(proxyInfo.lpszProxy)
GlobalFree(proxyInfo.lpszProxy);
if(proxyInfo.lpszProxyBypass)
GlobalFree(proxyInfo.lpszProxyBypass);
if(hSession)
WinHttpCloseHandle(hSession);
return err;
}

View File

@ -168,6 +168,15 @@ extern size_t sys_max_sector_size();
**/
LIB_API LibError sys_generate_random_bytes(u8* buf, size_t count);
/**
* get the proxy address for accessing the given HTTP URL.
*
* this may be very slow (tens of seconds).
*
* @return INFO::OK on success; INFO::SKIPPED if no proxy found.
**/
LIB_API LibError sys_get_proxy_config(const std::wstring& url, std::wstring& proxy);
/**
* directory separation character
**/

View File

@ -20,6 +20,7 @@
#include "UserReport.h"
#include "lib/timer.h"
#include "lib/utf8.h"
#include "lib/external_libraries/curl.h"
#include "lib/external_libraries/sdl.h"
#include "lib/external_libraries/zlib.h"
@ -82,7 +83,7 @@ class CUserReporterWorker
{
public:
CUserReporterWorker(const std::string& userID, const std::string& url) :
m_UserID(userID), m_Enabled(false), m_Shutdown(false), m_Status("disabled"),
m_URL(url), m_UserID(userID), m_Enabled(false), m_Shutdown(false), m_Status("disabled"),
m_PauseUntilTime(timer_Time()), m_LastUpdateTime(timer_Time())
{
// Set up libcurl:
@ -237,6 +238,15 @@ private:
void Run()
{
// Set libcurl's proxy configuration
// (This has to be done in the thread because it's potentially very slow)
SetStatus("proxy");
std::wstring proxy;
if (sys_get_proxy_config(wstring_from_utf8(m_URL), proxy) == INFO::OK)
curl_easy_setopt(m_Curl, CURLOPT_PROXY, utf8_from_wstring(proxy).c_str());
SetStatus("waiting");
/*
* We use a semaphore to let the thread be woken up when it has
* work to do. Various actions from the main thread can wake it:
@ -463,6 +473,7 @@ private:
std::string m_Status;
// Initialised in constructor by main thread; otherwise used only by worker thread:
std::string m_URL;
std::string m_UserID;
CURL* m_Curl;
curl_slist* m_Headers;