www.pudn.com > warsrc.rar > IPchkExt.cpp


// IPchkExt.cpp : Defines the initialization routines for the DLL. 
// 
 
#include "stdafx.h" 
#include "WarDaemon.h" 
#include "IPchkExt.h" 
#include  
#include "resource.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
static AFX_EXTENSION_MODULE IPchkExtDLL = { NULL, NULL }; 
 
int CallOnConnect(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); 
int CallOnVerifyIPAddress(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); 
int CallOnBadPassword(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); 
int CallOnHasLoggedOn(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); 
int CallOnVerifyLogin(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); 
int CallOnSocketIsDestroyed(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam); 
 
//////////////////////////////////////// 
// ADDED: This is required for plugin's 
static CIPchkExt *pMe; 
 
extern "C" int APIENTRY 
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) 
{ 
	if (dwReason == DLL_PROCESS_ATTACH) 
	{ 
		TRACE0("IPCHKEXT.DLL Initializing!\n"); 
		 
		// Extension DLL one-time initialization 
		AfxInitExtensionModule(IPchkExtDLL, hInstance); 
 
		// Insert this DLL into the resource chain 
		new CDynLinkLibrary(IPchkExtDLL); 
 
		pMe = new CIPchkExt; 
		if (pMe->Register("IPchkExt") != 0) 
			return 0; // Failure 
	} 
	else if (dwReason == DLL_PROCESS_DETACH) 
	{ 
		TRACE0("IPCHKEXT.DLL Terminating!\n"); 
 
		//////////////////////////////////////// 
		// ADDED: This is required for plugin's 
		if (pMe) 
			delete pMe; 
	} 
	return 1;   // ok 
} 
 
 
// Initialize the extended COptions variables 
void CIPchkExt::InitializeCOptions() 
{ 
	DeclOpt("Connection delay", m_ConnectionDelay, 1, 1, DATATYPE_INT); 
	DeclOpt("Passwd retries", m_MaxPasswdRetries, 3, 2, DATATYPE_INT); 
	DeclOpt("Passwd retry delay", m_PasswdRetryDelay, 3, 3, DATATYPE_INT); 
	DeclOpt("Hack pswd attmpt", m_MaxPasswdHacks, 9, 4, DATATYPE_INT); 
	DeclOpt("Hack delay", m_HackDely, 6, 5, DATATYPE_INT); 
} 
 
 
CIPchkExt::CIPchkExt() 
{ 
} 
 
CIPchkExt::~CIPchkExt() 
{ 
	CIPConnList::KillAll(this); 
} 
 
int CIPchkSock::OnConnect(int Event, WPARAM wParam, LPARAM lParam) 
{ 
	CIPConnList *pConn = NULL; 
	CString cBuf("- No message -"), cFmt; 
 
	m_SessionBadPwdCnt = 0; 
 
	if (Event) 
		return 0; // We don't want to handle socket errors... 
 
	if (!CIPConnList::VerifyConnection(pSock, &pConn)) 
	{ 
		if (pSock->IsKindOf(RUNTIME_CLASS(CTextSock))) 
		{ 
			CTextSock *pTextSock = (CTextSock *)pSock; 
 
			if (pConn->m_IsHacker) 
			{ 
				cBuf.LoadString(IDS_FUCKHACKERS); 
			} 
			else 
			{ 
				cFmt.LoadString(IDS_TIMEOUTACTIVE); 
				DWORD TimeLeft = pConn->m_Suspend.TimeLeft(pConn->m_SuspendVal); 
				TimeLeft /= (1000 * 60); 
				if (!TimeLeft) 
					TimeLeft = 1; 
				cBuf.Format(cFmt, TimeLeft ); 
			} 
		 
			// We need to send the message directly to bypass the framework's 
			// processing. (The connection is not yet ready for normal control messages). 
			cFmt.Format("421 %s\r\n", cBuf); 
			cBuf = cFmt; 
			pTextSock->CAsyncSocket::Send(cBuf, cBuf.GetLength()); 
		} 
		 
		pMe->LogMsg(LOGF_SECURITY, "CIPchkSock::OnConnect() Access denied. Told client: %s", cBuf); 
		CSocketException::Throw(pSock, "CIPchkSock::OnConnect()", -1, cBuf); 
	} 
 
	return 0; 
} 
 
