Added ENotifyKeypresses and ECaptureCtrlC flags to CCommandBase.
Commands can now get keypresses and handle ctrl-C via callbacks instead of having to implement custom active objects. As part of this extended the CCommandBase extension interface to MCommandExtensionsV2 for the new virtual functions KeyPressed(TUint aKeyCode, TUint aModifiers) and CtrlCPressed(). sudo now cleans up correctly by using ECaptureCtrlC.
// Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
// Accenture - minor mods to work as an fshell command and fixes to broken logic
//
// Description:
// pingmodel.cpp - icmp echo client engine
// This software has been implemented in the 6PACK
// project at the Mobile Networks Laboratory (MNW)
// http://www.research.nokia.com/projects/6pack/
// REMARKS:
// The application is prepared to admit the option Hop Limit and
// Numeric output (no name resolution), but these options are not used
// because the socket is open in ICMP mode. To use them need an IP
// socket and IP packets to set the options. Some code is commented and
// can be used in case of need
//
#ifdef IAPSETTING
#include <commdb.h>
#endif
#include <e32math.h>
#include <e32std.h>
//#include <eikenv.h>
//#include <netdial.h>
// IPv6 changes CPing::iType, ComposeFirstIcmpPacket()- ICMPType, Socket Open()
#include "pingmodel.h"
//#include <pingapp.rsg> //resource is not needed as console app
#include "ping_misc.h"
CPing::CPing():CActive(EPriorityStandard),iPacket(0,0),iReceivedData(0,0)
{
CActiveScheduler::Add(this); //Adds itself to the scheduler only the first time
}
//Sets all default values. Actually it's no more a L function
void CPing::ConstructL(TPreferences aPref)
{
// Base class second-phase construction.
//iHostname=_L("bart.research.nokia.com"); //default initial addr
//iHostname=_L("127.0.0.1"); //default initial addr
//iHostname=_L("dead::beef"); //default initial addr
iHostname=aPref.iHostname;
iPackLimit = aPref.iFlags & KPingPackLimit; //Unlimited packets
iTotalPackets=aPref.iTotalPackets; //Default packet number when limited number
iSecWait=aPref.iSecWait; //Default 1 second
iPacketDataSize=aPref.iPacketDataSize; //Default Size
iQuiet=aPref.iFlags & KPingQuiet; //No packet info, just statistics
iVerbose=aPref.iFlags & KPingVerbose; //Verbose Output. All ICMP packets, not only Echo reply. Default no
iPattern.Copy(aPref.iPattern); //Pattern 0xFF (1-filled byte)
iDebug=aPref.iFlags & KPingDebug; //Set SO_DEBUG flag
iLastSecWait=aPref.iLastSecWait; //Default time to wait for the last packet
#ifdef IAPSETTING
iIAP = aPref.iIAP;
#endif
//Not used by now
iNumericOutput=EFalse; //Resolve adresses by default
iHopLimit=0; //Time-to-live in hops default 255 ( 1 to 255) (0 means not set)
iDupPackets=0; //Duplicated packets
iRunning=EFalse; // Tells if there's an Instance of Ping Running
/*
iReceivedDataBuffer= HBufC8::NewL(iPacketDataSize + ICMP_ECHO_HEADER_SIZE); //Maximum size of a return packet
TPtr8 auxPtr(iReceivedDataBuffer->Des()); //must be used here because the buffer changes
iReceivedData.Set(auxPtr); //we use an aux var because can't use Des() directly Why??
*/
}
//return the current preferences
void CPing::GetPreferences(TPreferences &aPref)
{
aPref.iFlags=0;
if (iQuiet)
aPref.iFlags|=KPingQuiet;
if (iVerbose)
aPref.iFlags|=KPingVerbose;
if (iPackLimit)
aPref.iFlags|=KPingPackLimit;
if (iDebug)
aPref.iFlags|=KPingDebug;
aPref.iSecWait=iSecWait; //Time between sent packets (Default 1 second)
aPref.iPacketDataSize=iPacketDataSize;//Default Data Size (not including ICMP header)
aPref.iTotalPackets=iTotalPackets; //Number of ICMP Echo Request packets to send
aPref.iLastSecWait=iLastSecWait; //Time to wait for the Last packet. Default 2
aPref.iHostname=iHostname; //Address to Ping
aPref.iPattern.Copy(iPattern);
#ifdef IAPSETTING
aPref.iIAP = iIAP;
#endif
}
void CPing::DefaultPreferences(TPreferences &aPref)
{
aPref.iFlags=KPingVerbose|KPingPackLimit;
aPref.iSecWait=1; //Time between sent packets (Default 1 second)
aPref.iPacketDataSize=56;//Default Data Size (not including ICMP header)
aPref.iTotalPackets=5; //Number of ICMP Echo Request packets to send
aPref.iLastSecWait=2; //Time to wait for the Last packet. Default 2
aPref.iPattern=_L("FF");
aPref.iHostname=_L("127.0.0.1");
#ifdef IAPSETTING
aPref.iIAP=1;
#endif
}
void CPing::Statistics()
{
//TBuf<300> aux(_L("==========================================\n"));
TBuf<300> aux(_L("\n"));
TReal rLoss=iSentPackets-iRecvPackets-iChksumErrors; // An error is a received but wrong packet
//is not masked as received because of time calculations
if (rLoss<0)
rLoss=0;
TReal rSent=iSentPackets;
TReal r;
if (rSent>0)
r=rLoss/rSent;
else
r=0;
aux.AppendFormat(_L("Lost: %.1f%% Bad: %d"), r*100, iChksumErrors);
//If there's a timestamp and data received
if ((iRecvPackets>0) && (iPacketDataSize >= TIMESTAMP_SIZE))
{
r=(iTimeSum/iRecvPackets); //average in ms
aux.AppendFormat(_L(" Max: %d Min: %d Avg: "),iMaxTime,iMinTime);
TRealFormat format; //no decimals
format.iType|=KDoNotUseTriads; // no thousands separatorlast
aux.AppendNum(r,format);
aux.Append(_L(" ms"));
}
if (iDupPackets>0)
aux.AppendFormat(_L(" Dup: %d"),iDupPackets);
//aux.Append(_L("\n==========================================\n"));
iConsole->WriteLine(aux);
}
// Use when data is not going to be displayed in Quiet mode
void CPing::WriteLineIfNotQuiet(const TDesC& abuf)
{
if (!iQuiet)
iConsole->WriteLine(abuf);
}
TDes* CPing::GetHostName()
{
return &iHostname;
}
void CPing::SetHostName(const TDesC& ahostname)
{
iHostname=ahostname;
}
void CPing::SetConsole(CPingContainer* aConsole)
{
iConsole=aConsole;
}
CPing::~CPing()
{
Cancel();
delete iPacketData; // If something leaves.
delete iReceivedDataBuffer;
CloseAll();
}
//Shows the error and set the application as not running.
//Requires a return after calling it!
void CPing::ErrorL(const TDesC& string,TInt error)
{
TBuf<150> aux;
TBuf<100> errtxt;
//CEikonEnv::Static()->GetErrorText( errtxt,error);
aux.Format(string);
aux.Append(_L(": "));
aux.Append(errtxt);
aux.AppendFormat(_L(" (%d)\n"), error);
WriteLineIfNotQuiet(aux);
//iRunning=EFalse;
iSockErrors++;
if (error==KErrAbort) //Critical Error
{
/* RNetDial xnetdial;
TBool active;
xnetdial.Open();
TInt err=xnetdial.NetworkActive(active);
if (!active)
{
xnetdial.Start();
err=xnetdial.NetworkActive(active);
}
*/
EndPingL();
User::Leave(0); // NOT SURE IF IT'S THE BEST WAY!!!
}
if (iSockErrors>5) //To avoid a chain of errors that blocks everything. Should never happen though
{
EndPingL();
User::Leave(0); // NOT SURE IF IT'S THE BEST WAY!!!
}
}
// checksum of the ICMP packet (ICMP header + data)
TUint16 CPing::in_chksum(TUint16* data, TUint len)
{
TUint16 *d=data;
TUint left=len;
TUint32 sum=0;
TUint16 aux=0;
while (left > 1)
{
sum += *d++;
left -= 2; //because adding 16 bits numbers (2 bytes)
}
if (left==1) //If odd length
{
*(TUint8*) &aux = *(TUint8*) d;
sum += aux;
}
sum = (sum >> 16) + (sum & 0x0ffff);
sum += (sum >> 16);
aux = (TUint16) ~sum;
return aux;
}
// Clears a bit from the iDup table
void CPing::CLR(TUint16 num)
{
TInt num256 = num % 256; //num modulus 256
TInt pos = num256 >> 5; //Position of vector iDup
iDup[pos] &= ~((1 << num % 32 ));
}
// Sets a bit from the iDup table
void CPing::SET(TUint16 num)
{
TInt num256= num % 256; //num modulus 256
TInt pos=num256 >> 5; //Position of vector iDup
iDup[pos] |= (1 << num % 32 );
}
// Tests if the bit is set or not
TBool CPing::TEST(TUint16 num)
{
TUint num256= num % 256; //num modulus 256
TInt pos=num256 >> 5; //Position of vector iDup
return ((iDup[pos] & (1 << num % 32)) == (TUint32)(1 << num % 32));
}
//Generates a random number using iSeed
TUint16 CPing::RandomNumber()
{
if (iSeed==0) //Initialize seed randomly with time
{
TTime time;
time.HomeTime();
iSeed=time.Int64();
}
return ((TUint16)Math::Rand(iSeed)); //Just take the lowest 16 bits)
}
// Composes the whole ICMP packet except time stamp and checksum
void CPing::ComposeFirstICMPPacket()
{
ThdrICMP *hdr;
TUint firstPos = 0;
hdr = (ThdrICMP *)&iPacket[0]; // Can use this one for IPv6 because is the same format
if (iType == IPv4)
hdr->NetSetType(KICMPTypeEchoRequest);
else
hdr->NetSetType(KInet6ICMP_EchoRequest);
hdr->NetSetCode(KICMPCode);
iId = RandomNumber();
((ThdrICMP_Echo *)hdr)->NetSetId(iId);
((ThdrICMP_Echo *)hdr)->NetSetSeq(0);
CLR(0); // Clears the bit in the received/dup buffer
// Jump over possible timestamp
if (iPacketDataSize >= TIMESTAMP_SIZE)
{
firstPos = TIMESTAMP_SIZE;
}
// The rest is filled with a pattern
// The last part of the packet may only be a part of the pattern
TInt i;
TInt j;
//First transform the pattern from text to Hexadecimal
TLex lex;
//TUint8 hex_num;
TBuf<2> hex_digit;
TBuf<2 * MAX_PATTERN_LENGTH> text_pattern;
TUint8 hex_pattern[MAX_PATTERN_LENGTH];
if (iPattern.Length() % 2 == 0)
text_pattern.Copy(iPattern);
else //odd size so the pattern is doubled and we have a even size
{
text_pattern.Copy(iPattern);
text_pattern.Append(iPattern);
}
for (i = 0; i < text_pattern.Length(); i += 2)
{
hex_digit.Copy(text_pattern.Ptr() + i, 2); //Copy 2 text digits 1 byte
lex.Assign(hex_digit);
lex.Val(hex_pattern[i/2], EHex); //Can't fail because only 2 digits
//hex_pattern.Append((TUint8 *)&hex_num, sizeof(TUint8)); //Append the hex digit (byte)
}
for (j = 0, i = firstPos; (TUint)i < iPacketDataSize; i++, j++)
{
iPacket[ICMP_ECHO_HEADER_SIZE + i] = !text_pattern.Length()
? (TUint8)(i % 256)
: hex_pattern[j % (text_pattern.Length()/2)]; //hex_pattern is half the size of text pattern
}
// TBuf8<150> prova;
// prova.Copy(iPacket);
StampPacket();
}
// Only composes timestamp and sequence number and calculates checksum
void CPing::ComposeICMPPacket()
{
ThdrICMP *hdr = (ThdrICMP *)&iPacket[0];
TUint16 seq = (TUint16)(((ThdrICMP_Echo *)hdr)->NetGetSeq() + 1);
((ThdrICMP_Echo *)hdr)->NetSetSeq(seq); // Next seq. Number
CLR(seq); // Clears the bit in the received/dup buffer
StampPacket();
}
void CPing::StampPacket()
{
ThdrICMP *hdr = (ThdrICMP *)&iPacket[0];
if (iPacketDataSize >= TIMESTAMP_SIZE)
{
TTime time;
time.UniversalTime();
*(TInt64*)&iPacket[8] = time.Int64(); // Converts the time to a Int64 value
}
hdr->NetSetChecksum(0);
hdr->NetSetChecksum(in_chksum((TUint16 *)&iPacket[0], iPacketDataSize + ICMP_ECHO_HEADER_SIZE));
}
//Puts the next seq number in the packet (iPacket)
void CPing::NextSeq()
{
ThdrICMP_Echo *hdr;
hdr=(ThdrICMP_Echo *)&iPacket[8];
hdr->SetSeq((TUint16)((hdr->NetGetSeq()) + 1));
}
//TPtrC CPing::PacketTypev6(TInet6HeaderICMP_Echo *aHdr)
void CPing::PacketTypev6(TDes& buf,ThdrICMP *aHdr)
{
//TBuf<40> buf;
TInet6HeaderICMP *hdr = (TInet6HeaderICMP *)aHdr;
//TInet6HeaderICMP_Echo *echoHdr;
TInt8 code=hdr->Code();
switch(hdr->Type())
{
//Errors 0-127
case KInet6ICMP_Unreachable:
//buf.Format(_L("type= "));
buf.Append(_L("Dest. Unreachable: "));
//buf.Format(_L("type=%d "),type);
switch (code)
{
case KInet6ICMP_NoRoute:
//buf.Append(_L("code= "));
buf.Append(_L("No Route "));
break;
case KInet6ICMP_AdminProhibition:
//buf.Append(_L("code= "));
buf.Append(_L("Admin. Prohibition "));
break;
case KInet6ICMP_NotNeighbour:
//buf.Append(_L("code= "));
buf.Append(_L("Not a Neighbour "));
break;
case KInet6ICMP_AddrUnreach:
//buf.Append(_L("code= "));
buf.Append(_L("Addr. Unreachable "));
break;
case KInet6ICMP_PortUnreach:
//buf.Append(_L("code= "));
buf.Append(_L("Port Unreachable "));
break;
default: buf.AppendFormat(_L("code=%d "),code);
}
break;
case KInet6ICMP_PacketTooBig:
//buf.Format(_L("type= "));
buf.Append(_L("Pack. Too big "));
break;
case KInet6ICMP_TimeExceeded:
//buf.Format(_L("type= "));
buf.Append(_L("Time exceeded: "));
switch (code)
{
case KInet6ICMP_HopLimitExceeded:
//buf.Append(_L("code= "));
buf.Append(_L("Hop Limit "));
break;
case KInet6ICMP_FragReassExceeded:
//buf.Append(_L("code= "));
buf.Append(_L("Frag. Reassembly "));
break;
default: buf.AppendFormat(_L("code=%d "),code);
}
break;
case KInet6ICMP_ParameterProblem:
//buf.Format(_L("type= "));
buf.Append(_L("Parameter problem: "));
switch (code)
{
case KInet6ICMP_ErrHdrField:
//buf.Append(_L("code= "));
buf.Append(_L("Bad header filed"));
break;
case KInet6ICMP_NextHdrUnknown:
//buf.Append(_L("code= "));
buf.Append(_L("Unknown Next Header "));
break;
case KInet6ICMP_OptionUnkown:
//buf.Append(_L("code= "));
buf.Append(_L("Unknown Option"));
break;
default: buf.AppendFormat(_L("code=%d "),code);
}
break;
//Information 128-255
case KInet6ICMP_EchoRequest:
//echoHdr=(TInet6HeaderICMP_Echo *)hdr;
buf.Append(_L("Echo Request "));
//buf.AppendFormat(_L("id= %d "),echoHdr->Identifier());
//buf.AppendFormat(_L("Seq= %d "),echoHdr->Sequence());
break;
case KInet6ICMP_EchoReply:
//echoHdr=(TInet6HeaderICMP_Echo *)hdr;
buf.Append(_L("Echo Reply "));
//buf.AppendFormat(_L("id= %d "),echoHdr->Identifier());
//buf.AppendFormat(_L("Seq= %d "),echoHdr->Sequence());
break;
case KInet6ICMP_Redirect:
buf.Append(_L("Redirect "));
break;
case KICMPTypeRouterAdvert:
case KInet6ICMP_RouterAdv:
buf.Append(_L("Router advertisement "));
break;
case KICMPTypeRouterSolicit:
case KInet6ICMP_RouterSol:
buf.Append(_L("Router solicitation "));
break;
case KInet6ICMP_GroupQuery:
buf.Append(_L("KInet6ICMP_GroupQuery "));
break;
case KInet6ICMP_GroupReport:
buf.Append(_L("KInet6ICMP_GroupReport "));
break;
case KInet6ICMP_GroupDone:
buf.Append(_L("KInet6ICMP_GroupDone "));
break;
case KInet6ICMP_NeighborSol:
buf.Append(_L("Neighbor Solicitation "));
break;
case KInet6ICMP_NeighborAdv:
buf.Append(_L("Neighbor Advertisement "));
break;
default: //buf.Format(_L("Unknown ICMP Type"));
buf.Format(_L("Unknown ICMP Type"));
//buf.Format(_L("type=%d "),hdr->Type());
}
//buf.Append(_L("\n"));
}
TPtrC CPing::PacketType(ThdrICMP *aHdr)
{
switch(aHdr->NetGetType())
{
case KICMPTypeEchoReply: return _L("ICMP Echo reply");
case KICMPTypeUnreachable:
switch (aHdr->NetGetCode())
{
case KICMPCodeUnreachNet: return _L("Network Unreachable");
case KICMPCodeUnreachHost: return _L("Host Unreachable");
case KICMPCodeUnreachProtocol: return _L("Protocol Unreachable");
case KICMPCodeUnreachPort: return _L("Port Unreachable");
case KICMPCodeUnreachNeedFrag: return _L("Message too long. Fragmentation needed");
case KICMPCodeUnreachSrcRouteFail: return _L("Source Route Failed");
case KICMPCodeUnreachNetUnknown: return _L("Destination Network Unknown");
case KICMPCodeUnreachHostUnknown: return _L("Destination Host Unknown");
case KICMPCodeUnreachSrcHostIsolated: return _L("Source host isolated");
case KICMPCodeUnreachNetProhibited: return _L("Destination Network Administatively prohibited");
case KICMPCodeUnreachHostProhibited: return _L("Destination Host Administatively prohibited");
case KICMPCodeUnreachNetTOS: return _L("Network Unreachable for TOS");
case KICMPCodeUnreachHostTOS: return _L("Host Unreachable for TOS");
case KICMPCodeUnreachProhibited: return _L("Communication Administatively prohibited");
case KICMPCodeUnreachPrecVolation: return _L("Host Precedence violation");
case KICMPCodeUnreachPrecCutoff: return _L("Precedence cutoff in effect");
default: return _L("Unknown code for Destination Unreachable");
}
case KICMPTypeSourceQuench: return _L("Source Quench");
case KICMPTypeRedirect:
switch (aHdr->NetGetCode())
{
case KICMPCodeRedirectNet: return _L("Redirect for network");
case KICMPCodeRedirectHost: return _L("Redirect for Host");
case KICMPCodeRedirectNetTOS: return _L("Redirect for TOS and Network");
case KICMPCodeRedirectHostTOS: return _L("Redirect for TOS and Host");
default: return _L("Unknown code for ICMP Redirect");
}
case KICMPTypeEchoRequest: return _L("Echo Request");
case KICMPTypeRouterAdvert: return _L("Router advertisement");
case KICMPTypeRouterSolicit: return _L("Router solicitation");
case KICMPTypeTimeExceeded:
switch (aHdr->NetGetCode())
{
case KICMPCodeExceedInTransit: return _L("TTL 0 during Transit");
case KICMPCodeExceedInReasm: return _L("TTL 0 during Reassembly");
default: return _L("Unknown Code for Time exceeded type");
}
case KICMPTypeBadParameter: return _L("Parameter Problem");
case KICMPTypeTimeRequest: return _L("Timestamp Request");
case KICMPTypeTimeReply: return _L("Timestamp Reply");
case KICMPTypeInfoRequest: return _L("Information Request");
case KICMPTypeInfoReply: return _L("Information Reply");
case KICMPTypeMaskRequest: return _L("Adress Mask Request");
case KICMPTypeMaskReply: return _L("Adress Mask Reply");
default: return _L("Unknown ICMP Type");
}
}
// Shows ICMP data in the Packet and calculates time related info
void CPing::PrintICMPData(const TDesC8& data)
{
TBuf<300> aux;
ThdrICMP *hdr; // Use only this one because the packet format for ICMP_Echo_reply is
// identical in both ICMPv4 and ICMPv6
hdr=(ThdrICMP*)&data[0]; // ICMP packet format
TUint type;
if (iType==IPv4)
type=KICMPTypeEchoReply;
else // ICMPv6
type=KInet6ICMP_EchoReply;
if (hdr->NetGetType()!=type)
{
// We want to list other packets than ICMP Echo Reply
if (iVerbose)
{
if (iType==IPv4)
aux.Format(PacketType(hdr)); // Return a description of the packet Type and Code
else
{
TBuf<40> auxBuf;
PacketTypev6(auxBuf,hdr);
aux.Format(auxBuf); // Return a description of the packet Type and Code
}
aux.Append(_L("\n"));
WriteLineIfNotQuiet(aux);
} // else we ignore them
return;
}
// Checks if it's a the packet have a correct Id
// Useful if two instances if Ping running (not possible in this version)
if (((ThdrICMP_Echo *)hdr)->NetGetId()!=iId)
{
aux.Append(_L("Packet with wrong id received\n"));
WriteLineIfNotQuiet(aux);
return;
}
// Correct packet type and code
aux.AppendFormat(_L("Seq: %u"),((ThdrICMP_Echo *)hdr)->NetGetSeq());
// Checks if chksum is correct must be 0 (because includes the checksum field)
// else there's something wrong
if (iType==IPv4)
{
if (in_chksum((TUint16 *)&data[0], iPacketDataSize + ICMP_ECHO_HEADER_SIZE)!=0)
{
aux.Append(_L(" Checksum Error\n"));
WriteLineIfNotQuiet(aux);
iChksumErrors++;
return;
}
}
// ICMPv6 checks checksum internally
// Timestamp calculation
if (iPacketDataSize >= TIMESTAMP_SIZE)
{
TTime now;
TTime time(*(TInt64*)&data[8]);
now.UniversalTime();
TTimeIntervalMicroSeconds interval;
interval = now.MicroSecondsFrom(time);
#ifdef I64LOW
TUint num = I64LOW(interval.Int64()) / 1000;
#else
TUint num = interval.Int64().GetTInt() / 1000;
#endif
if (num > iMaxTime)
iMaxTime = num;
if (num < iMinTime)
iMinTime = num;
iTimeSum += num;
aux.AppendFormat(_L("\tTime: %d ms"),num);
}
iRecvPackets++;
// Test if duplicated
if (TEST(((ThdrICMP_Echo *)hdr)->NetGetSeq()))
{
aux.Append(_L("\tDUPLICATED"));
iRecvPackets--; // because duplicated
iDupPackets++; // to show it in statistics
}
else
{
SET(((ThdrICMP_Echo *)hdr)->NetGetSeq()); // Marks the packet as received
}
//aux.Append(_L("\n"));
WriteLineIfNotQuiet(aux);
}
void CPing::Stop()
{
iRunning=EFalse;
iConsole->OnEnd();
//CEikonEnv::Static()->BusyMsgCancel();
/*
delete iPacketData;
iPacketData=NULL;
delete iReceivedDataBuffer;
iReceivedDataBuffer=NULL;
*/
}
void CPing::BeginL()
{
TInt err=0;
if (IsRunning()) // There's another instance running
return;
else
iRunning=ETrue;
//INITIALIZATION
iSentPackets=0; //ICMP Echo Request Packets sent
iRecvPackets=0; //ICMP Echo Reply Packets received
iChksumErrors=0; //Packets with errors when checking checksum
iSockErrors=0; //Errors when writing/reading to/from the sockets
iDupPackets=0; //Duplicated packets
iMaxTime=0; //Time-related vars
iMinTime=KMaxTUint32;
iTimeSum=0;
if (iPacketDataSize > MAX_ICMP_PACKETSIZE) //Is prevented in .rss file
iPacketDataSize = MAX_ICMP_PACKETSIZE;
delete iPacketData;
iPacketData= HBufC8::NewL(iPacketDataSize + ICMP_HDRLEN);
//Allocates space for the ICMP packet
TPtr8 aux(iPacketData->Des()); //weird but necessary. Cannot use Des() directly in iPacket
iPacket.Set(aux);
//iPacket.SetMax(); //because it'll be written directly using [] in Compose...() and it would crash.
//HUOM!!! Cannot be SetMax because sometimes the reserved size is slightly bigger. Because of block size?
iPacket.SetLength(iPacketDataSize + ICMP_HDRLEN); //because it'll be written directly using [] in Compose...() and it would crash.
delete iReceivedDataBuffer;
iReceivedDataBuffer= HBufC8::NewL(iPacketDataSize + ICMP_ECHO_HEADER_SIZE); //Maximum size of a return packet
TPtr8 auxPtr(iReceivedDataBuffer->Des()); //must be used here because the buffer changes
iReceivedData.Set(auxPtr); //we use an aux var because can't use Des() directly Why??
iConsole->WriteHostL(iHostname);
iConsole->WriteLine(_L("Connecting...\n"));
iConsole->UpdateStatisticsL();
#ifdef IAPSETTING
//CStoreableOverrideSettings *settings = NULL;
//CCommDbOverrideSettings::TParamList ParamList = CCommDbOverrideSettings::TParamList::EParamListPartial;
//CCommDbOverrideSettings::TParamList ParamList = CCommDbOverrideSettings::TParamList::EParamListPartial;
TCommDbDatabaseType Type = EDatabaseTypeIAP;
CStoreableOverrideSettings *settings = CStoreableOverrideSettings::NewL(CCommDbOverrideSettings::EParamListPartial,Type);
CleanupStack::PushL(settings);
CCommsDbConnectionPrefTableView::TCommDbIapConnectionPref conPref;
conPref.iRanking = 1;
conPref.iDirection = ECommDbConnectionDirectionOutgoing;
CCommsDbConnectionPrefTableView::TCommDbIapBearer bearer;
bearer.iIapId = iIAP;
conPref.iBearer = bearer;
err = settings->SetConnectionPreferenceOverride(conPref);
User::LeaveIfError(err);
User::LeaveIfError(iGenericAgent.Open());
iGenericAgent.StartOutgoing(*settings,iStatus);
err = iGenericAgent.DisableTimers();
if (err != KErrAlreadyExists)
User::LeaveIfError(err);
CleanupStack::PopAndDestroy();
User::WaitForAnyRequest();
#endif
//connecting the Socket Server
err=iSockServ.Connect(); //KESockDefaultMessageSlots
if (err!=KErrNone)
{
ErrorL(_L("Socket Server Error (Connect)"),err);
Stop();
return;
}
err=iHostResolv.Open(iSockServ, KAfInet, KProtocolInetIcmp); // Address Resolver
if (err!=KErrNone)
{
ErrorL(_L("Resolver Error (Open)"),err);
Stop();
return;
}
//Report(_L("Resolver Open"));
iConsole->WriteLine(_L("Resolving..."));
iHostResolv.GetByName(iHostname,iEntry,iStatus);
//Report(_L("Resolver GetByName"));
iStage=0;
//CEikonEnv::Static()->BusyMsgL(R_RESOLVING_NAME);
//-------------------------------------------------
//DO NOT REMOVE the next commented code!!!!!!!!!!!!!!!!!!
/*
//Never will be used because the socket is opened as ICMP not IP so is not usable
//unless some changes are made to the socket and packet format.
if (iHopLimit!=0) // 0 means value not set
{ //Only setable for multicast adresses
err=iSocket.SetOpt(KSoIpTTL,KSOLSocket,iHopLimit); //Set TTL (max. hops)
if (err==KErrNotSupported)
iConsole->WriteLine(_L("TTL can only be set with multicast adress (Not used)\n"));
else
if (err<0)
{
ErrorL(_L("Socket Error (SetOpt)"),err);
return;
}
}
-------------------------------------------------------- */
IssueRequest();
/* iStage=0;
TRequestStatus *xst=&iStatus;
User::RequestComplete(xst,KErrNone);*/
}
void CPing::CreateSocketAOL()
{
iPingSender=new(ELeave) CPingSender;
iPingSender->ConstructL(this); //2nd phase
iPingSender->FirstRunL(); //Starts packet sending
}
//Issues next RunL execution
void CPing::IssueRequest()
{
SetActive(); //Sets the object as Active.
//RunL will be executed when iStatus!=KRequestPending (set by CPingSender)
}
void CPing::RunL()
{
TInt err=KErrNone;
TBuf<39> textIPaddress; //text address to be displayed
TBuf<356> aux;
TInetAddr anyaddr;
TRequestStatus xStatus;
switch (iStage)
{
case 0:
//Report(_L("Resolver GetByName end"));
if (iStatus==KErrNotFound)
{//The Nameserver couldn't find the Host.
TBuf<100> warn(iHostname);
warn.Append(_L(" not found!\n"));
iConsole->WriteLine(warn);
iHostResolv.Close();
Stop();
return;
}
if (iStatus!=KErrNone)
{
ErrorL(_L("Resolver Error (GetByName)"),iStatus.Int());
iHostResolv.Close();
//iSockServ.Close();
Stop();
return;
}
iHostResolv.Close();
iHostAddr = TInetAddr::Cast(iEntry().iAddr); //host address
// The type of PING (ICMPv4 or ICMPv6) depens purely on the destination address.
// ...for IPv4 (or IPv4 mapped), use ICMPv4
// ...for true IPv6, use ICMPv6
iType = (iHostAddr.Family() == KAfInet6 && !iHostAddr.IsV4Mapped()) ? IPv6 : IPv4;
iHostAddr.Output(textIPaddress);
/*aux.Append(iEntry().iName); // maybe the main name is not the entered
aux.Append(_L(" is "));
aux.Append(textIPaddress);
aux.AppendFormat(_L("\nUsing %d data bytes"), iPacketDataSize);
*/
aux.AppendFormat(_L("Pinging %S [%S] with %d bytes of data:\n"), &iEntry().iName, &textIPaddress, iPacketDataSize);
if (iPacketDataSize < TIMESTAMP_SIZE)
aux.AppendFormat(_L("timestamps disabled (min %d bytes)\n"), TIMESTAMP_SIZE);
//aux.Append(_L("\n\n"));
iConsole->WriteLine(aux);
if (iType==IPv4)
{
err=iSocket.Open(iSockServ,_L("icmp"));
}
else
{
err=iSocket.Open(iSockServ,_L("icmp6"));
}
if (err!=KErrNone)
{
ErrorL(_L("Socket Error (Open)"),err);
Stop();
return;
}
//iStage++;
anyaddr.SetAddress(KInetAddrAny); //Sniffs all packets
anyaddr.SetPort(KProtocolInetIcmp); //Sniffs all packets
err=iSocket.Bind(anyaddr);
if (err!=KErrNone)
{
ErrorL(_L("Socket Error (Bind)"),err);
Stop();
return;
}
iSocket.Connect(iHostAddr,xStatus); //must be wait here or panic esock14
User::WaitForRequest(xStatus);
if (xStatus.Int()!=KErrNone)
{
ErrorL(_L("Socket Error (Connect)"),xStatus.Int());
Stop();
return;
}
// Socket Options setting
if (iDebug)
err=iSocket.SetOpt(KSODebug,KSOLSocket,1); //Enable debugging
else
err=iSocket.SetOpt(KSODebug,KSOLSocket,0); //disable debugging (DEFAULT)
if (err!=KErrNone)
{
ErrorL(_L("Socket Error (SetOpt)"),err);
//iHostResolv.Close();
iSocket.Close();
//iSockServ.Close();
#ifdef IAPSETTING
iGenericAgent.Close();
#endif
Stop();
return;
}
//CEikonEnv::Static()->BusyMsgCancel(); //Cancel the resolving name msg
CreateSocketAOL(); //Creates the send A.O.
iSocket.Read(iReceivedData,iStatus);
iStage++;
IssueRequest(); //Prepares to receive it in RunL()
break;
case 1:
//Report(_L("Socket Read end"));
if (iStatus==KErrNone)
{
PrintICMPData(iReceivedData); //The previous packet
}
else
ErrorL(_L("Read (Recv)"),iStatus.Int());
iConsole->UpdateStatisticsL();
iSocket.Read(iReceivedData,iStatus); // NEXT Packet
IssueRequest(); // Prepares to receive it in RunL()
// No more stages!
break;
default:
//CEikonEnv::Static()->InfoMsg(_L("Bad Stage!!!"));
EndPingL();
}
}
void CPing::DoCancel()
{
if (iStage==0)
{
iHostResolv.Cancel();
}
else if (iStage==1)
{
//RSocket::Read has been called, so need to cancel this outstanding Read
iSocket.CancelRead();
}
}
void CPing::CloseAll()
{
iHostResolv.Close();
//iSocket.CancelAll(); //Cancel all outstanding requests
iSocket.Close();
iSockServ.Close();
#ifdef IAPSETTING
iGenericAgent.Close();
#endif
}
// Stops Ping
void CPing::EndPingL()
{
//CEikonEnv::Static()->BusyMsgCancel(); // Cancel the resolving name msg in case it wasn't
Statistics();
iConsole->UpdateStatisticsL();
if (iPingSender!=0) // Not needed if control dimmed because can't launch 2 pings
{
delete iPingSender;
iPingSender=0;
}
Cancel();
CloseAll();
iRunning = EFalse;
iConsole->OnEnd();
}
// Just checks if sending packets from a previous ping
TBool CPing::IsRunning()
{
return iRunning;
}
// Sends packets with an active Timer
// Created by CPing
// Not intended to run alone
CPingSender::CPingSender():CTimer(EPriorityStandard)//,iSentData(0,0)
{
CActiveScheduler::Add(this); //Adds itself to the scheduler only the first time
}
CPingSender::~CPingSender()
{
Cancel();
delete iSender;
}
void CPingSender::ConstructL(CPing *aPingModel)
{
//Base class 2nd phase constructor
CTimer::ConstructL();
iPingModel=aPingModel; // Poiter to the model which contains data
//iSentDataBuffer= HBufC8::NewL(iPingModel->iPacketDataSize + ICMP_HDRLEN);
//Allocates the maximum space needed the size chosen + ICMP Header
iSender=new(ELeave) CPingSingleSender(iPingModel);
}
//Issues next RunL execution
void CPingSender::IssueRequest()
{
After(iPingModel->iSecWait*SECOND); //Also sets the object as Active
}
//Issues last RunL execution
void CPingSender::IssueLastRequest()
{
After(iPingModel->iLastSecWait*SECOND); //Also sets the object as Active
}
void CPingSender::FirstRunL()
{
SendFirstPacketL();
if ((iPingModel->iSentPackets==iPingModel->iTotalPackets) && (iPingModel->iPackLimit))
IssueLastRequest(); //Last RunL have a special waiting time. to receive the last reply
else
IssueRequest(); //First RunL
}
// will send all the packets. One packet each Time
void CPingSender::RunL()
{
if ((iPingModel->iSentPackets>=iPingModel->iTotalPackets) && (iPingModel->iPackLimit))
{
iPingModel->EndPingL();
return;
}
else //There are packets to send or number unlimited
{
SendPacket();
if ((iPingModel->iSentPackets>=iPingModel->iTotalPackets) && (iPingModel->iPackLimit))
IssueLastRequest(); //Last RunL have a special waiting time. to receive the last reply
else
IssueRequest(); //Next RunL
}
}
//Creates a AO that sends a packets waits for it to be send and dies
void CPingSender::SendFirstPacketL()
{
iSender->FirstRunLD(); //Starts packet sending. Destroys itself
}
//Creates a AO that sends a packets waits for it to be send and dies
void CPingSender::SendPacket()
{
iSender->NextPacket();
}
//Cancel Packet Sending
void CPingSender::DoCancel()
{
CTimer::DoCancel();
}
// Used by CPingSender
// Sends packets. Cannot be done directly by CPingSender because there are conflicts with
// diferent TRequestStatus.
CPingSingleSender::CPingSingleSender(CPing *aPingModel):CActive(EPriorityStandard)
{
iPingModel=aPingModel; // Pointer to the model which contains data
CActiveScheduler::Add(this); //Adds itself to the scheduler only the first time
}
CPingSingleSender::~CPingSingleSender()
{
Cancel();
}
// Issues next RunL execution
void CPingSingleSender::IssueRequest()
{
SetActive(); // Sets the object as Active.
}
void CPingSingleSender::FirstRunLD()
{
iPingModel->ComposeFirstICMPPacket();
iPingModel->iSocket.Write(iPingModel->iPacket, iStatus); // iStatus used by CTimer
iPingModel->iSentPackets++;
iUnsent=0;
IssueRequest();
}
void CPingSingleSender::NextPacket()
{
if (IsActive()) // Still a packet being sent
iUnsent++; // Cannot sent here because iSatus would be overwritten
else
{
iPingModel->ComposeICMPPacket();
iPingModel->iSocket.Write(iPingModel->iPacket, iStatus); // No other request waiting
iPingModel->iSentPackets++;
IssueRequest();
}
}
// will send all the packets. One packet each Time
// when entering this function,
// it means either one packet has actually been sent, or failed
void CPingSingleSender::RunL()
{
// TBuf<50> aux;
// if (iStatus==KErrNone)
// iPingModel->iSentPackets++;
//else if (iStatus!=KRequestPending)
if (iStatus!=KErrNone)
{
iPingModel->iSentPackets--; //Packet sending failed
iPingModel->ErrorL(_L("Write"),iStatus.Int());
}
//aux.Format(_L("Write end (st=%d, sent=%d)"),iStatus,iPingModel->iSentPackets);
//iPingModel->WriteLineIfNotQuiet(aux);
iPingModel->iConsole->UpdateStatisticsL();
//Ignores the timer request because there are packets that should already be sent
if (iUnsent)
{
iPingModel->ComposeICMPPacket();
iPingModel->iSocket.Write(iPingModel->iPacket, iStatus); // iStatus used by CTimer
iPingModel->iSentPackets++;
iUnsent--;
IssueRequest();
}
}
// Cancel Packet Sending
void CPingSingleSender::DoCancel()
{
iPingModel->iSocket.CancelWrite();
}