You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3888 lines
93 KiB
3888 lines
93 KiB
/*++
|
|
|
|
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;
|
|
}
|