int CIPchkSock::OnBadPassword(int Event, WPARAM wParam, LPARAM lParam) 
{ 
	CIPConnList *pConn = NULL; 
	CString Name; 
 
	if (pConn = CIPConnList::Find(pSock, Name)) 
	{ 
		++m_SessionBadPwdCnt; 
		++pConn->m_BadPwdCnt; 
 
		if (pConn->m_BadPwdCnt >= pMe->m_MaxPasswdHacks) 
		{ 
			// Hacker! 
			pConn->m_IsHacker = TRUE; 
			pConn->m_SuspendVal = pMe->m_HackDely * 1000 * 60 * 60; 
			pConn->m_ExpiryVal = max(pConn->m_ExpiryVal, pConn->m_SuspendVal); 
			pConn->m_BadPwdCnt = 0; // Reset for the next user on this IP, - in a couple of hours... 
 
			// Say gently goodbye... 
			ASSERT(pSock->IsKindOf(RUNTIME_CLASS(CTextSock))); 
			CTextSock *pTextSock = (CTextSock *)pSock; 
			pTextSock->LogMsg(LOGF_SECURITY,"CIPchkSock::OnBadPassword() - HACKER detected. Turning on full protection."); 
			pTextSock->SendMsg(530,"Hacking is a very bad habbit... Goodbye."); 
 
			CSocketException::Throw(pSock, "CIPchkSock::OnBadPassword()", -1, "Hacker detected."); 
		} 
 
		if (m_SessionBadPwdCnt >= pMe->m_MaxPasswdRetries) 
		{ 
			pConn->m_SuspendVal = pMe->m_PasswdRetryDelay * 1000 * 60; 
			pConn->m_ExpiryVal = max(pConn->m_ExpiryVal, pConn->m_SuspendVal); 
 
			// Say gently goodbye... 
			ASSERT(pSock->IsKindOf(RUNTIME_CLASS(CTextSock))); 
			CTextSock *pTextSock = (CTextSock *)pSock; 
			pTextSock->LogMsg(LOGF_SECURITY,"CIPchkSock::OnBadPassword() - Too many bad passwords. Disconnecting user."); 
			pTextSock->SendMsg(530,"You better check your login name and password. Goodbye."); 
 
			CSocketException::Throw(pSock, "CIPchkSock::OnBadPassword()", -1, "Too many login attempts"); 
		} 
	} 
 
	return 0; 
} 
 
int CIPchkSock::OnVerifyLogin(int Event, WPARAM wParam, LPARAM lParam) 
{ 
	USER *pUser = (USER *)wParam; 
	LOGINPRMS *pLP = (LOGINPRMS *)lParam; 
 
	CIPConnList *pConn = NULL; 
	CString Name; 
 
	if (CUsr::IsAdmin(*pUser)) 
		return 0; // Don't mess with admins... 
 
	if (pConn = CIPConnList::Find(pSock, Name)) 
	{ 
		// Check IP/Domain shitlist for the user 
		if (pConn->IsBanned(*pUser, pSock)) 
		{ 
			pLP->Sock->LogMsg(LOGF_SECURITY,"CIPchkSock::OnVerifyLogin() - User %s from %s is shitlisted on IP/domain level. Access is denied.", pLP->UserID, pLP->Sock->m_DNSName); 
			pLP->Sock->SendMsg(530,"Your IP address or domain name is shitlisted. Goodbye."); 
			CSocketException::Throw(pSock, "CIPchkSock::OnVerifyLogin()", -1, "Shitlisted"); 
		} 
 
		// Check max logins on the IP for the user account 
		int MaxConnections = CUsr::GetRecursiveParam(*pUser, "IP MaxConn", 0); 
		if (MaxConnections > 0) 
		{ 
			int Count = 0; 
			for(CLinkedListItem *Item = CSock::m_SocketList.First() 
				; Item 
				; Item = CSock::m_SocketList.Next(Item)) 
			{ 
				CSock *pSck = (CSock *)CSock::m_SocketList.Ptr(Item); 
				if (!pSck->IsKindOf(RUNTIME_CLASS(CTextSock))) 
					continue; 
 
				if (pSck->m_PeerName == pConn->m_Name) 
					++Count; 
			} 
 
			if (Count > MaxConnections) 
			{ 
				pLP->Sock->LogMsg(LOGF_SECURITY,"CIPchkSock::OnVerifyLogin() - To many connections from IP %s. Access is denied.", pLP->UserID, pConn->m_Name); 
				pLP->Sock->SendMsg(530,"Your have exeeded your limit of %d concurrent sessions. Goodbye.", MaxConnections); 
				CSocketException::Throw(pSock, "CIPchkSock::OnVerifyLogin()", -1, "To many connections from IP"); 
			} 
		} 
	} 
 
	return 0; 
} 
 
