C++ ping-подобная функция без доступа суперпользователя

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

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

  1. Открытие необработанных сокетов в linux без суперпользователя
  2. ICMP сокеты (linux)
  3. как пинговать с помощью библиотеки сокетов-C
  4. почему ping работает без прав администратора?
  5. C++ Boost.asio Ping

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

спасибо

2 ответов


вот пример 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.

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

ping.ГЭС

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