SSH and SFTP in C++
C++ code to get ssh and sftp communications built into your computer program follows. A class is provided that encapsulates all communications details and provides a relatively simple facade to the user of the class.
Prerequisites: Libssh2 compiled and installed properly for your platform. Libssh2 is also available from SourceForge.
Class declaration:
#ifndef PJSSH_H #define PJSSH_H #include <libssh2.h> #include <iosfwd> #include <string> /// Class that encapsulates SSH and/or SFTP communications. /// Dedicated to the public domain. /// @author Peter Jansson http://peter.jansson.net/ /// @date 2008-05-17 class PJSSH { public: /// Create a SSH communications capable object. /// The SSH session will be initialized to communicate to the host:port /// specified in the arguments to this constructor, using the login /// credentials supplied. PJSSH( const char* aUserName, const char* aPassword, const char* aHostName, const int& aPortNumber); /// Destory this instance. /// The SSH session will be unitialized. ~PJSSH(); /// Execute the supplied command through the established SSH session. void ExecuteCmd(const char* aCommand) const; /// Get a file from the remote system and write as is (i.e. binary) /// on the supplied stream void GetFile(const char* aRemoteFileName, std::ostream& aStream) const; /// Read from the supplied stream and put it as is (i.e. binary) /// on the remote file. /// The remote file will be truncated and over written if it exists. void PutStream(std::istream& aStream, const char* aRemoteFileName) const; private: /// We don’t really support copying at this stage. PJSSH(const PJSSH & source); /// We don’t really support assignment copy at this stage. PJSSH & operator=(const PJSSH & source); /// The SSH session structure to use in all communcations using this /// instance. LIBSSH2_SESSION* mSession; /// Flag that indicates that the SSH session was successfully initiated. bool SessionIsOk; /// Error message set if any SSH communication failed. std::string LastErrorMessage; /// The socket used for communication. unsigned int hSock; /// Create a socket and connect to the host:port. /// @return the socket. int CreateSocketAndConnect( const char* aHostName, const int& aPortNumber); }; #endif // PJSSH_H
Class implementation:
#include "PJSSH.h" #include <sstream> #include <stdexcept> #include <libssh2_sftp.h> #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__CYGWIN__) #define PJSSH_POSIX //#include <unistd.h> //#include <sys/types.h> //#include <sys/socket.h> //#include <sys/times.h> //#include <sys/stat.h> //#include <stdio.h> //#include <netinet/in.h> #include <netdb.h> // gethostbyname #elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) #define PJSSH_WINDOWS #include <winsock2.h> #endif PJSSH::PJSSH( const char* aUserName, const char* aPassword, const char* aHostName, const int & aPortNumber): LastErrorMessage(""), SessionIsOk(false), hSock(CreateSocketAndConnect(aHostName,aPortNumber)) { if( hSock > 0 ) { // Now, we have the socket, let's initialize the session. mSession = libssh2_session_init(); if (libssh2_session_startup(mSession,hSock)) { LastErrorMessage = "Could not startup the ssh session."; } else { if(libssh2_userauth_password(mSession,aUserName,aPassword)) { LastErrorMessage = "Could not get authenticated."; } else { SessionIsOk = true; } } } } PJSSH::~PJSSH() { if( SessionIsOk ) { libssh2_session_disconnect(mSession,"Goodbye from PJSSH."); libssh2_session_free(mSession); } #ifdef PJSSH_WINDOWS closesocket(hSock); WSACleanup(); #else #ifdef PJSSH_POSIX close(hSock); #endif #endif } void PJSSH::ExecuteCmd(const char* aCommand) const { if( !SessionIsOk ) { std::ostringstream o; o<<"Can not execute command since the SSH session is not set up ok. " "Last error message: "<<LastErrorMessage; throw std::logic_error(o.str()); } // Try to open a channel to be used for executing the command. LIBSSH2_CHANNEL* channel = libssh2_channel_open_session(mSession); if( NULL == channel ) { throw std::runtime_error("Could not open communication channel for " "executing remote command."); } // Execute the command. if( -1 == libssh2_channel_exec(channel,aCommand)) { throw std::runtime_error("Failed to execute the remote command."); } // Close the channel. libssh2_channel_close(channel); // Free resources. libssh2_channel_free(channel); } PJSSH::PJSSH(const PJSSH & source) { } PJSSH & PJSSH::operator=(const PJSSH & source) { return *this; } void PJSSH::GetFile(const char* aRemoteFileName, std::ostream& aStream) const { LIBSSH2_SFTP* sftp = libssh2_sftp_init(mSession); if( NULL == sftp ) { throw std::runtime_error("Failed to open a sftp session."); } LIBSSH2_SFTP_HANDLE* file_handle = libssh2_sftp_open(sftp,aRemoteFileName,LIBSSH2_FXF_READ,0); if( NULL == file_handle ) { std::ostringstream o; o<<"Failed to open remote file for reading. Last error code=" < <libssh2_sftp_last_error(sftp); throw std::runtime_error(o.str()); } // Read the whole file and write the read data on the supplied stream. char buffer[1024]; size_t num_of_read_bytes(0); do { num_of_read_bytes = libssh2_sftp_read(file_handle,buffer,1024); aStream.write(buffer,num_of_read_bytes); } while( num_of_read_bytes == 1024 ); // Close sftp file handle and end SFTP session. libssh2_sftp_close_handle(file_handle); libssh2_sftp_shutdown(sftp); } void PJSSH::PutStream(std::istream & aStream, const char* aRemoteFileName) const { LIBSSH2_SFTP* sftp = libssh2_sftp_init(mSession); if( NULL == sftp ) { throw std::runtime_error("Failed to open a sftp session."); } LIBSSH2_SFTP_HANDLE* file_handle = libssh2_sftp_open(sftp,aRemoteFileName, LIBSSH2_FXF_TRUNC | LIBSSH2_FXF_WRITE,0); if( NULL == file_handle ) { std::ostringstream o; o<<"Failed to write on remote file. Last error code=" <<libssh2_sftp_last_error(sftp); throw std::runtime_error(o.str()); } // Write the stream to the remote file. char buffer[1024]; do { aStream.read(buffer,1024); const std::streamsize num_of_read_characters(aStream.gcount()); if( num_of_read_characters > 0 ) { const size_t num_of_bytes_written = libssh2_sftp_write( file_handle, buffer, num_of_read_characters); if( num_of_bytes_written == -1 ) { throw std::runtime_error("Failed to write to the remote file."); } else if( num_of_bytes_written != num_of_read_characters ) { throw std::runtime_error("Failed to write all bytes to remote file."); } } else { throw std::runtime_error("Failed to read characters from the input " "stream to be written to the remote file."); } } while( aStream ); // Close sftp file handle and end SFTP session. libssh2_sftp_close_handle(file_handle); libssh2_sftp_shutdown(sftp); } int PJSSH::CreateSocketAndConnect(const char* aHostName,const int& aPortNumber) { #ifdef PJSSH_WINDOWS int hSock(INVALID_SOCKET); WSADATA wsaData = {0}; if( 0 != WSAStartup( WINSOCK_VERSION, &wsaData )) { LastErrorMessage = "Failed to initialize communication."; } else { hSock = socket( AF_INET, SOCK_STREAM, IPPROTO_IP ); // Set the address to use for communication. sockaddr_in saServer = {0}; saServer.sin_family = PF_INET; saServer.sin_port = htons( aPortNumber ); saServer.sin_addr.s_addr = inet_addr( aHostName ); if( SOCKET_ERROR == connect(hSock,(sockaddr*)&saServer,sizeof(sockaddr))) { LastErrorMessage = "Failed to connect."; hSock = INVALID_SOCKET; } else { if( hSock == INVALID_SOCKET ) { LastErrorMessage = "Unable to connect."; } } } return hSock; #else #ifdef PJSSH_POSIX int hSock(-1); struct hostent* hp=gethostbyname(aHostName); if(!hp) { LastErrorMessage = "Failed to get IP adress of server."; } else { struct sockaddr_in s; s.sin_addr=*(struct in_addr *)hp->h_addr_list[0]; s.sin_family=hp->h_addrtype; s.sin_port=htons(aPortNumber); hSock = socket(AF_INET,SOCK_STREAM,0); if(hSock<0) { LastErrorMessage = "Failed to create socket."; } else { if(connect(hSock,(struct sockaddr *)&s,sizeof(s))<0) { LastErrorMessage = "Failed to connect."; hSock = -1; } } } return hSock; #else #error "Need to get a socket for this platform!" #endif #endif }
A test program:
#include "PJSSH.h" #include "PJSSH.cc" #include <fstream> int main(int argc,char** argv) { // user:pw@host:port const PJSSH ssh(argv[1],argv[2],argv[3],std::atoi(argv[4])); ssh.ExecuteCmd( "/bin/echo '-----------' > test.txt;" "/bin/ls >> test.txt;" "/bin/echo '-----------' >> test.txt;" ); std::ofstream test("test.txt"); ssh.GetFile("test.txt", test); ssh.ExecuteCmd("rm test.txt"); }