int CIPchkSock::OnHasLoggedOn(int Event, WPARAM wParam, LPARAM lParam) 
{ 
	CIPConnList *pConn = NULL; 
	CString Name; 
 
	if (pConn = CIPConnList::Find(pSock, Name)) 
	{ 
		// Reset counters and flags. 
		pConn->m_BadPwdCnt = 0; 
		pConn->m_SuspendVal = ((DWORD)pMe->m_ConnectionDelay * 1000 * 60); 
		pConn->m_IsHacker = FALSE; 
	} 
 
	return 0; 
} 
 
 
int CIPchkSock::OnVerifyIPAddress(int Event, WPARAM wParam, LPARAM lParam) 
{ 
	return 0; 
} 
 
int CallOnConnect(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) 
{ 
	return ((CIPchkSock *)Origin)->OnConnect(Event, wParam, lParam); 
} 
 
int CallOnVerifyIPAddress(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) 
{ 
	return ((CIPchkSock *)Origin)->OnVerifyIPAddress(Event, wParam, lParam); 
} 
 
int CallOnBadPassword(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) 
{ 
	return ((CIPchkSock *)Origin)->OnBadPassword(Event, wParam, lParam); 
} 
 
int CallOnHasLoggedOn(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) 
{ 
	return ((CIPchkSock *)Origin)->OnHasLoggedOn(Event, wParam, lParam); 
} 
 
int CallOnVerifyLogin(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) 
{ 
	return ((CIPchkSock *)Origin)->OnVerifyLogin(Event, wParam, lParam); 
} 
 
 
// Reqired function if sockets extentions are used 
// Creates a new socket derived  
int CallOnSocketIsDestroyed(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) 
{ 
	delete (CIPchkSock *)Origin; // CSocketAPI destructor will delete references 
	return 0; 
} 
 
// Required function 
int CallApiInitInstance(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) 
{ 
	return ((CIPchkExt *)Origin)->ApiInitInstance(Event, wParam, lParam); 
} 
 
// Required function 
int CallApiExitInstance(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) 
{ 
	return ((CIPchkExt *)Origin)->ApiExitInstance(Event, wParam, lParam); 
} 
 
 
void CIPchkExt::LogMsg(int flag, LPCSTR Format, ...) 
{ 
	ASSERT(AfxIsValidAddress(this,sizeof(CIPchkExt))); 
	ASSERT(m_pLog != NULL); 
	ASSERT(AfxIsValidAddress(m_pLog, sizeof(CLog))); 
 
 
	if (!ShouldLog(m_pLog, flag)) 
		return; 
 
	{ 
		CString cBuf; 
		ASSERT(AfxIsValidString(Format, FALSE)); 
 
		cBuf.Format("(CIPchkExt) %s", Format); 
 
		va_list argList; 
		va_start(argList, Format); 
		m_pLog->LogMsgV(flag, cBuf, argList); 
		va_end(argList); 
	} 
} 
 
// We check _all_ sockets. This to prevent server to server transfers to 
// banned sites. 
int CallOnNewSocket(LPVOID Origin, int Event, WPARAM wParam, LPARAM lParam) 
{ 
	CSock *pSock = (CSock *)lParam; 
 
	// Create a new CNTFTPConn object and link it to the calls we will use. 
	CDllInfo *pDLL = pMe->GetDLLInfo(); 
	ASSERT(pDLL != NULL); 
	if (pDLL == NULL) 
		return 0; 
 
	CIPchkSock *pConn = new CIPchkSock; 
	pConn->pSock = pSock; // Required 
	 
	pSock->m_Funcs[CSock::iOnSocketIsDestroyed].AddLast(pDLL, CSock::iOnSocketIsDestroyed, pConn, CallOnSocketIsDestroyed); 
	pSock->m_Funcs[CSock::iOnConnect].AddLast(pDLL, CSock::iOnConnect, pConn, CallOnConnect); 
	pSock->m_Funcs[CSock::iOnVerifyIPAddress].AddLast(pDLL, CSock::iOnVerifyIPAddress, pConn, CallOnVerifyIPAddress); 
	pSock->m_Funcs[CSock::iOnBadPassword].AddLast(pDLL, CSock::iOnBadPassword, pConn, CallOnBadPassword); 
	pSock->m_Funcs[CSock::iOnHasLoggedOn].AddLast(pDLL, CSock::iOnHasLoggedOn, pConn, CallOnHasLoggedOn); 
	pSock->m_Funcs[CSock::iOnVerifyLogin].AddLast(pDLL, CSock::iOnVerifyLogin, pConn, CallOnVerifyLogin); 
 
 
	return 0; 
} 
 
 
////////////////////////////////////////////////////////////////////////////////////////// 
// CIPConnList 
 
CIPConnList::CIPConnList(LPCSTR Name) 
{ 
	m_Name = Name; 
	m_BadPwdCnt = 0; 
	m_ExpiryVal = 1000 * 60 * 60; // Remember a connection for 1 hours 
	m_SuspendVal = 0; 
	m_IsHacker = FALSE; 
} 
 
CIPConnList::~CIPConnList() 
{ 
} 
 
void CIPConnList::KillAll(CIPchkExt *pExt) 
{ 
	 
	CIPConnList *pConn; 
 
	while(pConn = (CIPConnList *)pExt->m_History.GetAndDeleteFirst()) 
		delete pConn; 
} 
 
// Verify if a connection can be accepted. 
// Called from OnConnect() 
BOOL CIPConnList::VerifyConnection(CSock *pSock, CIPConnList **ppConn) 
{ 
	CIPConnList *pConn; 
	CString Name(""); 
	BOOL Rval = TRUE; 
 
	if (pConn = Find(pSock, Name)) 
	{ 
		if (pConn->m_IsHacker || pSock->IsKindOf(RUNTIME_CLASS(CTextSock))) 
		{ 
			if (!pConn->m_Suspend.TimeOut(pConn->m_SuspendVal)) 
			{ 
				Rval = FALSE; 
			} 
			else 
				pConn->m_IsHacker = FALSE; // Only hacker until timeout... 
			 
			if (pSock->IsKindOf(RUNTIME_CLASS(CTextSock))) 
			{ 
				// We always reset the timeout periods on CTextSock connection  
				// or attempted connections... 
				pConn->m_Suspend.Reset(); 
				pConn->m_Expiry.Reset(); 
			} 
		} 
	} 
	else 
	{ 
		// Just add the socket 
		if (Name.IsEmpty()) 
			return FALSE; // Find() is supposed to initialize 'Name' 
 
		pConn = new CIPConnList(Name); 
		pMe->m_History.AddFirst((LPVOID)pConn); 
	} 
 
	ASSERT(pConn != NULL); 
 
	// Set the suspend length. 
	if (!pConn->m_IsHacker && !pConn->m_BadPwdCnt) 
	{ 
		pConn->m_SuspendVal = ((DWORD)pMe->m_ConnectionDelay * 1000 * 60); 
	} 
 
	// Make sure that the expery val is larger or equal to than the suspend val 
	pConn->m_ExpiryVal = max(pConn->m_ExpiryVal, pConn->m_SuspendVal); 
 
	if (ppConn) 
		*ppConn = pConn; 
 
	return Rval; 
} 
 
