commands/ping/pingmodel.cpp
author Tom Sutcliffe <thomas.sutcliffe@accenture.com>
Thu, 26 Aug 2010 00:49:35 +0100
changeset 45 534b01198c2d
parent 0 7f656887cf89
child 103 56b6ee983610
permissions -rw-r--r--
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();
	}