// LiteProxyServerView.cpp : implementation of the CLiteProxyServerView class
//
#include "stdafx.h"
#include "LiteProxyServer.h"
#include "LiteProxyServerDoc.h"
#include "LiteProxyServerView.h"
#include "request.h"
#include "Listener.h"
#include "Iphlpapi.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CPtrList gptrArryTempListeners;
/////////////////////////////////////////////////////////////////////////////
// CLiteProxyServerView
IMPLEMENT_DYNCREATE(CLiteProxyServerView, CListView)
BEGIN_MESSAGE_MAP(CLiteProxyServerView, CListView)
//{{AFX_MSG_MAP(CLiteProxyServerView)
ON_WM_CREATE()
ON_COMMAND(ID_FILE_NEW, OnFileNew)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CLiteProxyServerView construction/destruction
CLiteProxyServerView::CLiteProxyServerView()
{
m_pListener = NULL;
}
CLiteProxyServerView::~CLiteProxyServerView()
{
// shutdown all listeners
delete m_pListener;
for(POSITION pos = gptrArryTempListeners.GetHeadPosition(); pos;)
{
try
{
Listener* pListener = (Listener*)gptrArryTempListeners.GetNext(pos);
delete pListener;
}
catch(...)
{
}
}
}
BOOL CLiteProxyServerView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style |= LVS_REPORT | LVS_SHOWSELALWAYS;
return CListView::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CLiteProxyServerView drawing
void CLiteProxyServerView::OnDraw(CDC* pDC)
{
CLiteProxyServerDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
}
void CLiteProxyServerView::OnInitialUpdate()
{
CListView::OnInitialUpdate();
GetNetworkParams();
}
/////////////////////////////////////////////////////////////////////////////
// CLiteProxyServerView diagnostics
#ifdef _DEBUG
void CLiteProxyServerView::AssertValid() const
{
CListView::AssertValid();
}
void CLiteProxyServerView::Dump(CDumpContext& dc) const
{
CListView::Dump(dc);
}
CLiteProxyServerDoc* CLiteProxyServerView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CLiteProxyServerDoc)));
return (CLiteProxyServerDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CLiteProxyServerView message handlers
CLiteProxyServerView *gpView;
int CLiteProxyServerView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CListView::OnCreate(lpCreateStruct) == -1)
return -1;
gpView = this;
m_pList = &GetListCtrl();
ListView_SetExtendedListViewStyle(m_pList->m_hWnd, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
m_pList->InsertColumn(0, "Serial", LVCFMT_LEFT, 60, -1);
m_pList->InsertColumn(1, "Date", LVCFMT_CENTER, 105, -1);
m_pList->InsertColumn(2, "Name", LVCFMT_LEFT, 100, -1);
m_pList->InsertColumn(3, "Request", LVCFMT_LEFT, 160, -1);
m_pList->InsertColumn(4, "Received", LVCFMT_RIGHT, 60, -1);
m_pList->InsertColumn(5, "Sent", LVCFMT_RIGHT, 55, -1);
m_pList->InsertColumn(6, "Content-Length", LVCFMT_RIGHT, 65, -1);
m_pList->InsertColumn(7, "Status", LVCFMT_LEFT, 110, -1);
m_pList->InsertColumn(8, "Time (ms)", LVCFMT_RIGHT, 60, -1);
CBitmap bitmap;
bitmap.LoadBitmap(IDB_LIST);
m_imageList.Create(16, 15, ILC_COLOR24, 11, 0);
m_imageList.Add(&bitmap, (CBitmap*)NULL);
m_pList->SetImageList(&m_imageList, 1);
return 0;
}
struct TwoWayStruct
{
TwoWayStruct(SOCKET Server, SOCKET Client, bool bNormal, Request* p)
{
SockServer = Server;
SockClient = Client;
bSockNormal = bNormal;
pRequest = p;
}
~TwoWayStruct()
{
if(bSockNormal)
{
shutdown(SockClient, 2);
closesocket(SockClient);
if(SockServer)
{
shutdown(SockServer, 2);
closesocket(SockServer);
}
}
if(--pRequest->nRefCount == 0)
{
gpView->UpdateLog(pRequest->nLogID, 8, Commas(::GetTickCount()-pRequest->nTime));
delete pRequest;
}
}
SOCKET SockServer;
SOCKET SockClient;
bool bSockNormal;
Request* pRequest;
};
#define ERROR_INVALIDIP 0
#define ERROR_BLOCKEDIP 1
#define ERROR_BLOCKEDFUNCTION 2
#define ERROR_BLOCKEDSIZE 3
#define ERROR_BLOCKEDEXTENSION 4
#define ERROR_BLOCKEDURLWORD 5
#define ERROR_BLOCKEDSITE 6
void SendError(SOCKET sock, int nErrorCode, CString strInfo)
{
CString str = "HTTP/1.0 200 OK\r\n\r\n<title>Proxy server alert</title><Body><br>";
switch(nErrorCode)
{
case ERROR_INVALIDIP:
str += "None registered IP ("+strInfo+")";
break;
case ERROR_BLOCKEDIP:
str += "You ("+strInfo+") are blocked.";
break;
case ERROR_BLOCKEDFUNCTION:
str += "The function ("+strInfo+") is not authorized to you.";
break;
case ERROR_BLOCKEDSIZE:
str += "Your request exceeded maximum allowed size ("+strInfo+").";
break;
case ERROR_BLOCKEDEXTENSION:
str += "Request extension ("+strInfo+") is not authorized to you.";
break;
case ERROR_BLOCKEDURLWORD:
str += "Request url includes restricted words ("+strInfo+"), if u want to view it any way,";
break;
case ERROR_BLOCKEDSITE:
str += strInfo+"<br>If you want to view it any way,";
break;
}
str += "<br>Please, Contact the network administrator.</Body>";
send(sock, str, str.GetLength(), 0);
}
CCriticalSection cs;
CString Time2String(CTime &time)
{
CString str = "If-Modified-Since: "+time.FormatGmt("%A").Left(3)+time.FormatGmt(", %d ")+time.FormatGmt("%B").Left(3)+time.Format(" %Y %H:%M:%S")+" GMT";
return str;
}
CString strMonthes = "janfebmaraprmayjunjulaugsepoctnovdec";
CTime String2Time(CString str)
{
if(str.GetLength() != 29)
return CTime(0);
str.MakeLower();
return CTime(atoi(str.Mid(12)), (strMonthes.Find(str.Mid(8,3))+1)/3+1, atoi(str.Mid(5)), atoi(str.Mid(17)), atoi(str.Mid(20)), atoi(str.Mid(23)));
}
int RequestSend(Request* pRequest, SOCKET s, char* buf, int len)
{
return send(s, buf, len, 0);
}
int RequestRecv(Request* pRequest, SOCKET s, char* buf, int len)
{
int nBytes = recv(s, buf, len, 0);
if(nBytes > 0)
buf[nBytes] = 0;
else
buf[0] = 0;
return nBytes;
}
UINT TwoWayThread(void* lpv)
{
TwoWayStruct* pTWS = (TwoWayStruct*)lpv;
try
{
int nLogID = pTWS->pRequest->nLogID;
if(pTWS->bSockNormal)
theApp.m_nInProgress++;
int nBytes = 0, nTotal = 0;
char by[10240] = "";
bool bUpdateLog = true;
while(true)
{
nBytes = 10240;
// retrieve from server
if(pTWS->SockServer)
// normal proxy step
nBytes = recv(pTWS->SockServer, by, 10240, 0);
if(nBytes <= 0)
break;
gpView->UpdateLog(nLogID, pTWS->bSockNormal ? 4 : 5, nTotal += nBytes);
if(pTWS->pRequest->nAppProtocol == Listener::ApplicationProtocol::TYPE_FTP)
{
bool bPasv = false, bPort = false;
if(pTWS->bSockNormal)
{
// some clients don't send "anonymous" for anonymous connections
if(memicmp(by, "USER ", 5) == 0 && nBytes == 7)
memcpy(by+5, "anonymous\r\n", 11), nBytes+= 9;
// check for PASV mode
bPasv = memcmp(by, "227 ", 4) == 0;
}
else
// check for PORT mode
bPort = memicmp(by, "PORT ", 5) == 0;
if(bPasv || bPort)
{
CString str = CString(by, nBytes), strSend;
int n[7] = { str.Find(bPasv?'(':' '), str.Find(',', n[0]), str.Find(',', n[1]+1), str.Find(',', n[2]+1), str.Find(',', n[3]+1), str.Find(',', n[4]+1), str.Find(bPasv?')':'\r', n[5]) };
CString strNewIP = gpView->m_strArrayIP[bPasv?gpView->m_strArrayIP.GetSize()-1:0];
strNewIP.Replace('.', ',');
// PASV: replace the server listener IP with LAN IP
// PORT: replace the client listener IP with WAN IP
strSend.Format("%s%s%s", str.Left(n[0]+1), strNewIP, str.Mid(n[4]));
// copy command to its packet after replacment
memcpy(by, strSend, nBytes = strSend.GetLength());
Listener* pListener = new Listener;
gptrArryTempListeners.AddTail(pListener);
pListener->nPort = atoi(str.Mid(n[4]+1, n[5]-n[4]-1))*256+atoi(str.Mid(n[5]+1, n[6]-n[5]-1));
pListener->nNextServerPort = pListener->nPort;
// adjust the listener next proxy to the command listener
pListener->strNextServerIP = str.Mid(n[0]+1, n[4]-n[0]-1);
pListener->strNextServerIP.Replace(',', '.');
pListener->nAppProtocol = Listener::ApplicationProtocol::TYPE_FTP_PASV+bPort;
pListener->StartListen();
}
}
// normal proxy step
if(pTWS->SockClient && send(pTWS->SockClient, by, nBytes, 0) == SOCKET_ERROR)
{
if(pTWS->bSockNormal)
{
gpView->UpdateLog(nLogID, 7, CString(pTWS->pRequest->pFileCache?(pTWS->pRequest->bRequestServer ? "Online Proxy Cache " : "Offline Proxy Cache "):"")+"Canceled");
bUpdateLog = false;
}
break;
}
}
if(pTWS->bSockNormal)
{
theApp.m_nReceivedBytes += nTotal;
pTWS->pRequest->nReceived += nTotal;
}
else
{
theApp.m_nSentBytes += nTotal;
pTWS->pRequest->nSent += nTotal;
}
if(pTWS->bSockNormal && bUpdateLog == true)
{
if(nTotal == SOCKET_ERROR)
gpView->UpdateLog(nLogID, 7, "Failed");
else
gpView->UpdateLog(nLogID, 7, "Success");
}
}
catch(...)
{
}
if(pTWS->bSockNormal)
theApp.m_nInProgress--;
delete pTWS;
return 0;
}
bool ConnectHost(SOCKET &s, Request* pRequest)
{
if(pRequest->nLogID == -1)
{
CString str;
str.Format("%s %s:%d", sAppProtocolTypes[pRequest->nAppProtocol], pRequest->strHost, pRequest->nPort);
pRequest->nLogID = gpView->Log(pRequest, str);
}
sockaddr_in host;
host.sin_family = AF_INET;
host.sin_port = htons(pRequest->nPort);
if(inet_addr(pRequest->strHost) == INADDR_NONE ) // connect to the appropriate server
{
hostent* hp = gethostbyname(pRequest->strHost); // server address is a DNS name
if(hp == NULL)
{
gpView->UpdateLog(pRequest->nLogID, 7, "Failed to resolve "+pRequest->strHost);
return false;
}
host.sin_addr.s_addr = *((unsigned long *) hp->h_addr);
}
else
host.sin_addr.s_addr = inet_addr(pRequest->strHost); // directly use the IP address
gpView->UpdateLog(pRequest->nLogID, 7, "Connecting "+pRequest->strHost);
if(connect(s, (sockaddr*)&host, sizeof(host)) == SOCKET_ERROR)
{
gpView->UpdateLog(pRequest->nLogID, 7, "Failed to Connect " + pRequest->strHost);
return false;
}
return true;
}
void HostPort(CString &strHost, INTERNET_PORT &nPort)
{
int nIndex;
if((nIndex = strHost.Find(':')) != -1)
{
nPort = atoi(strHost.Mid(nIndex+1));
strHost = strHost.Left(nIndex);
}
}
UINT StartRequest(void* lpv)
{
Request *pRequest = (Request *)lpv;
pRequest->nTime = ::GetTickCount();
SOCKET SocketClient = pRequest->socket;
try
{
SOCKET SocketHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
pRequest->strIP = inet_ntoa(pRequest->address.sin_addr);
bool bFtpPortCmd = pRequest->pListener->nAppProtocol == Listener::ApplicationProtocol::TYPE_FTP_PORT;
int nIndex;
CString str;
if(pRequest->pListener->strNextServerIP.IsEmpty() == false)
{
pRequest->strHost = pRequest->pListener->strNextServerIP;
pRequest->nPort = pRequest->pListener->nNextServerPort;
if(ConnectHost(SocketHost, pRequest) == false)
goto END_REQUEST;
}
else if(pRequest->nAppProtocol == Listener::ApplicationProtocol::TYPE_HTTP)
{
}
else if(pRequest->nAppProtocol == Listener::ApplicationProtocol::TYPE_FTP)
{
send(SocketClient, "220 FTP Virtual Server\r\n", 24, 0);
pRequest->nPort = 21;
CString strPass;
while(true)
{
pRequest->nLength = recv(SocketClient, str.GetBuffer(1024), 1024, 0);
str.ReleaseBuffer(pRequest->nLength);
if(memicmp(str, "USER", 4) == 0)
{
if((nIndex = str.Find("@")) != -1)
{
pRequest->strHost = str.Mid(nIndex+1);
pRequest->strHost.TrimRight();
HostPort(pRequest->strHost, pRequest->nPort);
pRequest->strRequest = str.Left(nIndex)+"\r\n";
break;
}
else
{
pRequest->strRequest = str;
str.TrimRight();
if(str.GetLength() == 4)
pRequest->strRequest.Insert(5, "anonymous");
// if host still not found then ask for login password
send(SocketClient, "331 send password.\r\n", 20, 0);
}
}
else if(memicmp(str, "SITE", 4) == 0 || memicmp(str, "OPEN", 4) == 0)
{
if((nIndex = str.Find(" ")) == -1)
goto END_REQUEST;
pRequest->strHost = str.Mid(nIndex+1);
pRequest->strHost.TrimRight();
HostPort(pRequest->strHost, pRequest->nPort);
break;
}
else if(memicmp(str, "PASS", 4) == 0)
{
strPass = str;
send(SocketClient, "230 Login OK. Proceed.\r\n", 24, 0);
}
else
break;
}
// connect to ftp server
if(ConnectHost(SocketHost, pRequest) == false)
goto END_REQUEST;
if(pRequest->strRequest.IsEmpty() == false)
{
// Receive ready command
RequestRecv(pRequest, SocketHost, str.GetBuffer(1024), 1024);
// send USER command
RequestSend(pRequest, SocketHost, pRequest->strRequest.GetBuffer(0), pRequest->strRequest.GetLength());
if(strPass.IsEmpty() == false)
{
// Receive reply of USER command
nIndex = RequestRecv(pRequest, SocketHost, str.GetBuffer(1024), 1024);
str.ReleaseBuffer(nIndex);
// send password
RequestSend(pRequest, SocketHost, strPass.GetBuffer(0), strPass.GetLength());
}
}
}
else if(pRequest->nAppProtocol == Listener::ApplicationProtocol::TYPE_NNTP)
{
}
else if(pRequest->nAppProtocol == Listener::ApplicationProtocol::TYPE_DNS)
{
}
else if(pRequest->nAppProtocol == Listener::ApplicationProtocol::TYPE_SMTP_SERVER)
{
}
else if(pRequest->nAppProtocol == Listener::ApplicationProtocol::TYPE_SOCKS4)
{
}
else if(pRequest->nAppProtocol == Listener::ApplicationProtocol::TYPE_SOCKS5)
{
}
gpView->UpdateLog(pRequest->nLogID, 7, "In progress...");
// gets ready to relay traffic on both directions
AfxBeginThread(TwoWayThread, new TwoWayStruct(SocketHost, SocketClient, !bFtpPortCmd, pRequest));
AfxBeginThread(TwoWayThread, new TwoWayStruct(SocketClient, SocketHost, bFtpPortCmd, pRequest));
return 0;
}
catch(...)
{
}
END_REQUEST:
shutdown(SocketClient, 2);
closesocket(SocketClient);
gpView->UpdateLog(pRequest->nLogID, 8, Commas(::GetTickCount()-pRequest->nTime));
delete pRequest;
return 1;
}
UINT StartListenThread(void* lpv)
{
CString str;
Listener* pListener = (Listener*)lpv;
int nType;
if(pListener->nTransProtocol == Listener::TransportProtocol::TYPE_TCP)
nType = SOCK_STREAM;
else if(pListener->nTransProtocol == Listener::TransportProtocol::TYPE_UDP)
nType = SOCK_DGRAM;
pListener->socketServer = socket(AF_INET, nType, 0);
if(pListener->socketServer == INVALID_SOCKET)
{
pListener->pThread = NULL;
if(pListener->IsTemp() == false)
AfxMessageBox("Fail to create listener socket");
return 1;
}
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons((u_short)pListener->nPort); // listen this port
if(bind(pListener->socketServer, (sockaddr *)&addr, sizeof(addr)) != 0)
{
pListener->pThread = NULL;
str.Format("Socket bind failed: port %d", pListener->nPort);
if(pListener->IsTemp() == false)
AfxMessageBox(str);
return 1;
}
if(pListener->nTransProtocol == Listener::TransportProtocol::TYPE_TCP)
if(listen(pListener->socketServer, SOMAXCONN) != 0)
{
pListener->pThread = NULL;
str.Format("Fail to start listening: port %d", pListener->nPort);
if(pListener->IsTemp() == false)
AfxMessageBox(str);
return 1;
}
int nLength;
do
{
Request *pRequest = new Request;
pRequest->pListener = pListener;
pRequest->nAppProtocol = pListener->nAppProtocol;
pRequest->nTransProtocol = pListener->nTransProtocol;
nLength = sizeof(pRequest->address);
if(pListener->nTransProtocol == Listener::TransportProtocol::TYPE_TCP)
{
if((pRequest->socket = accept(pListener->socketServer, (sockaddr*)&pRequest->address, &nLength)) != INVALID_SOCKET && pListener->pThread)
AfxBeginThread(StartRequest, (LPVOID)pRequest);
}
else if(pListener->nTransProtocol == Listener::TransportProtocol::TYPE_UDP)
{
}
else
break;
}
while(pListener->pThread && pListener->IsTemp() == false);
pListener->pThread = NULL;
return 0;
}
void Listener::StartListen()
{
if(pThread)
return;
pThread = AfxBeginThread(StartListenThread, this);
}
void Listener::StopListen()
{
if(pThread == NULL)
return;
shutdown(socketServer, 2);
closesocket(socketServer);
if(pThread)
{
::TerminateThread(pThread, 0);
pThread = NULL;
}
}
int CLiteProxyServerView::Log(Request* pRequest, LPCSTR lpcsLog)
{
int nItemCount = m_pList->GetItemCount();
try
{
CString str;
str.Format("%s", Commas(nItemCount+1));
m_pList->InsertItem(nItemCount, str, pRequest->nAppProtocol ? pRequest->nAppProtocol : -1);
m_pList->SetItemText(nItemCount, 1, CTime::GetCurrentTime().Format("%c"));
m_pList->SetItemText(nItemCount, 2, pRequest->strIP);
m_pList->SetItemText(nItemCount, 3, lpcsLog);
if(m_bEnsureVisible)
m_pList->EnsureVisible(nItemCount, FALSE);
}
catch(...)
{
}
return nItemCount;
}
void CLiteProxyServerView::UpdateLog(int nLogID, int nCol, int nLength)
{
if(nLogID < 0 || nLogID >= m_pList->GetItemCount())
return;
m_pList->SetItemText(nLogID, nCol, Commas(nLength));
}
void CLiteProxyServerView::UpdateLog(int nLogID, int nCol, LPCSTR lpcs)
{
if(nLogID < 0 || nLogID >= m_pList->GetItemCount())
return;
m_pList->SetItemText(nLogID, nCol, lpcs);
}
void CLiteProxyServerView::OnFileNew()
{
theApp.m_nInProgress = 0;
m_pList->DeleteAllItems();
}
void CLiteProxyServerView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
{
if(lHint == 0)
{
if(m_pListener != NULL)
delete m_pListener;
m_pListener = new Listener;
m_pListener->nAppProtocol = Listener::ApplicationProtocol::TYPE_FTP;
m_pListener->nTransProtocol = Listener::TransportProtocol::TYPE_TCP;
m_pListener->nPort = 21;
m_pListener->StartListen();
}
else if(lHint == 1)
{
delete m_pListener;
m_pListener = NULL;
}
}
void CLiteProxyServerView::GetNetworkParams()
{
try
{
m_strArrayDNS.RemoveAll();
ULONG pOutBufLen = 0;
::GetNetworkParams(NULL, &pOutBufLen);
FIXED_INFO* pInfo = new FIXED_INFO[pOutBufLen/sizeof(FIXED_INFO)+1];
if(ERROR_SUCCESS == ::GetNetworkParams(pInfo, &pOutBufLen))
{
IP_ADDR_STRING* pNext = &pInfo[0].DnsServerList;
do m_strArrayDNS.Add(pNext->IpAddress.String);
while(pNext = pNext->Next);
}
delete []pInfo;
m_strArrayIP.RemoveAll();
DWORD dwError;
ULONG pdwSize = 0;
GetIpAddrTable(NULL, &pdwSize, TRUE);
PMIB_IPADDRTABLE pIpAddrTable = new MIB_IPADDRTABLE[pdwSize/sizeof(MIB_IPADDRTABLE)+1];
if((dwError = GetIpAddrTable(pIpAddrTable, &pdwSize, TRUE)) == NO_ERROR)
for(int nIndex = 0; nIndex < (int)pIpAddrTable[0].dwNumEntries; nIndex++)
{
CString strIP = inet_ntoa((in_addr&)pIpAddrTable[0].table[nIndex].dwAddr);
if(strIP != "127.0.0.1" && strIP != "0.0.0.0")
m_strArrayIP.Add(strIP);
}
delete []pIpAddrTable;
}
catch(...)
{
AfxMessageBox("Failed in GetNetworkParams()");
}
}