aboutsummaryrefslogtreecommitdiff
path: root/examples/sample.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/sample.c')
-rw-r--r--examples/sample.c669
1 files changed, 669 insertions, 0 deletions
diff --git a/examples/sample.c b/examples/sample.c
new file mode 100644
index 00000000..c2aaa66e
--- /dev/null
+++ b/examples/sample.c
@@ -0,0 +1,669 @@
+/* client.c */
+/*
+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
+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>
+#include <sys/statvfs.h>
+#ifdef HAVE_PTY_H
+#include <pty.h>
+#endif
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <errno.h>
+#include <libssh/libssh.h>
+#include <libssh/sftp.h>
+
+#include <fcntl.h>
+
+#define MAXCMD 10
+char *host;
+char *user;
+int sftp;
+char *cmds[MAXCMD];
+struct termios terminal;
+void do_sftp(ssh_session session);
+
+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"
+ " -r : use RSA to verify host public key\n",
+ ssh_version(0));
+ exit(0);
+}
+
+static int opts(int argc, char **argv){
+ int i;
+ if(strstr(argv[0],"sftp"))
+ sftp=1;
+// for(i=0;i<argc;i++)
+// printf("%d : %s\n",i,argv[i]);
+ /* insert your own arguments here */
+ while((i=getopt(argc,argv,""))!=-1){
+ switch(i){
+ 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);
+}
+
+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();
+}
+static void select_loop(ssh_session session,ssh_channel channel){
+ fd_set fds;
+ struct timeval timeout;
+ char buffer[10];
+ ssh_buffer readbuf=buffer_new();
+ ssh_channel channels[2];
+ int lus;
+ int eof=0;
+ int maxfd;
+ int ret;
+ while(channel){
+ /* when a signal is caught, ssh_select will return
+ * 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)){
+ lus=read(0,buffer,10);
+ 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
+
+ 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)){
+ 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
+ write(1,buffer_get(readbuf),lus);
+ }
+ while(channel && channel_is_open(channel) && channel_poll(channel,1)){ /* 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
+ write(2,buffer_get(readbuf),lus);
+ }
+ }
+ if(channel && channel_is_closed(channel)){
+ channel_free(channel);
+ channel=NULL;
+ }
+ }
+ buffer_free(readbuf);
+}
+
+
+static void shell(ssh_session session){
+ 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);
+}
+
+static void batch_shell(ssh_session session){
+ 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);
+}
+
+#ifdef WITH_SFTP
+/* it's just a proof of concept code for sftp, till i write a real documentation about it */
+void do_sftp(ssh_session session){
+ SFTP_SESSION *sftp_session=sftp_new(session);
+ SFTP_DIR *dir;
+ SFTP_ATTRIBUTES *file;
+ SFTP_STATVFS *sftpstatvfs;
+ struct statvfs sysstatvfs;
+ SFTP_FILE *fichier;
+ SFTP_FILE *to;
+ int len=1;
+ unsigned int i;
+ char data[8000]={0};
+ char *lnk;
+
+ unsigned int count;
+
+ if(!sftp_session){
+ fprintf(stderr, "sftp error initialising channel: %s\n",
+ ssh_get_error(session));
+ return;
+ }
+ if(sftp_init(sftp_session)){
+ fprintf(stderr, "error initialising sftp: %s\n",
+ ssh_get_error(session));
+ return;
+ }
+
+ printf("Additional SFTP extensions provided by the server:\n");
+ count = sftp_extensions_get_count(sftp_session);
+ for (i = 0; i < count; i++) {
+ printf("\t%s, version: %s\n",
+ sftp_extensions_get_name(sftp_session, i),
+ sftp_extensions_get_data(sftp_session, i));
+ }
+
+ /* test symlink and readlink */
+ if (sftp_symlink(sftp_session, "/tmp/this_is_the_link",
+ "/tmp/sftp_symlink_test") < 0) {
+ fprintf(stderr, "Could not create link (%s)\n", ssh_get_error(session));
+ return;
+ }
+
+ lnk = sftp_readlink(sftp_session, "/tmp/sftp_symlink_test");
+ if (lnk == NULL) {
+ fprintf(stderr, "Could not read link (%s)\n", ssh_get_error(session));
+ return;
+ }
+ printf("readlink /tmp/sftp_symlink_test: %s\n", lnk);
+
+ sftp_unlink(sftp_session, "/tmp/sftp_symlink_test");
+
+ if (sftp_extension_supported(sftp_session, "statvfs@openssh.com", "2")) {
+ sftpstatvfs = sftp_statvfs(sftp_session, "/tmp");
+ if (sftpstatvfs == NULL) {
+ fprintf(stderr, "statvfs failed (%s)\n", ssh_get_error(session));
+ return;
+ }
+
+ printf("sftp statvfs:\n"
+ "\tfile system block size: %llu\n"
+ "\tfundamental fs block size: %llu\n"
+ "\tnumber of blocks (unit f_frsize): %llu\n"
+ "\tfree blocks in file system: %llu\n"
+ "\tfree blocks for non-root: %llu\n"
+ "\ttotal file inodes: %llu\n"
+ "\tfree file inodes: %llu\n"
+ "\tfree file inodes for to non-root: %llu\n"
+ "\tfile system id: %llu\n"
+ "\tbit mask of f_flag values: %llu\n"
+ "\tmaximum filename length: %llu\n",
+ (unsigned long long) sftpstatvfs->f_bsize,
+ (unsigned long long) sftpstatvfs->f_frsize,
+ (unsigned long long) sftpstatvfs->f_blocks,
+ (unsigned long long) sftpstatvfs->f_bfree,
+ (unsigned long long) sftpstatvfs->f_bavail,
+ (unsigned long long) sftpstatvfs->f_files,
+ (unsigned long long) sftpstatvfs->f_ffree,
+ (unsigned long long) sftpstatvfs->f_favail,
+ (unsigned long long) sftpstatvfs->f_fsid,
+ (unsigned long long) sftpstatvfs->f_flag,
+ (unsigned long long) sftpstatvfs->f_namemax);
+
+ sftp_statvfs_free(sftpstatvfs);
+
+ if (statvfs("/tmp", &sysstatvfs) < 0) {
+ fprintf(stderr, "statvfs failed (%s)\n", strerror(errno));
+ return;
+ }
+
+ printf("sys statvfs:\n"
+ "\tfile system block size: %llu\n"
+ "\tfundamental fs block size: %llu\n"
+ "\tnumber of blocks (unit f_frsize): %llu\n"
+ "\tfree blocks in file system: %llu\n"
+ "\tfree blocks for non-root: %llu\n"
+ "\ttotal file inodes: %llu\n"
+ "\tfree file inodes: %llu\n"
+ "\tfree file inodes for to non-root: %llu\n"
+ "\tfile system id: %llu\n"
+ "\tbit mask of f_flag values: %llu\n"
+ "\tmaximum filename length: %llu\n",
+ (unsigned long long) sysstatvfs.f_bsize,
+ (unsigned long long) sysstatvfs.f_frsize,
+ (unsigned long long) sysstatvfs.f_blocks,
+ (unsigned long long) sysstatvfs.f_bfree,
+ (unsigned long long) sysstatvfs.f_bavail,
+ (unsigned long long) sysstatvfs.f_files,
+ (unsigned long long) sysstatvfs.f_ffree,
+ (unsigned long long) sysstatvfs.f_favail,
+ (unsigned long long) sysstatvfs.f_fsid,
+ (unsigned long long) sysstatvfs.f_flag,
+ (unsigned long long) sysstatvfs.f_namemax);
+ }
+
+ /* the connection is made */
+ /* opening a directory */
+ dir=sftp_opendir(sftp_session,"./");
+ if(!dir) {
+ fprintf(stderr, "Directory not opened(%s)\n", ssh_get_error(session));
+ return ;
+ }
+ /* reading the whole directory, file by file */
+ while((file=sftp_readdir(sftp_session,dir))){
+ fprintf(stderr, "%30s(%.8o) : %.5d.%.5d : %.10llu bytes\n",
+ file->name,
+ file->permissions,
+ file->uid,
+ file->gid,
+ (long long unsigned int) file->size);
+ sftp_attributes_free(file);
+ }
+ /* when file=NULL, an error has occured OR the directory listing is end of file */
+ if(!sftp_dir_eof(dir)){
+ fprintf(stderr, "Error: %s\n", ssh_get_error(session));
+ return;
+ }
+ if(sftp_closedir(dir)){
+ fprintf(stderr, "Error: %s\n", ssh_get_error(session));
+ return;
+ }
+ /* this will open a file and copy it into your /home directory */
+ /* the small buffer size was intended to stress the library. of course, you can use a buffer till 20kbytes without problem */
+
+ fichier=sftp_open(sftp_session,"/usr/bin/ssh",O_RDONLY, 0);
+ if(!fichier){
+ fprintf(stderr, "Error opening /usr/bin/ssh: %s\n",
+ ssh_get_error(session));
+ return;
+ }
+ /* open a file for writing... */
+ to=sftp_open(sftp_session,"ssh-copy",O_WRONLY | O_CREAT, 0700);
+ 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("sftp session terminated\n");
+}
+#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){
+ ssh_session session;
+ int auth=0;
+ char *password;
+ char *banner;
+ char *hexa;
+ int state;
+ char buf[10];
+ unsigned char *hash = NULL;
+ int hlen;
+
+ session = ssh_new();
+
+ 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);
+
+ if (user) {
+ if (ssh_options_set(session, SSH_OPTIONS_USER, user) < 0) {
+ ssh_disconnect(session);
+ return 1;
+ }
+ }
+
+ if (ssh_options_set(session, SSH_OPTIONS_HOST ,host) < 0) {
+ ssh_disconnect(session);
+ return 1;
+ }
+
+ ssh_options_parse_config(session, NULL);
+
+ 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);
+ if (hlen < 0) {
+ 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 now :\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 safety.\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 the host key here,\n");
+ fprintf(stderr,"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);
+ 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 further 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;
+ case SSH_SERVER_ERROR:
+ free(hash);
+ fprintf(stderr,"%s",ssh_get_error(session));
+ ssh_disconnect(session);
+ ssh_finalize();
+ exit(-1);
+ }
+ free(hash);
+
+ ssh_userauth_none(session, NULL);
+
+ auth = ssh_auth_list(session);
+ 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(session));
+ 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(session));
+ 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
+ if(!sftp && !cmds[0])
+ do_cleanup(0);
+ ssh_disconnect(session);
+ ssh_finalize();
+
+ return 0;
+}