00001
00002
00003 #if _MSC_VER == 1200
00004 #pragma warning(disable : 4786)
00005 #endif // MSC ver 6.0
00006
00007 #include <winsock2.h>
00008 #include <iostream>
00009 #include <iomanip>
00010
00011 #include "icmp.h"
00012 #include "PingTarget.h"
00013 #include "format_system_message.h"
00014 #include "format_system_time.h"
00015 #include "PerformanceCounter.h"
00016 #include "VersionInfo.h"
00017 #include "console.h"
00018 #include "ProgramOptions.h"
00019 #include "format_paragraph.h"
00020
00021 const char* pszLicense =
00022 "\"Ping' em\" copyright Erik Frankenfeld - http://www.frankenfeld.dk/pingem\n"
00023 "\n"
00024 "This program and the source code is free software. "
00025 "You may use, modify and redistribute it at your own risk.";
00026
00027 const char* pszHelpIntro =
00028 "Usage: pingem [options] target(s)\n\n"
00029 "pingem pings each target, waits for responses and then pauses.\n"
00030 "Statistics are shown for each cycle of send and receive.\n\n"
00031 "Available options:";
00032
00033 const char* pszShortHelpIntro =
00034 "Usage: pingem [options] target(s)\n\n"
00035 "pingem -h shows available options and pingem -license displays the license.";
00036
00037 const char* pszMainReturnValues =
00038 "Program exit codes\n\n"
00039 " 0 : No errors occurred.\n"
00040 " 1 : Error in options or parameters.\n"
00041 " 2 : Runtime error occurred in network initialization or usage.\n"
00042 " 3 : One or more target names could not be resolved.\n"
00043 " 4 : Error occurred while creating or writing to the log file.";
00044
00045 #define MAIN_OK 0
00046 #define MAIN_OPTION_ERROR 1
00047 #define MAIN_NETWORK_ERROR 2
00048 #define MAIN_TARGET_UNRESOLVED 3
00049 #define MAIN_LOG_FILE_ERROR 4
00050
00051 PingTargets pingtargets;
00052 ProgramOptions pingoptions;
00053
00054 int main(int argc, char* argv[])
00055 {
00056 std::cout.imbue(std::locale(""));
00057 PerformanceCounter::Init();
00058
00059
00060 pingoptions.AddLong(
00061 'w', "timeout",
00062 1000, 0, LONG_MAX,
00063 false,
00064 "ms",
00065 "Time in milliseconds to wait while sending to and receiving from targets.\n"
00066 "Default value is 1000 ms."
00067 );
00068 pingoptions.AddLong(
00069 'p', "pause",
00070 1000, 0, LONG_MAX,
00071 false,
00072 "ms",
00073 "Time in milliseconds to pause between sending to and receiving from all targets.\n"
00074 "Default value is 1000 ms."
00075 );
00076 pingoptions.AddLong(
00077 'n', "count",
00078 0, 0, LONG_MAX,
00079 false,
00080 "n",
00081 "Number of pings to send to each target. "
00082 "The default value 0 sets no limit to the number of pings."
00083 );
00084 pingoptions.AddString(
00085 'g', "targetsfile",
00086 0, false,
00087 "filename",
00088 "Reads targets from the specified file. One target name or ip per line. "
00089 "Targets in file are added to targets specified as parameters."
00090 );
00091 pingoptions.AddString(
00092 'o', "logfile",
00093 0, false,
00094 "filename",
00095 "Sends output of each ping to the specified file.\n"
00096 "First line is a header with names of targets.\n"
00097 "Remaining lines contains date and time formatted according to the current "
00098 "locale and the response time for each target.\n"
00099 "Columns are separated by the tab character."
00100 );
00101 pingoptions.AddBoolean(
00102 'd', "daily",
00103 0, false,
00104 "Creates a log file for every day using the filename specified for logfile as a pattern: "
00105 "filenameYYYYMMDD.ext\n"
00106 "YYYY is the 4 digit year, MM is the month and DD is the date."
00107 );
00108 pingoptions.AddBoolean(
00109 'u', "hourly",
00110 0, false,
00111 "Creates a log file for every hour using the specified filename for logfile as a pattern: "
00112 "filenameYYYYMMDD_HH.ext\n"
00113 "YYYY is the 4 digit year, MM is the month, DD is the date and HH is the hour 00-23."
00114 );
00115 pingoptions.AddBoolean(
00116 'q', "quiet",
00117 false, false,
00118 "Quiet operation. Only errors terminating the application and their messages are sent to console."
00119 );
00120 pingoptions.AddBoolean(
00121 'h', "help",
00122 false, false,
00123 "Show this option help text and exit."
00124 );
00125 pingoptions.AddBoolean(
00126 '?', 0,
00127 false, false,
00128 "Show this option help text and exit."
00129 );
00130 pingoptions.AddBoolean(
00131 '\0', "exitcodes",
00132 false, false,
00133 "Show available exit codes."
00134 );
00135 pingoptions.AddBoolean(
00136 '\0', "license",
00137 false, false,
00138 "Show license information."
00139 );
00140
00141 if (!pingoptions.Parse(argc, argv, std::cout.getloc()))
00142 {
00143 std::cerr << std::endl << GetProductInfo() << std::endl << std::endl;
00144 std::cerr << format_paragraph(pszShortHelpIntro, 0, GetStdOutWidth()-2);
00145 return MAIN_OPTION_ERROR;
00146 }
00147 if (pingoptions.GetBoolValue("help") || pingoptions.GetBoolValue('?'))
00148 {
00149 std::cerr << GetProductInfo() << std::endl << std::endl;
00150 std::cerr << format_paragraph(pszHelpIntro, 0, GetStdOutWidth()-2);
00151 pingoptions.PrintHelp(std::cerr, GetStdOutWidth()-2);
00152 return MAIN_OK;
00153 }
00154 if (pingoptions.GetBoolValue("exitcodes"))
00155 {
00156 std::cerr << GetProductInfo() << std::endl << std::endl;
00157 std::cerr << format_paragraph(pszMainReturnValues, 0, GetStdOutWidth()-2) << std::endl;
00158 return MAIN_OK;
00159 }
00160 if (pingoptions.GetBoolValue("license"))
00161 {
00162 std::cerr << format_paragraph(pszLicense, 0, GetStdOutWidth()-2) << std::endl;
00163 return MAIN_OK;
00164 }
00165
00166 WSADATA wsa;
00167 if (WSAStartup(MAKEWORD(2,0), &wsa) != 0)
00168 {
00169 std::cerr << "Unable to initialize Winsock2 version 2.0" << std::endl;
00170 std::cerr << format_system_message(WSAGetLastError()) << std::endl;
00171 WSACleanup();
00172 return MAIN_NETWORK_ERROR;
00173 }
00174
00175 bool bAllTargetsResolved = true;
00176 std::string sTargetsFilename = pingoptions.GetStringValue("targetsfile");
00177 if (!sTargetsFilename.empty())
00178 {
00179 bAllTargetsResolved = pingtargets.AddTargets(sTargetsFilename.c_str());
00180 }
00181 for (ProgramOptions::const_parameter_iterator iParam = pingoptions.parameter_begin();
00182 iParam != pingoptions.parameter_end() && bAllTargetsResolved; ++iParam)
00183 {
00184 std::string s = *iParam;
00185 if (!pingtargets.AddTarget(s.c_str()))
00186 bAllTargetsResolved = false;
00187 }
00188 if (!bAllTargetsResolved)
00189 {
00190 std::cerr << std::endl << GetProductInfo() << std::endl << std::endl;
00191 std::cerr << format_paragraph(pszShortHelpIntro, 0, GetStdOutWidth()-2);
00192 return MAIN_TARGET_UNRESOLVED;
00193 }
00194 if (pingtargets.size() == 0)
00195 {
00196 std::cerr << "No available targets." << std::endl << std::endl;
00197 std::cerr << GetProductInfo() << std::endl << std::endl;
00198 std::cerr << format_paragraph(pszShortHelpIntro, 0, GetStdOutWidth()-2);
00199 return MAIN_OPTION_ERROR;
00200 }
00201
00202 long lTimeout = pingoptions.GetLongValue("timeout");
00203 long lPause = pingoptions.GetLongValue("pause");
00204 long lPingCount = pingoptions.GetLongValue("count");
00205 std::string sLogFilename = pingoptions.GetStringValue("logfile");
00206 bool bDaily = pingoptions.GetBoolValue("daily");
00207 bool bHourly = pingoptions.GetBoolValue("hourly");
00208 bool bQuiet = pingoptions.GetBoolValue("quiet");
00209
00210 PingTargets::LOG_FILENAME_TYPE LogFilenameType = PingTargets::LOG_FILENAME_PLAIN;
00211 if (bDaily && !bHourly)
00212 LogFilenameType = PingTargets::LOG_FILENAME_DAILY;
00213 else if (bHourly && !bDaily)
00214 LogFilenameType = PingTargets::LOG_FILENAME_HOURLY;
00215 else if (!bHourly && !bDaily)
00216 LogFilenameType = PingTargets::LOG_FILENAME_PLAIN;
00217 else
00218 {
00219 std::cerr << "Options daily and hourly cannot both be specified." << std::endl << std::endl;
00220 std::cerr << GetProductInfo() << std::endl << std::endl;
00221 std::cerr << format_paragraph(pszShortHelpIntro, 0, GetStdOutWidth()-2);
00222 return MAIN_OPTION_ERROR;
00223 }
00224 if (sLogFilename.empty() && (bDaily || bHourly))
00225 {
00226 std::cerr << "Options daily and hourly requires logfile to be specified." << std::endl << std::endl;
00227 std::cerr << GetProductInfo() << std::endl << std::endl;
00228 std::cerr << format_paragraph(pszShortHelpIntro, 0, GetStdOutWidth()-2);
00229 return MAIN_OPTION_ERROR;
00230 }
00231
00232
00233 SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
00234 if (sockRaw == INVALID_SOCKET)
00235 {
00236 std::cerr << "Unable to create raw socket" << std::endl;
00237 std::cerr << format_system_message(WSAGetLastError()) << std::endl;
00238 WSACleanup();
00239 return MAIN_NETWORK_ERROR;
00240 }
00241
00242 int iDataSize = 32;
00243 int iPacketSize = sizeof(ICMP_HEADER) + iDataSize;
00244 char* pPacket = new char[iPacketSize];
00245 int iRecvSize = iPacketSize + sizeof(IP_HEADER);
00246 char *pRecvBuf = new char[iRecvSize];
00247
00248 long lSequence = 1;
00249 long lPings = 0;
00250 SYSTEMTIME systemtimeStarted;
00251 GetLocalTime(&systemtimeStarted);
00252 PerformanceCounter tickStarted;
00253
00254 COORD coordStart;
00255 coordStart.X = 0;
00256 coordStart.Y = 0;
00257 if (!bQuiet)
00258 {
00259
00260 ClearStdOutBuffer();
00261 }
00262
00263 do
00264 {
00265 PingTargets::const_iterator itTarget = pingtargets.begin();
00266 pingtargets.ClearResponseFlags();
00267
00268 PerformanceCounter endtick;
00269 endtick += PerformanceCounter(0, 0, 0, 0, lTimeout, 0);
00270
00271 SYSTEMTIME systemtimeStartPing;
00272 GetLocalTime(&systemtimeStartPing);
00273 do
00274 {
00275 if (!bQuiet)
00276 {
00277 ClearStdOutLine();
00278 std::cout << "Sending and receiving...\r";
00279 }
00280
00281 timeval tmv;
00282 tmv.tv_sec = 0;
00283 tmv.tv_usec = 0;
00284
00285 if (itTarget != pingtargets.end())
00286 {
00287 const PingTarget& target = **itTarget;
00288 EncodePacket(pPacket, iDataSize, target.MakeSequence(lSequence));
00289 sockaddr_in sockaddrSend;
00290 memset(&sockaddrSend, 0, sizeof(sockaddrSend));
00291 sockaddrSend.sin_addr = target.GetInetAddr();
00292 sockaddrSend.sin_family = AF_INET;
00293 int iSent = sendto(sockRaw, pPacket, iPacketSize, 0, (sockaddr*)&sockaddrSend, sizeof(sockaddrSend));
00294 if (iSent == SOCKET_ERROR)
00295 {
00296
00297
00298 int err = WSAGetLastError();
00299 }
00300 itTarget++;
00301 }
00302 else
00303 {
00304
00305 PerformanceCounter tick;
00306 PerformanceCounter diff = endtick - tick;
00307 diff.GetAsSecondsAndMicroseconds(tmv.tv_sec, tmv.tv_usec);
00308 }
00309
00310 fd_set fds;
00311 FD_ZERO(&fds);
00312 #pragma warning(push)
00313 #pragma warning(disable : 4127)
00314 FD_SET(sockRaw, &fds);
00315 #pragma warning(pop)
00316 int iStatus = select(0, &fds, NULL, NULL, &tmv);
00317 if (iStatus == SOCKET_ERROR)
00318 {
00319
00320
00321 int err = WSAGetLastError();
00322 }
00323 if (iStatus > 0)
00324 {
00325
00326 sockaddr_in sockaddrRecv;
00327 int iRecvLen = sizeof(sockaddrRecv);
00328 memset(&sockaddrRecv, 0, sizeof(sockaddrRecv));
00329 int iRead = recvfrom(sockRaw, pRecvBuf, iRecvSize, 0, (sockaddr*)&sockaddrRecv, &iRecvLen);
00330 if (iRead == SOCKET_ERROR)
00331 {
00332 int err = WSAGetLastError();
00333 if (err == WSAEMSGSIZE)
00334 iRead = iRecvSize;
00335 }
00336 USHORT uRecvSeq;
00337 LONGLONG llRecvTick;
00338 PerformanceCounter tick;
00339 if (DecodePacket(pRecvBuf, iRead, uRecvSeq, llRecvTick))
00340 {
00341 long lRecvId = pingtargets.GetTargetId(uRecvSeq);
00342 long lRecvSeq = pingtargets.GetSequence(uRecvSeq);
00343 if (lRecvSeq == lSequence)
00344 {
00345
00346 PerformanceCounter diff = tick - PerformanceCounter(llRecvTick);
00347 pingtargets.AddResponse(lRecvId, diff.GetAsMilliseconds());
00348 }
00349 }
00350 }
00351 } while (PerformanceCounter() <= endtick);
00352 pingtargets.AddUnreachableResponses();
00353 lPings++;
00354 if (!bQuiet)
00355 {
00356 ClearStdOutLine();
00357 SetStdOutCoord(coordStart);
00358 std::cout << format_system_time(format_system_time::show_seconds) << " : ";
00359 std::cout << lPings << " pings sent to " << (long)pingtargets.size() << " targets in ";
00360 std::cout << format_performance_counter(
00361 PerformanceCounter()- tickStarted,
00362 format_performance_counter::show_seconds
00363 )
00364 << std::endl;
00365 pingtargets.PrintStatisticsHeader(std::cout, GetStdOutWidth());
00366 std::cout << std::endl;
00367 pingtargets.PrintStatistics(std::cout, GetStdOutWidth());
00368 std::cout << std::endl;
00369 }
00370 if (!sLogFilename.empty())
00371 {
00372
00373 if (!pingtargets.PrintLogCurrent(sLogFilename.c_str(),
00374 systemtimeStartPing, LogFilenameType))
00375 return MAIN_LOG_FILE_ERROR;
00376 }
00377 lSequence++;
00378 if (lSequence > pingtargets.GetMaxSequence())
00379 lSequence = 1;
00380
00381 if (!lPingCount || lPings < lPingCount)
00382 {
00383
00384 long lTimeWait = lPause;
00385 long lSec = 1;
00386 while (lTimeWait >= 1000)
00387 {
00388 if (!bQuiet)
00389 std::cout << "Waiting " << lSec << "/" << lPause/1000 << " seconds\r";
00390 Sleep(1000);
00391 lTimeWait -= 1000;
00392 lSec++;
00393 }
00394 if (!bQuiet)
00395 {
00396 ClearStdOutLine();
00397 std::cout << "Waiting less than a second\r";
00398 }
00399 Sleep(lTimeWait);
00400 if (!bQuiet)
00401 ClearStdOutLine();
00402 }
00403 } while (!lPingCount || lPings < lPingCount);
00404
00405 closesocket(sockRaw);
00406 delete []pPacket;
00407 delete []pRecvBuf;
00408 return MAIN_OK;
00409 }