#ifdef HAVE_CONFIG_H
#include "config.h"
#endif // HAVE_CONFIG_H
#include "ping.h"
#include <iostream>
#include <string>
#include <map>
#include <utility>
#include <cstdio>
#include <time.h>
#include <signal.h> // to trap Ctrl+C : SIGINT
#ifndef _WIN32
#include <fcntl.h>
#include <sys/types.h>
#include <netdb.h>
#include <errno.h>
#include <arpa/inet.h>
#endif // _WIN32
//---------------------------------------------------------------------------
// globals
typedef std::map<std::string,std::pair<unsigned, unsigned> > stat_type;
stat_type statistics;
unsigned Ping_Count, transmitted_packets;
bool Terminate;
//---------------------------------------------------------------------------
// Prototypes
unsigned short in_cksum(unsigned short*, int);
void ping(const std::string&);
void send_request(raw_socket_stream&, const std::string&);
bool recv_reply(raw_socket_stream&, ECHO_REPLY&);
void print_statistics(const ECHO_REPLY&);
void print_final_statistics();
bool is_broadcast_address();
//---------------------------------------------------------------------------
// signal handler
void CTLRC(int);
//---------------------------------------------------------------------------
int main(int argc, char** argv) {
Ping_Count = 4;
std::string host("");
if (argc < 2) {
std::cerr << "Usage: ping [-n NUM] host" << std::endl;
return 1;
}
Terminate = false;
transmitted_packets=0;
for(int i=1; i < argc; i++) {
if(std::string(argv[i]).compare("-n")==0) {
if(i == argc-1) {
std::cerr << "Usage: ping [-n NUM] host" << std::endl;
return 1;
}
i++;
char **error = NULL;
Ping_Count = strtoul(argv[i],error,0);
if((Ping_Count==0)||(error != NULL)) {
std::cerr << "Usage: ping [-n NUM] host" << std::endl;
return 1;
}
} else {
host.assign(argv[i]);
}
}
if(host.size() == 0){
std::cerr << "Usage: ping [-n NUM] host" << std::endl;
return 1;
}
// register SIGINT handler
signal(SIGINT, CTLRC);
#ifdef __BORLANDC__
#define CLOCKS_DIV 1.0/(CLK_TCK*1.0)
#else
#define CLOCKS_DIV 1.0/(CLOCKS_PER_SEC*1.0)
#endif
std::cout << "Time resolution: " << CLOCKS_DIV*1000.0 << " msec." << std::endl;
ping(host);
return 0;
}
//---------------------------------------------------------------------------
void ping(const std::string& host) {
raw_socket_stream ping_socket(FreeSockets::proto_ICMP);
if(!ping_socket) {
std::cerr << "Could not create raw socket." << std::endl;
exit(0);
}
if(is_broadcast_address()) {
if(!ping_socket.setBroadcast(true)) {
std::cerr << "Could not set broadcast socket." << std::endl;
exit(0);
}
}
ping_socket.setTarget(host,FreeSockets::echo);
ECHO_REPLY reply;
for(unsigned i=0; i < Ping_Count; i++) {
send_request(ping_socket,host);
transmitted_packets++;
if(recv_reply(ping_socket,reply))
print_statistics(reply);
}
while(!Terminate) {
if(recv_reply(ping_socket,reply))
print_statistics(reply);
else break; // quit on timeout
}
print_final_statistics();
}
//---------------------------------------------------------------------------
void send_request(raw_socket_stream& sock, const std::string& host) {
static ECHO_REQUEST echoReq;
static int nId = 1;
static int nSeq = 1;
// Fill in echo request
echoReq.icmpHdr.Type = ICMP_ECHOREQ;
echoReq.icmpHdr.Code = 0;
echoReq.icmpHdr.Checksum = 0;
echoReq.icmpHdr.ID = nId++;
echoReq.icmpHdr.Seq = nSeq++;
echoReq.dwTime = clock();
// Fill in some data to send
for(int i=0; i < REQ_DATASIZE; i++)
echoReq.cData[i] = ' '+i;
// Put data in packet and compute checksum
echoReq.icmpHdr.Checksum =
in_cksum((unsigned short*)&echoReq,sizeof(ECHO_REQUEST));
const sockaddr_storage & sst = sock.getOutpeer();
#ifndef HAVE_GETADDRINFO
#warning Legacy resolver code
std::cout << "Pinging "<<host<<"["<<::inet_ntoa(((sockaddr_in &)sst).sin_addr);
#else // HAVE_GETADDRINFO
char hbuf[NI_MAXHOST];
if (::getnameinfo((const sockaddr*)&sst, sock.getOutpeerSize(),
hbuf, sizeof(hbuf), 0, 0, NI_NUMERICHOST) == 0) {
std::cout << "Pinging " << host << "[" << hbuf;
} else {
std::cout << "Pinging [unknown";
}
#endif // HAVE_GETADDRINFO
std::cout << "] with " << REQ_DATASIZE << " bytes of data." << std::endl;
sock.setTimeout(3,0);
sock.write((char*)&echoReq,sizeof(ECHO_REQUEST));
sock.flush();
// Check for timeout
if(sock.fail()) {
if(sock.timeout()) {
std::cerr << "Timeout sending ICMP request." << std::endl;
} else {
unsigned error = sock.getLastError();
std::cerr << "SEND: Ping Error #" << error << std::endl;
#ifdef __linux__
std::cerr << strerror(error) << std::endl;
#endif
}
exit(0);
}
}
//---------------------------------------------------------------------------
bool recv_reply(raw_socket_stream& sock, ECHO_REPLY& reply)
{
sock.setTimeout(3,0);
sock.read((char*)&reply,sizeof(ECHO_REPLY));
// Check for timeout
if(sock.timeout()) return false;
// Check for errors
if(sock.fail()) {
unsigned error = sock.getLastError();
std::cerr << "RECV: Ping Error #" << error << std::endl;
#ifdef __linux__
std::cerr << strerror(error) << std::endl;
#endif
exit(0);
}
return true;
}
//---------------------------------------------------------------------------
void print_statistics(const ECHO_REPLY& reply) {
// again: portability vs. precision
clock_t elapsed = clock() - reply.echoRequest.dwTime;
std::string replier;
#ifndef HAVE_GETADDRINFO
#warning Legacy resolver code
replier = ::inet_ntoa(reply.ipHdr.iaSrc);
#else // HAVE_GETADDRINFO
char hbuf[NI_MAXHOST];
if (::getnameinfo((const sockaddr*)&reply.ipHdr.iaSrc,
sizeof(reply.ipHdr.iaSrc),
hbuf, sizeof(hbuf), 0, 0, NI_NUMERICHOST) == 0) {
replier = hbuf;
} else {
replier = "[unknown]";
}
#endif // HAVE_GETADDRINFO
stat_type::iterator iter = statistics.find(replier);
if(iter == statistics.end())
iter = statistics.insert(std::pair<std::string, std::pair<unsigned, unsigned> >(replier,std::pair<unsigned, unsigned>(0,0))).first;
(*iter).second.first++;
(*iter).second.second += elapsed;
std::cout << "Reply from: " << replier;
std::cout << " : bytes=" << REQ_DATASIZE << " time=" << elapsed << "ms";
std::cout << " TTL=" << int(reply.ipHdr.TTL) << std::endl;
}
//---------------------------------------------------------------------------
void print_final_statistics() {
stat_type::iterator iter = statistics.begin();
std::cout << "\nTransmitted Packets: " << transmitted_packets << std::endl;
while(iter != statistics.end()) {
std::cout << "\nStatistics for host " << (*iter).first << ":" << std::endl;
std::cout << "\tPackets received: " << (*iter).second.first << std::endl;
double mean = (*iter).second.second/((*iter).second.first*1.0);
std::cout << "\tMean ping time: " << mean*CLOCKS_DIV << " msec." << std::endl;
unsigned lost_packets = transmitted_packets - (*iter).second.first;
std::cout << "\tLost Packets: " << lost_packets << std::endl;
std::cout << "\tPacket loss: "<< double(lost_packets/(transmitted_packets*1.0))*100.0 << "%" << std::endl;
iter++;
}
}
//---------------------------------------------------------------------------
void CTLRC(int) {
std::cout << "Stopping service..." << std::endl;
Terminate=true;
}
//---------------------------------------------------------------------------
bool is_broadcast_address() {
return true;
}
//---------------------------------------------------------------------------
// Mike Muuss' in_cksum() function
// and his comments from the original
// ping program
//
// * Author -
// * Mike Muuss
// * U. S. Army Ballistic Research Laboratory
// * December, 1983
/*
* I N _ C K S U M
*
* Checksum routine for Internet Protocol family headers (C Version)
*
*/
unsigned short in_cksum(unsigned short *addr, int len) {
register int nleft = len;
register unsigned short *w = addr;
register unsigned short answer;
register int sum = 0;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum),
* we add sequential 16 bit words to it, and at the end, fold
* back all the carry bits from the top 16 bits into the lower
* 16 bits.
*/
while( nleft > 1 ) {
sum += *w++;
nleft -= 2;
}
/* mop up an odd byte, if necessary */
if( nleft == 1 ) {
u_short u = 0;
*(u_char *)(&u) = *(u_char *)w ;
sum += u;
}
/*
* add back carry outs from top 16 bits to low 16 bits
*/
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
syntax highlighted by Code2HTML, v. 0.9.1