📄 session.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include "session.h"
#include "proxydbg.h"
#include "filter.h"
#include <ws2tcpip.h>
const char gc_szProxyPac[] =
"function FindProxyForURL(url, host)\n"
"{\n"
"if (isInNet(host, \"%s\", \"%s\"))\n"
"{\n"
"return \"DIRECT\";\n"
"}\n"
"if (isPlainHostName(host))\n"
"{\n"
"return \"DIRECT\";\n"
"}\n"
"if (host == \"127.0.0.1\")\n"
"{\n"
"return \"DIRECT\";\n"
"}\n"
"if (url.substring(0, 6) == \"https:\" ||\n"
"url.substring(0, 5) == \"http:\")\n"
"{\n"
"return \"PROXY %s:%d\";\n"
"}\n"
"return \"DIRECT\";\n"
"}\n";
CHttpSession::CHttpSession(void) :
m_fRunning(FALSE),
m_fSSLTunnel(FALSE),
m_fSSLTunnelThruSecondProxy(FALSE),
m_fAuthInProgress(FALSE),
m_fChunked(FALSE),
m_dwSessionId(0),
m_cbResponseRemain(0),
m_cbRequestRemain(0)
{
}
CHttpSession::~CHttpSession(void)
{
}
void CHttpSession::SetId(DWORD dwSessionId)
{
SessionLock();
m_dwSessionId = dwSessionId;
SessionUnlock();
}
DWORD CHttpSession::GetId(void)
{
DWORD dwId;
SessionLock();
dwId = m_dwSessionId;
SessionUnlock();
return dwId;
}
DWORD CHttpSession::Start(SessionSettings* pSettings)
{
DWORD dwRetVal = ERROR_SUCCESS;
ASSERT(pSettings);
SessionLock();
ASSERT(! m_fRunning);
// Set session data
m_sockClient = pSettings->sockClient;
m_saClient = pSettings->saClient;
m_fRunning = TRUE;
memset(&m_auth, 0, sizeof(AUTH_NTLM));
m_auth.m_Conversation = NTLM_NO_INIT_CONTEXT;
// Create a thread to handle the session
g_pThreadPool->ScheduleEvent(SessionThread, this);
SessionUnlock();
return ERROR_SUCCESS;
}
DWORD CHttpSession::Shutdown(void)
{
DWORD dwRetVal = ERROR_SUCCESS;
SessionLock();
ASSERT(m_fRunning);
// Change thread status to signal shutdown
m_fRunning = FALSE;
IFDBG(DebugOut(ZONE_SESSION, _T("WebProxy: Closing client socket in session %d, sock=%d\n"), m_dwSessionId, m_sockClient));
IFDBG(DebugOut(ZONE_SESSION, _T("WebProxy: If valid, we will also close server socket in session %d, sock=%d\n"), m_dwSessionId, m_sockServer));
// Delete socket data (unblock any blocking socket calls)
if (SOCKET_ERROR == closesocket(m_sockClient)) {
IFDBG(DebugOut(ZONE_ERROR, _T("WebProxy: Error closing client socket in session %d, error: %d\n"), m_dwSessionId, WSAGetLastError()));
}
if (m_sockServer.valid() && (SOCKET_ERROR == closesocket(m_sockServer))) {
IFDBG(DebugOut(ZONE_ERROR, _T("WebProxy: Error closing server socket in session %d, error: %d\n"), m_dwSessionId, WSAGetLastError()));
}
SessionUnlock();
return dwRetVal;
}
DWORD WINAPI CHttpSession::SessionThread(LPVOID pv)
{
CHttpSession* pInst = (CHttpSession*) pv;
pInst->Run();
return 0;
}
void CHttpSession::Run(void)
{
DWORD dwErr;
FD_SET sockSet;
CBuffer buffer;
BOOL fCloseSessionReq = FALSE;
BOOL fCloseSessionResp = FALSE;
IFDBG(DebugOut(ZONE_SESSION, _T("WebProxy: Session %d has been started.\n"), m_dwSessionId));
timeval timeout = {0};
timeout.tv_sec = g_pSettings->iSessionTimeout;
SessionLock();
while (1) {
//
// Loop for the lifetime of the session
//
BOOL fServerConnected = FALSE;
FD_ZERO(&sockSet);
FD_SET(m_sockClient, &sockSet);
if (m_sockServer.valid()) {
fServerConnected = TRUE;
FD_SET(m_sockServer, &sockSet);
}
SessionUnlock();
int iSockets = select(0,&sockSet,NULL,NULL,&timeout);
SessionLock();
// Check if session was closed while blocking on select
if (! m_fRunning) {
IFDBG(DebugOut(ZONE_SESSION, _T("WebProxy: Session %d has been signalled to shutdown.\n"), m_dwSessionId));
break;
}
if (SOCKET_ERROR == iSockets) {
IFDBG(DebugOut(ZONE_ERROR, _T("WebProxy: Select call failed in session %d, error: %d.\n"), m_dwSessionId, WSAGetLastError()));
break;
}
if (0 == iSockets) {
IFDBG(DebugOut(ZONE_SESSION, _T("WebProxy: Session %d has expired\n"), m_dwSessionId));
break;
}
//
// Make sure the connections are not being closed
//
if (FD_ISSET(m_sockClient, &sockSet)) {
DWORD dwAvailable = 0;
if (SOCKET_ERROR == ioctlsocket(m_sockClient, FIONREAD, &dwAvailable)) {
IFDBG(DebugOut(ZONE_WARN, _T("WebProxy: Failed to query how much data is available on socket %d\n"), WSAGetLastError()));
dwAvailable = 0;
}
// If no data is available on client socket then close the session
if (0 == dwAvailable) {
IFDBG(DebugOut(ZONE_SESSION, _T("WebProxy: The client socket has been signalled to close. Closing session %d.\n"), m_dwSessionId));
break;
}
}
if (fServerConnected && FD_ISSET(m_sockServer, &sockSet)) {
DWORD dwAvailable = 0;
if (SOCKET_ERROR == ioctlsocket(m_sockServer, FIONREAD, &dwAvailable)) {
IFDBG(DebugOut(ZONE_WARN, _T("WebProxy: Failed to query how much data is available on socket %d\n"), WSAGetLastError()));
dwAvailable = 0;
}
// If no data is available on server socket close the session unless auth is in progress in which case we should
// just close the server socket.
if (0 == dwAvailable) {
if (m_fAuthInProgress) {
m_sockServer.close();
m_strServerName = "";
continue;
}
break;
}
}
//
// Check for data on the sockets
//
if (FD_ISSET(m_sockClient, &sockSet)) {
//
// Packet arrived from client
//
dwErr = ProcessMessage(buffer, &fCloseSessionReq, TRUE);
if (ERROR_SUCCESS != dwErr) {
IFDBG(DebugOut(ZONE_WARN, _T("WebProxy: Due to an internal error when processing a request, session %d is being aborted: %d.\n"), m_dwSessionId, dwErr));
break;
}
if (fCloseSessionReq && (0 == m_cbRequestRemain)) {
IFDBG(DebugOut(ZONE_SESSION, _T("WebProxy: Proxy was signalled to close session by a request, session %d is being closed.\n"), m_dwSessionId));
shutdown(m_sockClient, SD_SEND);
continue;
}
}
if (fServerConnected && FD_ISSET(m_sockServer, &sockSet)) {
//
// Packet arrived from server
//
dwErr = ProcessMessage(buffer, &fCloseSessionResp, FALSE);
if (ERROR_SUCCESS != dwErr) {
IFDBG(DebugOut(ZONE_WARN, _T("WebProxy: Proxy was signalled to close session by a response or an internal error occured, session %d is being closed: %d.\n"), m_dwSessionId, dwErr));
break;
}
if (fCloseSessionResp && (0 == m_cbResponseRemain)) {
IFDBG(DebugOut(ZONE_SESSION, _T("WebProxy: Proxy was signalled to close session by a response, session %d is being closed.\n"), m_dwSessionId));
shutdown(m_sockServer, SD_SEND);
continue;
}
}
}
// If we aborted not due to stopping service then we have to clean up the session
if (m_fRunning) {
Shutdown();
}
SessionUnlock();
dwErr = g_pSessionMgr->RemoveSession(m_dwSessionId);
#ifdef DEBUG
if (ERROR_SUCCESS != dwErr) {
IFDBG(DebugOut(ZONE_WARN, _T("WebProxy: Could not delete session %d, error: %d - potential memory leak\n"), m_dwSessionId, dwErr));
}
#endif // DEBUG
}
DWORD CHttpSession::ProcessMessage(CBuffer& buffer, BOOL* pfCloseConnection, BOOL fRequest)
{
DWORD dwRetVal = ERROR_SUCCESS;
int cchBuffer = 0;
int cchSent = 0;
int cchHeaders = 0; // size of the recved headers
IFDBG(DebugOut(ZONE_RESPONSE, _T("WebProxy: %s(s) arrived from server in session %d.\n"), (fRequest?L"Request":L"Response"), m_dwSessionId));
// This method is generic for both requests and responses. Need to set the following
// variables based on fRequest parameter.
int* pcbRemain = fRequest ? &m_cbRequestRemain : &m_cbResponseRemain;
SOCKET sock = fRequest ? m_sockClient : m_sockServer;
//
// Receive data until at least all the headers have been read.
//
dwRetVal = ProxyRecv(sock, buffer, &cchBuffer, *pcbRemain, &cchHeaders);
if (ERROR_SUCCESS != dwRetVal) {
goto exit;
}
IFDBG(DebugOut(ZONE_RESPONSE, _T("WebProxy: Received buffer. Total: %d, Headers: %d, Remaining: %d. Session %d.\n"), cchBuffer, cchHeaders, m_cbResponseRemain, m_dwSessionId));
//
// We may have read more than one request since multiple requests can be pipelined.
//
while(1) {
// The proxy will only handle one request/response. If more than one message is in the buffer, this
// is determined by the fact that we sent out less data than we received.
if (fRequest) {
dwRetVal = HandleClientMessage(buffer, &cchBuffer, cchHeaders, &cchSent, pfCloseConnection);
}
else {
dwRetVal = HandleServerMessage(buffer, &cchBuffer, cchHeaders, &cchSent, pfCloseConnection);
}
if (ERROR_SUCCESS != dwRetVal) {
goto exit;
}
if (m_fChunked) {
PBYTE pBuffer = buffer.GetBuffer(0, FALSE);
ASSERT(pBuffer);
IFDBG(DebugOut(ZONE_SESSION, _T("WebProxy: Detected chunked transfer in session %d.\n"), m_dwSessionId));
// Copy remaining data to beginning of buffer, reset sizes, and keep going
if (cchBuffer > cchSent) {
memmove(pBuffer, pBuffer + cchSent, cchBuffer - cchSent);
}
cchBuffer -= cchSent;
cchSent = 0;
pBuffer[cchBuffer] = '\0';
// Receive all chunks and forward it to the client
dwRetVal = HandleChunked(buffer, &cchBuffer, &cchSent, fRequest);
if (ERROR_SUCCESS != dwRetVal) {
goto exit;
}
}
if (*pfCloseConnection) {
break;
}
if ((cchSent > 0) && (cchSent < cchBuffer)) {
PBYTE pBuffer = buffer.GetBuffer(0, FALSE);
ASSERT(pBuffer);
IFDBG(DebugOut(ZONE_RESPONSE, _T("WebProxy: Detected pipelined %s.\n"), (fRequest?L"request":L"response")));
// Copy remaining data to beginning of buffer, reset sizes, and keep going
memmove(pBuffer, pBuffer + cchSent, cchBuffer - cchSent);
cchBuffer -= cchSent;
cchSent = 0;
pBuffer[cchBuffer] = '\0';
cchHeaders = GetHeadersLength(pBuffer);
if (0 == cchHeaders) {
dwRetVal = ProxyRecv(sock, buffer, &cchBuffer, *pcbRemain, &cchHeaders);
if (ERROR_SUCCESS != dwRetVal) {
goto exit;
}
}
}
else {
break;
}
}
exit:
return dwRetVal;
}
DWORD CHttpSession::HandleClientMessage(CBuffer& buffer, int* pcchBuffer, int cchHeaders, int* pcchSent, BOOL* pfCloseConnection)
{
DWORD dwRetVal = ERROR_SUCCESS;
CHttpHeaders headers;
*pcchSent = 0;
PBYTE pBuffer = buffer.GetBuffer(0, FALSE);
ASSERT(pBuffer);
if (m_fSSLTunnel) {
//
// If the current connection is just tunnelling SSL data then we do not parse the
// data, simply send it on to the server.
//
*pcchSent = *pcchBuffer;
}
else if (0 != m_cbRequestRemain) {
//
// If we have already parsed the headers and just need to read remaining content
// continuing over on a new packet, then just keep reading.
//
if (m_cbRequestRemain >= 0) {
m_cbRequestRemain -= *pcchBuffer;
// IE and Netscape both append a CRLF to POST data but do not count it as part of the
// content length. If we have read 2 bytes too many then check if these last two bytes
// are CRLF on a POST request and reset the remaining bytes to zero if so.
if ((m_strCurrMethod == gc_MethodPost) && (m_cbRequestRemain == -2)) {
if ((*pcchBuffer >= 2) && (pBuffer[*pcchBuffer - 2] == '\r') && (pBuffer[*pcchBuffer - 1] == '\n')) {
m_cbRequestRemain = 0;
}
}
}
else {
*pfCloseConnection = TRUE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -