bluedragon99
asked on
Sending Raw Packets (how to create data portion)
Hello,
I am very new to Visual C++ and have been fighting with this for hours. I am trying to allow a user to enter raw packet data into an edit control on my mfc app. I have TTL, source and dest ip all set. I can't seem to find where I would go about variabling the data payload. There will be an edit control where you should be able to enter the payload data. I know alot of this code is commented out, I was trying to find where the payload is constructed. When running ethereal I noticed a portion of the payload appears to go though ascii characters. Any help would be GREATLY appreciated. Code fixes/examples if possible. Thanks
#include "stdafx.h"
#include "BinaryTree.h"
#include "SpoofSocket.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//Message handler
LRESULT CALLBACK SocketMessageHandler(HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
//first get the socket
CSpoofSocket* cSock;
cSock=CSpoofSocket::GetSoc ketByID((i nt)wParam) ;
if (cSock)
//Socket exists
switch (uMsg)
{
case WM_SOCKET_GENERAL:
if (WSAGETSELECTEVENT(lParam) == FD_READ)
return cSock->OnSocketReceive(WSA GETSELECTE RROR(lPara m));
else if (WSAGETSELECTEVENT(lParam) == FD_WRITE)
return cSock->OnSocketWrite(WSAGE TSELECTERR OR(lParam) );
else if (WSAGETSELECTEVENT(lParam) == FD_OOB)
return cSock->OnSocketOOB(WSAGETS ELECTERROR (lParam));
else if (WSAGETSELECTEVENT(lParam) == FD_CLOSE)
return cSock->OnSocketClose(WSAGE TSELECTERR OR(lParam) );
break;
case WM_SOCKET_CONNECT:
if (WSAGETSELECTEVENT(lParam) == FD_CONNECT)
return cSock->OnSocketConnect(WSA GETSELECTE RROR(lPara m));
break;
case WM_SOCKET_ACCEPT:
if (WSAGETSELECTEVENT(lParam) == FD_ACCEPT)
return cSock->OnSocketAccept(WSAG ETSELECTER ROR(lParam ));
break;
case WM_TIMER:
//Inform the socket
return cSock->OnSocketTimeout();
default: /* Passes it on if unproccessed */
return (int)(DefWindowProc(hwnd, uMsg, wParam, lParam));
}
else
return (int)(DefWindowProc(hwnd, uMsg, wParam, lParam));
return TRUE;
}
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CSpoofSocket
CSpoofSocket::CSpoofSocket ()
{
InitializeIP();
}
CSpoofSocket::CSpoofSocket (SOCKET sok)
{
AssignSocket(sok);
}
CSpoofSocket::~CSpoofSocke t()
{
//Delete options
SetOptions(FALSE);
Close();
}
////////////////////////// ////////// ////////// ////////// ////////// ////////// /
// CSpoofSocket member functions
BOOL CSpoofSocket::Create(int iProtocol)
{
//Here we create the raw socket
m_SpoofSocket=socket(AF_IN ET,SOCK_RA W,iProtoco l); //iProtocol);
//Check for socket validity
if (m_SpoofSocket==INVALID_SO CKET)
{
//Error
m_LastError=WSAGetLastErro r();
return FALSE;
}
if (m_Raw)
{
//Set that the application will send the IP header
unsigned int iTrue=1;
if(setsockopt(m_SpoofSocke t,IPPROTO_ IP,IP_HDRI NCL,(char* )&iTrue,si zeof(iTrue ))==SOCKET _ERROR)
{
//Check for options error
m_LastError=WSAGetLastErro r();
return FALSE;
}
if (iProtocol==IPPROTO_ICMP)
SetHandlers();
}
else
SetHandlers();
//Well no errors
m_LastError=0;
//Add socket to list
AddSocketToList();
return TRUE;
}
BOOL CSpoofSocket::Send(LPCSTR lpDestinationAddress,char* buf,int bufLength)
{
//Quit if not ok
if (!CheckSocketValid())
return FALSE;
//Define the target address
sockaddr_in m_TargetAddress;
memset(&m_TargetAddress,0, sizeof(m_T argetAddre ss));
m_TargetAddress.sin_family =AF_INET;
m_TargetAddress.sin_addr.s _addr=inet _addr(lpDe stinationA ddress);
m_TargetAddress.sin_port=0 ;
//packet send status ?
int iResult;
//Only if allowing raw headers !!
if (m_Raw)
{
//Header length
unsigned char ucHeaderLength=IpHeaderLen gth;
if (m_Options)
ucHeaderLength+=m_IPOption s->GetBuff erLength() ;
//First construct the packet
LPIpHeader lpHead=ConstructIPHeader(g _nMyVar,Ip FragFlag_D ONT_FRAG,m _TTL,GetCu rrentProce ssId(),ucH eaderLengt h);
//Set the address
SetIPHeaderAddress(lpHead, m_SourceAd dress,lpDe stinationA ddress);
//Now add some more options
int iTotalLength;
iTotalLength=ucHeaderLengt h+bufLengt h;
//Set the header
lpHead->TotalLength=htons( iTotalLeng th);
//Need to construct a new packet
char* newBuf=new char[iTotalLength];
//Copy two buffers
memcpy(newBuf,lpHead,IpHea derLength) ;
//Do we need to copy options ?
if (m_Options)
memcpy(newBuf+IpHeaderLeng th,m_IPOpt ions->GetB uffer(),m_ IPOptions- >GetBuffer Length());
//Only if not null
if (buf)
memcpy(newBuf+ucHeaderLeng th,buf,buf Length);
//Calculate the checksum
lpHead->CheckSum=Calculate Checksum(( unsigned short*)newBuf,iTotalLength );
//Recopy the ip
memcpy(newBuf,lpHead,IpHea derLength) ;
//Send the data
iResult=sendto(getHandle() ,newBuf,iT otalLength ,0,(sockad dr*)&m_Tar getAddress ,sizeof(m_ TargetAddr ess));
//Dispose of the buffer
delete newBuf;
//Dispose the header
delete lpHead;
}
else
{
iResult=!SOCKET_ERROR;
//Insert options
if (m_Options)
if (setsockopt(getHandle(),IP PROTO_IP,I P_OPTIONS, m_IPOption s->GetBuff er(),m_IPO ptions->Ge tBufferLen gth()==SOC KET_ERROR) )
//Error
iResult=SOCKET_ERROR;
else
;
else
//No options
//iResult=setsockopt(getHa ndle(),IPP ROTO_IP,IP _OPTIONS,N ULL,0);
;
//Check if we had an error
if (iResult!=SOCKET_ERROR)
//Use regular send !!!
iResult=sendto(getHandle() ,(const char*)buf,bufLength,0,(soc kaddr*)&m_ TargetAddr ess,sizeof (m_TargetA ddress));
}
if (iResult==SOCKET_ERROR)
//Set the error
SetLastError();
else
m_LastError=0;
return iResult!=SOCKET_ERROR;
}
LPIpHeader CSpoofSocket::ConstructIPH eader(unsi gned char ucProtocol,
unsigned short usFragmentationFlags,
unsigned char ucTTL,
unsigned short usIdentification,
unsigned char ucHeaderLength)
{
//Need to construct the IP header
LPIpHeader lpHead=new _IpHeader;
//Header length (in 32 bits)
lpHead->HeaderLength_Versi on=ucHeade rLength/4 + IpVersion*16;
//Protocol
lpHead->Protocol=ucProtoco l;
//Fragmentation flags
lpHead->FragmentationFlags =htons(usF ragmentati onFlags);
//Time to live
lpHead->TTL=ucTTL;
//Checksum - set to 0
lpHead->CheckSum=0;
//Identification
lpHead->Identification=hto ns(usIdent ification) ;
//Precedence
lpHead->TypeOfService=IpSe rvice_ROUT INE;
//Return IP to user
return lpHead;
}
void CSpoofSocket::SetIPHeaderA ddress(LPI pHeader lpHead, LPCSTR lpSourceAddress, LPCSTR lpDestinationAddress)
{
//We need to place the header
//If source is NULL then we need to use default source
if (!lpSourceAddress)
{
//We will implement it
}
else
//Use sockets2
lpHead->sourceIPAddress=in et_addr(lp SourceAddr ess);
//Place destination address
lpHead->destIPAddress=inet _addr(lpDe stinationA ddress);
//Done
}
void CSpoofSocket::SetLastError ()
{
m_LastError=WSAGetLastErro r();
}
int CSpoofSocket::GetLastError ()
{
return m_LastError;
}
BOOL CSpoofSocket::ValidSocket( )
{
return m_SpoofSocket!=INVALID_SOC KET;
}
unsigned short CSpoofSocket::CalculateChe cksum(unsi gned short *usBuf, int iSize)
{
unsigned long usChksum=0;
//Calculate the checksum
while (iSize>1)
{
usChksum+=*usBuf++;
iSize-=sizeof(unsigned short);
}
//If we have one char left
if (iSize)
usChksum+=*(unsigned char*)usBuf;
//Complete the calculations
usChksum=(usChksum >> 16) + (usChksum & 0xffff);
usChksum+=(usChksum >> 16);
//Return the value (inversed)
return (unsigned short)(~usChksum);
}
BOOL CSpoofSocket::Bind(LPCSTR lpSourceAddress,int iPort)
{
//Quit if not ok
if (!CheckSocketValid())
return FALSE;
//Create the local address
sockaddr_in soSrc;
//Set to 0
memset(&soSrc,0,sizeof(soS rc));
soSrc.sin_family=AF_INET;
soSrc.sin_addr.s_addr=inet _addr(lpSo urceAddres s);
soSrc.sin_port=htons(iPort );
//Now we need to bind it
if (bind(getHandle(),(sockadd r*)&soSrc, sizeof(soS rc)))
{
//Error
m_LastError=WSAGetLastErro r();
return FALSE;
}
m_LastError=0;
//If already has a source address then don't change it
if (!m_SourceAddress)
m_SourceAddress=lpSourceAd dress;
return TRUE;
}
SOCKET CSpoofSocket::getHandle()
{
return m_SpoofSocket;
}
BOOL CSpoofSocket::CheckSocketV alid()
{
//Check if socket is invalid
if (!ValidSocket())
{
m_LastError=WSAESHUTDOWN;
return FALSE;
}
//OK
return TRUE;
}
BOOL CSpoofSocket::Close()
{
//Close the socket
//Quit if not ok
if (!CheckSocketValid())
return FALSE;
//Close it
if (closesocket(getHandle())= =SOCKET_ER ROR)
{
//Error in closing ?
m_LastError=WSAGetLastErro r();
return FALSE;
}
//Set the socket to invalid
m_SpoofSocket=INVALID_SOCK ET;
//Remove from tree
RemoveSocketFromList();
return TRUE;
}
BOOL CSpoofSocket::InitializeSo ckets()
{
//Initialize the sockets
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if (err!=0)
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
return FALSE;
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion)! =2 || HIBYTE(wsaData.wVersion)!= 2)
{
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup();
return FALSE;
}
//OK
//Intialize the tree
if (!m_SocketTree)
m_SocketTree=new CBinaryTree;
return TRUE;
}
void CSpoofSocket::SetProtocol( int iProtocol)
{
m_Protocol=iProtocol;
}
void CSpoofSocket::SetSourceAdd ress(LPCST R lpSourceAddress)
{
//Set the source address, in case we want to spoof it
m_SourceAddress=lpSourceAd dress;
}
unsigned short CSpoofSocket::CalculatePse udoChecksu m(char *buf, int BufLength,LPCSTR lpDestinationAddress,int iPacketLength)
{
//Calculate the checksum
LPPseudoHeader lpPseudo;
lpPseudo=new PseudoHeader;
lpPseudo->DestinationAddre ss=inet_ad dr(lpDesti nationAddr ess);
lpPseudo->SourceAddress=in et_addr(m_ SourceAddr ess);
lpPseudo->Zeros=0;
lpPseudo->PTCL=m_Protocol;
lpPseudo->Length=htons(iPa cketLength );
//Calculate checksum of all
int iTotalLength;
iTotalLength=PseudoHeaderL ength+BufL ength;
char* tmpBuf;
tmpBuf=new char[iTotalLength];
////Copy pseudo
//memcpy(tmpBuf,lpPseudo,P seudoHeade rLength);
////Copy header
//memcpy(tmpBuf+PseudoHead erLength,b uf,BufLeng th);
//Calculate the checksum
unsigned short usChecksum;
usChecksum=CalculateChecks um((unsign ed short*)tmpBuf,iTotalLength );
//Delete all
delete tmpBuf;
delete lpPseudo;
//Return checksum
return usChecksum;
}
void CSpoofSocket::SetTTL(unsig ned char ucTTL)
{
if (m_Raw)
{
//Set the ttl
m_TTL=ucTTL;
}
else
setsockopt(getHandle(),IPP ROTO_IP,IP _TTL,(cons t char*)&ucTTL,sizeof(ucTTL) );
}
BOOL CSpoofSocket::Listen(int iBackLog)
{
int iResult;
iResult=listen(getHandle() ,iBackLog) ;
if (iResult)
SetLastError();
return !iResult;
}
BOOL CSpoofSocket::ShutdownSock ets()
{
//Clear windows
BOOL bHandlers;
bHandlers=RemoveHandlers() ;
//Delete tree
delete m_SocketTree;
m_SocketTree=NULL;
if (WSACleanup()==SOCKET_ERRO R)
{
SetLastError();
return FALSE;
}
else
return bHandlers;
}
void CSpoofSocket::SetRaw(BOOL bRaw)
{
//Do we want to create raw socket (YES!!)
m_Raw=bRaw;
}
void CSpoofSocket::SetOptions(B OOL bOptions)
{
//Do we want options, normaly not
m_Options=bOptions;
if (m_IPOptions)
{
delete m_IPOptions;
m_IPOptions=NULL;
}
if (bOptions)
m_IPOptions=new CIPOptions;
}
CIPOptions::CIPOptions()
{
//Initialize our buffer
m_Buffer=new char[IPOption_SIZE];
//Set our buffer to nothing
Reset();
//Our buffer length
m_BufferLength=0;
//Set auto pad
m_AutoPAD=TRUE;
}
CIPOptions::~CIPOptions()
{
delete m_Buffer;
}
void CIPOptions::AddOption_Noth ing()
{
//Add option do nothing
//tOptionType OT;
////Get the option
//OT=GetOption(IPOption_DO NT_COPY,IP Option_CON TROL,IPOpt ion_NO_OPE RATION);
////Add it to buffer
//AddToBuffer((char*)&OT,s izeof(OT)) ;
}
tOptionType CIPOptions::GetOption(unsi gned char CopyFlag, unsigned char ClassFlag, unsigned char TypeFlag)
{
//Return a single option type
return CopyFlag | ClassFlag | TypeFlag;
}
void CIPOptions::AddToBuffer(ch ar *buf, int BufLength)
{
if (m_BufferLength<IPOption_S IZE)
{
//Add our option to the buffer
/*memcpy(m_Buffer+m_Buffer Length,buf ,BufLength );
m_BufferLength+=BufLength; */
}
}
const char* CIPOptions::GetBuffer()
{
return m_Buffer;
}
int CIPOptions::GetBufferLengt h()
{
//Check if auto pad or not
if (m_AutoPAD)
if (m_BufferLength/IPOption_W RAPSIZE==( m_BufferLe ngth/IPOpt ion_WRAPSI ZE)*IPOpti on_WRAPSIZ E && m_BufferLength>=IPOption_W RAPSIZE)
return m_BufferLength;
else
return int((float)m_BufferLength/ IPOption_W RAPSIZE+1) *IPOption_ WRAPSIZE;
else
return m_BufferLength;
}
void CIPOptions::AddOption_ENDL IST()
{
//End the list of options
tOptionType OT;
//Get the option
OT=GetOption(IPOption_DONT _COPY,IPOp tion_CONTR OL,IPOptio n_END_OPTI ON);
//AddToBuffer((char*)&OT,( OT));
}
void CIPOptions::SetAutoPad(BOO L bAutoPAD)
{
m_AutoPAD=bAutoPAD;
}
CIPOptions* CSpoofSocket::GetOptions()
{
return m_IPOptions;
}
void CIPOptions::Reset()
{
//Set all to zeros
memset(m_Buffer,0,IPOption _SIZE);
}
void CIPOptions::AddOption_Secu rity(unsig ned short usType)
{
////Add option security
//tOptionType OT;
////Get the option
//OT=GetOption(IPOption_CO PY,IPOptio n_CONTROL, IPOption_S ECURITY);
////Add it to buffer
//AddToBuffer((char*)&OT,s izeof(OT)) ;
////Add length
//OT=IPOption_SECURITY_LEN GTH;
//AddToBuffer((char*)&OT,s izeof(OT)) ;
////Add options
//AddToBuffer((char*)&usTy pe,sizeof( usType));
//Add zeros
unsigned short usZeros=0;
unsigned char ucZeros=0;
//A hacker would enumarate these values, according to the RFC
//Compartments
//AddToBuffer((char*)&usZe ros,sizeof (usZeros)) ;
////Handling restrictions
//AddToBuffer((char*)&usZe ros,sizeof (usZeros)) ;
////Transmition control code (TCC)
//AddToBuffer((char*)&usZe ros,sizeof (usZeros)) ;
//AddToBuffer((char*)&ucZe ros,sizeof (ucZeros)) ;
////Done
}
void CIPOptions::AddOption_Stre am(unsigne d short usStreamID)
{
// //Add option security
//tOptionType OT;
////Get the option
//OT=GetOption(IPOption_CO PY,IPOptio n_CONTROL, IPOption_S TREAM);
////Add it to buffer
//AddToBuffer((char*)&OT,s izeof(OT)) ;
////Add length
//OT=IPOption_STREAM_LENGT H;
//AddToBuffer((char*)&OT,s izeof(OT)) ;
////Add options
//unsigned short usnStreamID;
//usnStreamID=htons(usStre amID);
//AddToBuffer((char*)&usnS treamID,si zeof(usnSt reamID));
}
void CIPOptions::AddOption_Stri ctRoute(tR outing tRoute)
{
/*AddOption_Route(IPOption _STRICT_RO UTING,tRou te);*/
}
void CIPOptions::AddOption_Reco rdRoute(in t iMaxRoutes)
{
////Option for strict routine
////Add option strict route
//tOptionType OT;
////Get the option
//OT=GetOption(IPOption_DO NT_COPY,IP Option_CON TROL,IPOpt ion_RECORD _ROUTE);
////Add it to buffer
//AddToBuffer((char*)&OT,s izeof(OT)) ;
////Add the length
//OT=iMaxRoutes*4+IPOption _STRICT_RO UTING_LENG TH;
//AddToBuffer((char*)&OT,s izeof(OT)) ;
//
////Add the pointer
//OT=IPOption_STRICT_ROUTI NG_POINTER ;
//AddToBuffer((char*)&OT,s izeof(OT)) ;
//
//char cNothing[IPOption_SIZE]="" ;
//AddToBuffer(cNothing,iMa xRoutes*4) ;
}
void CIPOptions::AddOption_Rout e(tOptionT ype tRouteType,tRouting tRoute)
{
//Option for strict routine
//Add option strict route
tOptionType OT;
////Get the option
//OT=GetOption(IPOption_CO PY,IPOptio n_CONTROL, tRouteType );
////Add it to buffer
//AddToBuffer((char*)&OT,s izeof(OT)) ;
////Add the length
//OT=tRoute.iRoutes*4+IPOp tion_STRIC T_ROUTING_ LENGTH;
//AddToBuffer((char*)&OT,s izeof(OT)) ;
//
////Add the pointer
//OT=IPOption_STRICT_ROUTI NG_POINTER ;
//AddToBuffer((char*)&OT,s izeof(OT)) ;
//
////Add the routing table
//AddToBuffer((char*)tRout e.ulRoutes ,tRoute.iR outes*4);
}
void CIPOptions::AddOption_Loos eRoute(tRo uting tRoute)
{
AddOption_Route(IPOption_L OOSE_ROUTI NG,tRoute) ;
}
void CIPOptions::AddOption_Time stamp(tOpt ionType tFlags, int iMaxStamps)
{
////Add option for timestamp
//tOptionType OT;
////Get the option
//OT=GetOption(IPOption_DO NT_COPY,IP Option_DEB UGGING,IPO ption_TIME STAMP);
////Add it to buffer
//AddToBuffer((char*)&OT,s izeof(OT)) ;
////Add the length
//OT=iMaxStamps*IPOption_T IMESTAMP_S IZE+IPOpti on_TIMESTA MP_LENGTH- 1;
//AddToBuffer((char*)&OT,s izeof(OT)) ;
//
////Add the pointer
//OT=IPOption_TIMESTAMP_LE NGTH;
//AddToBuffer((char*)&OT,s izeof(OT)) ;
////Add the flags
//AddToBuffer((char*)&tFla gs,sizeof( tFlags));
////Add the empty buffer
//char cNothing[IPOption_SIZE]="" ;
//AddToBuffer(cNothing,iMa xStamps*IP Option_TIM ESTAMP_SIZ E);
}
BOOL CSpoofSocket::SetHandlers( )
{
//Check if we allow async sockets
if (!m_Async)
return TRUE;
//First create the window class
if (!m_Window)
if (!RegisterWindow())
{
//Error
m_LastError=::GetLastError ();
return FALSE;
}
else
{
m_WindowHandle=CreateWindo wEx(0,CSpo ofSocket_C lass,"Sock et notification sink",
WS_OVERLAPPED,0,0,0,0,0,NU LL,GetInst ance(),NUL L);
//Check the value of the window
if (!m_WindowHandle)
{
//Error
m_LastError=::GetLastError ();
return FALSE;
}
else
//We have a window
m_Window=TRUE;
}
//Created !!
//Success
return TRUE;
}
HINSTANCE CSpoofSocket::GetInstance( )
{
//Returns the instance of the application, must be overided
return m_Instance;
}
BOOL CSpoofSocket::OnSocketRece ive(int iErrorCode)
{
//Must override!
return TRUE;
}
BOOL CSpoofSocket::OnSocketWrit e(int iErrorCode)
{
return TRUE;
}
BOOL CSpoofSocket::OnSocketOOB( int iErrorCode)
{
return TRUE;
}
BOOL CSpoofSocket::OnSocketClos e(int iErrorCode)
{
return TRUE;
}
BOOL CSpoofSocket::OnSocketAcce pt(int iErrorCode)
{
return TRUE;
}
BOOL CSpoofSocket::OnSocketConn ect(int iErrorCode)
{
return TRUE;
}
int CSpoofSocket::GetSocketID( )
{
return m_SocketID;
}
CSpoofSocket* CSpoofSocket::GetSocketByI D(int iSockID)
{
//Get the socket
CBinaryTree* retVal;
retVal=m_SocketTree->getNo de(iSockID );
//Check if valid
if (retVal)
//Got socket
return (CSpoofSocket*)retVal->get Data();
else
//Nothing
return NULL;
}
BOOL CSpoofSocket::RegisterWind ow()
{
WNDCLASS wc;
/* Fill in window class structure with parameters that describe the */
/* main window. */
wc.style = 0; /* Class style(s). */
wc.lpfnWndProc = (WNDPROC)SocketMessageHand ler; /* Function to retrieve messages for */
/* windows of this class. */
wc.cbClsExtra = 0; /* No per-class extra data. */
wc.cbWndExtra = 0; /* No per-window extra data. */
wc.hIcon = NULL; /* Icon name from .RC */
wc.hInstance = GetInstance(); /* Application that owns the class. */
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL; /* Name of menu resource in .RC file. */
wc.lpszClassName = CSpoofSocket_Class ; /* Name used in call to CreateWindow. */
/* Register the window class and return success/failure code. */
return (RegisterClass(&wc));
}
//Static members
CBinaryTree* CSpoofSocket::m_SocketTree =NULL;
BOOL CSpoofSocket::m_Window=FAL SE;
HWND CSpoofSocket::m_WindowHand le=0;
void CSpoofSocket::SetInstance( HINSTANCE hInst)
{
m_Instance=hInst;
}
BOOL CSpoofSocket::isRaw()
{
return m_Raw;
}
BOOL CSpoofSocket::RemoveHandle rs()
{
//First shut down the windows
if (m_Window)
{
if (!DestroyWindow(m_WindowHa ndle))
return FALSE;
if (!UnregisterClass(CSpoofSo cket_Class ,GetInstan ce()))
return FALSE;
}
m_Window=FALSE;
m_WindowHandle=NULL;
return TRUE;
}
HWND CSpoofSocket::getWindowHan dle()
{
return m_WindowHandle;
}
void CSpoofSocket::InitializeIP ()
{
//Invalid the socket
m_SpoofSocket=INVALID_SOCK ET;
//More invalids
m_SourceAddress=NULL;
//Some defaults
m_TTL=g_nTTLVAR;
//We don't want raw header (so it can work on win 98/NT)
m_Raw=FALSE;
//Set our options
m_IPOptions=NULL;
//Set options to false
SetOptions(FALSE);
//We want async socket
SetBlocking(FALSE);
}
void CSpoofSocket::AssignSocket (SOCKET sok)
{
//Binds to a socket
m_SpoofSocket=sok;
//Set non raw
SetRaw(FALSE);
//Set the handlers
SetHandlers();
//Append socket to list
AddSocketToList();
}
void CSpoofSocket::AddSocketToL ist()
{
//Add socket to list
m_SocketID=m_SpoofSocket;
m_SocketTree->newNode(GetS ocketID()) ->setData( this);
}
void CSpoofSocket::RemoveSocket FromList()
{
m_SocketTree->deleteNode(m _SocketID) ;
}
int CSpoofSocket::Receive(char *buf, int bufLen)
{
if (!ValidSocket())
return SOCKET_ERROR;
//Receive data
int iResult;
//Receive
if (m_Protocol!=IPPROTO_TCP)
iResult=recvfrom(getHandle (),buf,buf Len,NULL,N ULL,NULL);
else
iResult=recv(getHandle(),b uf,bufLen, NULL);
//Check if error
if (iResult==SOCKET_ERROR)
//Error
SetLastError();
//Number of bytes received
return iResult;
}
char FAR * CSpoofSocket::LongToString (long lAddr)
{
//First create the address
in_addr addr;
//Assign it
addr.S_un.S_addr=lAddr;
//Return the value
return inet_ntoa(addr);
}
BOOL CSpoofSocket::OnSocketTime out()
{
//Indicating timeout proccessed
return FALSE;
}
BOOL CSpoofSocket::SetTimeout(i nt iMs)
{
if (!m_Window)
return FALSE;
//Set the timer
return SetTimer(getWindowHandle() ,getHandle (),iMs,NUL L);
}
BOOL CSpoofSocket::KillTimer()
{
if (!m_Window)
return FALSE;
return ::KillTimer(getWindowHandl e(),getHan dle());
}
BOOL CSpoofSocket::ValidAddress (LPCTSTR lpAddress)
{
return inet_addr(lpAddress)!=INAD DR_NONE;
}
void CSpoofSocket::SetBlocking( BOOL bBlock)
{
//Do we want blocking socket
m_Async=!bBlock;
}
easy 500 points here I just need to know where to insert data payload text...
I am very new to Visual C++ and have been fighting with this for hours. I am trying to allow a user to enter raw packet data into an edit control on my mfc app. I have TTL, source and dest ip all set. I can't seem to find where I would go about variabling the data payload. There will be an edit control where you should be able to enter the payload data. I know alot of this code is commented out, I was trying to find where the payload is constructed. When running ethereal I noticed a portion of the payload appears to go though ascii characters. Any help would be GREATLY appreciated. Code fixes/examples if possible. Thanks
#include "stdafx.h"
#include "BinaryTree.h"
#include "SpoofSocket.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//Message handler
LRESULT CALLBACK SocketMessageHandler(HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
//first get the socket
CSpoofSocket* cSock;
cSock=CSpoofSocket::GetSoc
if (cSock)
//Socket exists
switch (uMsg)
{
case WM_SOCKET_GENERAL:
if (WSAGETSELECTEVENT(lParam)
return cSock->OnSocketReceive(WSA
else if (WSAGETSELECTEVENT(lParam)
return cSock->OnSocketWrite(WSAGE
else if (WSAGETSELECTEVENT(lParam)
return cSock->OnSocketOOB(WSAGETS
else if (WSAGETSELECTEVENT(lParam)
return cSock->OnSocketClose(WSAGE
break;
case WM_SOCKET_CONNECT:
if (WSAGETSELECTEVENT(lParam)
return cSock->OnSocketConnect(WSA
break;
case WM_SOCKET_ACCEPT:
if (WSAGETSELECTEVENT(lParam)
return cSock->OnSocketAccept(WSAG
break;
case WM_TIMER:
//Inform the socket
return cSock->OnSocketTimeout();
default: /* Passes it on if unproccessed */
return (int)(DefWindowProc(hwnd, uMsg, wParam, lParam));
}
else
return (int)(DefWindowProc(hwnd, uMsg, wParam, lParam));
return TRUE;
}
//////////////////////////
// CSpoofSocket
CSpoofSocket::CSpoofSocket
{
InitializeIP();
}
CSpoofSocket::CSpoofSocket
{
AssignSocket(sok);
}
CSpoofSocket::~CSpoofSocke
{
//Delete options
SetOptions(FALSE);
Close();
}
//////////////////////////
// CSpoofSocket member functions
BOOL CSpoofSocket::Create(int iProtocol)
{
//Here we create the raw socket
m_SpoofSocket=socket(AF_IN
//Check for socket validity
if (m_SpoofSocket==INVALID_SO
{
//Error
m_LastError=WSAGetLastErro
return FALSE;
}
if (m_Raw)
{
//Set that the application will send the IP header
unsigned int iTrue=1;
if(setsockopt(m_SpoofSocke
{
//Check for options error
m_LastError=WSAGetLastErro
return FALSE;
}
if (iProtocol==IPPROTO_ICMP)
SetHandlers();
}
else
SetHandlers();
//Well no errors
m_LastError=0;
//Add socket to list
AddSocketToList();
return TRUE;
}
BOOL CSpoofSocket::Send(LPCSTR lpDestinationAddress,char*
{
//Quit if not ok
if (!CheckSocketValid())
return FALSE;
//Define the target address
sockaddr_in m_TargetAddress;
memset(&m_TargetAddress,0,
m_TargetAddress.sin_family
m_TargetAddress.sin_addr.s
m_TargetAddress.sin_port=0
//packet send status ?
int iResult;
//Only if allowing raw headers !!
if (m_Raw)
{
//Header length
unsigned char ucHeaderLength=IpHeaderLen
if (m_Options)
ucHeaderLength+=m_IPOption
//First construct the packet
LPIpHeader lpHead=ConstructIPHeader(g
//Set the address
SetIPHeaderAddress(lpHead,
//Now add some more options
int iTotalLength;
iTotalLength=ucHeaderLengt
//Set the header
lpHead->TotalLength=htons(
//Need to construct a new packet
char* newBuf=new char[iTotalLength];
//Copy two buffers
memcpy(newBuf,lpHead,IpHea
//Do we need to copy options ?
if (m_Options)
memcpy(newBuf+IpHeaderLeng
//Only if not null
if (buf)
memcpy(newBuf+ucHeaderLeng
//Calculate the checksum
lpHead->CheckSum=Calculate
//Recopy the ip
memcpy(newBuf,lpHead,IpHea
//Send the data
iResult=sendto(getHandle()
//Dispose of the buffer
delete newBuf;
//Dispose the header
delete lpHead;
}
else
{
iResult=!SOCKET_ERROR;
//Insert options
if (m_Options)
if (setsockopt(getHandle(),IP
//Error
iResult=SOCKET_ERROR;
else
;
else
//No options
//iResult=setsockopt(getHa
;
//Check if we had an error
if (iResult!=SOCKET_ERROR)
//Use regular send !!!
iResult=sendto(getHandle()
}
if (iResult==SOCKET_ERROR)
//Set the error
SetLastError();
else
m_LastError=0;
return iResult!=SOCKET_ERROR;
}
LPIpHeader CSpoofSocket::ConstructIPH
unsigned short usFragmentationFlags,
unsigned char ucTTL,
unsigned short usIdentification,
unsigned char ucHeaderLength)
{
//Need to construct the IP header
LPIpHeader lpHead=new _IpHeader;
//Header length (in 32 bits)
lpHead->HeaderLength_Versi
//Protocol
lpHead->Protocol=ucProtoco
//Fragmentation flags
lpHead->FragmentationFlags
//Time to live
lpHead->TTL=ucTTL;
//Checksum - set to 0
lpHead->CheckSum=0;
//Identification
lpHead->Identification=hto
//Precedence
lpHead->TypeOfService=IpSe
//Return IP to user
return lpHead;
}
void CSpoofSocket::SetIPHeaderA
{
//We need to place the header
//If source is NULL then we need to use default source
if (!lpSourceAddress)
{
//We will implement it
}
else
//Use sockets2
lpHead->sourceIPAddress=in
//Place destination address
lpHead->destIPAddress=inet
//Done
}
void CSpoofSocket::SetLastError
{
m_LastError=WSAGetLastErro
}
int CSpoofSocket::GetLastError
{
return m_LastError;
}
BOOL CSpoofSocket::ValidSocket(
{
return m_SpoofSocket!=INVALID_SOC
}
unsigned short CSpoofSocket::CalculateChe
{
unsigned long usChksum=0;
//Calculate the checksum
while (iSize>1)
{
usChksum+=*usBuf++;
iSize-=sizeof(unsigned short);
}
//If we have one char left
if (iSize)
usChksum+=*(unsigned char*)usBuf;
//Complete the calculations
usChksum=(usChksum >> 16) + (usChksum & 0xffff);
usChksum+=(usChksum >> 16);
//Return the value (inversed)
return (unsigned short)(~usChksum);
}
BOOL CSpoofSocket::Bind(LPCSTR lpSourceAddress,int iPort)
{
//Quit if not ok
if (!CheckSocketValid())
return FALSE;
//Create the local address
sockaddr_in soSrc;
//Set to 0
memset(&soSrc,0,sizeof(soS
soSrc.sin_family=AF_INET;
soSrc.sin_addr.s_addr=inet
soSrc.sin_port=htons(iPort
//Now we need to bind it
if (bind(getHandle(),(sockadd
{
//Error
m_LastError=WSAGetLastErro
return FALSE;
}
m_LastError=0;
//If already has a source address then don't change it
if (!m_SourceAddress)
m_SourceAddress=lpSourceAd
return TRUE;
}
SOCKET CSpoofSocket::getHandle()
{
return m_SpoofSocket;
}
BOOL CSpoofSocket::CheckSocketV
{
//Check if socket is invalid
if (!ValidSocket())
{
m_LastError=WSAESHUTDOWN;
return FALSE;
}
//OK
return TRUE;
}
BOOL CSpoofSocket::Close()
{
//Close the socket
//Quit if not ok
if (!CheckSocketValid())
return FALSE;
//Close it
if (closesocket(getHandle())=
{
//Error in closing ?
m_LastError=WSAGetLastErro
return FALSE;
}
//Set the socket to invalid
m_SpoofSocket=INVALID_SOCK
//Remove from tree
RemoveSocketFromList();
return TRUE;
}
BOOL CSpoofSocket::InitializeSo
{
//Initialize the sockets
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if (err!=0)
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
return FALSE;
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if (LOBYTE(wsaData.wVersion)!
{
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup();
return FALSE;
}
//OK
//Intialize the tree
if (!m_SocketTree)
m_SocketTree=new CBinaryTree;
return TRUE;
}
void CSpoofSocket::SetProtocol(
{
m_Protocol=iProtocol;
}
void CSpoofSocket::SetSourceAdd
{
//Set the source address, in case we want to spoof it
m_SourceAddress=lpSourceAd
}
unsigned short CSpoofSocket::CalculatePse
{
//Calculate the checksum
LPPseudoHeader lpPseudo;
lpPseudo=new PseudoHeader;
lpPseudo->DestinationAddre
lpPseudo->SourceAddress=in
lpPseudo->Zeros=0;
lpPseudo->PTCL=m_Protocol;
lpPseudo->Length=htons(iPa
//Calculate checksum of all
int iTotalLength;
iTotalLength=PseudoHeaderL
char* tmpBuf;
tmpBuf=new char[iTotalLength];
////Copy pseudo
//memcpy(tmpBuf,lpPseudo,P
////Copy header
//memcpy(tmpBuf+PseudoHead
//Calculate the checksum
unsigned short usChecksum;
usChecksum=CalculateChecks
//Delete all
delete tmpBuf;
delete lpPseudo;
//Return checksum
return usChecksum;
}
void CSpoofSocket::SetTTL(unsig
{
if (m_Raw)
{
//Set the ttl
m_TTL=ucTTL;
}
else
setsockopt(getHandle(),IPP
}
BOOL CSpoofSocket::Listen(int iBackLog)
{
int iResult;
iResult=listen(getHandle()
if (iResult)
SetLastError();
return !iResult;
}
BOOL CSpoofSocket::ShutdownSock
{
//Clear windows
BOOL bHandlers;
bHandlers=RemoveHandlers()
//Delete tree
delete m_SocketTree;
m_SocketTree=NULL;
if (WSACleanup()==SOCKET_ERRO
{
SetLastError();
return FALSE;
}
else
return bHandlers;
}
void CSpoofSocket::SetRaw(BOOL bRaw)
{
//Do we want to create raw socket (YES!!)
m_Raw=bRaw;
}
void CSpoofSocket::SetOptions(B
{
//Do we want options, normaly not
m_Options=bOptions;
if (m_IPOptions)
{
delete m_IPOptions;
m_IPOptions=NULL;
}
if (bOptions)
m_IPOptions=new CIPOptions;
}
CIPOptions::CIPOptions()
{
//Initialize our buffer
m_Buffer=new char[IPOption_SIZE];
//Set our buffer to nothing
Reset();
//Our buffer length
m_BufferLength=0;
//Set auto pad
m_AutoPAD=TRUE;
}
CIPOptions::~CIPOptions()
{
delete m_Buffer;
}
void CIPOptions::AddOption_Noth
{
//Add option do nothing
//tOptionType OT;
////Get the option
//OT=GetOption(IPOption_DO
////Add it to buffer
//AddToBuffer((char*)&OT,s
}
tOptionType CIPOptions::GetOption(unsi
{
//Return a single option type
return CopyFlag | ClassFlag | TypeFlag;
}
void CIPOptions::AddToBuffer(ch
{
if (m_BufferLength<IPOption_S
{
//Add our option to the buffer
/*memcpy(m_Buffer+m_Buffer
m_BufferLength+=BufLength;
}
}
const char* CIPOptions::GetBuffer()
{
return m_Buffer;
}
int CIPOptions::GetBufferLengt
{
//Check if auto pad or not
if (m_AutoPAD)
if (m_BufferLength/IPOption_W
return m_BufferLength;
else
return int((float)m_BufferLength/
else
return m_BufferLength;
}
void CIPOptions::AddOption_ENDL
{
//End the list of options
tOptionType OT;
//Get the option
OT=GetOption(IPOption_DONT
//AddToBuffer((char*)&OT,(
}
void CIPOptions::SetAutoPad(BOO
{
m_AutoPAD=bAutoPAD;
}
CIPOptions* CSpoofSocket::GetOptions()
{
return m_IPOptions;
}
void CIPOptions::Reset()
{
//Set all to zeros
memset(m_Buffer,0,IPOption
}
void CIPOptions::AddOption_Secu
{
////Add option security
//tOptionType OT;
////Get the option
//OT=GetOption(IPOption_CO
////Add it to buffer
//AddToBuffer((char*)&OT,s
////Add length
//OT=IPOption_SECURITY_LEN
//AddToBuffer((char*)&OT,s
////Add options
//AddToBuffer((char*)&usTy
//Add zeros
unsigned short usZeros=0;
unsigned char ucZeros=0;
//A hacker would enumarate these values, according to the RFC
//Compartments
//AddToBuffer((char*)&usZe
////Handling restrictions
//AddToBuffer((char*)&usZe
////Transmition control code (TCC)
//AddToBuffer((char*)&usZe
//AddToBuffer((char*)&ucZe
////Done
}
void CIPOptions::AddOption_Stre
{
// //Add option security
//tOptionType OT;
////Get the option
//OT=GetOption(IPOption_CO
////Add it to buffer
//AddToBuffer((char*)&OT,s
////Add length
//OT=IPOption_STREAM_LENGT
//AddToBuffer((char*)&OT,s
////Add options
//unsigned short usnStreamID;
//usnStreamID=htons(usStre
//AddToBuffer((char*)&usnS
}
void CIPOptions::AddOption_Stri
{
/*AddOption_Route(IPOption
}
void CIPOptions::AddOption_Reco
{
////Option for strict routine
////Add option strict route
//tOptionType OT;
////Get the option
//OT=GetOption(IPOption_DO
////Add it to buffer
//AddToBuffer((char*)&OT,s
////Add the length
//OT=iMaxRoutes*4+IPOption
//AddToBuffer((char*)&OT,s
//
////Add the pointer
//OT=IPOption_STRICT_ROUTI
//AddToBuffer((char*)&OT,s
//
//char cNothing[IPOption_SIZE]=""
//AddToBuffer(cNothing,iMa
}
void CIPOptions::AddOption_Rout
{
//Option for strict routine
//Add option strict route
tOptionType OT;
////Get the option
//OT=GetOption(IPOption_CO
////Add it to buffer
//AddToBuffer((char*)&OT,s
////Add the length
//OT=tRoute.iRoutes*4+IPOp
//AddToBuffer((char*)&OT,s
//
////Add the pointer
//OT=IPOption_STRICT_ROUTI
//AddToBuffer((char*)&OT,s
//
////Add the routing table
//AddToBuffer((char*)tRout
}
void CIPOptions::AddOption_Loos
{
AddOption_Route(IPOption_L
}
void CIPOptions::AddOption_Time
{
////Add option for timestamp
//tOptionType OT;
////Get the option
//OT=GetOption(IPOption_DO
////Add it to buffer
//AddToBuffer((char*)&OT,s
////Add the length
//OT=iMaxStamps*IPOption_T
//AddToBuffer((char*)&OT,s
//
////Add the pointer
//OT=IPOption_TIMESTAMP_LE
//AddToBuffer((char*)&OT,s
////Add the flags
//AddToBuffer((char*)&tFla
////Add the empty buffer
//char cNothing[IPOption_SIZE]=""
//AddToBuffer(cNothing,iMa
}
BOOL CSpoofSocket::SetHandlers(
{
//Check if we allow async sockets
if (!m_Async)
return TRUE;
//First create the window class
if (!m_Window)
if (!RegisterWindow())
{
//Error
m_LastError=::GetLastError
return FALSE;
}
else
{
m_WindowHandle=CreateWindo
WS_OVERLAPPED,0,0,0,0,0,NU
//Check the value of the window
if (!m_WindowHandle)
{
//Error
m_LastError=::GetLastError
return FALSE;
}
else
//We have a window
m_Window=TRUE;
}
//Created !!
//Success
return TRUE;
}
HINSTANCE CSpoofSocket::GetInstance(
{
//Returns the instance of the application, must be overided
return m_Instance;
}
BOOL CSpoofSocket::OnSocketRece
{
//Must override!
return TRUE;
}
BOOL CSpoofSocket::OnSocketWrit
{
return TRUE;
}
BOOL CSpoofSocket::OnSocketOOB(
{
return TRUE;
}
BOOL CSpoofSocket::OnSocketClos
{
return TRUE;
}
BOOL CSpoofSocket::OnSocketAcce
{
return TRUE;
}
BOOL CSpoofSocket::OnSocketConn
{
return TRUE;
}
int CSpoofSocket::GetSocketID(
{
return m_SocketID;
}
CSpoofSocket* CSpoofSocket::GetSocketByI
{
//Get the socket
CBinaryTree* retVal;
retVal=m_SocketTree->getNo
//Check if valid
if (retVal)
//Got socket
return (CSpoofSocket*)retVal->get
else
//Nothing
return NULL;
}
BOOL CSpoofSocket::RegisterWind
{
WNDCLASS wc;
/* Fill in window class structure with parameters that describe the */
/* main window. */
wc.style = 0; /* Class style(s). */
wc.lpfnWndProc = (WNDPROC)SocketMessageHand
/* windows of this class. */
wc.cbClsExtra = 0; /* No per-class extra data. */
wc.cbWndExtra = 0; /* No per-window extra data. */
wc.hIcon = NULL; /* Icon name from .RC */
wc.hInstance = GetInstance(); /* Application that owns the class. */
wc.hCursor = NULL;
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL; /* Name of menu resource in .RC file. */
wc.lpszClassName = CSpoofSocket_Class ; /* Name used in call to CreateWindow. */
/* Register the window class and return success/failure code. */
return (RegisterClass(&wc));
}
//Static members
CBinaryTree* CSpoofSocket::m_SocketTree
BOOL CSpoofSocket::m_Window=FAL
HWND CSpoofSocket::m_WindowHand
void CSpoofSocket::SetInstance(
{
m_Instance=hInst;
}
BOOL CSpoofSocket::isRaw()
{
return m_Raw;
}
BOOL CSpoofSocket::RemoveHandle
{
//First shut down the windows
if (m_Window)
{
if (!DestroyWindow(m_WindowHa
return FALSE;
if (!UnregisterClass(CSpoofSo
return FALSE;
}
m_Window=FALSE;
m_WindowHandle=NULL;
return TRUE;
}
HWND CSpoofSocket::getWindowHan
{
return m_WindowHandle;
}
void CSpoofSocket::InitializeIP
{
//Invalid the socket
m_SpoofSocket=INVALID_SOCK
//More invalids
m_SourceAddress=NULL;
//Some defaults
m_TTL=g_nTTLVAR;
//We don't want raw header (so it can work on win 98/NT)
m_Raw=FALSE;
//Set our options
m_IPOptions=NULL;
//Set options to false
SetOptions(FALSE);
//We want async socket
SetBlocking(FALSE);
}
void CSpoofSocket::AssignSocket
{
//Binds to a socket
m_SpoofSocket=sok;
//Set non raw
SetRaw(FALSE);
//Set the handlers
SetHandlers();
//Append socket to list
AddSocketToList();
}
void CSpoofSocket::AddSocketToL
{
//Add socket to list
m_SocketID=m_SpoofSocket;
m_SocketTree->newNode(GetS
}
void CSpoofSocket::RemoveSocket
{
m_SocketTree->deleteNode(m
}
int CSpoofSocket::Receive(char
{
if (!ValidSocket())
return SOCKET_ERROR;
//Receive data
int iResult;
//Receive
if (m_Protocol!=IPPROTO_TCP)
iResult=recvfrom(getHandle
else
iResult=recv(getHandle(),b
//Check if error
if (iResult==SOCKET_ERROR)
//Error
SetLastError();
//Number of bytes received
return iResult;
}
char FAR * CSpoofSocket::LongToString
{
//First create the address
in_addr addr;
//Assign it
addr.S_un.S_addr=lAddr;
//Return the value
return inet_ntoa(addr);
}
BOOL CSpoofSocket::OnSocketTime
{
//Indicating timeout proccessed
return FALSE;
}
BOOL CSpoofSocket::SetTimeout(i
{
if (!m_Window)
return FALSE;
//Set the timer
return SetTimer(getWindowHandle()
}
BOOL CSpoofSocket::KillTimer()
{
if (!m_Window)
return FALSE;
return ::KillTimer(getWindowHandl
}
BOOL CSpoofSocket::ValidAddress
{
return inet_addr(lpAddress)!=INAD
}
void CSpoofSocket::SetBlocking(
{
//Do we want blocking socket
m_Async=!bBlock;
}
easy 500 points here I just need to know where to insert data payload text...
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER