2015-05-20 3 views
6

Я пытаюсь написать простую функцию пинга C++, чтобы узнать, отвечает ли сетевой адрес. Мне не нужен ICMP специально, мне просто нужно посмотреть, есть ли сервер и что на что ответить. Я занимаюсь некоторыми исследованиями, и каждое решение, которое я придумал, требует создания сырого сокета или чего-то, что требует, чтобы программа имела доступ к sudo. Я не буду в состоянии гарантировать, что система, в которой я запускаю это, сможет изменить сетевой стек, поэтому это недопустимо.C++ ping-like функция без доступа суперпользователя

Вот некоторые связанные вопросы, на которые я уже посмотрел.

  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

Оказывается, что пинг требуется доступ суперпользователя к хорошей причине. Я не хочу целенаправленно создавать лазейку безопасности, я просто хочу посмотреть, отвечает ли сервер. Есть ли хорошая функция или ресурс C++, который может это сделать? Я обязательно отправлю любое пробное решение, которое я придумал. Мне нужно решение для Linux (BSD). Поскольку почти каждая unix-подобная система запускает SSH, я могу даже просто протестировать ее против порта 22. Я собираюсь ориентироваться только на Linux-системы как ограничение.

Благодаря

+1

Похоже, вы уже пробовали очевидный подход и врезался в стену из-за ограничений доступа. Итак, какие другие подходы вы можете придумать? Этот вопрос слишком широк, если вы просите нас перечислить те, о которых мы можем думать. –

+1

Было бы слишком тяжело, чтобы выполнить программу 'ping' и разобрать выход? – 01d55

+0

Хорошая точка. Я могу использовать popen и записывать его в строку. Позвольте мне взломать примерное решение кода. Это не так элегантно, как я думал, но нищие не могут быть выборами. – msmith81886

ответ

5

Вот пример с popen. Я хотел бы получить лучшее решение, поэтому, если есть сокет или другой метод, который не прибегает к вызову оболочки, я был бы очень признателен. Это должно работать только с g++ ping_demo.cpp -o ping_demo. Дайте мне знать, если это вызывает ошибки компиляции.

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

Примеры выходов

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

спасибо! это намного лучше, чем C – xinthose

+0

Просто для уточнения, pclose не возвращает 0 или 1 (из man-страницы): Функция pclose() возвращает -1, если wait4 (2) возвращает ошибку или обнаруживается некоторая другая ошибка , В случае ошибки эти функции устанавливают errno, чтобы указать причину ошибки. В целом, однако, отличный ответ. – bergercookie

1

Я создал простой в использовании класс пинг с дополнительной возможностью обнаружения порта Ethernet. Я основал код на ответе msmith81886, но хотел сконденсироваться для тех, кто использует его в промышленности.

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

не могли бы вы объяснить, для чего предназначена 'std :: string & details'? Что мы передаем для этой области? в настоящее время я только передаю '' '' ' – user2883071

+1

@ user2883071 Конечно. 'details' передается по ссылке и печатается только при наличии ошибки. Он содержит ответ команды. Вы должны оставить функцию как есть и только вызвать 'test_connection'. – xinthose

Смежные вопросы