С++ SSH КЛИЕНТ С ФАЙЛОМ КОНФИГУРАЦИИ

1588050224402184

 

Ну вот и все, братцы... Теперь программистом может стать любая, даже самая тупорылая макака. Достаточно уметь тыкать пальчиком в смартфон. А что случилось? Да вот, вышла такая нейросеть как GPT Chat, которая, словно те двое из Ларца, все сделает за вас! Например... У меня давно была идея написать свой собственный ssh-клиент с конфиг-файлом, но чот не получалось, я просто не знал как подойти к этой задаче... Но сегодня утром мне попался один бот в Телеграмме, который решил ее за 5 секунд! Запрос был такой:

Show me c++ code of Linux ssh client that reading config file to get remote host address, password and text of command, connects to server, execute that command on server and then disconnect. And example of his config file.

И вот ответ:

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <libssh/libssh.h>

using namespace std;

int main()
{
    string host, user, pass, cmd;
    int port;

    // Read config file
    ifstream configFile("config.txt");
    if(configFile.is_open())
    {
        string line;
        while(getline(configFile, line))
        {
            istringstream iss(line);
            string key, value;
            if(getline(iss, key, '=') && getline(iss, value))
            {
                if(key == "host")
                    host = value;
                else if(key == "user")
                    user = value;
                else if(key == "pass")
                    pass = value;
                else if(key == "port")
                    port = stoi(value);
                else if(key == "cmd")
                    cmd = value;
            }
        }
    }
    else
    {
        cout << "Error: Could not open config file" << endl;
        return 1;
    }

    // Connect to server
    ssh_session session = ssh_new();
    if(session == NULL)
    {
        cout << "Error: Could not create ssh session" << endl;
        return 1;
    }

    ssh_options_set(session, SSH_OPTIONS_HOST, host.c_str());
    ssh_options_set(session, SSH_OPTIONS_USER, user.c_str());
    ssh_options_set(session, SSH_OPTIONS_PORT, &port);

    int rc = ssh_connect(session);
    if(rc != SSH_OK)
    {
        cout << "Error: Could not connect to server" << endl;
        return 1;
    }

    // Authenticate
    rc = ssh_userauth_password(session, NULL, pass.c_str());
    if(rc != SSH_AUTH_SUCCESS)
    {
        cout << "Error: Could not authenticate" << endl;
        return 1;
    }

    // Execute command
    ssh_channel channel = ssh_channel_new(session);
    if(channel == NULL)
    {
        cout << "Error: Could not create channel" << endl;
        return 1;
    }

    rc = ssh_channel_open_session(channel);
    if(rc != SSH_OK)
    {
        cout << "Error: Could not open channel" << endl;
        return 1;
    }

    rc = ssh_channel_request_exec(channel, cmd.c_str());
    if(rc != SSH_OK)
    {
        cout << "Error: Could not execute command" << endl;
        return 1;
    }

    // Read output
    char buffer[1024];
    int nbytes;
    nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
    while(nbytes > 0)
    {
        cout << buffer;
        nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
    }

    // Disconnect
    ssh_channel_send_eof(channel);
    ssh_channel_close(channel);
    ssh_disconnect(session);
    ssh_free(session);

    return 0;
}

// Example of config.txt
host=example.com
user=user
pass=password
port=22
cmd=ls -l

И все же, как выяснилось, система не идальна. Например, приведенный выше код считывает сразу всю строку из конфиг файла. Получается, вместо IP удаленного хоста, он подставляет в переменную hostname всю строку, т. е., такое:

host=example.com

Если собственными ручками присвоить переменной hostname какой-нибудь IP, то аутентификация инициируется, но программа предлагает пользователю ввести пароль самостоятельно. То есть, бот, формально, выполнил задание достаточно точно, ведь в нем не было указано что после прочтения конфига аутентификация должна происходить автоматически... Ну что же, ничто не мешает уточнить запрос:

Show me the C++ code of a Linux ssh client that reads a config file to get the remote host IP address, port number, password, and command text. Authentication by login and password taken from the configuration file should be automated. After connecting to the server, the program (ssh client) executes a command from the configuration file on the server and then disconnects. Additionally, show me a sample of configuration file used by the program (ssh client).

