libssh ssh_select нарушает состояние сеанса в Windows - PullRequest
0 голосов
/ 21 февраля 2019

Протестируйте код, как показано ниже, в основном из документа libssh 0.7.7 для Windows.

#include <stdlib.h>
#include <libssh/libssh.h>

#include <errno.h>
#include <string.h>
#include <stdio.h>

int verify_knownhost(ssh_session session)
{
    int state, hlen;
    unsigned char *hash = NULL;
    char *hexa;
    char buf[10];

    state = ssh_is_server_known(session);

    hlen = ssh_get_pubkey_hash(session, &hash);
    if (hlen < 0)
        return -1;

    switch (state)
    {
    case SSH_SERVER_KNOWN_OK:
        break; /* ok */

    case SSH_SERVER_KNOWN_CHANGED:
        fprintf(stderr, "Host key for server changed: it is now:\n");
        ssh_print_hexa("Public key hash", hash, hlen);
        fprintf(stderr, "For security reasons, connection will be stopped\n");
        free(hash);
        return -1;

    case SSH_SERVER_FOUND_OTHER:
        fprintf(stderr, "The host key for this server was not found but an other"
            "type of key exists.\n");
        fprintf(stderr, "An attacker might change the default server key to"
            "confuse your client into thinking the key does not exist\n");
        free(hash);
        return -1;

    case SSH_SERVER_FILE_NOT_FOUND:
        fprintf(stderr, "Could not find known host file.\n");
        fprintf(stderr, "If you accept the host key here, the file will be"
            "automatically created.\n");
        /* fallback to SSH_SERVER_NOT_KNOWN behavior */

    case SSH_SERVER_NOT_KNOWN:
        hexa = ssh_get_hexa(hash, hlen);
        fprintf(stderr, "The server is unknown. Do you trust the host key?\n");
        fprintf(stderr, "Public key hash: %s\n", hexa);
        free(hexa);
        // if (fgets(buf, sizeof(buf), stdin) == NULL)
        // {
           //  free(hash);
           //  return -1;
        // }
        // if (strncasecmp(buf, "yes", 3) != 0)
        // {
           //  free(hash);
           //  return -1;
        // }
        if (ssh_write_knownhost(session) < 0)
        {
            //fprintf(stderr, "Error %s\n", strerror_s(errno));
            free(hash);
            return -1;
        }
        break;

    case SSH_SERVER_ERROR:
        fprintf(stderr, "Error %s", ssh_get_error(session));
        free(hash);
        return -1;
    }

    free(hash);
    return 0;
}

int interactive_shell_session(ssh_session session, ssh_channel channel)
{
    int rc;

    rc = ssh_channel_request_pty(channel);
    if (rc != SSH_OK) return rc;

    rc = ssh_channel_change_pty_size(channel, 80, 24);
    if (rc != SSH_OK) return rc;

    rc = ssh_channel_request_shell(channel);
    if (rc != SSH_OK) return rc;

    char buffer[256];
    int nbytes, nwritten;

    while (ssh_channel_is_open(channel) &&
        !ssh_channel_is_eof(channel))
    {
        struct timeval timeout;
        ssh_channel in_channels[2], out_channels[2];
        fd_set fds;
        int maxfd;

        timeout.tv_sec = 30;
        timeout.tv_usec = 0;
        in_channels[0] = channel;
        in_channels[1] = NULL;
        //out_channels[0] = out_channels[1] = NULL;
        FD_ZERO(&fds);
        FD_SET(0, &fds);
        int fd;
        fd = ssh_get_fd(session);
        FD_SET(fd, &fds);
        maxfd = ssh_get_fd(session) + 1;

#if 0 // it's working without ssh_select
        nbytes = ssh_channel_read(channel, buffer, sizeof(buffer)-1, 0);
        if (nbytes < 0) return SSH_ERROR;
        if (nbytes > 0)
        {
            //        nwritten = write(1, buffer, nbytes);
            //        if (nwritten != nbytes) return SSH_ERROR;
            buffer[nbytes] = '\0';
            printf("%s\n", buffer);
            memset(buffer, '\0', nbytes);
        }
#else
        ssh_select(in_channels, out_channels, maxfd, &fds, &timeout);

        if (out_channels[0] != NULL)
        {
            nbytes = ssh_channel_read(channel, buffer, sizeof(buffer)-1, 0);
            if (nbytes < 0) return SSH_ERROR; // nbytes is always -1
            if (nbytes > 0)
            {
                //        nwritten = write(1, buffer, nbytes);
                //        if (nwritten != nbytes) return SSH_ERROR;
                buffer[nbytes] = '\0';
                printf("%s\n", buffer);
                memset(buffer, '\0', nbytes);
            }
        }
#endif
    }

    return rc;
}

int shell_session(ssh_session session)
{
    ssh_channel channel;
    int rc;

    channel = ssh_channel_new(session);
    if (channel == nullptr)
        return SSH_ERROR;

    rc = ssh_channel_open_session(channel);
    if (rc != SSH_OK)
    {
        ssh_channel_free(channel);
        return rc;
    }

    interactive_shell_session(session, channel);

    ssh_channel_close(channel);
    ssh_channel_send_eof(channel);
    ssh_channel_free(channel);

    return SSH_OK;
}

void ssh()
{
    ssh_session my_ssh_session;
    int rc;
    char *password;

    // Open session and set options
    my_ssh_session = ssh_new();
    if (my_ssh_session == nullptr)
        exit(-1);
    ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "192.168.123.241");
    ssh_options_set(my_ssh_session, SSH_OPTIONS_USER, "test");

    // Connect to server
    rc = ssh_connect(my_ssh_session);
    if (rc != SSH_OK)
    {
        fprintf(stderr, "Error connecting to localhost: %s\n",
            ssh_get_error(my_ssh_session));
        ssh_free(my_ssh_session);
        exit(-1);
    }

    // Verify the server's identity
    // For the source code of verify_knowhost(), check previous example
    if (verify_knownhost(my_ssh_session) < 0)
    {
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        exit(-1);
    }

    // Authenticate ourselves
    password = _strdup("test");
    rc = ssh_userauth_password(my_ssh_session, nullptr, password);
    if (rc != SSH_AUTH_SUCCESS)
    {
        fprintf(stderr, "Error authenticating with password: %s\n",
            ssh_get_error(my_ssh_session));
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        exit(-1);
    }

    shell_session(my_ssh_session);

    ssh_disconnect(my_ssh_session);
    ssh_free(my_ssh_session);
}

int main(int argc, char *argv[])
{
    ssh_set_log_level(SSH_LOG_PROTOCOL);
    ssh();
    return 0;
}

При использовании ssh_select ssh_channel_read всегда возвращает -1, я отлаживал код,

  if (session->session_state == SSH_SESSION_STATE_ERROR){
      return SSH_ERROR;
  }

Похоже, ssh_select нарушает состояние сеанса.Если ssh_select не используется, ssh_channel_read получает данные без проблем.

Я пробовал последнюю версию 0.8.6 и более старую версию 0.6.5, но все они терпят неудачу с одной и той же ошибкой.

И приведенный выше код был протестирован на Ubuntu 18.04, он отлично работает.

...