主要在Windows上的libssh 0.7.7文档中测试以下代码。
#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上进行了测试,效果很好。