CIPConnList *CIPConnList::Find(CSock *pSock, CString& Name) 
{ 
	UINT Dummy; 
 
	if (!pMe) 
		return FALSE; // Dll not loaded. Something bad is going on... 
 
	if (!pSock->GetPeerName(Name, Dummy)) 
	{ 
		pMe->LogMsg(LOGF_WARNINGS,"CIPConnList::Find() - Can't resolve IP name. Connection refused."); 
		return FALSE; 
	} 
	return Find(Name); 
} 
 
CIPConnList *CIPConnList::Find(LPCSTR Name) 
{ 
	if (!pMe) 
		return NULL; // Dll not loaded. Something bad is going on... 
 
	for(CLinkedListItem *Item = pMe->m_History.First(); Item;) 
	{ 
		CLinkedListItem *Curr = Item; 
		Item = pMe->m_History.Next(Item); 
		CIPConnList *pConn = (CIPConnList *)pMe->m_History.Ptr(Curr); 
 
		if (pConn->m_Expiry.TimeOut(pConn->m_ExpiryVal)) 
		{ 
			pMe->m_History.DeleteItem(Curr);			 
			delete pConn; 
			continue; 
		} 
 
		if (pConn->m_Name == Name) 
			return pConn; 
	} 
 
	return NULL; 
} 
 
 
// Check if a connection is banned. 
// Verification is done on both IP name and DNS name. 
// Exceptions override denials. 
// Scan order: Server, class, group, user 
BOOL CIPConnList::IsBanned(USER User, CSock *pSock) 
{ 
	USER ParseOrder[5]; 
	memset(&ParseOrder, 0, sizeof(ParseOrder)); 
	int Index; 
	BOOL Denied = FALSE; 
 
	if (User == INVALID_USER_VALUE) 
	{ 
		// Use the system list only 
		ParseOrder[0] = CUsr::FindUser(UT_SYSTEM, "*"); 
 
		// Don't do anything if the shitlist is disabled 
		if (CUsr::GetRecursiveParam(ParseOrder[0], "Disable IP Shitlist", FALSE) == TRUE) 
			return FALSE; 
	} 
	else 
	{ 
		// Don't do anything if the shitlist is disabled 
		if (CUsr::GetRecursiveParam(User, "Disable IP Shitlist", FALSE) == TRUE) 
			return FALSE; 
 
		// Full scan 
		Index = 0; 
		USER Test; 
		if ((Test = CUsr::GetUserSystem(User)) != INVALID_USER_VALUE) 
			ParseOrder[Index++] = Test; 
		if ((Test = CUsr::GetUserClass(User)) != INVALID_USER_VALUE) 
			ParseOrder[Index++] = Test; 
		if ((Test = CUsr::GetUserGroup(User)) != INVALID_USER_VALUE) 
			ParseOrder[Index++] = Test; 
 
		ParseOrder[Index] = User; 
	} 
 
	// Check shitlist 
	for(Index = 0; ParseOrder[Index]; Index++) 
	{ 
		CString AccessList; 
		CUsr::GetParam(ParseOrder[Index], "IP Shitlist", "", AccessList); 
		if (!AccessList.IsEmpty()) 
		{ 
			TranslateList(AccessList); 
			Denied = IPGenericIsIpInList(m_Name, AccessList); 
			if (!Denied && (m_Name != pSock->m_DNSName)) 
				Denied = IPGenericIsIpInList(pSock->m_DNSName, AccessList); 
			if (Denied) 
				break; 
		} 
	} 
 
	if (Denied) 
	{ 
		// Check viplist 
		for(Index = 0; ParseOrder[Index]; Index++) 
		{ 
			CString AccessList; 
			CUsr::GetParam(ParseOrder[Index], "IP Viplist", "", AccessList); 
			if (!AccessList.IsEmpty()) 
			{ 
				TranslateList(AccessList); 
				Denied = (IPGenericIsIpInList(m_Name, AccessList) == FALSE); 
				if (Denied && (m_Name != pSock->m_DNSName)) 
					Denied = (IPGenericIsIpInList(pSock->m_DNSName, AccessList) == FALSE); 
				if (!Denied) 
					break; 
			} 
		} 
	} 
 
	return Denied; 
} 
 
