--- /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 <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();
+ }