sample.c   sample.c 
/* client.c */ /* client.c */
/* /*
Copyright 2003 Aris Adamantiadis Copyright 2003-2009 Aris Adamantiadis
This file is part of the SSH Library This file is part of the SSH Library
You are free to copy this file, modify it in any way, consider it being pub lic You are free to copy this file, modify it in any way, consider it being pub lic
domain. This does not apply to the rest of the library though, but it is 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 allowed to cut-and-paste working code from this file to any license of
program. program.
The goal is to show the API in action. It's not a reference on how terminal 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. clients must be made or how a client should react.
*/ */
skipping to change at line 30 skipping to change at line 30
#include <termios.h> #include <termios.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/time.h> #include <sys/time.h>
#ifdef HAVE_PTY_H #ifdef HAVE_PTY_H
#include <pty.h> #include <pty.h>
#endif #endif
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <signal.h> #include <signal.h>
#include <errno.h> #include <errno.h>
#include <libssh/callbacks.h>
#include <libssh/libssh.h> #include <libssh/libssh.h>
#include <libssh/sftp.h> #include <libssh/sftp.h>
#include <fcntl.h> #include <fcntl.h>
#include "examples_common.h"
#define MAXCMD 10 #define MAXCMD 10
char *host; char *host;
char *user; char *user;
int sftp;
char *cmds[MAXCMD]; char *cmds[MAXCMD];
struct termios terminal; struct termios terminal;
void do_sftp(SSH_SESSION *session);
#ifdef WITH_PCAP
/* this header file won't be necessary in the future */
#include <libssh/pcap.h>
char *pcap_file=NULL;
#endif
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;
}
struct ssh_callbacks_struct cb = {
.auth_function=auth_callback,
.userdata=NULL
};
static void add_cmd(char *cmd){ static void add_cmd(char *cmd){
int n; int n;
for(n=0;cmds[n] && (n<MAXCMD);n++); for(n=0;cmds[n] && (n<MAXCMD);n++);
if(n==MAXCMD) if(n==MAXCMD)
return; return;
cmds[n]=strdup(cmd); cmds[n]=strdup(cmd);
} }
static void usage(){ static void usage(){
fprintf(stderr,"Usage : ssh [options] [login@]hostname\n" fprintf(stderr,"Usage : ssh [options] [login@]hostname\n"
"sample client - libssh-%s\n" "sample client - libssh-%s\n"
"Options :\n" "Options :\n"
" -l user : log in as user\n" " -l user : log in as user\n"
" -p port : connect to port\n" " -p port : connect to port\n"
" -d : use DSS to verify host public key\n" " -d : use DSS to verify host public key\n"
" -r : use RSA to verify host public key\n", " -r : use RSA to verify host public key\n"
#ifdef WITH_PCAP
" -P file : create a pcap debugging file\n"
#endif
,
ssh_version(0)); ssh_version(0));
exit(0); exit(0);
} }
static int opts(int argc, char **argv){ static int opts(int argc, char **argv){
int i; int i;
if(strstr(argv[0],"sftp"))
sftp=1;
// for(i=0;i<argc;i++) // for(i=0;i<argc;i++)
// printf("%d : %s\n",i,argv[i]); // printf("%d : %s\n",i,argv[i]);
/* insert your own arguments here */ /* insert your own arguments here */
while((i=getopt(argc,argv,""))!=-1){ while((i=getopt(argc,argv,"P:"))!=-1){
switch(i){ switch(i){
#ifdef WITH_PCAP
case 'P':
pcap_file=optarg;
break;
#endif
default: default:
fprintf(stderr,"unknown option %c\n",optopt); fprintf(stderr,"unknown option %c\n",optopt);
usage(); usage();
} }
} }
if(optind < argc) if(optind < argc)
host=argv[optind++]; host=argv[optind++];
while(optind < argc) while(optind < argc)
add_cmd(argv[optind++]); add_cmd(argv[optind++]);
if(host==NULL) if(host==NULL)
skipping to change at line 111 skipping to change at line 155
} }
static void do_exit(int i) { static void do_exit(int i) {
/* unused variable */ /* unused variable */
(void) i; (void) i;
do_cleanup(0); do_cleanup(0);
exit(0); exit(0);
} }
CHANNEL *chan; ssh_channel chan;
int signal_delayed=0; int signal_delayed=0;
static void sigwindowchanged(int i){ static void sigwindowchanged(int i){
(void) i; (void) i;
signal_delayed=1; signal_delayed=1;
} }
static void setsignal(void){ static void setsignal(void){
signal(SIGWINCH, sigwindowchanged); signal(SIGWINCH, sigwindowchanged);
signal_delayed=0; signal_delayed=0;
} }
static void sizechanged(void){ static void sizechanged(void){
struct winsize win = { 0, 0, 0, 0 }; struct winsize win = { 0, 0, 0, 0 };
ioctl(1, TIOCGWINSZ, &win); ioctl(1, TIOCGWINSZ, &win);
channel_change_pty_size(chan,win.ws_col, win.ws_row); channel_change_pty_size(chan,win.ws_col, win.ws_row);
// printf("Changed pty size\n"); // printf("Changed pty size\n");
setsignal(); setsignal();
} }
static void select_loop(SSH_SESSION *session,CHANNEL *channel){
/* 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)
*/
static void select_loop(ssh_session session,ssh_channel channel){
fd_set fds; fd_set fds;
struct timeval timeout; struct timeval timeout;
char buffer[10]; char buffer[4096];
BUFFER *readbuf=buffer_new(); ssh_buffer readbuf=buffer_new();
CHANNEL *channels[2]; ssh_channel channels[2];
int lus; int lus;
int eof=0; int eof=0;
int maxfd; int maxfd;
int ret; int ret;
while(channel){ while(channel){
/* when a signal is caught, ssh_select will return /* when a signal is caught, ssh_select will return
* with SSH_EINTR, which means it should be started * with SSH_EINTR, which means it should be started
* again. It lets you handle the signal the faster you * again. It lets you handle the signal the faster you
* can, like in this window changed example. Of course, if * can, like in this window changed example. Of course, if
* your signal handler doesn't call libssh at all, you're * your signal handler doesn't call libssh at all, you're
skipping to change at line 161 skipping to change at line 220
if(!eof) if(!eof)
FD_SET(0,&fds); FD_SET(0,&fds);
timeout.tv_sec=30; timeout.tv_sec=30;
timeout.tv_usec=0; timeout.tv_usec=0;
FD_SET(ssh_get_fd(session),&fds); FD_SET(ssh_get_fd(session),&fds);
maxfd=ssh_get_fd(session)+1; maxfd=ssh_get_fd(session)+1;
ret=select(maxfd,&fds,NULL,NULL,&timeout); ret=select(maxfd,&fds,NULL,NULL,&timeout);
if(ret==EINTR) if(ret==EINTR)
continue; continue;
if(FD_ISSET(0,&fds)){ if(FD_ISSET(0,&fds)){
lus=read(0,buffer,10); lus=read(0,buffer,sizeof(buffer));
if(lus) if(lus)
channel_write(channel,buffer,lus); channel_write(channel,buffer,lus);
else { else {
eof=1; eof=1;
channel_send_eof(channel); channel_send_eof(channel);
} }
} }
if(FD_ISSET(ssh_get_fd(session),&fds)){ if(FD_ISSET(ssh_get_fd(session),&fds)){
ssh_set_fd_toread(session); ssh_set_fd_toread(session);
} }
skipping to change at line 228 skipping to change at line 287
write(2,buffer_get(readbuf),lus); write(2,buffer_get(readbuf),lus);
} }
} }
if(channel && channel_is_closed(channel)){ if(channel && channel_is_closed(channel)){
channel_free(channel); channel_free(channel);
channel=NULL; channel=NULL;
} }
} }
buffer_free(readbuf); 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 wan
t to read from
channels[1]=NULL;
ret=ssh_select(channels,outchannels,maxfd,&fds,&time
out);
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]){
while(channel && channel_is_open(channel) &&
channel_poll(channel,0)){
lus=channel_read(channel,buffer,size
of(buffer),0);
if(lus==-1){
fprintf(stderr, "Error readi
ng channel: %s\n",
ssh_get_erro
r(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
write(1,buffer,lus);
}
while(channel && channel_is_open(channel) &&
channel_poll(channel,1)){ /* stderr */
lus=channel_read(channel,buffer,size
of(buffer),1);
if(lus==-1){
fprintf(stderr, "Error readi
ng channel: %s\n",
ssh_get_erro
r(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
write(2,buffer,lus);
}
}
if(channel && channel_is_closed(channel)){
channel_free(channel);
channel=NULL;
}
} while (ret==EINTR || ret==SSH_EINTR);
}
}
#endif
static void shell(SSH_SESSION *session){ static void shell(ssh_session session){
CHANNEL *channel; ssh_channel channel;
struct termios terminal_local; struct termios terminal_local;
int interactive=isatty(0); int interactive=isatty(0);
channel = channel_new(session); channel = channel_new(session);
if(interactive){ if(interactive){
tcgetattr(0,&terminal_local); tcgetattr(0,&terminal_local);
memcpy(&terminal,&terminal_local,sizeof(struct termios)); memcpy(&terminal,&terminal_local,sizeof(struct termios));
} }
if(channel_open_session(channel)){ if(channel_open_session(channel)){
printf("error opening channel : %s\n",ssh_get_error(session)); printf("error opening channel : %s\n",ssh_get_error(session));
return; return;
skipping to change at line 258 skipping to change at line 406
printf("Requesting shell : %s\n",ssh_get_error(session)); printf("Requesting shell : %s\n",ssh_get_error(session));
return; return;
} }
if(interactive){ if(interactive){
cfmakeraw(&terminal_local); cfmakeraw(&terminal_local);
tcsetattr(0,TCSANOW,&terminal_local); tcsetattr(0,TCSANOW,&terminal_local);
setsignal(); setsignal();
} }
signal(SIGTERM,do_cleanup); signal(SIGTERM,do_cleanup);
select_loop(session,channel); select_loop(session,channel);
if(interactive)
do_cleanup(0);
} }
static void batch_shell(SSH_SESSION *session){ static void batch_shell(ssh_session session){
CHANNEL *channel; ssh_channel channel;
char buffer[1024]; char buffer[1024];
int i,s=0; int i,s=0;
for(i=0;i<MAXCMD && cmds[i];++i) for(i=0;i<MAXCMD && cmds[i];++i)
s+=snprintf(buffer+s,sizeof(buffer)-s,"%s ",cmds[i]); s+=snprintf(buffer+s,sizeof(buffer)-s,"%s ",cmds[i]);
channel=channel_new(session); channel=channel_new(session);
channel_open_session(channel); channel_open_session(channel);
if(channel_request_exec(channel,buffer)){ if(channel_request_exec(channel,buffer)){
printf("error executing \"%s\" : %s\n",buffer,ssh_get_error(session )); printf("error executing \"%s\" : %s\n",buffer,ssh_get_error(session ));
return; return;
} }
select_loop(session,channel); select_loop(session,channel);
} }
#ifdef WITH_SFTP static int client(ssh_session session){
/* it's just a proof of concept code for sftp, till i write a real document int auth=0;
ation about it */ char *banner;
void do_sftp(SSH_SESSION *session){ int state;
SFTP_SESSION *sftp_session=sftp_new(session); if (user)
SFTP_DIR *dir; if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0)
SFTP_ATTRIBUTES *file; return -1;
SFTP_FILE *fichier; if (ssh_options_set(session, SSH_OPTIONS_HOST ,host) < 0)
SFTP_FILE *to; return -1;
int len=1;
int i; ssh_options_parse_config(session, NULL);
char data[8000];
if(!sftp_session){ if(ssh_connect(session)){
fprintf(stderr, "sftp error initialising channel: %s\n", fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session));
ssh_get_error(session)); return -1;
return; }
} state=verify_knownhost(session);
if(sftp_init(sftp_session)){ if (state != 0)
fprintf(stderr, "error initialising sftp: %s\n", return -1;
ssh_get_error(session)); ssh_userauth_none(session, NULL);
return; banner=ssh_get_issue_banner(session);
} if(banner){
/* the connection is made */ printf("%s\n",banner);
/* opening a directory */ free(banner);
dir=sftp_opendir(sftp_session,"./"); }
if(!dir) { auth=authenticate_console(session);
fprintf(stderr, "Directory not opened(%s)\n", ssh_get_error(session if(auth != SSH_AUTH_SUCCESS){
)); return -1;
return ; }
} ssh_log(session, SSH_LOG_FUNCTIONS, "Authentication success");
/* reading the whole directory, file by file */ if(!cmds[0])
while((file=sftp_readdir(sftp_session,dir))){ shell(session);
fprintf(stderr, "%30s(%.8o) : %.5d.%.5d : %.10llu bytes\n", else
file->name, batch_shell(session);
file->permissions, return 0;
file->uid, }
file->gid,
(long long unsigned int) file->size); #ifdef WITH_PCAP
sftp_attributes_free(file); ssh_pcap_file pcap;
} void set_pcap(ssh_session session);
/* when file=NULL, an error has occured OR the directory listing is end void set_pcap(ssh_session session){
of file */ if(!pcap_file)
if(!sftp_dir_eof(dir)){ return;
fprintf(stderr, "Error: %s\n", ssh_get_error(session)); pcap=ssh_pcap_file_new();
return; if(ssh_pcap_file_open(pcap,pcap_file) == SSH_ERROR){
} printf("Error opening pcap file\n");
if(sftp_closedir(dir)){ ssh_pcap_file_free(pcap);
fprintf(stderr, "Error: %s\n", ssh_get_error(session)); pcap=NULL;
return; return;
} }
/* this will open a file and copy it into your /home directory */ ssh_set_pcap_file(session,pcap);
/* the small buffer size was intended to stress the library. of course, }
you can use a buffer till 20kbytes without problem */
void cleanup_pcap(void);
fichier=sftp_open(sftp_session,"/usr/bin/ssh",O_RDONLY, 0); void cleanup_pcap(){
if(!fichier){ ssh_pcap_file_free(pcap);
fprintf(stderr, "Error opening /usr/bin/ssh: %s\n", pcap=NULL;
ssh_get_error(session));
return;
}
/* open a file for writing... */
to=sftp_open(sftp_session,"ssh-copy",O_WRONLY | O_CREAT, 0);
if(!to){
fprintf(stderr, "Error opening ssh-copy for writing: %s\n",
ssh_get_error(session));
return;
}
while((len=sftp_read(fichier,data,4096)) > 0){
if(sftp_write(to,data,len)!=len){
fprintf(stderr, "Error writing %d bytes: %s\n",
len, ssh_get_error(session));
return;
}
}
printf("finished\n");
if(len<0)
fprintf(stderr, "Error reading file: %s\n", ssh_get_error(session))
;
sftp_close(fichier);
sftp_close(to);
printf("fichiers ferm\n");
to=sftp_open(sftp_session,"/tmp/grosfichier",O_WRONLY|O_CREAT, 0644);
for(i=0;i<1000;++i){
len=sftp_write(to,data,8000);
printf("wrote %d bytes\n",len);
if(len != 8000){
printf("chunk %d : %d (%s)\n",i,len,ssh_get_error(session));
}
}
sftp_close(to);
/* close the sftp session */
sftp_free(sftp_session);
printf("session sftp termin�\n");
} }
#endif #endif
static int auth_kbdint(SSH_SESSION *session){
int err=ssh_userauth_kbdint(session,NULL,NULL);
const char *name, *instruction, *prompt;
char *ptr;
char buffer[128];
int i,n;
char echo;
while (err==SSH_AUTH_INFO){
name=ssh_userauth_kbdint_getname(session);
instruction=ssh_userauth_kbdint_getinstruction(session);
n=ssh_userauth_kbdint_getnprompts(session);
if(strlen(name)>0)
printf("%s\n",name);
if(strlen(instruction)>0)
printf("%s\n",instruction);
for(i=0;i<n;++i){
prompt=ssh_userauth_kbdint_getprompt(session,i,&echo);
if(echo){
printf("%s",prompt);
fgets(buffer,sizeof(buffer),stdin);
buffer[sizeof(buffer)-1]=0;
if((ptr=strchr(buffer,'\n')))
*ptr=0;
if (ssh_userauth_kbdint_setanswer(session,i,buffer) < 0) {
return SSH_AUTH_ERROR;
}
memset(buffer,0,strlen(buffer));
} else {
ptr=getpass(prompt);
if (ssh_userauth_kbdint_setanswer(session,i,ptr) < 0) {
return SSH_AUTH_ERROR;
}
}
}
err=ssh_userauth_kbdint(session,NULL,NULL);
}
return err;
}
int main(int argc, char **argv){ int main(int argc, char **argv){
SSH_SESSION *session; ssh_session session;
SSH_OPTIONS *options;
int auth=0;
char *password;
char *banner;
char *hexa;
int state;
char buf[10];
unsigned char *hash = NULL;
int hlen;
options=ssh_options_new();
if(ssh_options_getopt(options,&argc, argv)){
fprintf(stderr,"error parsing command line :%s\n",ssh_get_error(opti
ons));
usage();
}
opts(argc,argv);
signal(SIGTERM, do_exit);
if (user) {
if (ssh_options_set_username(options,user) < 0) {
ssh_options_free(options);
return 1;
}
}
if (ssh_options_set_host(options,host) < 0) { session = ssh_new();
ssh_options_free(options);
return 1;
}
session=ssh_new();
ssh_set_options(session,options);
if(ssh_connect(session)){
fprintf(stderr,"Connection failed : %s\n",ssh_get_error(session));
ssh_disconnect(session);
ssh_finalize();
return 1;
}
state=ssh_is_server_known(session);
hlen = ssh_get_pubkey_hash(session, &hash); ssh_callbacks_init(&cb);
if (hlen < 0) { ssh_set_callbacks(session,&cb);
ssh_disconnect(session);
ssh_finalize();
return 1;
}
switch(state){
case SSH_SERVER_KNOWN_OK:
break; /* ok */
case SSH_SERVER_KNOWN_CHANGED:
fprintf(stderr,"Host key for server changed : server's one is n
ow :\n");
ssh_print_hexa("Public key hash",hash, hlen);
free(hash);
fprintf(stderr,"For security reason, connection will be stopped
\n");
ssh_disconnect(session);
ssh_finalize();
exit(-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"
"We advise you to rerun the client with -d or -r for more s
afety.\n");
ssh_disconnect(session);
ssh_finalize();
exit(-1);
case SSH_SERVER_FILE_NOT_FOUND:
fprintf(stderr,"Could not find known host file. If you accept t
he host key here,\n");
fprintf(stderr,"the file will be automatically created.\n");
/* fallback to SSH_SERVER_NOT_KNOWN behaviour */
case SSH_SERVER_NOT_KNOWN:
hexa = ssh_get_hexa(hash, hlen);
fprintf(stderr,"The server is unknown. Do you trust the host ke
y ?\n");
fprintf(stderr, "Public key hash: %s\n", hexa);
free(hexa);
fgets(buf,sizeof(buf),stdin);
if(strncasecmp(buf,"yes",3)!=0){
ssh_disconnect(session);
exit(-1);
}
fprintf(stderr,"This new key will be written on disk for furthe
r usage. do you agree ?\n");
fgets(buf,sizeof(buf),stdin);
if(strncasecmp(buf,"yes",3)==0){
if (ssh_write_knownhost(session) < 0) {
free(hash);
fprintf(stderr, "error %s\n", strerror(errno));
exit(-1);
}
}
break; if(ssh_options_getopt(session, &argc, argv)) {
case SSH_SERVER_ERROR: fprintf(stderr, "error parsing command line :%s\n",
free(hash); ssh_get_error(session));
fprintf(stderr,"%s",ssh_get_error(session)); usage();
ssh_disconnect(session);
ssh_finalize();
exit(-1);
} }
free(hash); opts(argc,argv);
signal(SIGTERM, do_exit);
ssh_userauth_none(session, NULL); #ifdef WITH_PCAP
set_pcap(session);
auth = ssh_auth_list(session);
printf("auth: 0x%04x\n", auth);
printf("supported auth methods: ");
if (auth & SSH_AUTH_METHOD_PUBLICKEY) {
printf("publickey");
}
if (auth & SSH_AUTH_METHOD_INTERACTIVE) {
printf(", keyboard-interactive");
}
printf("\n");
/* no ? you should :) */
auth=ssh_userauth_autopubkey(session, NULL);
if(auth==SSH_AUTH_ERROR){
fprintf(stderr,"Authenticating with pubkey: %s\n",ssh_get_error(ses
sion));
ssh_finalize();
return -1;
}
banner=ssh_get_issue_banner(session);
if(banner){
printf("%s\n",banner);
free(banner);
}
if(auth!=SSH_AUTH_SUCCESS){
auth=auth_kbdint(session);
if(auth==SSH_AUTH_ERROR){
fprintf(stderr,"authenticating with keyb-interactive: %s\n",
ssh_get_error(session));
ssh_finalize();
return -1;
}
}
if(auth!=SSH_AUTH_SUCCESS){
password=getpass("Password: ");
if(ssh_userauth_password(session,NULL,password) != SSH_AUTH_SUCCESS
){
fprintf(stderr,"Authentication failed: %s\n",ssh_get_error(sess
ion));
ssh_disconnect(session);
ssh_finalize();
return -1;
}
memset(password,0,strlen(password));
}
ssh_log(session, SSH_LOG_FUNCTIONS, "Authentication success");
if(strstr(argv[0],"sftp")){
sftp=1;
ssh_log(session, SSH_LOG_FUNCTIONS, "Doing sftp instead");
}
if(!sftp){
if(!cmds[0])
shell(session);
else
batch_shell(session);
}
#ifdef WITH_SFTP
else
do_sftp(session);
#endif #endif
if(!sftp && !cmds[0]) client(session);
do_cleanup(0);
ssh_disconnect(session); ssh_disconnect(session);
ssh_free(session);
#ifdef WITH_PCAP
cleanup_pcap();
#endif
ssh_finalize(); ssh_finalize();
return 0; return 0;
} }
 End of changes. 26 change blocks. 
316 lines changed or deleted 258 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/