diff -r 000000000000 -r 7f656887cf89 commands/ping/pingmodel.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/commands/ping/pingmodel.cpp Wed Jun 23 15:52:26 2010 +0100 @@ -0,0 +1,1255 @@ +// 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 +#endif + +#include +#include +//#include +//#include + +// IPv6 changes CPing::iType, ComposeFirstIcmpPacket()- ICMPType, Socket Open() + +#include "pingmodel.h" + +//#include //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(); + }