///////////////////////////////////////////////////////////////////////////////////// 
// IP mask based pattern matching 
 
// Parse the rule and return the mask 
BOOL IPVerifyRule(LPCSTR Rule,int **IPmask) 
{ 
	int Index = 0; 
	int FromTo = 0; 
	int MyMask[4][2]; 
 
	if (!Rule || !*Rule) 
		return FALSE; 
 
	// 1.2.3.4 
	// *.2.3.4 
	// 1-4.200-203.*.4 
 
	// Initialize 
	memset(MyMask,0,sizeof(MyMask)); 
 
	while(*Rule) 
	{ 
		// Determine token type 
		if (*Rule == '*') 
		{ 
			if (MyMask[Index][0]) 
				return FALSE; 
			MyMask[Index][0] = 0; 
			MyMask[Index][1] = 255; 
			FromTo = 1; 
			++Rule; 
			if (*Rule && (*Rule != '.')) 
				return FALSE; 
		} 
		else if (*Rule == ' ') 
		{ 
			++Rule; // Ignore space 
		} 
		else if (*Rule == '-') 
		{ 
			if (FromTo) 
				return FALSE; 
			++FromTo; 
			if (!isdigit(*++Rule)) 
				return FALSE; 
		} 
		else if (*Rule == '.') 
		{ 
			if (!FromTo) 
				MyMask[Index][1] = MyMask[Index][0]; 
			FromTo = 0; 
			if (!isdigit(*++Rule) && (*Rule != '*')) 
				return FALSE; 
			if ((MyMask[Index][0] > 255) || (MyMask[Index][1] > 255)) 
				return FALSE; 
			if (++Index == 4) 
				return FALSE; 
		} 
		else 
		{ 
			MyMask[Index][FromTo] *= 10; 
			MyMask[Index][FromTo] += *(Rule++) - '0'; 
			if (*Rule == '*') 
				return FALSE; 
		} 
	} 
 
	if (Index != 3) 
		return FALSE; 
 
	if (!FromTo) 
		MyMask[Index][1] = MyMask[Index][0]; 
 
	if ((MyMask[Index][0] > 255) || (MyMask[Index][1] > 255)) 
		return FALSE; 
 
	if (IPmask) 
		memcpy(IPmask,MyMask,sizeof(MyMask)); 
 
	return TRUE; 
} 
 
 
BOOL IPGenericIsIpInList(LPCSTR IPaddess,CString &cList) 
{ 
	int MyMask[4][2]; 
	int MyIp[4]; 
	int Index; 
	CString Rules = cList; 
	char *p = Rules.GetBuffer(1); 
	int Match; 
 
	if (!*p) 
		return FALSE; // No rule 
 
	if (inet_addr(IPaddess) == INADDR_NONE) 
		goto name_lookup; 
 
	memset(MyIp,0,sizeof(MyIp)); 
 
	// Parse IPaddess and get the ip address numbers 
	if (sscanf(IPaddess,"%d.%d.%d.%d", &MyIp[0], &MyIp[1], &MyIp[2], &MyIp[3]) != 4) 
		goto name_lookup; 
	 
 
	for(p = strtok(p,";\r\n"); p && *p; p = strtok(NULL, ";\r\n")) 
	{ 
		if (IPVerifyRule(p,(int **)MyMask)) 
		{ 
			Match = 0; 
			for(Index = 0; Index < 4; Index++) 
			{ 
				if ((MyIp[Index] >= MyMask[Index][0]) && (MyIp[Index] <= MyMask[Index][1])) 
					++Match; 
			} 
			if (Match == 4) 
				return TRUE; 
		} 
		else 
		{ 
name_lookup: 
			if (PatternMatchesName(p, IPaddess)) 
				return TRUE; 
		} 
	} 
	return FALSE; 
} 
 
void TranslateList(CString &Val) 
{ 
	CString cBuf = Val; 
	LPSTR p = cBuf.GetBuffer(1); 
	Val = ""; 
 
	for(p = strtok(p, ";\r\n"); p; p = strtok(NULL, ";\r\n")) 
	{ 
		if (!Val.IsEmpty()) 
			Val += "\r\n"; 
		Val += p; 
	} 
}