Ну и результат, соответственно, получился точнее:

#include <iostream>
#include <fstream>
#include <string>
#include <libssh/libssh.h>

using namespace std;

// Function to read configuration file
void readConfig(string &host, int &port, string &user, string &password, string &command) {
    ifstream configFile("config.txt");
    string line;
    while (getline(configFile, line)) {
        if (line.find("host:") != string::npos) {
            host = line.substr(6);
        } else if (line.find("port:") != string::npos) {
            port = stoi(line.substr(6));
        } else if (line.find("user:") != string::npos) {
            user = line.substr(6);
        } else if (line.find("password:") != string::npos) {
            password = line.substr(9);
        } else if (line.find("command:") != string::npos) {
            command = line.substr(9);
        }
    }
    configFile.close();
}

int main() {
    // Variables to store information from config file
    string host, user, password, command;
    int port;

    // Read configuration file
    readConfig(host, port, user, password, command);

    // Create SSH session
    ssh_session session = ssh_new();
    if (session == NULL) {
        cerr << "Error creating SSH session" << endl;
        return 1;
    }

    // Connect to remote host
    int rc = ssh_options_set(session, SSH_OPTIONS_HOST, host.c_str());
    if (rc != SSH_OK) {
        cerr << "Error setting hostname: " << ssh_get_error(session) << endl;
        return 1;
    }
    rc = ssh_options_set(session, SSH_OPTIONS_PORT, &port);
    if (rc != SSH_OK) {
        cerr << "Error setting port: " << ssh_get_error(session) << endl;
        return 1;
    }
    rc = ssh_connect(session);
    if (rc != SSH_OK) {
        cerr << "Error connecting: " << ssh_get_error(session) << endl;
        return 1;
    }

    // Authenticate with username and password
    rc = ssh_userauth_password(session, user.c_str(), password.c_str());
    if (rc != SSH_AUTH_SUCCESS) {
        cerr << "Error authenticating: " << ssh_get_error(session) << endl;
        return 1;
    }

    // Execute command
    ssh_channel channel = ssh_channel_new(session);
    if (channel == NULL) {
        cerr << "Error creating channel: " << ssh_get_error(session) << endl;
        return 1;
    }
    rc = ssh_channel_open_session(channel);
    if (rc != SSH_OK) {
        cerr << "Error opening channel: " << ssh_get_error(session) << endl;
        return 1;
    }
    rc = ssh_channel_request_exec(channel, command.c_str());
    if (rc != SSH_OK) {
        cerr << "Error executing command: " << ssh_get_error(session) << endl;
        return 1;
    }

    // Disconnect
    ssh_disconnect(session);
    ssh_free(session);

    return 0;
}

// Sample Configuration File
host: 192.168.0.1
port: 22
user: username
password: password123
command: ls -l

Однако, с данным кодом тоже возникли небольшие проблемы. А именно, компилятор ругнулся, дескать, не знает никакого "libssh". Пришлось установить соответствующий пакет:

sudo apt install libssh-dev

И затем необходимо уточнить параметри линковки при компиляции, иначе лыжи не поедут.

g++ ssh_client.cpp -o madSSH -lssh

Важен именно этот ключик, "-lssh". Ну, и еще заметил, что опять какая-то херня происходит при чтении конфига. Он, вроде бы, все читает верно, но в итоге почему-то жалуется на переменную "password".

Если в функции чтения конфига строго присвоить этой переменной необходимое значение, заменив line.substr(9) на наш рабочий "password123", ошибка уходит. И это, пожалуй, как раз-таки подходящая тема для вопроса на ru stack overflow.

Думаю, цикл статей "Двое из ларца" только начинается... Причем, не только здесь, для меня лично - тут уже вырисовывается глобальная проблема для всего Человечества. Один мой друг, московский программист, Женя Жуков, говорит, что такой код, дескать, громоздкая и монструозная хуйня, но... Ребята, он был сгенерирован по словестному описанию за несколько секунд и пусть он написан на уровне джуна - он, тем не менее, работает!!!

Далее будет статья на тему математического парсера. У меня теперь есть программа, которая парсит математическую строку, прочитанную из конфиг-файла. Я пока еще не знаю, как она устроена, но уже проверил... Я просто в тихом ахуе...