2011-06-06

SSH communication using libssh2 and boost::asio

I once needed to create a small class to execute a SSH command on a remote server. The environment was Windows XP and Windows Vista and MinGW was the compiler system. So, what I did can be found below. It uses Boost::Asio and the libssh2 library. Below the class code is a small program that make use of the class to execute the command uptime on a remote (*NIX) server.

My intention has been to write the code so that no or very few comments are needed. Let me know if you need more information.

To make this work you would need to compile the boost and libssh2 libraries and link them to your application. For libssh2, you will also need the OpenSSL library (and the zlib library which is optional for OpenSSL). If you need help with this, my consulting company can be of service.

#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/utility.hpp>
#include <libssh2.h>
#include <ostream>
#include <stdexcept>
using namespace boost::asio;
/// @brief Class to use for communications via the
/// SSH protocol.
/// Dedicated to the public domain.
/// @author Peter Jansson
/// @date 2011-06-06
class SshComm : public boost::noncopyable
{
   typedef std::runtime_error re;
   public:
   /// @brief Create a SSH communications capable object.
   /// @details The SSH session will be initialized to
   /// communicate to the host:port specified in the
   /// arguments to this constructor, using the login
   /// credentials supplied.
   SshComm(
         const char* IP,
         const int & PortNumber,
         const char* user,
         const char* pw)
      :io(),sck(io, ip::tcp::v4()),ss(libssh2_session_init())
   {
      sck.connect(
            ip::tcp::endpoint(
               ip::address::from_string(IP),
               PortNumber) );

      if( 0 > libssh2_session_startup(ss,sck.native()) )
      {
         throw re("Could not startup the SSH communication"
                  " session.");
      }
      if( libssh2_userauth_password(ss,user,pw) )
      {
         throw re("Access denied");
      }
   };
   /// @brief Deallocate memory needed for this object.
   virtual ~SshComm()
   {
      libssh2_session_disconnect(ss,"Goodbye.");
      libssh2_session_free(ss);
   };
   /// @brief Execute a command on the SSH server and expect
   /// a response.
   /// @param cmd[in] The command to execute.
   /// @param response_stream[in,out] The stream to write the
   /// expected response to.
   void ExecuteCmdResponse(
         const char* cmd,
         std::ostream & response_stream) const
   {
      LIBSSH2_CHANNEL* ch = libssh2_channel_open_session(ss);
      if( NULL == ch )
      {
         throw re("Could not open SSH communication channel.");
      }
      libssh2_channel_set_blocking(ch,1);
      if( -1 == libssh2_channel_exec(ch,cmd))
      {
         throw re("Failed to execute command.");
      }
      char buf[1024];
      int num_of_read_bytes;
      do
      {
         num_of_read_bytes = libssh2_channel_read(ch,buf,1024);
         response_stream.write(buf,num_of_read_bytes);
      } while(1==libssh2_poll_channel_read(ch,0)
            || 1024==num_of_read_bytes );
      libssh2_channel_close(ch);
      libssh2_channel_free(ch);
   };
   private:
   /// @brief The io_service needed for boost::asio.
   io_service io;
   /// @brief The underlying socket used for ssh communication.
   ip::tcp::socket sck;
   /// @brief The SSH session structure to use in all
   /// communcations using this instance.
   LIBSSH2_SESSION* ss;
};
#include <iostream>
#include <cstdlib>
#include <sstream>
int main(int ac,char** av)
{
   try
   {
      if( 5 != ac )
      {
         std::ostringstream o;
         o << "Usage: " << av[0] << " IP username password port";
         throw std::invalid_argument(o.str());
      }
      const SshComm ssh(av[1],std::atoi(av[4]),av[2],av[3]);
      ssh.ExecuteCmdResponse("uptime",std::cout);
      return EXIT_SUCCESS;
   }
   catch( const std::exception& e )
   {
      std::cerr << e.what() << '\n';
      return EXIT_FAILURE;
   }
}

0 kommentarer:

Post a Comment