I am trying to write a simple C++ ping function to see if a network address is responding. I don't need ICMP specifically, I just need to see if the server is there and responding to anything. I have been doing some research and every solution I come up with requires creating a raw socket or something which requires the program to have sudo access. I won't be able to guarantee that the system I am running this on will be able to modify the network stack, so this is not valid.
Here are some related questions I have already looked at.
It appears that ping requires superuser access for a good reason. I don't want to purposefully create a security loophole, I just want to see if a server is responding. Is there a good c++ function or resource that can do this? I will make sure to post any sample solution I come up with. I need a Linux (BSD) socket solution. Since almost every unix-like system runs SSH, I can even just test against port 22. I am only going to target Linux systems as a constraint.
Thanks
Here is an example with popen
. I would love a better solution, so if there is a socket or other method which does not resort to a shell call, I would greatly appreciate it. This should run with just g++ ping_demo.cpp -o ping_demo
. Let me know if it causes compile errors.
// C++ Libraries
#include <cstdio>
#include <iostream>
#include <sstream>
#include <string>
/**
* @brief Convert String to Number
*/
template <typename TP>
TP str2num( std::string const& value ){
std::stringstream sin;
sin << value;
TP output;
sin >> output;
return output;
}
/**
* @brief Convert number to string
*/
template <typename TP>
std::string num2str( TP const& value ){
std::stringstream sin;
sin << value;
return sin.str();
}
/**
* @brief Execute Generic Shell Command
*
* @param[in] command Command to execute.
* @param[out] output Shell output.
* @param[in] mode read/write access
*
* @return 0 for success, 1 otherwise.
*
*/
int Execute_Command( const std::string& command,
std::string& output,
const std::string& mode = "r")
{
// Create the stringstream
std::stringstream sout;
// Run Popen
FILE *in;
char buff[512];
// Test output
if(!(in = popen(command.c_str(), mode.c_str()))){
return 1;
}
// Parse output
while(fgets(buff, sizeof(buff), in)!=NULL){
sout << buff;
}
// Close
int exit_code = pclose(in);
// set output
output = sout.str();
// Return exit code
return exit_code;
}
/**
* @brief Ping
*
* @param[in] address Address to ping.
* @param[in] max_attempts Number of attempts to try and ping.
* @param[out] details Details of failure if one occurs.
*
* @return True if responsive, false otherwise.
*
* @note { I am redirecting stderr to stdout. I would recommend
* capturing this information separately.}
*/
bool Ping( const std::string& address,
const int& max_attempts,
std::string& details )
{
// Format a command string
std::string command = "ping -c " + num2str(max_attempts) + " " + address + " 2>&1";
std::string output;
// Execute the ping command
int code = Execute_Command( command, details );
return (code == 0);
}
/**
* @brief Main Function
*/
int main( int argc, char* argv[] )
{
// Parse input
if( argc < 2 ){
std::cerr << "usage: " << argv[0] << " <address> <max-attempts = 3>" << std::endl;
return 1;
}
// Get the address
std::string host = argv[1];
// Get the max attempts
int max_attempts = 1;
if( argc > 2 ){
max_attempts = str2num<int>(argv[2]);
}
if( max_attempts < 1 ){
std::cerr << "max-attempts must be > 0" << std::endl;
return 1;
}
// Execute the command
std::string details;
bool result = Ping( host, max_attempts, details );
// Print the result
std::cout << host << " ";
if( result == true ){
std::cout << " is responding." << std::endl;
}
else{
std::cout << " is not responding. Cause: " << details << std::endl;
}
return 0;
}
Sample outputs
$> g++ ping_demo.cpp -o ping_demo
$> # Valid Example
$> ./ping_demo localhost
localhost is responding.
$> # Invalid Example
$> ./ping_demo localhostt
localhostt is not responding. Cause: ping: unknown host localhostt
$> # Valid Example
$> ./ping_demo 8.8.8.8
8.8.8.8 is responding.
I created an easy to use ping class with added ethernet port detection capability. I based the code on msmith81886's answer but wanted to condense for those using it in industry.
ping.hpp
#ifndef __PING_HPP_
#define __PING_HPP_
#include <cstdio>
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <cerrno>
#include <cstring>
class system_ping
{
public:
int test_connection (std::string ip_address, int max_attempts, bool check_eth_port = false, int eth_port_number = 0);
private:
int ping_ip_address(std::string ip_address, int max_attempts, std::string& details);
};
#endif
ping.cpp
#include "../include/ping.hpp"
int system_ping::ping_ip_address(std::string ip_address, int max_attempts, std::string& details)
{
std::stringstream ss;
std::string command;
FILE *in;
char buff[512];
int exit_code;
try
{
command = "ping -c " + std::to_string(max_attempts) + " " + ip_address + " 2>&1";
if(!(in = popen(command.c_str(), "r"))) // open process as read only
{
std::cerr << __FILE__ << "(" << __FUNCTION__ << ":" << __LINE__ << ") | popen error = " << std::strerror(errno) << std::endl;
return -1;
}
while(fgets(buff, sizeof(buff), in) != NULL) // put response into stream
{
ss << buff;
}
exit_code = pclose(in); // blocks until process is done; returns exit status of command
details = ss.str();
}
catch (const std::exception &e)
{
std::cerr << __FILE__ << "(" << __FUNCTION__ << ":" << __LINE__ << ") | e.what() = " << e.what() << std::endl;
return -2;
}
return (exit_code == 0);
}
int system_ping::test_connection (std::string ip_address, int max_attempts, bool check_eth_port, int eth_port_number)
{
std::cout << __FILE__ << "(" << __FUNCTION__ << ":" << __LINE__ << ") | started" << std::endl;
int eth_conn_status_int;
std::string details;
try
{
if (check_eth_port)
{
std::ifstream eth_conn_status ("/sys/class/net/eth" + std::to_string(eth_port_number) + "/carrier");
eth_conn_status >> eth_conn_status_int; // 0: not connected; 1: connected
if (eth_conn_status_int != 1)
{
std::cerr << __FILE__ << "(" << __FUNCTION__ << ":" << __LINE__ << ") | eth" << std::to_string(eth_port_number) << " unplugged";
return -1;
}
}
if (ping_ip_address(ip_address, max_attempts, details) != 1)
{
std::cerr << __FILE__ << "(" << __FUNCTION__ << ":" << __LINE__ << ") | cannot ping " << ip_address << " | " << details << std::endl;
return -2;
}
}
catch (const std::exception &e)
{
std::cerr << __FILE__ << "(" << __FUNCTION__ << ":" << __LINE__ << ") | e.what() = " << e.what() << std::endl;
return -3;
}
std::cout << __FILE__ << "(" << __FUNCTION__ << ":" << __LINE__ << ") | ping " << ip_address << " OK" << std::endl;
std::cout << __FILE__ << "(" << __FUNCTION__ << ":" << __LINE__ << ") | ended" << std::endl;
return 0;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With