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.
1415 lines
36 KiB
1415 lines
36 KiB
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
socket.cxx
|
|
|
|
Abstract:
|
|
|
|
This file contains general socket utilities.
|
|
|
|
Contents:
|
|
HTTP_REQUEST_HANDLE_OBJECT::OpenConnection
|
|
CFsm_OpenConnection::RunSM
|
|
HTTP_REQUEST_HANDLE_OBJECT::OpenConnection_Fsm
|
|
HTTP_REQUEST_HANDLE_OBJECT::CloseConnection
|
|
HTTP_REQUEST_HANDLE_OBJECT::ReleaseConnection
|
|
HTTP_REQUEST_HANDLE_OBJECT::AbortConnection
|
|
HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel
|
|
CFsm_OpenProxyTunnel::RunSM
|
|
HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel_Fsm
|
|
HTTP_REQUEST_HANDLE_OBJECT::CloneResponseBuffer
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 16-Nov-1994
|
|
|
|
Revision History:
|
|
|
|
18-Dec-1995 rfirth
|
|
Reworked for C++
|
|
|
|
27-Mar-1996 arthurbi
|
|
Added OpenProxyTunnel Method
|
|
|
|
--*/
|
|
|
|
#include <wininetp.h>
|
|
#include <perfdiag.hxx>
|
|
#include "httpp.h"
|
|
|
|
//
|
|
// functions
|
|
//
|
|
|
|
|
|
DWORD
|
|
HTTP_REQUEST_HANDLE_OBJECT::OpenConnection(
|
|
IN BOOL bNewConnection,
|
|
IN BOOL fNoCreate /* = FALSE */
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get a connection to the web server. Either use a pre-existing keep-alive
|
|
connection from the global pool or create a new connection
|
|
|
|
Arguments:
|
|
|
|
bNewConnection - TRUE if we are NOT to get a connection from the keep-alive
|
|
pool
|
|
|
|
fNoCreate - TRUE if we should NOT create a new socket if a k-a isn't found.
|
|
This is currently for the SSL tunneling case where we want to break
|
|
and send a CONNECT if a k-a doesn't match our criteria.
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
Opened connection
|
|
|
|
ERROR_IO_PENDING
|
|
Operation will complete asynchronously
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"HTTP_REQUEST_HANDLE_OBJECT::OpenConnection",
|
|
"%B",
|
|
bNewConnection
|
|
));
|
|
|
|
DWORD error = DoFsm(New CFsm_OpenConnection(bNewConnection, this, fNoCreate));
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CFsm_OpenConnection::RunSM(
|
|
IN CFsm * Fsm
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Runs next OpenConnection state
|
|
|
|
Arguments:
|
|
|
|
Fsm - containing open connection state info
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"CFsm_OpenConnection::RunSM",
|
|
"%#x",
|
|
Fsm
|
|
));
|
|
|
|
DWORD error;
|
|
HTTP_REQUEST_HANDLE_OBJECT * pRequest;
|
|
CFsm_OpenConnection * stateMachine = (CFsm_OpenConnection *)Fsm;
|
|
|
|
START_SENDREQ_PERF();
|
|
|
|
pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)Fsm->GetContext();
|
|
switch (Fsm->GetState()) {
|
|
case FSM_STATE_INIT:
|
|
case FSM_STATE_CONTINUE:
|
|
error = pRequest->OpenConnection_Fsm(stateMachine);
|
|
break;
|
|
|
|
default:
|
|
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
|
Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR);
|
|
|
|
INET_ASSERT(FALSE);
|
|
|
|
break;
|
|
}
|
|
|
|
STOP_SENDREQ_PERF();
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
HTTP_REQUEST_HANDLE_OBJECT::OpenConnection_Fsm(
|
|
IN CFsm_OpenConnection * Fsm
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open connection FSM
|
|
|
|
Arguments:
|
|
|
|
Fsm - containing state info
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"HTTP_REQUEST_HANDLE_OBJECT::OpenConnection_Fsm",
|
|
"%#x",
|
|
Fsm
|
|
));
|
|
|
|
CFsm_OpenConnection & fsm = *Fsm;
|
|
DWORD error = fsm.GetError();
|
|
CServerInfo * pServerInfo = GetServerInfo();
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// BUGBUG - redundancy. Either put these in the FSM or figure out why we need
|
|
// to do proxy name processing here
|
|
//
|
|
|
|
//
|
|
// if this object was created from an InternetOpen() handle which specified
|
|
// INTERNET_OPEN_TYPE_PROXY then we connect to the proxy, otherwise we
|
|
// connect to the server specified in InternetConnect()
|
|
//
|
|
|
|
LPSTR hostName;
|
|
LPSTR hostNameServer;
|
|
DWORD hostLength;
|
|
INTERNET_PORT hostPort;
|
|
|
|
hostName = hostNameServer = GetHostName(&hostLength);
|
|
hostPort = GetHostPort();
|
|
|
|
LPSTR proxyHostName;
|
|
DWORD proxyHostNameLength;
|
|
INTERNET_PORT proxyHostPort;
|
|
|
|
GetProxyName(&proxyHostName,
|
|
&proxyHostNameLength,
|
|
&proxyHostPort
|
|
);
|
|
|
|
if ((proxyHostName != NULL) && (proxyHostNameLength > 0)) {
|
|
SetViaProxy(TRUE);
|
|
hostName = proxyHostName;
|
|
hostLength = proxyHostNameLength;
|
|
hostPort = proxyHostPort;
|
|
}
|
|
|
|
INET_ASSERT(hostName != NULL);
|
|
INET_ASSERT(hostPort != INTERNET_INVALID_PORT_NUMBER);
|
|
|
|
if (fsm.GetState() != FSM_STATE_INIT) {
|
|
switch (fsm.GetFunctionState()) {
|
|
case FSM_STATE_1:
|
|
goto get_continue;
|
|
case FSM_STATE_2:
|
|
goto connect_continue;
|
|
default:
|
|
|
|
INET_ASSERT(FALSE);
|
|
|
|
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
|
goto quit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// we may already have a keep-alive connection - don't ask for a new one.
|
|
// This happens in the challenge phase of a multi-part (e.g. NTLM) auth
|
|
// negotiation over keep-alive
|
|
//
|
|
|
|
if (IsWantKeepAlive() && !fsm.m_bNewConnection && (_Socket != NULL)
|
|
&& _Socket->IsOpen()) {
|
|
|
|
//INET_ASSERT(_bKeepAliveConnection);
|
|
|
|
//if ( IsTunnel() )
|
|
//{
|
|
// dprintf("Tunnel for nested req=%#x, ALREADY open on Socket=%#x\n", this, _Socket );
|
|
//}
|
|
|
|
|
|
error = ERROR_SUCCESS;
|
|
goto quit;
|
|
}
|
|
|
|
INET_ASSERT(pServerInfo != NULL);
|
|
|
|
if (pServerInfo == NULL) {
|
|
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// if this request wants a keep-alive connection AND we are allowed to use
|
|
// one (i.e. not forced to generate a new connection) AND we can find one
|
|
// then we're done, otherwise we have to generate a new connection
|
|
//
|
|
|
|
DWORD dwSocketFlags;
|
|
|
|
dwSocketFlags = IsAsyncHandle() ? SF_NON_BLOCKING : 0;
|
|
if ((IsWantKeepAlive() || (GetOpenFlags() & INTERNET_FLAG_KEEP_CONNECTION))
|
|
&& !fsm.m_bNewConnection) {
|
|
dwSocketFlags |= SF_KEEP_ALIVE;
|
|
}
|
|
if (GetOpenFlags() & WINHTTP_FLAG_SECURE) {
|
|
dwSocketFlags |= SF_SECURE;
|
|
}
|
|
if ( IsTunnel() )
|
|
{
|
|
dwSocketFlags |= SF_TUNNEL;
|
|
// dprintf("Opening Tunnel for nested req=%#x, Socket Flags=%#x, K-A=%B, Secure=%B, N-B=%B\n",
|
|
// this, dwSocketFlags, (dwSocketFlags & SF_KEEP_ALIVE), (dwSocketFlags & SF_SECURE),
|
|
// (dwSocketFlags & SF_NON_BLOCKING));
|
|
}
|
|
|
|
|
|
INET_ASSERT(_Socket == NULL);
|
|
|
|
_Socket = NULL;
|
|
fsm.SetFunctionState(FSM_STATE_1);
|
|
// If m_fNoCreate flag is set, then we're attempting to find
|
|
// a matching SSL tunnel that's already been established.
|
|
error = DoFsm(new CFsm_GetConnection(
|
|
dwSocketFlags,
|
|
fsm.m_fNoCreate ? GetHostPort() : hostPort,
|
|
GetTimeoutValue(WINHTTP_OPTION_CONNECT_TIMEOUT),
|
|
10000, // dwLimitTimeout
|
|
&_Socket,
|
|
pServerInfo,
|
|
fsm.m_fNoCreate ? hostNameServer : NULL
|
|
));
|
|
|
|
get_continue:
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
goto quit;
|
|
}
|
|
|
|
if (_Socket != NULL) {
|
|
|
|
//
|
|
// _bKeepAliveConnection now means "this is a pre-existing k-a socket".
|
|
// Only meaningful when re-establishing connect when dropped by server
|
|
//
|
|
|
|
//if ( IsTunnel() )
|
|
//{
|
|
// dprintf("Tunnel for nested req=%#x\n opened on K-A Socket=%#x\n", this, _Socket );
|
|
//}
|
|
|
|
|
|
//dprintf("%s existing K-A connection %#x\n", GetURL(), _Socket->GetSocket());
|
|
_bKeepAliveConnection = TRUE;
|
|
|
|
//
|
|
// Get any security Info
|
|
//
|
|
|
|
if (_Socket->IsSecure()) {
|
|
if (m_pSecurityInfo != NULL) {
|
|
/* SCLE ref */
|
|
m_pSecurityInfo->Release();
|
|
}
|
|
/* SCLE ref */
|
|
m_pSecurityInfo = ((ICSecureSocket *)_Socket)->GetSecurityEntry();
|
|
((ICSecureSocket*)_Socket)->SetSecureFlags(SECURITY_FLAG_SECURE);
|
|
}
|
|
|
|
//
|
|
// successfully got keep-alive connection from the pool
|
|
//
|
|
|
|
DEBUG_PRINT(HTTP,
|
|
INFO,
|
|
("%skeep-alive connection: socket %#x, port %d\n",
|
|
_Socket->IsSecure() ? "SSL " : "",
|
|
_Socket->GetSocket(),
|
|
_Socket->GetSourcePort()
|
|
));
|
|
|
|
goto quit;
|
|
}
|
|
else if (fsm.m_fNoCreate)
|
|
{
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// the socket didn't come from the pool
|
|
//
|
|
|
|
_bKeepAliveConnection = FALSE;
|
|
|
|
//
|
|
// we may already have a socket if we're reusing the object
|
|
//
|
|
|
|
if (GetOpenFlags() & WINHTTP_FLAG_SECURE)
|
|
{
|
|
_Socket = New ICSecureSocket();
|
|
|
|
// Search on the appropriate cache, session vs. global
|
|
INTERNET_HANDLE_OBJECT * pInternet;
|
|
pInternet = GetRootHandle (this);
|
|
|
|
if (m_pSecurityInfo == NULL)
|
|
{
|
|
/* SCLE ref */
|
|
m_pSecurityInfo = (pInternet->GetSslSessionCache())->Find(GetHostName());
|
|
if (NULL == m_pSecurityInfo)
|
|
{
|
|
/* SCLE ref */
|
|
m_pSecurityInfo = New SECURITY_CACHE_LIST_ENTRY(GetHostName());
|
|
}
|
|
}
|
|
|
|
if (_Socket != NULL)
|
|
{
|
|
_Socket->SetEncryption();
|
|
/* SCLE ref */
|
|
((ICSecureSocket *)_Socket)->SetSecurityEntry(m_pSecurityInfo);
|
|
/* SCLE ref */
|
|
((ICSecureSocket *)_Socket)->SetHostName(GetHostName(), pInternet->GetSslSessionCache());
|
|
((ICSecureSocket *)_Socket)->SetSecureFlags(GetOpenFlags() & SECURITY_INTERNET_MASK);
|
|
if (GetEnableFlags() & WINHTTP_ENABLE_SSL_REVOCATION)
|
|
((ICSecureSocket *)_Socket)->SetSecureFlags(SECURITY_FLAG_CHECK_REVOCATION);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dwSocketFlags & SF_TUNNEL)
|
|
{
|
|
_Socket = New ICSecureSocket;
|
|
((ICSecureSocket *)_Socket)->ResetFlags(FALSE);
|
|
}
|
|
else
|
|
{
|
|
_Socket = New ICSocket;
|
|
}
|
|
}
|
|
if (_Socket != NULL)
|
|
{
|
|
fsm.m_bCreatedSocket = TRUE;
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// balance number of available connections
|
|
//
|
|
|
|
ReleaseConnection(FALSE, FALSE, FALSE);
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// Turn on Socks, if needed.
|
|
//
|
|
|
|
GetSocksProxyName(&proxyHostName,
|
|
&proxyHostNameLength,
|
|
&proxyHostPort
|
|
);
|
|
|
|
if ((proxyHostName != NULL) && (proxyHostNameLength > 0)) {
|
|
_Socket->EnableSocks(proxyHostName, proxyHostPort);
|
|
}
|
|
|
|
//
|
|
// NOTE: if secure connection is required, TargetServer must
|
|
// be a fully qualified domain name.
|
|
// The hostname is used in comparison with CN found in
|
|
// the certificate. The hostname MUST NOT BE the
|
|
// result of a DNS lookup. DNS lookups are open to
|
|
// spoofing, and that may prevent a security from
|
|
// being detected.
|
|
//
|
|
//
|
|
// If we're Posting or sending data, make sure
|
|
// the SSL connection code knows about it. Therefore we set
|
|
// the flag "SF_SENDING_DATA" for the purposes of
|
|
// generating errors if found while making the connection.
|
|
//
|
|
|
|
_Socket->SetPort(hostPort);
|
|
fsm.SetFunctionState(FSM_STATE_2);
|
|
error = _Socket->Connect(GetTimeoutValue(WINHTTP_OPTION_CONNECT_TIMEOUT),
|
|
GetTimeoutValue(WINHTTP_OPTION_CONNECT_RETRIES),
|
|
SF_INDICATE
|
|
| (IsAsyncHandle() ? SF_NON_BLOCKING : 0)
|
|
| (((GetMethodType() == HTTP_METHOD_TYPE_POST)
|
|
|| (GetMethodType() == HTTP_METHOD_TYPE_PUT))
|
|
? SF_SENDING_DATA
|
|
: 0)
|
|
);
|
|
|
|
connect_continue:
|
|
|
|
if (error == ERROR_SUCCESS) {
|
|
//dprintf("%s NEW connection %#x\n", GetURL(), _Socket->GetSocket());
|
|
|
|
DEBUG_PRINT(HTTP,
|
|
INFO,
|
|
("new connection: socket %#x\n",
|
|
_Socket->GetSocket()
|
|
));
|
|
|
|
//if ( IsTunnel() )
|
|
//{
|
|
// dprintf("Tunnel for nested req=%#x opened for Socket=%#x\n", this, _Socket );
|
|
//}
|
|
|
|
|
|
/*
|
|
24918: Invalid ASSERT because the m_Socket member in _Socket can be invalidated
|
|
due to closing handle : code downstream of this assert takes that into consideration.
|
|
for eg. SetLinger puts the call in a try..except.
|
|
*/
|
|
|
|
//INET_ASSERT(_Socket->IsOpen());
|
|
|
|
//pServerInfo->AddActiveConnection();
|
|
|
|
// enable send and receive timeout - ignore any errors
|
|
|
|
_Socket->SetTimeout(SEND_TIMEOUT,
|
|
GetTimeoutValue(WINHTTP_OPTION_SEND_TIMEOUT)
|
|
);
|
|
|
|
_Socket->SetTimeout(RECEIVE_TIMEOUT,
|
|
GetTimeoutValue(WINHTTP_OPTION_RECEIVE_TIMEOUT)
|
|
);
|
|
|
|
//
|
|
// set zero linger: force connection closed at transport level when
|
|
// we close the socket. Ignore the error
|
|
//
|
|
|
|
_Socket->SetLinger(TRUE, 0);
|
|
}
|
|
|
|
quit:
|
|
|
|
if (error != ERROR_IO_PENDING) {
|
|
|
|
//
|
|
// if we created the socket but failed to connect then delete the socket
|
|
// object
|
|
//
|
|
|
|
if ((error != ERROR_SUCCESS) && fsm.m_bCreatedSocket) {
|
|
|
|
//
|
|
// we created a socket so we must increase the available connection
|
|
// count on failure
|
|
//
|
|
|
|
INET_ASSERT(_Socket != NULL);
|
|
|
|
ReleaseConnection(TRUE, // close socket (if open)
|
|
FALSE, // don't indicate
|
|
TRUE // dispose of socket object
|
|
);
|
|
}
|
|
//dprintf("%s get/connect pending socket %#x\n", GetURL(), _Socket ? _Socket->GetSocket() : 0);
|
|
fsm.SetDone();
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
HTTP_REQUEST_HANDLE_OBJECT::CloseConnection(
|
|
IN BOOL bForceClosed
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs the opposite of OpenConnection(), i.e. closes the socket or marks
|
|
it not in use if keep-alive
|
|
|
|
Arguments:
|
|
|
|
bForceClosed - TRUE if we are to forcibly release a keep-alive connection
|
|
(i.e. the server timed out before we did)
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - WSA error
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"HTTP_REQUEST_HANDLE_OBJECT::CloseConnection",
|
|
"%B",
|
|
bForceClosed
|
|
));
|
|
|
|
//dprintf("*** closing %s%s socket %#x\n",
|
|
// (_bKeepAliveConnection || IsKeepAlive()) ? "K-A " : "",
|
|
// GetURL(),
|
|
// _Socket ? _Socket->GetSocket() : 0
|
|
// );
|
|
|
|
DWORD error = ERROR_SUCCESS;
|
|
BOOL bClose = TRUE;
|
|
BOOL bDelete = TRUE;
|
|
|
|
if (_Socket == NULL) {
|
|
|
|
DEBUG_PRINT(HTTP,
|
|
WARNING,
|
|
("socket already deleted\n"
|
|
));
|
|
|
|
goto quit;
|
|
}
|
|
if (_bKeepAliveConnection || IsKeepAlive()) {
|
|
|
|
//
|
|
// keep-alive connection: just return the connection to the pool
|
|
//
|
|
|
|
if ((IsContentLength() && (GetBytesInSocket() != 0))
|
|
|| (IsChunkEncoding() && !IsDecodingFinished())
|
|
|| IsNoLongerKeepAlive() || _Socket->IsClosed()
|
|
|| ((_State & 0x0F) < (HttpRequestStateObjectData & 0x0F))) {
|
|
|
|
DEBUG_PRINT(HTTP,
|
|
INFO,
|
|
("forcing %#x [%#x] closed: bytes left = %d/%d; no longer k-a = %B; closed = %B\n",
|
|
_Socket,
|
|
_Socket->GetSocket(),
|
|
GetBytesInSocket(),
|
|
GetContentLength(),
|
|
IsNoLongerKeepAlive(),
|
|
_Socket->IsClosed()
|
|
));
|
|
|
|
//dprintf("forcing k-a %#x closed - bytes=%d/%d, no longer=%B, chunked=%B, chunk-finished=%B\n",
|
|
// _Socket->GetSocket(),
|
|
// GetBytesInSocket(),
|
|
// GetContentLength(),
|
|
// IsNoLongerKeepAlive(),
|
|
// IsChunkEncoding(),
|
|
// IsDecodingFinished()
|
|
// );
|
|
bForceClosed = TRUE;
|
|
}
|
|
if (!bForceClosed) {
|
|
bClose = FALSE;
|
|
bDelete = FALSE;
|
|
} else {
|
|
//dprintf("%#x forced close\n", _Socket->GetSocket());
|
|
}
|
|
}
|
|
|
|
ReleaseConnection(bClose, TRUE, bDelete);
|
|
_Socket = NULL;
|
|
_bKeepAliveConnection = FALSE;
|
|
_bNoLongerKeepAlive = FALSE;
|
|
|
|
quit:
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
VOID
|
|
HTTP_REQUEST_HANDLE_OBJECT::ReleaseConnection(
|
|
IN BOOL bClose,
|
|
IN BOOL bIndicate,
|
|
IN BOOL bDispose
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Releases the connection back to the server limited pool and optionally
|
|
closes the socket handle and destroys the socket object
|
|
|
|
Arguments:
|
|
|
|
bClose - if TRUE, increments the available connection count in the
|
|
server info object and closes the handle, else we are
|
|
returning a keep-alive connection; after this call we no
|
|
longer have a socket object owned by this request handle
|
|
object
|
|
|
|
bIndicate - TRUE if we indicate to the user when we close the socket
|
|
handle
|
|
|
|
bDispose - TRUE if we are disposing of the socket object (mutually
|
|
exclusive with !bClose), in which case we will no longer have
|
|
a socket object after this call returns
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
None,
|
|
"HTTP_REQUEST_HANDLE_OBJECT::ReleaseConnection",
|
|
"%B, %B, %B",
|
|
bClose,
|
|
bIndicate,
|
|
bDispose
|
|
));
|
|
|
|
INET_ASSERT(_Socket != NULL);
|
|
//INET_ASSERT(_Socket->IsOpen());
|
|
|
|
CServerInfo * pServerInfo = GetServerInfo();
|
|
|
|
// Always disconnect sockets which have been marked as authenticated.
|
|
// This is to avoid posting data to IIS4 while preauthenticating
|
|
// and inducing the server to close the connection.
|
|
if (_Socket)
|
|
bClose = (bClose || _Socket->IsAuthenticated());
|
|
|
|
ICSocket * pSocket = bClose ? NULL : _Socket;
|
|
|
|
INET_ASSERT(pServerInfo != NULL);
|
|
|
|
if (pServerInfo != NULL) {
|
|
if (bClose && (_Socket != NULL)) {
|
|
|
|
//
|
|
// BUGBUG - this should be set based on bGraceful parameter
|
|
//
|
|
|
|
_Socket->SetLinger(FALSE, 0);
|
|
|
|
//INET_ASSERT(!_bKeepAliveConnection || _bNoLongerKeepAlive);
|
|
_Socket->Shutdown(SD_BOTH);
|
|
_Socket->Disconnect(bIndicate ? SF_INDICATE : 0);
|
|
if (bDispose) {
|
|
_Socket->Dereference();
|
|
_Socket = NULL;
|
|
}
|
|
} else {
|
|
_Socket = NULL;
|
|
}
|
|
//if (IsResponseHttp1_1() && IsKeepAlive()) {
|
|
// pServerInfo->ReleasePipelinedConnection(pSocket);
|
|
//} else {
|
|
pServerInfo->ReleaseConnection(pSocket);
|
|
//}
|
|
}
|
|
|
|
DEBUG_LEAVE(0);
|
|
}
|
|
|
|
|
|
DWORD
|
|
HTTP_REQUEST_HANDLE_OBJECT::AbortConnection(
|
|
IN BOOL bForce
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Aborts the current connection. Closes the socket and frees up all receive
|
|
buffers. If the connection is keep-alive, we have the option to forcefully
|
|
terminate the connection, or just return the socket to the keep-alive pool
|
|
|
|
Arguments:
|
|
|
|
bForce - if TRUE and keep-alive, forcefully close the keep-alive socket
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - WSA error
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"HTTP_REQUEST_HANDLE_OBJECT::AbortConnection",
|
|
"%B",
|
|
bForce
|
|
));
|
|
|
|
DWORD error;
|
|
|
|
error = CloseConnection(bForce);
|
|
if (error == ERROR_SUCCESS) {
|
|
|
|
//
|
|
// destroy all response variables. This is similar to ReuseObject()
|
|
// except we don't change the object state, or reset the end-of-file
|
|
// state
|
|
//
|
|
|
|
_ResponseHeaders.FreeHeaders();
|
|
FreeResponseBuffer();
|
|
ResetResponseVariables();
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a connection with the requested server via a Proxy
|
|
tunnelling method.
|
|
|
|
Works by creating a nested child HTTP and Connect request object.
|
|
These objects send a "CONNECT" verb to the proxy server asking for
|
|
a connection to made with the destination server. Upon completion the
|
|
child objects are discarded. If a class 200 response is not received from
|
|
proxy server, the proxy response is copied into this object
|
|
and returned to the user.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY
|
|
Ran out of resources
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel",
|
|
NULL
|
|
));
|
|
|
|
DWORD error = DoFsm(New CFsm_OpenProxyTunnel(this));
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CFsm_OpenProxyTunnel::RunSM(
|
|
IN CFsm * Fsm
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Runs next OpenProxyTunnel state
|
|
|
|
Arguments:
|
|
|
|
Fsm - contains state info
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"CFsm_OpenProxyTunnel::RunSM",
|
|
"%#x",
|
|
Fsm
|
|
));
|
|
|
|
DWORD error;
|
|
HTTP_REQUEST_HANDLE_OBJECT * pRequest;
|
|
CFsm_OpenProxyTunnel * stateMachine = (CFsm_OpenProxyTunnel *)Fsm;
|
|
|
|
START_SENDREQ_PERF();
|
|
|
|
pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)Fsm->GetContext();
|
|
switch (Fsm->GetState()) {
|
|
case FSM_STATE_INIT:
|
|
case FSM_STATE_CONTINUE:
|
|
error = pRequest->OpenProxyTunnel_Fsm(stateMachine);
|
|
break;
|
|
|
|
default:
|
|
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
|
Fsm->SetDone(ERROR_WINHTTP_INTERNAL_ERROR);
|
|
|
|
INET_ASSERT(FALSE);
|
|
|
|
break;
|
|
}
|
|
|
|
STOP_SENDREQ_PERF();
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
DWORD
|
|
HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel_Fsm(
|
|
IN CFsm_OpenProxyTunnel * Fsm
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
State machine for OpenProxyTunnel
|
|
|
|
Arguments:
|
|
|
|
Fsm - contains state info
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure -
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"HTTP_REQUEST_HANDLE_OBJECT::OpenProxyTunnel_Fsm",
|
|
"%#x",
|
|
Fsm
|
|
));
|
|
|
|
CFsm_OpenProxyTunnel & fsm = *Fsm;
|
|
DWORD error = fsm.GetError();
|
|
LPINTERNET_THREAD_INFO lpThreadInfo = fsm.GetThreadInfo();
|
|
|
|
// Need to bury error on blocked async item that failed to find
|
|
// established SSL tunnel
|
|
if (error != ERROR_SUCCESS && fsm.GetFunctionState() != FSM_STATE_2) {
|
|
goto quit;
|
|
}
|
|
if (lpThreadInfo == NULL) {
|
|
|
|
INET_ASSERT(FALSE);
|
|
|
|
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// Need to use handle magic for async requests
|
|
// so we'll have the reference before jumping.
|
|
//
|
|
|
|
INTERNET_HANDLE_OBJECT * pInternet;
|
|
pInternet = GetRootHandle (this);
|
|
|
|
if (fsm.GetState() != FSM_STATE_INIT) {
|
|
switch (fsm.GetFunctionState()) {
|
|
case FSM_STATE_1:
|
|
goto send_continue;
|
|
|
|
case FSM_STATE_2:
|
|
goto keep_alive_tunnel;
|
|
|
|
default:
|
|
error = ERROR_WINHTTP_INTERNAL_ERROR;
|
|
|
|
INET_ASSERT(FALSE);
|
|
goto quit;
|
|
}
|
|
}
|
|
|
|
// Do not continue if handle is in NTLM challenge state - we
|
|
// already have a valid socket set up for tunnelling.
|
|
if ((_Socket != NULL) && (GetAuthState() == AUTHSTATE_CHALLENGE))
|
|
{
|
|
error = ERROR_SUCCESS;
|
|
goto quit;
|
|
}
|
|
|
|
|
|
// First, try and fetch an already established tunnel
|
|
// from the keep-alive pool. If so, we can avoid the nested
|
|
// CONNECT request.
|
|
//
|
|
if (_Socket == NULL)
|
|
{
|
|
error = OpenConnection(FALSE, TRUE);
|
|
|
|
if (error == ERROR_IO_PENDING)
|
|
{
|
|
fsm.SetFunctionState(FSM_STATE_2);
|
|
goto quit;
|
|
}
|
|
keep_alive_tunnel:
|
|
if (error == ERROR_SUCCESS && _Socket != NULL)
|
|
{
|
|
// No need to create nested request. We found an active SSL tunnel
|
|
// for this server in the keep-alive pool.
|
|
goto quit;
|
|
}
|
|
else
|
|
{
|
|
// Start over as normal tunnel since bypass to find
|
|
// keep-alive failed.
|
|
error = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// With the Internet Handle Object,
|
|
// construct a new Connect Object, and new HttpRequest Object.
|
|
//
|
|
|
|
//
|
|
// increment the nested request level around InternetConnect(). This is
|
|
// required to stop InternetConnect() believing this is the async part of
|
|
// a two-part (FTP) request (original async hackery)
|
|
//
|
|
|
|
_InternetIncNestingCount();
|
|
fsm.m_hConnect = InternetConnect(pInternet->GetPseudoHandle(),
|
|
GetHostName(),
|
|
GetHostPort(),
|
|
0, // no flags
|
|
NULL
|
|
);
|
|
_InternetDecNestingCount(1);
|
|
if (!fsm.m_hConnect) {
|
|
error = GetLastError();
|
|
|
|
INET_ASSERT(error != ERROR_IO_PENDING);
|
|
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// Now do an Open Request. This will pick up the secure proxy flag.
|
|
//
|
|
|
|
fsm.m_hRequest = HttpOpenRequest(fsm.m_hConnect,
|
|
"CONNECT",
|
|
"/", // we don't need this for a CONNECT
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
if (!fsm.m_hRequest) {
|
|
error = GetLastError();
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// map the handle
|
|
//
|
|
|
|
error = MapHandleToAddress(fsm.m_hRequest,
|
|
(LPVOID *)&fsm.m_hRequestMapped,
|
|
FALSE);
|
|
if ((error != ERROR_SUCCESS) || (fsm.m_hRequestMapped == NULL)) {
|
|
goto quit;
|
|
}
|
|
|
|
fsm.m_pRequest = (HTTP_REQUEST_HANDLE_OBJECT *)fsm.m_hRequestMapped;
|
|
|
|
//
|
|
// we need to set the special secure proxy flag in the request object
|
|
//
|
|
|
|
fsm.m_pRequest->SetTunnel();
|
|
|
|
|
|
LPSTR proxyHostName;
|
|
DWORD proxyHostNameLength;
|
|
INTERNET_PORT proxyHostPort;
|
|
|
|
GetProxyName(&proxyHostName,
|
|
&proxyHostNameLength,
|
|
&proxyHostPort
|
|
);
|
|
fsm.m_pRequest->SetProxyName(proxyHostName,
|
|
proxyHostNameLength,
|
|
proxyHostPort
|
|
);
|
|
|
|
//
|
|
// Transfer any proxy user/pass from the handle.
|
|
//
|
|
LPSTR lpszUser, lpszPass;
|
|
|
|
// Get username + password off of outer handle.
|
|
if (GetUserAndPass(IS_PROXY, &lpszUser, &lpszPass))
|
|
{
|
|
// This will automatically re-validate the username/password
|
|
// on the tunneling handle.
|
|
fsm.m_pRequest->SetProp (WINHTTP_OPTION_PROXY_USERNAME
|
|
& WINHTTP_OPTION_MASK, lpszUser);
|
|
fsm.m_pRequest->SetProp (WINHTTP_OPTION_PROXY_PASSWORD
|
|
& WINHTTP_OPTION_MASK, lpszPass);
|
|
}
|
|
|
|
if (_pProxyCreds)
|
|
{
|
|
fsm.m_pRequest->_pProxyCreds = new WINHTTP_REQUEST_CREDENTIALS(_pProxyCreds->_AuthScheme,
|
|
_pProxyCreds->_pszRealm,
|
|
_pProxyCreds->_pszUserName,
|
|
_pProxyCreds->_pszPassword);
|
|
if (fsm.m_pRequest->_pProxyCreds == NULL)
|
|
{
|
|
// If we couldn't clone it, transfer it. This is not efficient, but it will work.
|
|
fsm.m_pRequest->_pProxyCreds = _pProxyCreds;
|
|
_pProxyCreds = NULL; // ownership transferred
|
|
}
|
|
}
|
|
|
|
//
|
|
// Transfer any authentication context to the tunnelling handle.
|
|
//
|
|
|
|
//fsm.m_pRequest->SetAuthCtx (_pTunnelAuthCtx);
|
|
|
|
|
|
//dprintf("New tunnel request %#x making nested request= %#x\n", this, fsm.m_pRequest);
|
|
|
|
//
|
|
// Do the Nested SendRequest to the Proxy Server.
|
|
// ie send the CONNECT method.
|
|
//
|
|
|
|
fsm.SetFunctionState(FSM_STATE_1);
|
|
if (!HttpSendRequest(fsm.m_hRequest, NULL, 0, NULL, 0)) {
|
|
error = GetLastError();
|
|
if (error == ERROR_IO_PENDING) {
|
|
goto done;
|
|
}
|
|
goto quit;
|
|
}
|
|
|
|
send_continue:
|
|
|
|
//
|
|
// Check Status Code Returned from proxy Server Here.
|
|
// If its not 200 we let the user view it as a Proxy Error
|
|
// and DON'T continue our connection to the SSL/PCT Server.
|
|
//
|
|
|
|
//dprintf("Received Nested Response, Socket=%#x, org request=%#x, nested request=%#x\n", fsm.m_pRequest->_Socket, this, fsm.m_pRequest);
|
|
|
|
_StatusCode = fsm.m_pRequest->GetStatusCode();
|
|
|
|
switch (_StatusCode) {
|
|
|
|
case HTTP_STATUS_OK:
|
|
break;
|
|
|
|
case HTTP_STATUS_PROXY_AUTH_REQ:
|
|
if ((error = CloneResponseBuffer(fsm.m_pRequest)) != ERROR_SUCCESS)
|
|
goto quit;
|
|
break;
|
|
|
|
default:
|
|
if ((error = CloneResponseBuffer(fsm.m_pRequest)) != ERROR_SUCCESS)
|
|
goto quit;
|
|
goto quit;
|
|
}
|
|
|
|
|
|
//
|
|
// Transfer any authentication context back to the outer handle.
|
|
//
|
|
|
|
if ( _pTunnelAuthCtx ) {
|
|
delete _pTunnelAuthCtx;
|
|
}
|
|
|
|
_pTunnelAuthCtx = fsm.m_pRequest->GetAuthCtx();
|
|
// Don't leave the potential for a deleted request reference
|
|
// to be left in the trasferred context.
|
|
if (_pTunnelAuthCtx)
|
|
{
|
|
_pTunnelAuthCtx->_pRequest = NULL;
|
|
}
|
|
|
|
fsm.m_pRequest->SetAuthCtx (NULL);
|
|
|
|
_PreferredScheme = fsm.m_pRequest->_PreferredScheme;
|
|
_SupportedSchemes = fsm.m_pRequest->_SupportedSchemes;
|
|
_AuthTarget = fsm.m_pRequest->_AuthTarget;
|
|
|
|
//
|
|
// pull the socket handle from the socket object used to communicate with
|
|
// the proxy
|
|
//
|
|
|
|
INET_ASSERT(fsm.m_pRequest->_Socket != NULL);
|
|
|
|
/*
|
|
if server returned anything other than 200 then we failed; revert to non-
|
|
secure socket
|
|
*/
|
|
|
|
if (_Socket == NULL)
|
|
{
|
|
// transfer socket reference from nested request
|
|
_Socket = fsm.m_pRequest->_Socket;
|
|
fsm.m_pRequest->_Socket = NULL;
|
|
|
|
// Socket should now be marked as secure
|
|
((ICSecureSocket *)_Socket)->ResetFlags(TRUE);
|
|
}
|
|
if(m_pSecurityInfo == NULL)
|
|
{
|
|
/* SCLE ref */
|
|
m_pSecurityInfo = pInternet->GetSslSessionCache()->Find(GetHostName());
|
|
if (NULL == m_pSecurityInfo)
|
|
{
|
|
/* SCLE ref */
|
|
m_pSecurityInfo = New SECURITY_CACHE_LIST_ENTRY(GetHostName());
|
|
}
|
|
}
|
|
if (_Socket != NULL)
|
|
{
|
|
|
|
INET_ASSERT(_Socket->IsSecure());
|
|
|
|
/* SCLE ref */
|
|
((ICSecureSocket *)_Socket)->SetSecurityEntry(m_pSecurityInfo);
|
|
/* SCLE ref */
|
|
((ICSecureSocket *)_Socket)->SetHostName(GetHostName(), pInternet->GetSslSessionCache());
|
|
((ICSecureSocket *)_Socket)->SetSecureFlags(GetOpenFlags() & SECURITY_INTERNET_MASK);
|
|
if (GetEnableFlags() & WINHTTP_ENABLE_SSL_REVOCATION)
|
|
((ICSecureSocket *)_Socket)->SetSecureFlags(SECURITY_FLAG_CHECK_REVOCATION);
|
|
|
|
// Update values for the established tunnel
|
|
_Socket->SetPort(fsm.m_pRequest->GetHostPort());
|
|
}
|
|
else
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
quit:
|
|
|
|
if (fsm.m_hRequestMapped != NULL) {
|
|
DereferenceObject((LPVOID)fsm.m_hRequestMapped);
|
|
}
|
|
|
|
if (fsm.m_hRequest != NULL) {
|
|
|
|
BOOL bOk;
|
|
bOk = WinHttpCloseHandle(fsm.m_hRequest);
|
|
INET_ASSERT(bOk);
|
|
}
|
|
|
|
if (fsm.m_hConnect != NULL) {
|
|
|
|
BOOL bOk;
|
|
bOk = WinHttpCloseHandle(fsm.m_hConnect);
|
|
INET_ASSERT(bOk);
|
|
}
|
|
|
|
//
|
|
// We Reset the ThreadInfo back to the the previous
|
|
// object handle, and context values.
|
|
//
|
|
|
|
if (lpThreadInfo != NULL) {
|
|
_InternetSetObjectHandle(lpThreadInfo, GetPseudoHandle(), (HINTERNET)this);
|
|
_InternetClearLastError(lpThreadInfo);
|
|
}
|
|
|
|
done:
|
|
|
|
if (error != ERROR_IO_PENDING) {
|
|
fsm.SetDone();
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|
|
|
|
//
|
|
// private methods
|
|
//
|
|
|
|
|
|
PRIVATE
|
|
DWORD
|
|
HTTP_REQUEST_HANDLE_OBJECT::CloneResponseBuffer(
|
|
IN HTTP_REQUEST_HANDLE_OBJECT *pChildRequestObj
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
HTTP_REQUEST_HANDLE_OBJECT CloneResponseBuffer method.
|
|
|
|
Copies a Child Request Object's Response Buffer into "this"
|
|
request object. Also forces header parsing to be rerun on
|
|
the header.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
Success - ERROR_SUCCESS
|
|
|
|
Failure - ERROR_NOT_ENOUGH_MEMORY
|
|
Ran out of resources
|
|
|
|
--*/
|
|
|
|
{
|
|
DEBUG_ENTER((DBG_HTTP,
|
|
Dword,
|
|
"HTTP_REQUEST_HANDLE_OBJECT::CloneResponseBuffer",
|
|
"%#x",
|
|
pChildRequestObj
|
|
));
|
|
|
|
DWORD error;
|
|
LPBYTE lpBuffer;
|
|
|
|
error = ERROR_SUCCESS;
|
|
|
|
lpBuffer = (LPBYTE)ALLOCATE_FIXED_MEMORY(pChildRequestObj->_BytesReceived);
|
|
|
|
if ( lpBuffer == NULL )
|
|
{
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto quit;
|
|
}
|
|
|
|
//
|
|
// pull out headers, and data from Child Request into our request.
|
|
//
|
|
|
|
|
|
CopyMemory(
|
|
lpBuffer,
|
|
pChildRequestObj->_ResponseBuffer,
|
|
pChildRequestObj->_BytesReceived
|
|
);
|
|
|
|
//
|
|
// Recreate and reparse our header structure into our Object,
|
|
// this is kindof inefficent, but it only happens on errors
|
|
//
|
|
|
|
error = CreateResponseHeaders(
|
|
(LPSTR*) &lpBuffer,
|
|
pChildRequestObj->_BytesReceived
|
|
);
|
|
|
|
if (error != ERROR_SUCCESS) {
|
|
goto quit;
|
|
}
|
|
|
|
|
|
SetState(HttpRequestStateObjectData);
|
|
|
|
//
|
|
// record the amount of data immediately available to the app
|
|
//
|
|
|
|
SetAvailableDataLength(BufferedDataLength());
|
|
|
|
quit:
|
|
|
|
if (lpBuffer) {
|
|
FREE_MEMORY (lpBuffer);
|
|
}
|
|
|
|
DEBUG_LEAVE(error);
|
|
|
|
return error;
|
|
}
|