2015-05-20 8 views
6

Ich versuche, eine einfache C++ Ping-Funktion zu schreiben, um zu sehen, ob eine Netzwerkadresse reagiert. Ich brauche ICMP nicht speziell, ich muss nur sehen, ob der Server da ist und auf irgendetwas reagiert. Ich habe einige Nachforschungen angestellt und jede Lösung, die ich mir ausgedacht habe, erfordert das Erstellen eines rohen Sockets oder etwas, das erfordert, dass das Programm sudo-Zugriff hat. Ich kann nicht garantieren, dass das System, auf dem ich es ausführe, den Netzwerkstapel ändern kann, daher ist dies nicht gültig.C++ ping-ähnliche Funktion ohne Superuser-Zugriff

Hier sind einige verwandte Fragen, die ich bereits angeschaut habe.

  1. Opening RAW sockets in linux without being superuser
  2. ICMP sockets (linux)
  3. How to Ping Using Sockets Library - C
  4. Why does ping work without administrator privileges?
  5. C++ Boost.asio Ping

Es scheint, dass Ping für einen guten Grund Superuser-Zugriff erfordert. Ich möchte nicht absichtlich eine Sicherheitslücke schaffen, ich möchte nur sehen, ob ein Server antwortet. Gibt es eine gute C++ - Funktion oder Ressource, die dies tun kann? Ich werde sicherstellen, dass ich jede mögliche Beispiellösung posten werde. Ich brauche eine Linux (BSD) Socket-Lösung. Da fast jedes Unix-ähnliche System SSH ausführt, kann ich sogar nur gegen Port 22 testen. Ich werde nur Linux-Systeme als Einschränkung anvisieren.

Dank

+1

Scheint, wie Sie bereits die offensichtliche Ansatz versucht haben und prallte gegen einen Wand wegen Zugriffsbeschränkungen. Welche anderen Ansätze können Sie sich vorstellen? Diese Frage ist zu weit gefasst, wenn Sie uns bitten, diejenigen aufzulisten, an die wir denken können. –

+1

Wäre es zu schwer, das Programm 'ping' auszuführen und die Ausgabe zu parsen? – 01d55

+0

Guter Punkt. Ich kann Popen verwenden und es in eine Zeichenfolge schreiben. Lassen Sie mich an einer Beispielcodelösung knacken. Das ist nicht so elegant wie ich dachte, aber Bettler können nicht wählerisch sein. – msmith81886

Antwort

5

Hier ist ein Beispiel mit popen. Ich würde eine bessere Lösung lieben, also wenn es einen Sockel oder eine andere Methode gibt, die nicht auf einen Shell-Aufruf zurückgreift, würde ich es sehr schätzen. Dies sollte nur mit g++ ping_demo.cpp -o ping_demo laufen. Lassen Sie es mich wissen, wenn es Kompilierungsfehler verursacht.

// 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; 
} 

Beispiel Ausgänge

$> 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. 
+0

danke! das ist viel besser als C – xinthose

+0

Nur zur Klarstellung, pclose gibt 0 oder 1 (aus der Manpage) nicht zurück: Die pclose() - Funktion gibt -1 zurück, wenn wait4 (2) einen Fehler zurückgibt, oder ein anderer Fehler festgestellt wird . Im Falle eines Fehlers setzen diese Funktionen errno, um die Fehlerursache anzuzeigen. Alles in allem, gute Antwort. – bergercookie

1

habe ich eine einfache Ping-Klasse mit zusätzlichen Ethernet-Port Erkennungsfähigkeit zu verwenden. Ich habe den Code auf der Antwort von msmith81886 basiert, wollte aber für diejenigen kondensieren, die ihn in der Industrie verwenden.

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; 
} 
+0

Könntest du bitte erklären, wofür '' std :: string & details'' gedacht ist? Was gehen wir für dieses Feld ein? zur Zeit übergebe ich nur ein '' '' – user2883071

+1

@ user2883071 Sicher. "Details" wird als Referenz übergeben und nur gedruckt, wenn ein Fehler vorliegt. Es enthält die Antwort des Befehls. Sie sollten die Funktion unverändert belassen und nur 'test_connection' aufrufen. – xinthose