WindowsXP-SP1/inetcore/wininet/handles/proxysup.cxx
2020-09-30 16:53:49 +02:00

3889 lines
93 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
proxysup.cxx
Abstract:
Contains class implementation for proxy server and proxy bypass list
Contents:
IsLocalMacro
PROXY_SERVER_LIST_ENTRY::WriteEntry
PROXY_SERVER_LIST::AddList
PROXY_SERVER_LIST::Find
PROXY_SERVER_LIST::Add
PROXY_SERVER_LIST::ProxyScheme
PROXY_SERVER_LIST::GetProxyHostName
PROXY_SERVER_LIST::AddToBypassList
PROXY_SERVER_LIST::GetList
PROXY_BYPASS_LIST_ENTRY::WriteEntry
PROXY_BYPASS_LIST::AddList
PROXY_BYPASS_LIST::Find
PROXY_BYPASS_LIST::Add
PROXY_BYPASS_LIST::IsBypassed
PROXY_BYPASS_LIST::IsHostInBypassList
PROXY_BYPASS_LIST::GetList
PROXY_INFO::GetProxyStringInfo
PROXY_INFO::HostBypassesProxy
PROXY_INFO::RedoSendRequest
PROXY_INFO::Terminate
PROXY_INFO::CleanOutLists
PROXY_STATE::GetNextProxy
(GetRegistryProxyParameter)
Author:
Richard L Firth (rfirth) 03-Feb-1996
Revision History:
03-Feb-1996 rfirth
Created
--*/
#include <wininetp.h>
//
// private manifests
//
#define DEFAULT_PROXY_BUFFER_LENGTH (4 K)
#define MAX_IP_ADDRESS_STRING_LENGTH (4 * 4 - 1) // ###.###.###.###
#define PROXY_REGISTRY_STRING_LENGTH (4 K)
//
// private types
//
typedef enum {
STATE_START,
STATE_PROTOCOL,
STATE_SCHEME,
STATE_SERVER,
STATE_PORT,
STATE_END,
STATE_ERROR
} PARSER_STATE;
//
// private prototypes
//
PRIVATE
LPSTR
GetRegistryProxyParameter(
IN LPSTR lpszParameterName
);
//
// functions
//
BOOL
IsLocalMacro(
IN LPSTR lpszMetaName,
IN DWORD dwMetaNameLength
)
/*++
Routine Description:
Checks for local macro name
Arguments:
lpszMetaName - name to check
dwMetaNameLength - length
Return Value:
BOOL
TRUE - it is <local>
FALSE - not
--*/
{
INET_ASSERT(lpszMetaName != NULL);
static const char s_local[] = "<local>";
return (strnicmp(s_local, lpszMetaName, dwMetaNameLength) == 0);
}
//PRIVATE
//BOOL
//IsNetscapeProxyListString(
// IN LPSTR lpszProxyStr,
// IN DWORD dwcbProxyStr
// )
//
///*++
//
//Routine Description:
//
// Determines if a string is part of a Netscape style multiple proxy list.
//
//Arguments:
//
// lpszProxyStr - The string to examine
//
// dwcbProxyStr - Size of the string to check.
//
//Return Value:
//
// BOOL
// TRUE - if its a Netscape list of proxies.
//
// FALSE - its just a proxy name.
//
//--*/
//
//{
// INET_ASSERT(lpszProxyStr);
// INET_ASSERT(dwcbProxyStr > 0);
//
// SKIPWS(lpszProxyStr);
//
// //
// // If it SOCKS, PROXY, or DIRECT (and) there is some delimiter
// // we accept it as a Netscape Proxy String.
// //
//
// if ( strncmp( lpszProxyStr, "DIRECT", min(dwcbProxyStr, sizeof("DIRECT")-1) ) == 0 )
// {
// return TRUE;
// }
//
// if ( strncmp( lpszProxyStr, "PROXY", min(dwcbProxyStr, sizeof("PROXY")-1) ) == 0 ||
// strncmp( lpszProxyStr, "SOCKS", min(dwcbProxyStr, sizeof("SOCKS")-1) ) == 0 )
// {
// for ( DWORD i = 0; i < dwcbProxyStr; i++ )
// {
// if ( lpszProxyStr[i] == ':' || lpszProxyStr[i] == ';' )
// {
// return TRUE;
// }
// }
// }
//
// return FALSE;
//}
//
// member functions
//
BOOL
PROXY_SERVER_LIST_ENTRY::WriteEntry(
OUT LPSTR lpszBuffer,
IN OUT LPDWORD lpdwBufferLength
)
/*++
Routine Description:
Writes this proxy server list entry as a string in the supplied buffer
Arguments:
lpszBuffer - pointer to buffer where string is written
lpdwBufferLength - IN: amount of space in buffer
OUT: number of bytes copied, or required size
Return Value:
BOOL
TRUE - entry written to buffer
FALSE - entry not written to buffer - *lpdwBufferLength contains
required size
--*/
{
DWORD requiredLength;
LPSTR protocolName;
DWORD protocolNameLength;
LPSTR schemeName;
DWORD schemeNameLength;
INTERNET_PORT magnitude;
protocolName = MapUrlScheme(_Protocol, &protocolNameLength);
if (protocolName != NULL) {
requiredLength = protocolNameLength + 1; // for '='
} else {
requiredLength = 0;
}
schemeName = MapUrlScheme(_Scheme, &schemeNameLength);
if (schemeName != NULL) {
requiredLength += schemeNameLength + sizeof("://") - 1;
}
requiredLength += _ProxyName.StringLength();
if (_ProxyPort != INTERNET_INVALID_PORT_NUMBER) {
for (INTERNET_PORT n = 10000, i = 5; n > 0; n /= 10, --i) {
if (_ProxyPort / n) {
requiredLength += i + 1; // for ':'
magnitude = n;
break;
}
}
}
BOOL success;
if (*lpdwBufferLength > requiredLength) {
if (protocolName != NULL) {
memcpy(lpszBuffer, protocolName, protocolNameLength);
lpszBuffer += protocolNameLength;
*lpszBuffer++ = '=';
}
if (schemeName != NULL) {
memcpy(lpszBuffer, schemeName, schemeNameLength);
lpszBuffer += schemeNameLength;
memcpy(lpszBuffer, "://", sizeof("://") - 1);
lpszBuffer += sizeof("://") - 1;
}
_ProxyName.CopyTo(lpszBuffer);
lpszBuffer += _ProxyName.StringLength();
if (_ProxyPort != INTERNET_INVALID_PORT_NUMBER) {
*lpszBuffer++ = ':';
for (INTERNET_PORT n = _ProxyPort, i = magnitude; i; i /= 10) {
*lpszBuffer++ = (char)(n / i) + '0';
n %= i;
}
}
success = TRUE;
} else {
success = FALSE;
}
*lpdwBufferLength = requiredLength;
return success;
}
DWORD
PROXY_SERVER_LIST::AddList(
IN LPSTR lpszList
)
/*++
Routine Description:
Parses a list of proxy servers and creates a PROXY_SERVER_LIST_ENTRY for
each one
Arguments:
lpszList - pointer to list of proxies of the form:
[<scheme>=][<scheme>"://"]<server>[":"<port>][";"*]
The list can be NULL, in which case we read it from the
registry
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_INVALID_PARAMETER
At least one entry in lpszList is bogus
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_SERVER_LIST::AddList",
"%.80q",
lpszList
));
DWORD entryLength;
LPSTR protocolName;
DWORD protocolLength;
LPSTR schemeName;
DWORD schemeLength;
LPSTR serverName;
DWORD serverLength;
PARSER_STATE state;
DWORD nSlashes;
INTERNET_PORT port;
BOOL done;
entryLength = 0;
protocolName = lpszList;
protocolLength = 0;
schemeName = NULL;
schemeLength = 0;
serverName = NULL;
serverLength = 0;
state = STATE_PROTOCOL;
nSlashes = 0;
port = 0;
done = FALSE;
//
// walk the list, pulling out the various scheme parts
//
do {
char ch = *lpszList++;
if ((nSlashes == 1) && (ch != '/')) {
state = STATE_ERROR;
break;
}
switch (ch) {
case '=':
if ((state == STATE_PROTOCOL) && (entryLength != 0)) {
protocolLength = entryLength;
entryLength = 0;
state = STATE_SCHEME;
schemeName = lpszList;
} else {
//
// '=' can't legally appear anywhere else
//
state = STATE_ERROR;
}
break;
case ':':
switch (state) {
case STATE_PROTOCOL:
if (*lpszList == '/') {
schemeName = protocolName;
protocolName = NULL;
schemeLength = entryLength;
protocolLength = 0;
state = STATE_SCHEME;
} else if (*lpszList != '\0') {
serverName = protocolName;
serverLength = entryLength;
state = STATE_PORT;
} else {
state = STATE_ERROR;
}
entryLength = 0;
break;
case STATE_SCHEME:
if (*lpszList == '/') {
schemeLength = entryLength;
} else if (*lpszList != '\0') {
serverName = schemeName;
serverLength = entryLength;
state = STATE_PORT;
} else {
state = STATE_ERROR;
}
entryLength = 0;
break;
case STATE_SERVER:
serverLength = entryLength;
state = STATE_PORT;
entryLength = 0;
break;
default:
state = STATE_ERROR;
break;
}
break;
case '/':
if ((state == STATE_SCHEME) && (nSlashes < 2) && (entryLength == 0)) {
if (++nSlashes == 2) {
state = STATE_SERVER;
serverName = lpszList;
}
} else {
state = STATE_ERROR;
}
break;
default:
if (state != STATE_PORT) {
++entryLength;
} else if (isdigit(ch)) {
//
// BUGBUG - we will overflow if >65535
//
port = port * 10 + (ch - '0');
} else {
//
// STATE_PORT && non-digit character - error
//
state = STATE_ERROR;
}
break;
case '\0':
done = TRUE;
//
// fall through
//
case '\t':
case '\n':
case '\v': // vertical tab, 0x0b
case '\f': // form feed, 0x0c
case '\r':
case ' ':
case ';':
case ',':
if (serverLength == 0) {
serverLength = entryLength;
}
if (serverLength != 0) {
if (serverName == NULL) {
serverName = (schemeName != NULL)
? schemeName
: protocolName;
}
INET_ASSERT(serverName != NULL);
INTERNET_SCHEME protocol;
if (protocolLength != 0) {
protocol = MapUrlSchemeName(protocolName, protocolLength);
} else {
protocol = INTERNET_SCHEME_DEFAULT;
}
INTERNET_SCHEME scheme;
if (schemeLength != 0) {
scheme = MapUrlSchemeName(schemeName, schemeLength);
} else {
scheme = INTERNET_SCHEME_DEFAULT;
}
//
// add an entry if this is a protocol we handle and we don't
// already have an entry for it
//
if ((protocol != INTERNET_SCHEME_UNKNOWN)
&& (scheme != INTERNET_SCHEME_UNKNOWN)
//
// we can only currently handle CERN (secure or unsecure) and
// FTP proxies, so kick out anything that wants to go via any
// other proxy scheme
//
&& ((scheme == INTERNET_SCHEME_DEFAULT)
|| (scheme == INTERNET_SCHEME_FTP)
|| (scheme == INTERNET_SCHEME_HTTP)
|| (scheme == INTERNET_SCHEME_HTTPS))) {
if (!Find(protocol)) {
//
// don't worry if Add() fails - we just continue
//
Add(protocol, scheme, serverName, serverLength, port);
}
}
}
entryLength = 0;
protocolName = lpszList;
protocolLength = 0;
schemeName = NULL;
schemeLength = 0;
serverName = NULL;
serverLength = 0;
nSlashes = 0;
port = 0;
state = STATE_PROTOCOL;
break;
}
if (state == STATE_ERROR) {
break;
}
} while (!done);
DWORD error;
if (state == STATE_ERROR) {
error = ERROR_INVALID_PARAMETER;
} else {
error = ERROR_SUCCESS;
}
DEBUG_LEAVE(error);
return error;
}
BOOL
PROXY_SERVER_LIST::Find(
IN INTERNET_SCHEME tScheme
)
/*++
Routine Description:
Find a PROXY_SERVER_LIST_ENTRY based on the scheme
Arguments:
tScheme - protocol scheme to find
Return Value:
BOOL
--*/
{
DEBUG_ENTER((DBG_PROXY,
Bool,
"PROXY_SERVER_LIST::Find",
"%s",
InternetMapScheme(tScheme)
));
BOOL found = FALSE;
LockSerializedList(&_List);
for (PLIST_ENTRY entry = HeadOfSerializedList(&_List);
entry != (PLIST_ENTRY)SlSelf(&_List);
entry = entry->Flink) {
PROXY_SERVER_LIST_ENTRY * info;
info = CONTAINING_RECORD(entry, PROXY_SERVER_LIST_ENTRY, _List);
if (info->_Protocol == tScheme) {
found = TRUE;
break;
}
}
UnlockSerializedList(&_List);
DEBUG_LEAVE(found);
return found;
}
DWORD
PROXY_SERVER_LIST::Add(
IN INTERNET_SCHEME tProtocol,
IN INTERNET_SCHEME tScheme,
IN LPSTR lpszHostName,
IN DWORD dwHostNameLength,
IN INTERNET_PORT nPort
)
/*++
Routine Description:
Create an add a PROXY_SERVER_LIST_ENTRY to the PROXY_SERVER_LIST
Arguments:
tProtocol - protocol which uses the proxy
tScheme - scheme used to talk to the proxy
lpszHostName - proxy host name
dwHostNameLength - length of proxy host name
nPort - port at proxy host
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_NOT_ENOUGH_MEMORY
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_SERVER_LIST::Add",
"%s, %s, %.*q, %d, %d",
InternetMapScheme(tProtocol),
InternetMapScheme(tScheme),
dwHostNameLength,
lpszHostName,
dwHostNameLength,
nPort
));
PROXY_SERVER_LIST_ENTRY * entry;
entry = new PROXY_SERVER_LIST_ENTRY(tProtocol,
tScheme,
lpszHostName,
dwHostNameLength,
nPort
);
DWORD error;
if (entry != NULL) {
//error = entry->ResolveAddress();
//if (error == ERROR_SUCCESS) {
// InsertAtTailOfSerializedList(&_List, &entry->_List);
//}
if (entry->_Protocol == INTERNET_SCHEME_DEFAULT) {
InsertAtTailOfSerializedList(&_List, &entry->_List);
} else {
InsertAtHeadOfSerializedList(&_List, &entry->_List);
}
error = ERROR_SUCCESS;
} else {
error = ERROR_NOT_ENOUGH_MEMORY;
}
DEBUG_LEAVE(error);
return error;
}
INTERNET_SCHEME
PROXY_SERVER_LIST::ProxyScheme(
IN INTERNET_SCHEME tProtocol
)
/*++
Routine Description:
Determines protocol over which tScheme goes through proxy
Arguments:
tProtocol - protocol scheme used to retrieve data (e.g. FTP)
Return Value:
INTERNET_SCHEME
Success - scheme by which protocol goes via proxy
Failure - INTERNET_SCHEME_UNKNOWN
--*/
{
DEBUG_ENTER((DBG_PROXY,
Int,
"PROXY_SERVER_LIST::ProxyScheme",
"%s",
InternetMapScheme(tProtocol)
));
INTERNET_SCHEME tScheme = INTERNET_SCHEME_UNKNOWN;
LockSerializedList(&_List);
//
// the list really shouldn't be empty if we're here
//
INET_ASSERT(!IsSerializedListEmpty(&_List));
for (PLIST_ENTRY entry = HeadOfSerializedList(&_List);
entry != (PLIST_ENTRY)SlSelf(&_List);
entry = entry->Flink) {
PROXY_SERVER_LIST_ENTRY * info;
info = CONTAINING_RECORD(entry, PROXY_SERVER_LIST_ENTRY, _List);
//
// if we find a match for the protocol, or this protocol is handled by
// the default proxy entry then we are done
//
if ((info->_Protocol == tProtocol)
|| (info->_Protocol == INTERNET_SCHEME_DEFAULT)) {
tScheme = info->_Scheme;
//
// the default scheme is HTTP (CERN proxy)
//
if (tScheme == INTERNET_SCHEME_DEFAULT) {
tScheme = INTERNET_SCHEME_HTTP;
}
break;
}
}
UnlockSerializedList(&_List);
DEBUG_LEAVE(tScheme);
return tScheme;
}
BOOL
PROXY_SERVER_LIST::GetProxyHostName(
IN INTERNET_SCHEME tProtocol,
IN OUT LPINTERNET_SCHEME lptScheme,
OUT LPSTR * lplpszHostName,
OUT LPBOOL lpbFreeHostName,
OUT LPDWORD lpdwHostNameLength,
OUT LPINTERNET_PORT lpHostPort
)
/*++
Routine Description:
Given a protocol, map it to the proxy we use to retrieve the data
Arguments:
tProtocol - protocol to map (e.g. find the proxy for FTP)
lptScheme - IN: preferred scheme if INTERNET_SCHEME_DEFAULT
OUT: returned scheme
lplpszHostName - pointer to returned pointer to host name
lpbFreeHostName - returned TRUE if *lplpszHostName allocated
lpdwHostNameLength - pointer to returned host name length
lpHostPort - pointer to returned host port
Return Value:
BOOL
TRUE - requested info has been returned
FALSE - requested info was not found
--*/
{
DEBUG_ENTER((DBG_PROXY,
Bool,
"PROXY_SERVER_LIST::GetProxyHostName",
"%s, %#x, %#x, %#x, %#x, %#x",
InternetMapScheme(tProtocol),
lptScheme,
lplpszHostName,
lpbFreeHostName,
lpdwHostNameLength,
lpHostPort
));
INET_ASSERT(tProtocol != INTERNET_SCHEME_UNKNOWN);
//
// *lptScheme must now be one of the recognized schemes, or the default
//
INET_ASSERT((*lptScheme == INTERNET_SCHEME_DEFAULT)
|| (*lptScheme == INTERNET_SCHEME_FTP)
|| (*lptScheme == INTERNET_SCHEME_GOPHER)
|| (*lptScheme == INTERNET_SCHEME_HTTP)
|| (*lptScheme == INTERNET_SCHEME_HTTPS)
|| (*lptScheme == INTERNET_SCHEME_SOCKS)
);
BOOL found = FALSE;
LockSerializedList(&_List);
//
// the list really shouldn't be empty if we're here
//
INET_ASSERT(!IsSerializedListEmpty(&_List));
for (PLIST_ENTRY entry = HeadOfSerializedList(&_List);
entry != (PLIST_ENTRY)SlSelf(&_List);
entry = entry->Flink) {
PROXY_SERVER_LIST_ENTRY * info;
info = CONTAINING_RECORD(entry, PROXY_SERVER_LIST_ENTRY, _List);
//
// if we find a match for the protocol, or this protocol is handled by
// the default proxy entry then we are done
//
// Hack: But make sure its NOT socks since, socks must be
// an exact match !!! No defaults.
//
if ((info->_Protocol == tProtocol)
|| ((info->_Protocol == INTERNET_SCHEME_DEFAULT)
&& (tProtocol != INTERNET_SCHEME_SOCKS) )) {
INTERNET_SCHEME scheme = info->_Scheme;
//
// the returned scheme is the input preferred scheme unless it was
// the default scheme in which case we return HTTP (CERN proxy)
//
if (scheme == INTERNET_SCHEME_DEFAULT) {
scheme = (*lptScheme == INTERNET_SCHEME_DEFAULT)
? INTERNET_SCHEME_HTTP
: *lptScheme;
}
*lptScheme = scheme;
*lpbFreeHostName = FALSE;
*lpdwHostNameLength = 0;
*lplpszHostName = NewString(info->_ProxyName.StringAddress(),
info->_ProxyName.StringLength()
);
if (*lplpszHostName != NULL) {
*lpbFreeHostName = TRUE;
*lpdwHostNameLength = info->_ProxyName.StringLength();
}
INTERNET_PORT port = info->_ProxyPort;
//
// map the default port value
//
if (port == INTERNET_INVALID_PORT_NUMBER) {
switch (scheme) {
case INTERNET_SCHEME_FTP:
port = INTERNET_DEFAULT_FTP_PORT;
break;
case INTERNET_SCHEME_GOPHER:
port = INTERNET_DEFAULT_GOPHER_PORT;
break;
case INTERNET_SCHEME_HTTP:
port = INTERNET_DEFAULT_HTTP_PORT;
break;
case INTERNET_SCHEME_HTTPS:
port = INTERNET_DEFAULT_HTTPS_PORT;
break;
case INTERNET_SCHEME_SOCKS:
port = INTERNET_DEFAULT_SOCKS_PORT;
break;
}
}
*lpHostPort = port;
found = TRUE;
DEBUG_PRINT(PROXY,
INFO,
("proxy = %s://%s:%d\n",
MapUrlSchemeToName(scheme),
info->_ProxyName.StringAddress(),
port
));
break;
}
}
UnlockSerializedList(&_List);
DEBUG_LEAVE(found);
return found;
}
DWORD
PROXY_SERVER_LIST::AddToBypassList(
IN PROXY_BYPASS_LIST * lpBypassList
)
/*++
Routine Description:
For all proxy servers in the server list, we add the details to the bypass
list. By default, an app mustn't send a request to the proxy via the proxy!
Additionally, the app should not have to specifically nominate the proxy
server(s) as bypassing the proxy
Arguments:
lpBypassList - pointer to bypass proxy list where proxy servers will be
added
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_NOT_ENOUGH_MEMORY
--*/
{
DWORD error = ERROR_SUCCESS;
PLIST_ENTRY entry = HeadOfSerializedList(&_List);
while ((entry != (PLIST_ENTRY)SlSelf(&_List)) && (error == ERROR_SUCCESS)) {
PROXY_SERVER_LIST_ENTRY * info = (PROXY_SERVER_LIST_ENTRY *)entry;
if (!lpBypassList->Find(info->_Scheme,
info->_ProxyName.StringAddress(),
info->_ProxyName.StringLength(),
info->_ProxyPort)) {
error = lpBypassList->Add(info->_Scheme,
info->_ProxyName.StringAddress(),
info->_ProxyName.StringLength(),
info->_ProxyPort
);
}
entry = entry->Flink;
}
return error;
}
VOID
PROXY_SERVER_LIST::GetList(
OUT LPSTR * lplpszList,
IN DWORD dwBufferLength,
IN OUT LPDWORD lpdwRequiredLength
)
/*++
Routine Description:
Writes the list of proxy servers to a buffer, and/or returns the required
buffer length
Arguments:
lplpszList - pointer to pointer to buffer where list is written, if
sufficient space
dwBufferLength - amount of space in *lplpszList
lpdwRequiredLength - OUT: cumulative size of data
Return Value:
None.
--*/
{
LPSTR lpszList = *lplpszList;
BOOL firstTime = TRUE;
BOOL outOfBuffer = FALSE;
LockSerializedList(&_List);
for (PLIST_ENTRY entry = HeadOfSerializedList(&_List);
entry != (PLIST_ENTRY)SlSelf(&_List);
entry = entry->Flink) {
PROXY_SERVER_LIST_ENTRY * info;
info = CONTAINING_RECORD(entry, PROXY_SERVER_LIST_ENTRY, _List);
if (!firstTime) {
//
// write delimiter if enough space
//
if (dwBufferLength >= 1) {
*lpszList++ = ' ';
--dwBufferLength;
}
++*lpdwRequiredLength;
} else {
firstTime = FALSE;
}
//
// find the length of the current entry & write it to the buffer if
// enough space
//
DWORD length = dwBufferLength;
info->WriteEntry(lpszList, &length);
if (dwBufferLength >= length) {
//
// we wrote it
//
dwBufferLength -= length;
} else {
//
// no buffer left
//
dwBufferLength = 0;
outOfBuffer = TRUE;
}
*lpdwRequiredLength += length;
lpszList += length;
}
if (!outOfBuffer) {
if (dwBufferLength > 0) {
*lpszList++ = '\0';
*lplpszList = lpszList;
}
}
//
// add 1 for the terminating NUL
//
++*lpdwRequiredLength;
UnlockSerializedList(&_List);
}
BOOL
PROXY_BYPASS_LIST_ENTRY::WriteEntry(
OUT LPSTR lpszBuffer,
IN OUT LPDWORD lpdwBufferLength
)
/*++
Routine Description:
Writes this proxy bypass list entry as a string in the supplied buffer
Arguments:
lpszBuffer - pointer to buffer where string is written
lpdwBufferLength - IN: amount of space in buffer
OUT: number of bytes copied, or required size
Return Value:
BOOL
TRUE - entry written to buffer
FALSE - entry not written to buffer - *lpdwBufferLength contains
required size
--*/
{
DWORD requiredLength;
LPSTR schemeName;
DWORD schemeNameLength;
INTERNET_PORT magnitude;
if (_Scheme != INTERNET_SCHEME_DEFAULT) {
schemeName = MapUrlScheme(_Scheme, &schemeNameLength);
requiredLength = schemeNameLength + sizeof("://") - 1;
} else {
schemeName = NULL;
requiredLength = 0;
}
if (IsLocal()) {
requiredLength += sizeof("<local>") - 1;
} else {
requiredLength += _Name.StringLength();
}
if (_Port != INTERNET_INVALID_PORT_NUMBER) {
for (INTERNET_PORT n = 10000, i = 5; n > 0; n /= 10, --i) {
if (_Port / n) {
requiredLength += i + 1;
magnitude = n;
break;
}
}
}
BOOL success;
if (*lpdwBufferLength > requiredLength) {
if (schemeName != NULL) {
memcpy(lpszBuffer, schemeName, schemeNameLength);
lpszBuffer += schemeNameLength;
memcpy(lpszBuffer, "://", sizeof("://") - 1);
lpszBuffer += sizeof("://") - 1;
}
if (IsLocal()) {
memcpy(lpszBuffer, "<local>", sizeof("<local>") - 1);
lpszBuffer += sizeof("<local>") - 1;
} else {
_Name.CopyTo(lpszBuffer);
lpszBuffer += _Name.StringLength();
}
if (_Port != INTERNET_INVALID_PORT_NUMBER) {
*lpszBuffer++ = ':';
for (INTERNET_PORT n = _Port, i = magnitude; i; i /= 10) {
*lpszBuffer++ = (char)(n / i) + '0';
n %= i;
}
}
success = TRUE;
} else {
success = FALSE;
}
*lpdwBufferLength = requiredLength;
return success;
}
DWORD
PROXY_BYPASS_LIST::AddList(
IN LPSTR lpszList
)
/*++
Routine Description:
Parses a list of proxy bypass specifiers and adds them to the list
Arguments:
lpszList - pointer to string containing list of proxy bypass specifiers.
The format is:
[<scheme>"://"][<server>][":"<port>"]
The list can be NULL, in which case we read it from the
registry
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_INVALID_PARAMETER
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_BYPASS_LIST::AddList",
"%.80q",
lpszList
));
DWORD entryLength;
LPSTR schemeName;
DWORD schemeLength;
LPSTR serverName;
DWORD serverLength;
PARSER_STATE state;
DWORD nSlashes;
INTERNET_PORT port;
BOOL done;
entryLength = 0;
schemeName = lpszList;
schemeLength = 0;
serverName = NULL;
serverLength = 0;
state = STATE_SCHEME;
nSlashes = 0;
port = 0;
done = FALSE;
//
// walk the list, pulling out the various scheme parts
//
do {
char ch = *lpszList++;
if ((nSlashes == 1) && (ch != '/')) {
state = STATE_ERROR;
break;
}
switch (ch) {
case ':':
switch (state) {
case STATE_SCHEME:
if (*lpszList == '/') {
schemeLength = entryLength;
} else if (*lpszList != '\0') {
serverName = schemeName;
serverLength = entryLength;
if (serverLength == 0) {
serverLength = 1;
serverName = "*";
}
state = STATE_PORT;
} else {
state = STATE_ERROR;
}
entryLength = 0;
break;
case STATE_SERVER:
serverLength = entryLength;
state = STATE_PORT;
entryLength = 0;
break;
default:
state = STATE_ERROR;
break;
}
break;
case '/':
if ((state == STATE_SCHEME) && (nSlashes < 2) && (entryLength == 0)) {
if (++nSlashes == 2) {
state = STATE_SERVER;
serverName = lpszList;
}
} else {
state = STATE_ERROR;
}
break;
default:
if (state != STATE_PORT) {
++entryLength;
} else if (isdigit(ch)) {
//
// BUGBUG - we will overflow if >65535
//
port = port * 10 + (ch - '0');
} else {
//
// STATE_PORT && non-digit character - error
//
state = STATE_ERROR;
}
break;
case '\0':
done = TRUE;
//
// fall through
//
case '\t':
case '\n':
case '\v': // vertical tab, 0x0b
case '\f': // form feed, 0x0c
case '\r':
case ' ':
case ';':
case ',':
if (serverLength == 0) {
serverLength = entryLength;
if ((serverLength == 0)
&& ((state == STATE_SERVER) || (state == STATE_PORT))) {
//
// we found e.g. "http://" or "http://:80". We allow this as
// "http://*" or "http://*:80"
//
serverLength = 1;
serverName = "*";
}
}
if (serverLength != 0) {
if (serverName == NULL) {
serverName = schemeName;
}
INTERNET_SCHEME scheme;
if (schemeLength != 0) {
scheme = MapUrlSchemeName(schemeName, schemeLength);
} else {
scheme = INTERNET_SCHEME_DEFAULT;
}
//
// add an entry if this is a protocol we handle and we don't
// already have an entry for it
//
if ((scheme != INTERNET_SCHEME_UNKNOWN)
&& !Find(scheme, serverName, serverLength, port)) {
//
// don't worry if Add() fails - we just continue
//
Add(scheme, serverName, serverLength, port);
}
}
entryLength = 0;
schemeName = lpszList;
schemeLength = 0;
serverName = NULL;
serverLength = 0;
nSlashes = 0;
port = 0;
state = STATE_SCHEME;
break;
}
if (state == STATE_ERROR) {
break;
}
} while (!done);
DWORD error;
if (state == STATE_ERROR) {
error = ERROR_INVALID_PARAMETER;
} else {
error = ERROR_SUCCESS;
}
DEBUG_LEAVE(error);
return error;
}
BOOL
PROXY_BYPASS_LIST::Find(
IN INTERNET_SCHEME tScheme,
IN LPSTR lpszHostName OPTIONAL,
IN DWORD dwHostNameLength,
IN INTERNET_PORT nPort
)
/*++
Routine Description:
Determines if a proxy bypass entry matches the criteria.
Currently, name matching is simplistic: e.g. "*.com" and "**.com" are
treated as 2 separate strings, where we should collapse multiple wildcard
specifiers, etc. Also: "w*" should replace "ww*", etc.
Arguments:
tScheme - scheme for this entry
lpszHostName - host name or address. May contain wildcards (*)
dwHostNameLength - length of host name or address
nPort - port
Return Value:
BOOL
TRUE - an entry corresponding to the arguments was found
FALSE - didn't find entry
--*/
{
DEBUG_ENTER((DBG_PROXY,
Bool,
"PROXY_BYPASS_LIST::Find",
"%s, %.*q, %d, %d",
InternetMapScheme(tScheme),
dwHostNameLength,
lpszHostName,
dwHostNameLength,
nPort
));
BOOL isLocal = IsLocalMacro(lpszHostName, dwHostNameLength);
BOOL found = FALSE;
LockSerializedList(&_List);
for (PLIST_ENTRY entry = HeadOfSerializedList(&_List);
entry != (PLIST_ENTRY)SlSelf(&_List);
entry = entry->Flink) {
PROXY_BYPASS_LIST_ENTRY * info;
info = CONTAINING_RECORD(entry, PROXY_BYPASS_LIST_ENTRY, _List);
//
// do the easy bits first
//
if (!((info->_Scheme == tScheme)
|| (info->_Scheme == INTERNET_SCHEME_DEFAULT))) {
continue;
}
if (!((info->_Port == nPort)
|| (info->_Port == INTERNET_INVALID_PORT_NUMBER))) {
continue;
}
//
// check for name match
//
if (info->_LocalSemantics) {
if (isLocal) {
found = TRUE;
break;
} else {
continue;
}
}
//
// not local semantics, have to match target
//
//
// BUGBUG - we only do simplistic matching. If the strings don't match
// exactly, except for case, they are deemed to be different
//
if (info->_Name.Strnicmp(lpszHostName, (int)dwHostNameLength) != 0) {
continue;
}
//
// any path that didn't continue, or has not already broken out has
// succeeded in finding a match
//
DEBUG_PRINT(PROXY,
INFO,
("Matched: %q, %q\n",
lpszHostName,
info->_Name.StringAddress()
));
found = TRUE;
break;
}
UnlockSerializedList(&_List);
DEBUG_LEAVE(found);
return found;
}
DWORD
PROXY_BYPASS_LIST::Add(
IN INTERNET_SCHEME tScheme,
IN LPSTR lpszHostName,
IN DWORD dwHostNameLength,
IN INTERNET_PORT nPort
)
/*++
Routine Description:
Create and add a PROXY_BYPASS_LIST_ENTRY to the PROXY_BYPASS_LIST
Arguments:
tScheme - scheme to bypass. May be 0 meaning any protocol
lpszHostName - name of host to bypass. May be name or IP address and
may contain wildcard characters
dwHostNameLength - length of bypass name string
nPort - port to bypass. May be 0, meaning any port
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_NOT_ENOUGH_MEMORY
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_BYPASS_LIST::Add",
"%s, %.*q, %d, %d",
InternetMapScheme(tScheme),
dwHostNameLength,
lpszHostName,
dwHostNameLength,
nPort
));
PROXY_BYPASS_LIST_ENTRY * entry;
entry = new PROXY_BYPASS_LIST_ENTRY(tScheme,
lpszHostName,
dwHostNameLength,
nPort
);
DWORD error;
if (entry != NULL) {
//
// if the bypass entry uses local name matching semantics, then we add
// it to the end of the list, else the head. The reason we do this is
// to allow <local> to be a default after all other (possibly also
// local) entries are checked
//
if (entry->IsLocal()) {
InsertAtTailOfSerializedList(&_List, &entry->_List);
} else {
InsertAtHeadOfSerializedList(&_List, &entry->_List);
}
error = ERROR_SUCCESS;
} else {
error = ERROR_NOT_ENOUGH_MEMORY;
}
DEBUG_LEAVE(error);
return error;
}
BOOL
PROXY_BYPASS_LIST::IsBypassed(
IN INTERNET_SCHEME tScheme,
IN LPSTR lpszHostName,
IN DWORD dwHostNameLength,
IN INTERNET_PORT nPort
)
/*++
Routine Description:
Determines if a scheme/name/port is bypassed
Arguments:
tScheme - can be 0, meaning match any scheme
lpszHostName - can contain wildcards. May be name or IP address
dwHostNameLength - length of name/address part. May be 0, meaning match
any name/address
nPort - can be 0, meaning match any port
Return Value:
BOOL
TRUE - an entry on the bypass list matched the criteria
FALSE - the host identified by the parameters is not on this bypass
list
--*/
{
DEBUG_ENTER((DBG_PROXY,
Bool,
"PROXY_BYPASS_LIST::IsBypassed",
"%s, %.*q, %d, %d",
InternetMapScheme(tScheme),
dwHostNameLength,
lpszHostName,
dwHostNameLength,
nPort
));
INET_ASSERT(lpszHostName != NULL);
INET_ASSERT(dwHostNameLength != 0);
//
// determine if what we were given is an address, in which case we don't
// perform <local> semantics matching
//
BOOL isAddress = FALSE;
LPSTR mappedName = NULL;
LPSTR allocedName = NULL;
if (dwHostNameLength <= MAX_IP_ADDRESS_STRING_LENGTH) {
char addressBuffer[MAX_IP_ADDRESS_STRING_LENGTH + 1];
//
// make the host name/address an ASCIIZ string
//
memcpy((LPVOID)addressBuffer, (LPVOID)lpszHostName, dwHostNameLength);
addressBuffer[dwHostNameLength] = '\0';
if (_I_inet_addr(addressBuffer) != INADDR_NONE) {
//
// looks like we were given an IP address
//
//
// maybe this is the IP address of a known server (in cache)
//
mappedName = MapNetAddressToName(addressBuffer, &allocedName);
if (mappedName == addressBuffer) {
//
// BUGBUG - transport independence?
//
isAddress = TRUE;
} else {
lpszHostName = mappedName;
dwHostNameLength = lstrlen(lpszHostName);
}
}
}
BOOL found;
found = IsHostInBypassList (
tScheme,
lpszHostName,
dwHostNameLength,
nPort,
isAddress);
if (allocedName != NULL) {
allocedName = (LPSTR)FREE_MEMORY(allocedName);
INET_ASSERT(allocedName == NULL);
}
DEBUG_LEAVE(found);
return found;
}
BOOL
PROXY_BYPASS_LIST::IsHostInBypassList(
IN INTERNET_SCHEME tScheme,
IN LPSTR lpszHostName,
IN DWORD dwHostNameLength,
IN INTERNET_PORT nPort,
IN BOOL isAddress
)
/*++
Routine Description:
description-of-function.
Arguments:
tScheme -
lpszHostName -
dwHostNameLength -
nPort -
isAddress -
Return Value:
BOOL
--*/
{
DEBUG_ENTER((DBG_PROXY,
Bool,
"PROXY_BYPASS_LIST::IsHostInBypassList",
"%d (%s), %.*q, %d, %d, %B",
tScheme,
InternetMapScheme(tScheme),
dwHostNameLength,
lpszHostName,
dwHostNameLength,
nPort,
isAddress
));
BOOL found = FALSE;
//
// if not an address, determine if the name contains at least one dot
//
BOOL isDot;
if (!isAddress) {
isDot = FALSE;
for (DWORD i = 0; i < dwHostNameLength; ++i) {
if (lpszHostName[i] == '.') {
isDot = TRUE;
break;
}
}
} else {
//
// addresses have dots
//
isDot = TRUE;
}
LockSerializedList(&_List);
for (PLIST_ENTRY entry = HeadOfSerializedList(&_List);
entry != (PLIST_ENTRY)SlSelf(&_List);
entry = entry->Flink) {
PROXY_BYPASS_LIST_ENTRY * info;
info = CONTAINING_RECORD(entry, PROXY_BYPASS_LIST_ENTRY, _List);
//
// do the easy bits first
//
if (!((info->_Scheme == tScheme)
|| (info->_Scheme == INTERNET_SCHEME_DEFAULT))) {
continue;
}
if (!((info->_Port == nPort)
|| (info->_Port == INTERNET_INVALID_PORT_NUMBER))) {
continue;
}
//
// check local semantics
//
if (info->_LocalSemantics) {
if (!isDot) {
DEBUG_PRINT(PROXY,
INFO,
("%q matched by <local>\n",
lpszHostName
));
found = TRUE;
//
// <local> is in the bypass list and the name does not contain a
// dot. It bypasses the proxy
//
break;
} else {
//
// the name contains a dot, but it may be matched by another
// proxy bypass entry
//
continue;
}
}
//
// check for name match. Note that we take no special action if the host
// name contains wildcard characters
//
LPSTR target = info->_Name.StringAddress();
//
// NULL target name matches any server name/address
//
if (target != NULL) {
DEBUG_PRINT(PROXY,
INFO,
("trying to match %q with %q\n",
lpszHostName,
target
));
DWORD i = 0;
DWORD j = 0;
DWORD i_back = (DWORD)-1;
while ((target[i] != '\0') && (j < dwHostNameLength)) {
if (target[i] == tolower(lpszHostName[j])) {
++i;
++j;
} else if (target[i] == '*') {
while (target[i + 1] == '*') {
++i;
}
i_back = i;
++i;
while ((tolower(lpszHostName[j]) != target[i])
&& (j < dwHostNameLength)) {
++j;
}
} else if (i_back != (DWORD)-1) {
//
// '*' is greedy closure. We already saw a '*' but later we
// discovered a mismatch. We will go back and try to eat as
// many characters as we can till the next match, or we hit
// the end of the string
//
i = i_back;
} else {
//
// no match; quit
//
j = 0;
break;
}
//
// if we reached the end of the target, but not the host name
// AND we already met a '*' then back up
//
if ((target[i] == '\0')
&& (j != dwHostNameLength)
&& (i_back != (DWORD)-1)) {
i = i_back;
}
}
//
// if we hit the end of the host name while matching any character,
// bump the target to the next non-star character
//
while (target[i] == '*') {
++i;
}
//
// the host name matched if we reached the end of the target and end
// of the host name
//
if (!((target[i] == '\0') && (j == dwHostNameLength))) {
continue;
}
}
//
// any path that didn't continue, or has not already broken out has
// succeeded in finding a match
//
DEBUG_PRINT(PROXY,
INFO,
("Matched: %q, %q\n",
lpszHostName,
target
));
found = TRUE;
break;
}
UnlockSerializedList(&_List);
DEBUG_LEAVE(found);
return found;
}
VOID
PROXY_BYPASS_LIST::GetList(
OUT LPSTR * lplpszList,
IN DWORD dwBufferLength,
IN OUT LPDWORD lpdwRequiredLength
)
/*++
Routine Description:
Writes the list of proxy bypass servers to a buffer, and/or returns the
required buffer length
Arguments:
lplpszList - pointer to pointer to buffer where list is written, if
sufficient space
dwBufferLength - amount of space in *lplpszList
lpdwRequiredLength - OUT: cumulative size of data
Return Value:
None.
--*/
{
LPSTR lpszList = *lplpszList;
BOOL firstTime = TRUE;
BOOL outOfBuffer = FALSE;
LockSerializedList(&_List);
for (PLIST_ENTRY entry = HeadOfSerializedList(&_List);
entry != (PLIST_ENTRY)SlSelf(&_List);
entry = entry->Flink) {
PROXY_BYPASS_LIST_ENTRY * info;
info = CONTAINING_RECORD(entry, PROXY_BYPASS_LIST_ENTRY, _List);
if (!firstTime) {
//
// write delimiter if enough space
//
if (dwBufferLength >= 1) {
*lpszList++ = ' ';
--dwBufferLength;
}
++*lpdwRequiredLength;
} else {
firstTime = FALSE;
}
//
// find the length of the current entry & write it to the buffer if
// enough space
//
DWORD length = dwBufferLength;
info->WriteEntry(lpszList, &length);
if (dwBufferLength >= length) {
//
// we wrote it
//
dwBufferLength -= length;
} else {
//
// no buffer left
//
dwBufferLength = 0;
outOfBuffer = TRUE;
}
*lpdwRequiredLength += length;
lpszList += length;
}
if (!outOfBuffer) {
if (dwBufferLength > 0) {
*lpszList++ = '\0';
*lplpszList = lpszList;
}
}
//
// add 1 for the terminating NUL
//
++*lpdwRequiredLength;
UnlockSerializedList(&_List);
}
//
// PROXY_INFO - methods are defined below
//
VOID
PROXY_INFO::InitializeProxySettings(
VOID
)
/*++
Routine Description:
Initalizes Proxy_Info objects
Arguments:
None.
Return Value:
None.
--*/
{
_ProxyServerList = NULL;
_ProxyBypassList = NULL;
_fDisableDirect = FALSE;
_fModifiedInProcess = FALSE;
_Lock.Initialize();
_Error = _Lock.IsInitialized()
? ERROR_SUCCESS
: ERROR_INTERNET_INTERNAL_ERROR;
}
BOOL PROXY_INFO_GLOBAL::IsAutoProxyDownloaded(VOID)
{
return !(IsGlobal() &&
_AutoProxyList &&
_AutoProxyList->IsAutoProxy() &&
!_AutoProxyList->IsOnAsyncAutoProxyThread() &&
GlobalAutoProxyNeedsInit);
}
VOID
PROXY_INFO::TerminateProxySettings(
VOID
)
/*++
Routine Description:
Cleans up and destroys Proxy_Info objects
Arguments:
None.
Return Value:
None.
--*/
{
//DEBUG_ENTER((DBG_OBJECTS,
// None,
// "PROXY_INFO::TerminateProxySettings",
// NULL
// ));
Lock(TRUE);
CleanOutLists();
Unlock();
//DEBUG_LEAVE(0);
}
DWORD
PROXY_INFO::SetProxySettings(
IN LPINTERNET_PROXY_INFO_EX lpProxySettings,
IN BOOL fModifiedInProcess
)
/*++
Routine Description:
Sets the proxy info. Either creates new proxy server and bypass lists, or
removes them (proxy to direct)
Assumes: 1. The parameters have already been validated in the API that calls
this method (i.e. InternetOpen(), InternetSetOption())
Arguments:
lpProxySettings - a set of relevent fields describing proxy settings
fModifiedInProcess - TRUE, if this object keeps a seperate set of values from those
stored in the registry store
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_INVALID_PARAMETER
The lpszProxy or lpszProxyBypass list was bad
ERROR_NOT_ENOUGH_MEMORY
Failed to create an object or allocate space for a list,
etc.
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_INFO::SetProxySettings",
"%x, %B",
lpProxySettings,
fModifiedInProcess
));
//
// parameters should already be validated by caller
//
BOOL newList;
BOOL possibleNewAutoProxy;
LPCTSTR serverList;
LPCTSTR bypassList;
DWORD error = ERROR_SUCCESS;
serverList = NULL;
bypassList = NULL;
newList = FALSE;
_fModifiedInProcess = fModifiedInProcess;
_dwSettingsVersion = lpProxySettings->dwCurrentSettingsVersion;
UPDATE_GLOBAL_PROXY_VERSION();
if ( lpProxySettings->dwFlags & PROXY_TYPE_PROXY )
{
serverList = lpProxySettings->lpszProxy;
bypassList = lpProxySettings->lpszProxyBypass;
if (serverList != NULL) {
newList = TRUE;
}
}
//
// about to start changing contents - acquire lock
//
Lock(TRUE);
// remember disable direct flag...
SetDisableDirect( (lpProxySettings->dwFlags & PROXY_TYPE_DIRECT) ? FALSE : TRUE );
//
// clear out current contents,
//
CleanOutLists();
//
// Set the Static Proxy Lists
//
if (newList)
{
INET_ASSERT((serverList != NULL) && (*serverList != 0));
_ProxyServerList = new PROXY_SERVER_LIST(serverList);
_ProxyBypassList = new PROXY_BYPASS_LIST(bypassList);
if ((_ProxyServerList != NULL) && (_ProxyBypassList != NULL)) {
_Error = _ProxyServerList->GetError();
if (_Error == ERROR_SUCCESS) {
_Error = _ProxyBypassList->GetError();
if (_Error == ERROR_SUCCESS) {
//
// add all proxy servers to bypass list
//
_ProxyServerList->AddToBypassList(_ProxyBypassList);
}
}
} else {
_Error = ERROR_NOT_ENOUGH_MEMORY;
CleanOutLists();
}
error = _Error;
}
//
// other threads free to access this PROXY_INFO again
//
Unlock();
DEBUG_LEAVE(error);
return error;
}
DWORD
PROXY_INFO::GetProxySettings(
OUT LPINTERNET_PROXY_INFO_EX lpProxySettings,
IN BOOL fCheckVersion = FALSE
)
/*++
Routine Description:
Gets the proxy info.
Assumes: 1. The parameters have already been validated in the API that calls
this method (i.e. InternetOpen(), InternetSetOption())
Arguments:
lpProxySettings - a set of relevent fields describing proxy settings
fCheckVersion - ignored
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_INFO::GetProxySettings",
"%x, %B",
lpProxySettings,
fCheckVersion
));
DWORD error = ERROR_SUCCESS;
if ( fCheckVersion == TRUE )
{
INET_ASSERT(FALSE);
error = ERROR_INVALID_PARAMETER;
goto quit;
}
//
// about to start reading contents - acquire lock
//
Lock(FALSE);
if ( ! IsDisableDirect() ) {
lpProxySettings->dwFlags |= PROXY_TYPE_DIRECT;
}
if ( IsProxySettingsConfigured() )
{
lpProxySettings->dwFlags |= PROXY_TYPE_PROXY;
lpProxySettings->lpszProxy = _ProxyServerList->CopyString();
lpProxySettings->lpszProxyBypass = _ProxyBypassList->CopyString();
if ( lpProxySettings->lpszProxy == NULL ||
lpProxySettings->lpszProxyBypass == NULL )
{
error = ERROR_NOT_ENOUGH_MEMORY;
}
}
//
// other threads free to access this PROXY_INFO again
//
Unlock();
quit:
DEBUG_LEAVE(error);
return error;
}
DWORD
PROXY_INFO::RefreshProxySettings(
IN BOOL fForceRefresh
)
/*++
Routine Description:
Refreshes the Proxy Information
This doesn't make sense on PROXY_INFO, nothing done
Arguments:
fForceRefresh - forces a resync of all settings, turning this on slows things down
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure -
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_INFO::RefreshProxySettings",
"%B",
fForceRefresh
));
DEBUG_LEAVE(ERROR_SUCCESS);
return ERROR_SUCCESS;
}
DWORD
PROXY_INFO::QueryProxySettings(
IN AUTO_PROXY_ASYNC_MSG **ppQueryForProxyInfo
)
/*++
Routine Description:
Determines what proxy type, proxy name, and port the caller should use
given an Url, its length, a target host, a target port, and output space
to store the result.
The result may be a complete string containing a Netscape style string with
a delimited set of proxies, and access methods. An example of this may
look like:
"PROXY itgproxy:80; PROXY proxy:80; PROXY 192.168.100.2:1080; SOCKS 192.168.100.2; DIRECT"
This means we must try itgproxy, if this proxy fails we go on to proxy, and on to 192.168.100.2, etc.
Note that if itgproxy, proxy, and 192.168.100.2 all fail we can try a SOCKS proxy, and if this fails we
can try a direct connection.
OR
The result can be a simply IE 3.0 proxyname. Ex: "proxy". You can figure out
which by calling IsNetscapeProxyListString() on the returned proxy hostname.
If there is an external proxy DLL registered and valid, we defer to it to decide
what proxy to use, and thus ignore internal proxy information.
Note this function can also be used to retrive mapping of protocol to proxy. For example,
if tUrlProtocol == INTERNET_SCHEME_FTP, the result *lptProxyScheme == INTERNET_SCHEME_SOCKS
which means we should use a socks proxy/firewall for FTP accesss.
Arguments:
tScheme - can be 0, meaning match any scheme
lpszHostName - can contain wildcards. May be name or IP address
nPort - can be 0, meaning match any port
pfAutoProxy - TRUE if an auto-proxy is being used.
Return Value:
BOOL
TRUE - an entry on the bypass list matched the criteria
FALSE - the host identified by the parameters is not on this bypass
list
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_INFO::QueryProxySettings",
"%#X",
ppQueryForProxyInfo
));
AUTO_PROXY_ASYNC_MSG *pQueryForProxyInfo = *ppQueryForProxyInfo;
INTERNET_SCHEME tProxyScheme = pQueryForProxyInfo->_tUrlProtocol;
BOOL fIsByPassed = FALSE;
BOOL fProxyConnect = FALSE;
DWORD error = ERROR_SUCCESS;
Lock(FALSE);
if (!IsProxySettingsConfigured()) // virtual func, perhaps replace with faster internal?
{
fProxyConnect = FALSE;
goto quit;
}
//
// Ok, if we're here we are NOT using the Auto-Proxy DLL.
// 1. Determine if we are Bypassed ( and thus prevented from using a proxy )
// 2. Map the Protocol to a Proxy type.
// 3. Grab the hostname of the proxy we wish to use.
//
if ( pQueryForProxyInfo->_lpszUrlHostName && pQueryForProxyInfo->_dwUrlHostNameLength > 0 )
{
fIsByPassed = IsBypassed(
pQueryForProxyInfo->_tUrlProtocol,
pQueryForProxyInfo->_lpszUrlHostName,
pQueryForProxyInfo->_dwUrlHostNameLength,
pQueryForProxyInfo->_nUrlPort
);
if ( fIsByPassed )
{
goto quit;
}
}
pQueryForProxyInfo->_tProxyScheme = ProxyScheme(pQueryForProxyInfo->_tUrlProtocol);
if ( pQueryForProxyInfo->_tProxyScheme == INTERNET_SCHEME_UNKNOWN )
{
pQueryForProxyInfo->_tProxyScheme = INTERNET_SCHEME_SOCKS;
pQueryForProxyInfo->_tUrlProtocol = INTERNET_SCHEME_SOCKS;
}
if (pQueryForProxyInfo->_bFreeProxyHostName
&& (pQueryForProxyInfo->_lpszProxyHostName != NULL)) {
FREE_MEMORY(pQueryForProxyInfo->_lpszProxyHostName);
}
fProxyConnect = GetProxyHostName(
pQueryForProxyInfo->_tUrlProtocol,
&(pQueryForProxyInfo->_tProxyScheme),
&(pQueryForProxyInfo->_lpszProxyHostName),
&(pQueryForProxyInfo->_bFreeProxyHostName),
&(pQueryForProxyInfo->_dwProxyHostNameLength),
&(pQueryForProxyInfo->_nProxyHostPort)
);
quit:
pQueryForProxyInfo->_dwQueryResult = (DWORD) fProxyConnect;
//
// If we've disabled direct connections, then fail
// when there is no proxy
//
if ( !fProxyConnect && IsDisableDirect() ) {
error = ERROR_INTERNET_CANNOT_CONNECT;
}
Unlock();
DEBUG_LEAVE(error);
return error;
}
DWORD
PROXY_INFO::GetProxyStringInfo(
OUT LPVOID lpBuffer,
IN OUT LPDWORD lpdwBufferLength
)
/*++
Routine Description:
IMPORTANT PLEASE READ: LEGACY FUNCTION, this does not support all the new
proxy behaviors, left here for Wininet compat with older programs
Returns the proxy server and bypass lists in an INTERNET_PROXY_INFO. Called
by InternetQueryOption(INTERNET_OPTION_PROXY)
Assumes: Access to this is serialized while we are getting this info
Arguments:
lpBuffer - pointer to buffer where information will be returned
lpdwBufferLength - IN: size of lpBuffer in BYTEs
OUT: number of BYTEs returned in lpBuffer
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_INSUFFICIENT_BUFFER
*lpdwBufferLength contains the required buffer length
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_INFO::GetProxyStringInfo",
"%#x, %#x [%d]",
lpBuffer,
lpdwBufferLength,
*lpdwBufferLength
));
DEBUG_PRINT(PROXY,
INFO,
("Calling Legacy GetProxyStringInfo, NEW CODE SHOULD AVOID THIS CODE PATH\n"
));
DWORD requiredSize = sizeof(INTERNET_PROXY_INFO);
LPSTR lpVariable = (LPSTR)(((LPINTERNET_PROXY_INFO)lpBuffer) + 1);
LPSTR lpszProxy;
Lock(FALSE);
if (_ProxyServerList != NULL) {
lpszProxy = lpVariable;
_ProxyServerList->GetList(&lpVariable,
(*lpdwBufferLength > requiredSize)
? (*lpdwBufferLength - requiredSize)
: 0,
&requiredSize
);
} else {
lpszProxy = NULL;
}
LPSTR lpszProxyBypass;
if (_ProxyBypassList != NULL) {
DWORD size = requiredSize;
lpszProxyBypass = lpVariable;
_ProxyBypassList->GetList(&lpVariable,
(*lpdwBufferLength > requiredSize)
? (*lpdwBufferLength - requiredSize)
: 0,
&requiredSize
);
if (requiredSize == size) {
lpszProxyBypass = NULL;
}
} else {
lpszProxyBypass = NULL;
}
DWORD error;
if (*lpdwBufferLength >= requiredSize) {
LPINTERNET_PROXY_INFO lpInfo = (LPINTERNET_PROXY_INFO)lpBuffer;
lpInfo->dwAccessType = (lpszProxy == NULL)
? INTERNET_OPEN_TYPE_DIRECT
: INTERNET_OPEN_TYPE_PROXY;
lpInfo->lpszProxy = lpszProxy;
lpInfo->lpszProxyBypass = lpszProxyBypass;
error = ERROR_SUCCESS;
} else {
error = ERROR_INSUFFICIENT_BUFFER;
}
*lpdwBufferLength = requiredSize;
Unlock();
DEBUG_LEAVE(error);
return error;
}
BOOL
PROXY_INFO::RedoSendRequest(
IN OUT LPDWORD lpdwError,
IN AUTO_PROXY_ASYNC_MSG *pQueryForProxyInfo,
IN CServerInfo *pOriginServer,
IN CServerInfo *pProxyServer
)
/*++
Routine Description:
Determines whether a connection needs to be retried do to a failed proxy.
Arguments:
lpdwError - Error code of connection.
pProxyState - Pointer to proxy_state returned when acquiring the proxy information.
Return Value:
LPSTR
Success - pointer to allocated buffer
Failure - NULL
--*/
{
DEBUG_ENTER((DBG_PROXY,
Bool,
"PROXY_INFO::RedoSendRequest",
"%#x [%d], %#x",
lpdwError,
lpdwError ? *lpdwError : 0,
pQueryForProxyInfo
));
BOOL fReturn = FALSE;
PROXY_STATE *pProxyState = NULL;
DWORD dwVersion;
LPSTR lpszConnection;
BOOL fCanCache = FALSE;
if ( pQueryForProxyInfo )
{
pProxyState = pQueryForProxyInfo->_pProxyState;
//
// On success,
//
if ( *lpdwError == ERROR_SUCCESS )
{
if ( pQueryForProxyInfo->IsCanCacheResult() &&
pProxyState &&
pOriginServer &&
pProxyServer )
{
pOriginServer->SetCachedProxyServerInfo(
pProxyServer,
pQueryForProxyInfo->GetVersion(),
pQueryForProxyInfo->IsUseProxy(),
pQueryForProxyInfo->_tUrlProtocol,
pQueryForProxyInfo->_nUrlPort,
pQueryForProxyInfo->_tProxyScheme,
pQueryForProxyInfo->_nProxyHostPort
);
}
}
else if ( *lpdwError != ERROR_SUCCESS &&
*lpdwError != ERROR_INTERNET_OPERATION_CANCELLED &&
*lpdwError != ERROR_INTERNET_SEC_CERT_ERRORS &&
*lpdwError != ERROR_INTERNET_SECURITY_CHANNEL_ERROR &&
*lpdwError != ERROR_INTERNET_SEC_CERT_REVOKED &&
*lpdwError != ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED )
{
//
// For backround detection, we need to retry
// waiting for the backround results to complete
//
// Otherwise, If we have additional proxies,
// we need to retry them as well.
//
if ( pQueryForProxyInfo->IsBackroundDetectionPending() )
{
*lpdwError = ERROR_SUCCESS;
fReturn = TRUE;
}
else if ( pProxyState &&
!pProxyState->IsEmpty() &&
pProxyState->IsAnotherProxyAvail() )
{
INTERNET_PORT LastProxyUsedPort;
LPSTR lpszLastProxyUsed = pProxyState->GetLastProxyUsed(&LastProxyUsedPort);
Lock(FALSE);
if ( ( lpszLastProxyUsed == NULL ) ||
_BadProxyList.AddEntry(lpszLastProxyUsed, LastProxyUsedPort) != ERROR_SUCCESS )
{
fReturn = FALSE;
}
else
{
*lpdwError = ERROR_SUCCESS;
fReturn = TRUE;
}
Unlock();
}
}
}
DEBUG_LEAVE(fReturn);
return fReturn;
}
VOID
PROXY_INFO::CleanOutLists(
VOID
)
/*++
Routine Description:
Delete proxy server and bypass lists if not empty
N.B. Exclusive lock MUST be acquired before calling this method
Arguments:
None.
Return Value:
None.
--*/
{
DEBUG_ENTER((DBG_PROXY,
None,
"PROXY_INFO::CleanOutLists",
NULL
));
if (_ProxyServerList != NULL) {
delete _ProxyServerList;
_ProxyServerList = NULL;
}
if (_ProxyBypassList != NULL) {
delete _ProxyBypassList;
_ProxyBypassList = NULL;
}
DEBUG_LEAVE(0);
}
//
// PROXY_INFO_GLOBAL - Global Object thats inherits and expands the basic functionality
// of basic PROXY_INFO behavior. The new functionality includes wrapping Auto-Proxy
/// and Auto-detection routines
//
VOID
PROXY_INFO_GLOBAL::TerminateProxySettings(
VOID
)
/*++
Routine Description:
Destroy PROXY_INFO_GLOBAL object
Arguments:
None.
Return Value:
None.
--*/
{
//DEBUG_ENTER((DBG_OBJECTS,
// None,
// "PROXY_INFO_GLOBAL::TerminateProxySettings",
// NULL
// ));
Lock(TRUE);
if (_AutoProxyList != NULL) {
_AutoProxyList->FreeAutoProxyInfo(); // free async component of object
delete _AutoProxyList;
_AutoProxyList = NULL;
}
Unlock();
PROXY_INFO::TerminateProxySettings();
//DEBUG_LEAVE(0);
}
DWORD
PROXY_INFO_GLOBAL::SetProxySettings(
IN LPINTERNET_PROXY_INFO_EX lpProxySettings,
IN BOOL fModifiedInProcess
)
/*++
Routine Description:
Sets the proxy info. Mainly handles Auto-Config, its decendent will handle static stuff
Assumes: 1. The parameters have already been validated in the API that calls
this method (i.e. InternetOpen(), InternetSetOption())
Arguments:
lpProxySettings - a set of relevent fields describing proxy settings
fModifiedInProcess - TRUE if this object is not from the registry, but
a modifed setting for this process (that overrides reg values)
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_INVALID_PARAMETER
The lpszProxy or lpszProxyBypass list was bad
ERROR_NOT_ENOUGH_MEMORY
Failed to create an object or allocate space for a list,
etc.
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_INFO_GLOBAL::SetProxySettings",
"%x, %B",
lpProxySettings,
fModifiedInProcess
));
DWORD error = ERROR_SUCCESS;
//
// about to start changing contents - acquire lock
//
Lock(TRUE);
//
// Once we're set to Modified, we're modified for the lifetime of the
// the process, and thus we no longer accept Registry settings
//
if ( IsModifiedInProcess() &&
!fModifiedInProcess )
{
error = ERROR_SUCCESS;
goto quit;
}
if ( _lpszConnectionName != NULL ) {
FREE_MEMORY(_lpszConnectionName);
}
_lpszConnectionName = lpProxySettings->lpszConnectionName ?
NewString(lpProxySettings->lpszConnectionName) :
NULL;
_dwProxyFlags = lpProxySettings->dwFlags;
//
// Allocate Auto-Proxy/Auto-Detect Object, and reset its settings
//
if ( ((lpProxySettings->dwFlags & PROXY_TYPE_AUTO_PROXY_URL) ||
(lpProxySettings->dwFlags & PROXY_TYPE_AUTO_DETECT)) &&
IsGlobal() &&
!( _AutoProxyList &&
_AutoProxyList->IsOnAsyncAutoProxyThread()) )
{
if (_AutoProxyList == NULL)
{
//
// Create a new Auto-Proxy List, this will pull down registry
// info (for the DLLs) at initialization
//
_AutoProxyList = new AUTO_PROXY_DLLS();
if ( _AutoProxyList == NULL )
{
error = ERROR_NOT_ENOUGH_MEMORY;
goto quit;
}
error = _AutoProxyList->GetError();
if ( error != ERROR_SUCCESS ) {
goto quit;
}
}
error = _AutoProxyList->SetProxySettings(
lpProxySettings,
fModifiedInProcess,
TRUE // fAllowOverwrite
);
if ( error != ERROR_SUCCESS ) {
goto quit;
}
}
//
// Set the Static Proxy Lists
//
error = PROXY_INFO::SetProxySettings(lpProxySettings, fModifiedInProcess);
quit:
//
// other threads free to access this PROXY_INFO again
//
Unlock();
DEBUG_LEAVE(error);
return error;
}
DWORD
PROXY_INFO_GLOBAL::GetProxySettings(
IN LPINTERNET_PROXY_INFO_EX lpProxySettings,
IN BOOL fCheckVersion = FALSE
)
/*++
Routine Description:
Gather the proxy info. Mainly handles Auto-Config, its decendent will handle static stuff
Arguments:
lpProxySettings - a set of relevent fields describing proxy settings
fCheckVersion -
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_INVALID_PARAMETER
The lpszProxy or lpszProxyBypass list was bad
ERROR_NOT_ENOUGH_MEMORY
Failed to create an object or allocate space for a list,
etc.
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_INFO_GLOBAL::GetProxySettings",
"%x, %B",
lpProxySettings,
fCheckVersion
));
//
// about to start reading contents - acquire lock
//
Lock(FALSE);
DWORD error = ERROR_SUCCESS;
lpProxySettings->lpszConnectionName =
_lpszConnectionName ?
NewString(_lpszConnectionName) :
NULL;
lpProxySettings->dwFlags = _dwProxyFlags;
//
// Check Auto-Proxy/Auto-Detect Object, and read its settings
//
if ( IsGlobal() &&
_AutoProxyList)
{
error = _AutoProxyList->GetProxySettings(
lpProxySettings,
fCheckVersion
);
if ( error != ERROR_SUCCESS ) {
goto quit;
}
}
//
// Get the Static Proxy Lists
//
error = PROXY_INFO::GetProxySettings(lpProxySettings, fCheckVersion);
quit:
//
// other threads free to access this PROXY_INFO again
//
Unlock();
DEBUG_LEAVE(error);
return error;
}
DWORD
PROXY_INFO_GLOBAL::RefreshProxySettings(
IN BOOL fForceRefresh
)
/*++
Routine Description:
Force a refresh of automatic settings, such as auto-proxy, auto-detect
Arguments:
fForceRefresh - Forces a hard refresh
Return Value:
DWORD
Success - ERROR_SUCCESS
Failure - ERROR_INVALID_PARAMETER
The lpszProxy or lpszProxyBypass list was bad
ERROR_NOT_ENOUGH_MEMORY
Failed to create an object or allocate space for a list,
etc.
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_INFO_GLOBAL::RefreshProxySettings",
"%B",
fForceRefresh
));
DWORD error = ERROR_SUCCESS;
Lock(FALSE);
//
// Force reload of registry settings and download of auto-proxy file from server
//
if ( IsRefreshDisabled())
{
QueueRefresh();
goto quit;
}
//
// Check Auto-Proxy/Auto-Detect Object, and read its settings
//
if ( IsGlobal() &&
_AutoProxyList &&
!(_AutoProxyList->IsOnAsyncAutoProxyThread()) )
{
error = _AutoProxyList->RefreshProxySettings(
fForceRefresh
);
if ( error != ERROR_SUCCESS ) {
goto quit;
}
}
//
// Get the Static Proxy Lists
//
//error = PROXY_INFO::RefreshProxySettings(fForceRefresh);
quit:
//
// other threads free to access this PROXY_INFO again
//
Unlock();
DEBUG_LEAVE(error);
return error;
}
VOID
PROXY_INFO_GLOBAL::ReleaseQueuedRefresh(
VOID
)
/*++
Routine Description:
Force a refresh of automatic settings, such as auto-proxy, auto-detect,
When InternetOpen is called, allowing async threads.
Arguments:
None.
Return Value:
None.
--*/
{
DWORD error = ERROR_SUCCESS;
Lock(FALSE);
SetRefreshDisabled(FALSE);
if ( _fQueuedRefresh )
{
error = RefreshProxySettings(
FALSE
);
}
_fQueuedRefresh = FALSE;
//
// other threads free to access this PROXY_INFO again
//
Unlock();
}
DWORD
PROXY_INFO_GLOBAL::QueryProxySettings(
IN AUTO_PROXY_ASYNC_MSG **ppQueryForProxyInfo
)
/*++
Routine Description:
Aquries Proxy Information.
Note: if ppProxyState returns a non-NULL pointer than the Proxy
strings can be assumed to be allocated pointers to strings. Freeing
the ppProxyState object will result in the pointers being freed as well.
Otherwise the pointers will be to global string data that will not be
freed unexpectedly.
Arguments:
tUrlProtocol - Scheme type, protocol that is being used.
lpszUrl - Url being accessed.
dwUrlLength - size of Url.
lpszUrlHostName - Host name of site to connect to.
dwUrlHostNameLength - Host name length.
nUrlPort - Port of server to connect to.
lptProxyScheme - On output contains the correct proxy server type to use.
ex: if a SOCKS proxy is to be used to handle the FTP protocol,
this will be a INTERNET_SCHEME_SOCKS.
lplpszProxyHostName - Pointer to allocated memory containing the address of
the proxy host name.
Return Value:
LPSTR
Success - pointer to allocated buffer
Failure - NULL
--*/
{
DEBUG_ENTER((DBG_PROXY,
Dword,
"PROXY_INFO_GLOBAL::QueryProxySettings",
"%#x",
ppQueryForProxyInfo
));
INET_ASSERT(ppQueryForProxyInfo);
BOOL fReturn = FALSE;
DWORD error = ERROR_SUCCESS;
BOOL fNeedsGetNextProxy = FALSE;
BOOL fLocked = FALSE;
AUTO_PROXY_ASYNC_MSG *pQueryForProxyInfo = *ppQueryForProxyInfo;
//
// If we're dealing with a list of Proxies, we may have already tried one
// proxy and failed. So go to the next proxy in the list
// and see if another one is availble to try.
//
if ( pQueryForProxyInfo->IsProxyEnumeration() )
{
fNeedsGetNextProxy = TRUE;
goto quit;
}
if ( pQueryForProxyInfo->IsQueryOnCallback() &&
! pQueryForProxyInfo->IsAvoidAsyncCall())
{
goto quit;
}
Lock(FALSE);
fLocked = TRUE;
//
// Determine if we're dealing with an Auto-Proxy DLL,
// that needs a call into its own implimentation of GetProxyInfo
//
if ( _AutoProxyList &&
((_dwProxyFlags & PROXY_TYPE_AUTO_PROXY_URL) ||
(_dwProxyFlags & PROXY_TYPE_AUTO_DETECT))
)
{
AUTO_PROXY_DLLS *pAutoProxyList = _AutoProxyList;
Unlock();
fLocked = FALSE;
error = pAutoProxyList->QueryProxySettings(ppQueryForProxyInfo);
if ( error == ERROR_IO_PENDING ) {
goto fastquit;
}
pQueryForProxyInfo = *ppQueryForProxyInfo;
if ( error != ERROR_SUCCESS ||
pQueryForProxyInfo->IsUseProxy())
{
goto quit;
}
Lock(FALSE);
fLocked = TRUE;
}
//
// BUGBUG [arthurbi] I am NOT happy with this outragously bad logic
// and feel this is getting way out of hand. This code below is to
// simply catch a case where we have auto-proxy and InternetOpenUrl
// needs to detect whether to use HTTP over proxy for {gopher, ftp}
// OR direct gopher, ftp.
//
if ( pQueryForProxyInfo->IsAvoidAsyncCall() &&
pQueryForProxyInfo->IsDontWantProxyStrings() &&
_AutoProxyList &&
_AutoProxyList->IsAutoProxy() &&
_AutoProxyList->IsAutoProxyEnabled() )
{
pQueryForProxyInfo->SetUseProxy(TRUE);
//if ( pQueryForProxyInfo->_tUrlProtocol == INTERNET_SCHEME_HTTPS )
//{
// pQueryForProxyInfo->_tProxyScheme = INTERNET_SCHEME_HTTPS;
//}
//else
//{
pQueryForProxyInfo->_tProxyScheme = INTERNET_SCHEME_HTTP;
//}
goto quit;
}
//
// Use normal Proxy infomation stored in the registry
//
error = PROXY_INFO::QueryProxySettings(&pQueryForProxyInfo);
if ( error != ERROR_SUCCESS)
{
goto quit;
}
quit:
if ( error == ERROR_SUCCESS &&
( fNeedsGetNextProxy ||
pQueryForProxyInfo->IsProxyEnumeration()) )
{
error = pQueryForProxyInfo->GetNextProxy(_BadProxyList);
}
if ( fLocked )
{
Unlock();
}
fastquit:
DEBUG_LEAVE(error);
return error;
}
BOOL
PROXY_INFO_GLOBAL::HostBypassesProxy(
IN INTERNET_SCHEME tScheme,
IN LPSTR lpszHostName,
IN DWORD cchHostName
)
/*++
Routine Description:
Determine if request should bypass proxy for host
Arguments:
tScheme -
lpszHostName -
cchHostName -
Return Value:
BOOL
--*/
{
BOOL bReturn = FALSE;
DWORD dwServiceType;
BOOL bTryByPassList = TRUE;
// Only do this if it is for a scheme wininet supports.
if (tScheme == INTERNET_SCHEME_HTTP ||
tScheme == INTERNET_SCHEME_HTTPS ||
tScheme == INTERNET_SCHEME_DEFAULT)
{
dwServiceType = INTERNET_SERVICE_HTTP;
}
else if (tScheme == INTERNET_SCHEME_FTP)
{
dwServiceType = INTERNET_SERVICE_FTP;
}
else if (tScheme == INTERNET_SCHEME_GOPHER)
{
dwServiceType = INTERNET_SERVICE_GOPHER;
}
else
{
return bReturn;
}
// LOCK
Lock(FALSE);
if (IsAutoProxy() &&
_AutoProxyList->IsAutoProxyEnabled() &&
_AutoProxyList->IsAutoProxyThreadReadyForRequests() &&
((_dwProxyFlags & PROXY_TYPE_AUTO_PROXY_URL) ||
(_dwProxyFlags & PROXY_TYPE_AUTO_DETECT))
)
{
CServerInfo * pServerInfo = NULL;
DWORD error = ::GetServerInfo(lpszHostName,
dwServiceType,
FALSE,
&pServerInfo
);
if (pServerInfo != NULL)
{
bTryByPassList = FALSE;
if (pServerInfo->IsProxyByPassSet())
{
bReturn = pServerInfo->WasProxyByPassed();
}
else
{
// We have to know call the autoproxy code to determine if
// the proxy is being bypassed.
// First create an URL which corresponds to
// scheme://hostname
ICSTRING urlName;
char hostName[INTERNET_MAX_HOST_NAME_LENGTH + 1];
DWORD dwSchemeLength;
LPSTR schemeName = MapUrlScheme(tScheme, &dwSchemeLength);
int bufLength = dwSchemeLength
+ 3 // For the ://
+ cchHostName
+ 1 ; // Trailing NULL.
urlName.CreateStringBuffer((LPVOID)schemeName, dwSchemeLength, bufLength);
if (dwSchemeLength != 0)
{
urlName += "://" ;
}
urlName.Strncat(lpszHostName, cchHostName);
AUTO_PROXY_ASYNC_MSG proxyInfoQuery(tScheme,
urlName.StringAddress(),
hostName,
sizeof(hostName));
AUTO_PROXY_ASYNC_MSG *pProxyInfoQuery = &proxyInfoQuery;
if ( _AutoProxyList->IsAutoProxyThreadReadyForRequests() ) {
proxyInfoQuery.SetBlockUntilCompletetion(TRUE);
} else {
proxyInfoQuery.SetAvoidAsyncCall(TRUE);
}
Unlock();
START_GUARD_AGAINST_ASYNC_AUTOPROXY_CALL; // Make sure corresponding END is in codepath !!!
error = QueryProxySettings(&pProxyInfoQuery);
INET_ASSERT(error != ERROR_IO_PENDING);
END_GUARD_AGAINST_ASYNC_AUTOPROXY_CALL;
// LOCK AGAIN
Lock(FALSE);
if (error == ERROR_SUCCESS)
{
bReturn = !pProxyInfoQuery->IsUseProxy();
pServerInfo->SetProxyByPassed(bReturn);
if (pProxyInfoQuery && pProxyInfoQuery->IsAlloced())
{
delete pProxyInfoQuery;
}
}
}
::ReleaseServerInfo(pServerInfo);
}
}
if (bTryByPassList) {
bReturn = IsHostInBypassList(lpszHostName, cchHostName);
}
Unlock();
return bReturn;
}
//
// PROXY_STATE - an abstraction object used to provice simple string enumeration
// given a list of proxies that need to be tested
//
BOOL
PROXY_STATE::GetNextProxy(
IN INTERNET_SCHEME tUrlScheme,
IN BAD_PROXY_LIST & BadProxyList,
OUT LPINTERNET_SCHEME lptProxyScheme,
OUT LPSTR * lplpszProxyHostName,
OUT LPBOOL lpbFreeProxyHostName,
OUT LPDWORD lpdwProxyHostNameLength,
OUT LPINTERNET_PORT lpProxyHostPort
)
/*++
Routine Description:
Parses the current Proxy State information to acquire the
proxy name, port, type to use.
Arguments:
tUrlScheme - Scheme type, protocol that is being used.
BadProxyList - Reference to array of bad proxies that we can add/remove/check
from.
lptProxyScheme - On output contains the correct proxy server type to use.
ex: if a SOCKS proxy is to be used to handle the FTP protocol,
this will be a INTERNET_SCHEME_SOCKS.
lplpszProxyHostName - Pointer to allocated memory containing the address of
the proxy host name.
lpbFreeProxyHostName - TRUE if *lplpszProxyHostName was allocated
lpdwProxyHostNameLength - length of lplpszProxyHostName.
lpProxyHostPort - Host Port of Proxy.
Return Value:
LPSTR
Success - pointer to allocated buffer
Failure - NULL
--*/
{
LPSTR lpszDelimiter = NULL;
BOOL fReturn = FALSE;
LPSTR lpszPortDelim = NULL;
LPSTR lpszPort = NULL;
if ( !_fIsMultiProxyList )
{
*lptProxyScheme = _tProxyScheme;
*lplpszProxyHostName = _lpszAutoProxyList;
*lpbFreeProxyHostName = FALSE;
*lpdwProxyHostNameLength = _dwcbAutoProxyList;
*lpProxyHostPort = _proxyHostPort;
}
_fIsAnotherProxyAvail = FALSE;
while ( *_lpszOffset != '\0' )
{
LPSTR lpszNewOffset ;
//
// Remove the delimiter so we can see the next token.
// ex: PROXY foo:80; DIRECT
// we would find DIRECT first with strstr, if we didn't
// delimit first.
//
lpszDelimiter = strchr(_lpszOffset, ';' );
if ( lpszDelimiter == NULL )
{
lpszDelimiter = strchr(_lpszOffset, ',' );
}
if ( lpszDelimiter )
{
*lpszDelimiter = '\0';
}
lpszNewOffset=
strstr(_lpszOffset, "DIRECT");
if ( lpszNewOffset )
{
lpszNewOffset += sizeof("DIRECT");
_lpszOffset = lpszNewOffset;
//
// FALSE means direct.
//
fReturn = FALSE;
goto quit;
}
//
// Its not direct, try PROXY or SOCKS.
//
lpszNewOffset =
strstr(_lpszOffset, "PROXY");
if ( lpszNewOffset)
{
lpszNewOffset += sizeof("PROXY");
*lpProxyHostPort = INTERNET_DEFAULT_HTTP_PORT;
if ( tUrlScheme == INTERNET_SCHEME_HTTPS )
{
*lptProxyScheme = INTERNET_SCHEME_HTTPS;
}
else
{
*lptProxyScheme = INTERNET_SCHEME_HTTP;
}
}
else
{
lpszNewOffset =
strstr(_lpszOffset, "SOCKS");
if ( lpszNewOffset )
{
lpszNewOffset += sizeof("SOCKS");
*lptProxyScheme = INTERNET_SCHEME_SOCKS;
*lpProxyHostPort = INTERNET_DEFAULT_SOCKS_PORT;
}
}
//
// Now do the generic common things for SOCKS, or PROXY
// entries, ie: get port, hostname, and hostname size.
//
if ( lpszNewOffset )
{
_lpszOffset = lpszNewOffset;
SKIPWS(_lpszOffset);
*lplpszProxyHostName = _lpszOffset;
*lpbFreeProxyHostName = FALSE;
lpszPortDelim = strchr(_lpszOffset, ':');
if ( lpszPortDelim )
{
*lpszPortDelim = '\0';
lpszPort = lpszPortDelim+1;
*lpProxyHostPort = (INTERNET_PORT)
atoi(lpszPort);
}
*lpdwProxyHostNameLength = lstrlen(_lpszOffset);
if (BadProxyList.IsBadProxyName(*lplpszProxyHostName, *lpProxyHostPort))
{
if ( lpszDelimiter )
{
_lpszOffset = (lpszDelimiter+1);
}
else
{
_lpszOffset = _lpszAutoProxyList + _dwcbAutoProxyList;
}
continue;
}
fReturn = TRUE;
}
break;
}
quit:
//if ( lpszPortDelim )
//{
// *lpszPortDelim = ':';
//}
if ( lpszDelimiter )
{
*lpszDelimiter = ';';
_lpszOffset = (lpszDelimiter+1);
}
else
{
_lpszOffset = _lpszAutoProxyList + _dwcbAutoProxyList;
}
if ( fReturn )
{
_lpszLastProxyUsed = *lplpszProxyHostName;
_LastProxyUsedPort = *lpProxyHostPort;
//
// If theres another possible proxy in this list,
// then we'll need to remember that
//
if ( _lpszOffset &&
*_lpszOffset &&
(strstr(_lpszOffset, "PROXY") ||
strstr(_lpszOffset, "DIRECT") ||
strstr(_lpszOffset, "SOCKS"))
)
{
_fIsAnotherProxyAvail = TRUE;
}
}
return fReturn;
}
PRIVATE
LPSTR
GetRegistryProxyParameter(
IN LPSTR lpszParameterName
)
/*++
Routine Description:
Reads a string from the registry into a buffer, then shrinks the buffer
Arguments:
lpszParameterName - name of string to retrieve
Return Value:
LPSTR
Success - pointer to allocated buffer
Failure - NULL
--*/
{
LPSTR buffer = NULL;
DWORD length = PROXY_REGISTRY_STRING_LENGTH;
BOOL done = FALSE;
do {
buffer = (LPSTR)ResizeBuffer(buffer, length, FALSE);
if (done || (buffer == NULL)) {
break;
}
DWORD error;
error = InternetReadRegistryString(lpszParameterName, buffer, &length);
length = (error == ERROR_SUCCESS) ? ((length == 0) ? 0 : (length + 1)) : 0;
done = TRUE;
} while (TRUE);
return buffer;
}
//
// wrapper function for urlzones.
//
BOOLAPI IsHostInProxyBypassList (INTERNET_SCHEME tScheme, LPCSTR pszHost, DWORD cchHost)
{
BOOL fRet = FALSE;
if (!GlobalDataInitialized) {
GlobalDataInitialize();
}
if(ERROR_SUCCESS == LoadWinsock())
{
fRet = GlobalProxyInfo.HostBypassesProxy(tScheme, (LPSTR)pszHost, cchHost);
}
return fRet;
}