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");
}
 

Inlägg (RSS) och Kommentarer (RSS).

 


Advertisements:
Cheap Gas - Loans - Internet Marketing - Loans