2015-05-20 16 views
6

Sto provando a scrivere una semplice funzione di ping C++ per vedere se un indirizzo di rete sta rispondendo. Non ho bisogno di ICMP in particolare, ho solo bisogno di vedere se il server è lì e rispondere a qualcosa. Ho fatto qualche ricerca e ogni soluzione che mi viene in mente richiede la creazione di un socket raw o qualcosa che richiede che il programma abbia accesso sudo. Non sarò in grado di garantire che il sistema su cui sto girando sarà in grado di modificare lo stack di rete, quindi questo non è valido.Funzione tipo ping C++ senza accesso superutente

Ecco alcune domande correlate che ho già esaminato.

  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

Sembra che da ping richiede l'accesso superutente per una buona ragione. Non voglio creare una scappatoia di sicurezza, voglio solo vedere se un server sta rispondendo. Esiste una buona funzione o risorsa C++ in grado di farlo? Farò in modo di pubblicare qualsiasi soluzione campione che trovo. Ho bisogno di una soluzione socket Linux (BSD). Poiché quasi tutti i sistemi unix-like eseguono SSH, posso anche solo provare contro la porta 22. Ho intenzione di indirizzare solo i sistemi Linux come un vincolo.

Grazie

+1

Sembra che tu hai già provato l'approccio ovvio e colpito un muro a causa di limitazioni di accesso. Quindi quali altri approcci riesci a pensare? Questa domanda è troppo ampia se ci stai chiedendo di elencare quelli a cui possiamo pensare. –

+1

Sarebbe troppo pesante per eseguire il programma 'ping' e analizzare l'output? – 01d55

+0

Buon punto. Posso usare il popen e scriverlo su una stringa. Permettetemi di prendere una decisione su una soluzione di codice di esempio. Non è elegante come pensavo, ma i mendicanti non possono essere scelti. – msmith81886

risposta

5

Ecco un esempio con popen. Mi piacerebbe una soluzione migliore, quindi se c'è un socket o un altro metodo che non ricorre a una chiamata shell, lo apprezzerei molto. Questo dovrebbe essere eseguito solo con g++ ping_demo.cpp -o ping_demo. Fammi sapere se causa errori di compilazione.

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

uscite Esempi

$> 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

grazie! questo è molto meglio di C – xinthose

+0

Giusto per chiarire, pclose non restituisce 0 o 1 (dalla manpage): La funzione pclose() restituisce -1 se wait4 (2) restituisce un errore, o viene rilevato qualche altro errore . In caso di errore, queste funzioni impostano errno per indicare la causa dell'errore. Tutto sommato, ottima risposta. – bergercookie

1

ho creato un facile da usare classe ping dotata di sistema di rilevamento di porta ethernet aggiunto. Ho basato il codice sulla risposta di msmith81886 ma volevo condensare per chi lo utilizzava nell'industria.

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

potresti per favore spiegare cosa è per 'std :: string & details'? Cosa stiamo passando per quel campo? attualmente ho solo un "" " – user2883071

+1

@ user2883071 Sicuro. 'details' è passato per riferimento e viene stampato solo se c'è un errore. Contiene la risposta del comando. Dovresti lasciare la funzione così com'è, e chiamare solo 'test_connection'. – xinthose