1
1
libssh/examples/sample.c

520 строки
14 KiB
C
Исходник Обычный вид История

/* client.c */
/*
2009-07-13 02:23:42 +04:00
Copyright 2003-2009 Aris Adamantiadis
This file is part of the SSH Library
You are free to copy this file, modify it in any way, consider it being public
2009-10-09 23:44:05 +04:00
domain. This does not apply to the rest of the library though, but it is
allowed to cut-and-paste working code from this file to any license of
program.
The goal is to show the API in action. It's not a reference on how terminal
clients must be made or how a client should react.
*/
#include "config.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <sys/select.h>
#include <sys/time.h>
#ifdef HAVE_PTY_H
#include <pty.h>
#endif
#include <sys/ioctl.h>
#include <signal.h>
#include <errno.h>
#include <libssh/callbacks.h>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include <fcntl.h>
2009-11-07 05:45:27 +03:00
#include "examples_common.h"
#define MAXCMD 10
char *host;
char *user;
char *cmds[MAXCMD];
struct termios terminal;
2009-10-10 18:25:40 +04:00
2009-11-17 00:36:31 +03:00
char *pcap_file=NULL;
static int auth_callback(const char *prompt, char *buf, size_t len,
int echo, int verify, void *userdata) {
char *answer = NULL;
char *ptr;
(void) verify;
(void) userdata;
if (echo) {
while ((answer = fgets(buf, len, stdin)) == NULL);
if ((ptr = strchr(buf, '\n'))) {
ptr = '\0';
}
} else {
answer = getpass(prompt);
}
if (answer == NULL) {
return -1;
}
strncpy(buf, answer, len);
return 0;
}
2009-10-10 18:25:40 +04:00
struct ssh_callbacks_struct cb = {
.auth_function=auth_callback,
.userdata=NULL
};
static void add_cmd(char *cmd){
int n;
for(n=0;cmds[n] && (n<MAXCMD);n++);
if(n==MAXCMD)
return;
cmds[n]=strdup(cmd);
}
static void usage(){
fprintf(stderr,"Usage : ssh [options] [login@]hostname\n"
"sample client - libssh-%s\n"
"Options :\n"
" -l user : log in as user\n"
" -p port : connect to port\n"
" -d : use DSS to verify host public key\n"
2009-11-17 00:36:31 +03:00
" -r : use RSA to verify host public key\n"
#ifdef WITH_PCAP
" -P file : create a pcap debugging file\n"
#endif
,
ssh_version(0));
exit(0);
}
static int opts(int argc, char **argv){
int i;
// for(i=0;i<argc;i++)
// printf("%d : %s\n",i,argv[i]);
/* insert your own arguments here */
2009-11-17 00:36:31 +03:00
while((i=getopt(argc,argv,"P:"))!=-1){
switch(i){
2009-11-17 00:36:31 +03:00
case 'P':
pcap_file=optarg;
break;
default:
fprintf(stderr,"unknown option %c\n",optopt);
usage();
}
}
if(optind < argc)
host=argv[optind++];
while(optind < argc)
add_cmd(argv[optind++]);
if(host==NULL)
usage();
return 0;
}
#ifndef HAVE_CFMAKERAW
static void cfmakeraw(struct termios *termios_p){
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
termios_p->c_oflag &= ~OPOST;
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
termios_p->c_cflag &= ~(CSIZE|PARENB);
termios_p->c_cflag |= CS8;
}
#endif
static void do_cleanup(int i) {
/* unused variable */
(void) i;
tcsetattr(0,TCSANOW,&terminal);
}
static void do_exit(int i) {
/* unused variable */
(void) i;
do_cleanup(0);
exit(0);
}
2009-07-25 00:15:33 +04:00
ssh_channel chan;
int signal_delayed=0;
static void sigwindowchanged(int i){
(void) i;
signal_delayed=1;
}
static void setsignal(void){
signal(SIGWINCH, sigwindowchanged);
signal_delayed=0;
}
static void sizechanged(void){
struct winsize win = { 0, 0, 0, 0 };
ioctl(1, TIOCGWINSZ, &win);
channel_change_pty_size(chan,win.ws_col, win.ws_row);
// printf("Changed pty size\n");
setsignal();
}
/* There are two flavors of select loop: the one based on
* ssh_select and the one based on channel_select.
* The ssh_select one permits you to give your own file descriptors to
* follow. It is thus a complete select loop.
* The second one only selects on channels. It is simplier to use
* but doesn't permit you to fill in your own file descriptor. It is
* more adapted if you can't use ssh_select as a main loop (because
* you already have another main loop system).
*/
#ifdef USE_CHANNEL_SELECT
/* channel_select base main loop, with a standard select(2)
*/
2009-09-23 23:55:54 +04:00
static void select_loop(ssh_session session,ssh_channel channel){
fd_set fds;
struct timeval timeout;
2009-11-07 19:35:24 +03:00
char buffer[4096];
ssh_buffer readbuf=buffer_new();
2009-07-25 00:15:33 +04:00
ssh_channel channels[2];
int lus;
int eof=0;
int maxfd;
int ret;
while(channel){
/* when a signal is caught, ssh_select will return
2009-10-09 23:44:05 +04:00
* with SSH_EINTR, which means it should be started
* again. It lets you handle the signal the faster you
* can, like in this window changed example. Of course, if
* your signal handler doesn't call libssh at all, you're
* free to handle signals directly in sighandler.
*/
do{
FD_ZERO(&fds);
if(!eof)
FD_SET(0,&fds);
timeout.tv_sec=30;
timeout.tv_usec=0;
FD_SET(ssh_get_fd(session),&fds);
maxfd=ssh_get_fd(session)+1;
ret=select(maxfd,&fds,NULL,NULL,&timeout);
if(ret==EINTR)
continue;
if(FD_ISSET(0,&fds)){
2009-11-07 19:35:24 +03:00
lus=read(0,buffer,sizeof(buffer));
if(lus)
channel_write(channel,buffer,lus);
else {
eof=1;
channel_send_eof(channel);
}
}
if(FD_ISSET(ssh_get_fd(session),&fds)){
ssh_set_fd_toread(session);
}
channels[0]=channel; // set the first channel we want to read from
channels[1]=NULL;
ret=channel_select(channels,NULL,NULL,NULL); // no specific timeout - just poll
if(signal_delayed)
sizechanged();
} while (ret==EINTR || ret==SSH_EINTR);
// we already looked for input from stdin. Now, we are looking for input from the channel
2009-10-09 23:44:05 +04:00
if(channel && channel_is_closed(channel)){
ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel));
channel_free(channel);
channel=NULL;
channels[0]=NULL;
}
if(channels[0]){
while(channel && channel_is_open(channel) && channel_poll(channel,0)>0){
lus=channel_read_buffer(channel,readbuf,0,0);
if(lus==-1){
fprintf(stderr, "Error reading channel: %s\n",
ssh_get_error(session));
return;
}
if(lus==0){
ssh_log(session,SSH_LOG_RARE,"EOF received\n");
ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel));
channel_free(channel);
channel=channels[0]=NULL;
} else
if (write(1,buffer_get(readbuf),lus) < 0) {
fprintf(stderr, "Error writing to buffer\n");
return;
}
}
while(channel && channel_is_open(channel) && channel_poll(channel,1)>0){ /* stderr */
lus=channel_read_buffer(channel,readbuf,0,1);
if(lus==-1){
fprintf(stderr, "Error reading channel: %s\n",
ssh_get_error(session));
return;
}
if(lus==0){
ssh_log(session,SSH_LOG_RARE,"EOF received\n");
ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel));
channel_free(channel);
channel=channels[0]=NULL;
} else
if (write(2,buffer_get(readbuf),lus) < 0) {
fprintf(stderr, "Error writing to buffer\n");
return;
}
}
}
if(channel && channel_is_closed(channel)){
channel_free(channel);
channel=NULL;
}
}
buffer_free(readbuf);
}
#else /* CHANNEL_SELECT */
static void select_loop(ssh_session session,ssh_channel channel){
fd_set fds;
struct timeval timeout;
char buffer[4096];
/* channels will be set to the channels to poll.
* outchannels will contain the result of the poll
*/
ssh_channel channels[2], outchannels[2];
int lus;
int eof=0;
int maxfd;
int ret;
while(channel){
do{
FD_ZERO(&fds);
if(!eof)
FD_SET(0,&fds);
timeout.tv_sec=30;
timeout.tv_usec=0;
FD_SET(ssh_get_fd(session),&fds);
maxfd=ssh_get_fd(session)+1;
channels[0]=channel; // set the first channel we want to read from
channels[1]=NULL;
ret=ssh_select(channels,outchannels,maxfd,&fds,&timeout);
if(signal_delayed)
sizechanged();
if(ret==EINTR)
continue;
if(FD_ISSET(0,&fds)){
lus=read(0,buffer,sizeof(buffer));
if(lus)
channel_write(channel,buffer,lus);
else {
eof=1;
channel_send_eof(channel);
}
}
if(channel && channel_is_closed(channel)){
ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel));
channel_free(channel);
channel=NULL;
channels[0]=NULL;
}
if(outchannels[0]){
2010-04-25 01:22:00 +04:00
while(channel && channel_is_open(channel) && channel_poll(channel,0)!=0){
lus=channel_read(channel,buffer,sizeof(buffer),0);
if(lus==-1){
fprintf(stderr, "Error reading channel: %s\n",
ssh_get_error(session));
return;
}
if(lus==0){
ssh_log(session,SSH_LOG_RARE,"EOF received\n");
ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel));
channel_free(channel);
channel=channels[0]=NULL;
} else
2010-04-25 01:22:00 +04:00
if (write(1,buffer,lus) < 0) {
fprintf(stderr, "Error writing to buffer\n");
return;
}
}
2010-04-25 01:22:00 +04:00
while(channel && channel_is_open(channel) && channel_poll(channel,1)!=0){ /* stderr */
lus=channel_read(channel,buffer,sizeof(buffer),1);
if(lus==-1){
fprintf(stderr, "Error reading channel: %s\n",
ssh_get_error(session));
return;
}
if(lus==0){
ssh_log(session,SSH_LOG_RARE,"EOF received\n");
ssh_log(session,SSH_LOG_RARE,"exit-status : %d\n",channel_get_exit_status(channel));
channel_free(channel);
channel=channels[0]=NULL;
} else
2010-04-25 01:22:00 +04:00
if (write(2,buffer,lus) < 0) {
fprintf(stderr, "Error writing to buffer\n");
return;
}
}
}
if(channel && channel_is_closed(channel)){
channel_free(channel);
channel=NULL;
}
} while (ret==EINTR || ret==SSH_EINTR);
}
}
#endif
2009-09-23 23:55:54 +04:00
static void shell(ssh_session session){
2009-07-25 00:15:33 +04:00
ssh_channel channel;
struct termios terminal_local;
int interactive=isatty(0);
channel = channel_new(session);
if(interactive){
tcgetattr(0,&terminal_local);
memcpy(&terminal,&terminal_local,sizeof(struct termios));
}
if(channel_open_session(channel)){
printf("error opening channel : %s\n",ssh_get_error(session));
return;
}
chan=channel;
if(interactive){
channel_request_pty(channel);
sizechanged();
}
if(channel_request_shell(channel)){
printf("Requesting shell : %s\n",ssh_get_error(session));
return;
}
if(interactive){
cfmakeraw(&terminal_local);
tcsetattr(0,TCSANOW,&terminal_local);
setsignal();
}
signal(SIGTERM,do_cleanup);
select_loop(session,channel);
2009-11-06 21:21:18 +03:00
if(interactive)
do_cleanup(0);
}
2009-09-23 23:55:54 +04:00
static void batch_shell(ssh_session session){
2009-07-25 00:15:33 +04:00
ssh_channel channel;
char buffer[1024];
int i,s=0;
for(i=0;i<MAXCMD && cmds[i];++i)
s+=snprintf(buffer+s,sizeof(buffer)-s,"%s ",cmds[i]);
channel=channel_new(session);
channel_open_session(channel);
if(channel_request_exec(channel,buffer)){
printf("error executing \"%s\" : %s\n",buffer,ssh_get_error(session));
return;
}
select_loop(session,channel);
}
2009-11-07 19:23:06 +03:00
static int client(ssh_session session){
int auth=0;
char *banner;
int state;
if (user)
if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0)
return -1;
if (ssh_options_set(session, SSH_OPTIONS_HOST ,host) < 0)
return -1;
ssh_options_parse_config(session, NULL);
if(ssh_connect(session)){
fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session));
return -1;
}
state=verify_knownhost(session);
if (state != 0)
return -1;
ssh_userauth_none(session, NULL);
banner=ssh_get_issue_banner(session);
if(banner){
printf("%s\n",banner);
free(banner);
}
auth=authenticate_console(session);
if(auth != SSH_AUTH_SUCCESS){
return -1;
}
ssh_log(session, SSH_LOG_FUNCTIONS, "Authentication success");
if(!cmds[0])
shell(session);
else
batch_shell(session);
return 0;
}
2009-11-17 00:36:31 +03:00
ssh_pcap_file pcap;
void set_pcap(ssh_session session);
void set_pcap(ssh_session session){
if(!pcap_file)
return;
pcap=ssh_pcap_file_new();
if(!pcap)
return;
2009-11-17 00:36:31 +03:00
if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){
printf("Error opening pcap file\n");
ssh_pcap_file_free(pcap);
pcap=NULL;
return;
}
2009-11-17 01:20:16 +03:00
ssh_set_pcap_file(session,pcap);
2009-11-17 00:36:31 +03:00
}
2009-11-17 01:20:16 +03:00
2009-11-17 00:36:31 +03:00
void cleanup_pcap(void);
void cleanup_pcap(){
if(pcap)
ssh_pcap_file_free(pcap);
2009-11-17 00:36:31 +03:00
pcap=NULL;
}
int main(int argc, char **argv){
2009-09-23 23:55:54 +04:00
ssh_session session;
2009-11-07 19:23:06 +03:00
2009-10-02 16:06:41 +04:00
session = ssh_new();
2009-10-10 18:25:40 +04:00
ssh_callbacks_init(&cb);
ssh_set_callbacks(session,&cb);
2009-10-02 16:06:41 +04:00
if(ssh_options_getopt(session, &argc, argv)) {
fprintf(stderr, "error parsing command line :%s\n",
ssh_get_error(session));
usage();
}
opts(argc,argv);
signal(SIGTERM, do_exit);
2009-11-17 00:36:31 +03:00
set_pcap(session);
2009-11-07 19:23:06 +03:00
client(session);
2009-11-17 00:36:31 +03:00
ssh_disconnect(session);
2009-10-10 14:04:16 +04:00
ssh_free(session);
2009-11-17 00:36:31 +03:00
cleanup_pcap();
ssh_finalize();
return 0;
}