├── src ├── ssh │ ├── SshSocket.cpp │ ├── SshChannel.cpp │ └── SshSession.cpp ├── mikrotik-ssh-piano.cpp ├── Mikrotik.cpp ├── SshWrapper.cpp └── KeyToNote.cpp ├── include ├── ssh │ ├── SshSocket.hpp │ ├── SshChannel.hpp │ └── SshSession.hpp ├── KeyToNote.hpp ├── Mikrotik.hpp └── SshWrapper.hpp ├── MikrotikSSHPiano.sln ├── README.md └── CMakeLists.txt /src/ssh/SshSocket.cpp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/mikrotik-ssh-piano.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/altucor/MikrotikSSHPiano/HEAD/src/mikrotik-ssh-piano.cpp -------------------------------------------------------------------------------- /src/ssh/SshChannel.cpp: -------------------------------------------------------------------------------- 1 | #include "ssh/SshChannel.hpp" 2 | 3 | SshChannel::SshChannel(SshSession &session, std::string ip, std::string port) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /include/ssh/SshSocket.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SSH_SOCKET_HPP 2 | #define SSH_SOCKET_HPP 3 | 4 | class SshSocket 5 | { 6 | public: 7 | SshSocket(/* args */); 8 | ~SshSocket(); 9 | private: 10 | /* data */ 11 | }; 12 | 13 | SshSocket::SshSocket(/* args */) 14 | { 15 | } 16 | 17 | SshSocket::~SshSocket() 18 | { 19 | } 20 | 21 | #endif // SSH_SOCKET_HPP 22 | -------------------------------------------------------------------------------- /include/ssh/SshChannel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SSH_CHANNEL_HPP 2 | #define SSH_CHANNEL_HPP 3 | 4 | #include "libssh2.h" 5 | #include "ssh/SshSession.hpp" 6 | 7 | class SshChannel 8 | { 9 | public: 10 | SshChannel(SshSession &session, std::string ip, std::string port); 11 | ~SshChannel(); 12 | private: 13 | LIBSSH2_CHANNEL *channel; 14 | }; 15 | 16 | #endif // SSH_CHANNEL_HPP 17 | -------------------------------------------------------------------------------- /include/KeyToNote.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KEY_TO_NOTE_HPP 2 | #define KEY_TO_NOTE_HPP 3 | 4 | class KeyToNote 5 | { 6 | public: 7 | explicit KeyToNote(); 8 | ~KeyToNote(); 9 | float keyToFreq(unsigned char key); 10 | void octaveUp(); 11 | void octaveDown(); 12 | int getCurOctave() { return m_octave; }; 13 | 14 | private: 15 | int m_octave = 5; 16 | }; 17 | 18 | #endif // KEY_TO_NOTE_HPP 19 | -------------------------------------------------------------------------------- /include/Mikrotik.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MIKROTIK_HPP 2 | #define MIKROTIK_HPP 3 | 4 | #include "ssh/SshSession.hpp" 5 | 6 | class Mikrotik 7 | { 8 | public: 9 | Mikrotik( 10 | std::string hostname, 11 | const uint16_t port, 12 | std::string username, 13 | std::string password 14 | ); 15 | int playNote(float freq, float length); 16 | private: 17 | SshSession m_sshSession; 18 | }; 19 | 20 | 21 | #endif // MIKROTIK_HPP 22 | -------------------------------------------------------------------------------- /include/ssh/SshSession.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SSH_SESSION_HPP 2 | #define SSH_SESSION_HPP 3 | 4 | #include "libssh2.h" 5 | #include 6 | 7 | class SshSession 8 | { 9 | public: 10 | SshSession(std::string hostname, const uint16_t port, std::string username, std::string password); 11 | ~SshSession(); 12 | int runCmd(std::string cmd, std::string &response); 13 | private: 14 | int m_sock = 0; 15 | LIBSSH2_SESSION *m_session = NULL; 16 | }; 17 | 18 | #endif // SSH_SESSION_HPP 19 | -------------------------------------------------------------------------------- /src/Mikrotik.cpp: -------------------------------------------------------------------------------- 1 | #include "Mikrotik.hpp" 2 | 3 | Mikrotik::Mikrotik( 4 | std::string hostname, 5 | const uint16_t port, 6 | std::string username, 7 | std::string password 8 | ) : m_sshSession(hostname, port, username, password) 9 | { 10 | 11 | } 12 | 13 | int Mikrotik::playNote(float freq, float length) 14 | { 15 | std::string cmd = "beep"; 16 | cmd.append(" frequency=" + std::to_string(freq)); 17 | cmd.append(" length=" + std::to_string(length)); 18 | 19 | std::string reposnse; 20 | return m_sshSession.runCmd(cmd, reposnse); 21 | } 22 | -------------------------------------------------------------------------------- /include/SshWrapper.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SSH_WRAPPER_HPP 2 | #define SSH_WRAPPER_HPP 3 | 4 | #include "libssh2.h" 5 | 6 | class SshWrapper 7 | { 8 | public: 9 | explicit SshWrapper(std::string &user, std::string &ip, uint16_t port = 22); 10 | ~SshWrapper(); 11 | void playNote(float freq, float length); 12 | 13 | private: 14 | std::string m_user = ""; 15 | std::string m_ip = ""; 16 | uint16_t m_port = 22; 17 | LIBSSH2_SESSION *m_session; 18 | 19 | private: 20 | std::string m_getpass(const char *prompt, bool show_asterisk = true); 21 | int m_runCmd(std::string &cmd); 22 | }; 23 | 24 | #endif // SSH_WRAPPER_HPP 25 | -------------------------------------------------------------------------------- /MikrotikSSHPiano.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2003 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MikrotikSSHPiano", "MikrotikSSHPiano\MikrotikSSHPiano.vcxproj", "{27568AC1-DB41-472D-8F35-F062F9B8BBE2}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {27568AC1-DB41-472D-8F35-F062F9B8BBE2}.Debug|x64.ActiveCfg = Debug|x64 17 | {27568AC1-DB41-472D-8F35-F062F9B8BBE2}.Debug|x64.Build.0 = Debug|x64 18 | {27568AC1-DB41-472D-8F35-F062F9B8BBE2}.Debug|x86.ActiveCfg = Debug|Win32 19 | {27568AC1-DB41-472D-8F35-F062F9B8BBE2}.Debug|x86.Build.0 = Debug|Win32 20 | {27568AC1-DB41-472D-8F35-F062F9B8BBE2}.Release|x64.ActiveCfg = Release|x64 21 | {27568AC1-DB41-472D-8F35-F062F9B8BBE2}.Release|x64.Build.0 = Release|x64 22 | {27568AC1-DB41-472D-8F35-F062F9B8BBE2}.Release|x86.ActiveCfg = Release|Win32 23 | {27568AC1-DB41-472D-8F35-F062F9B8BBE2}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {5A35074E-EC56-497A-AB43-172152FFA996} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/SshWrapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | #include "SshWrapper.hpp" 11 | 12 | SshWrapper::SshWrapper(std::string &user, std::string &ip, uint16_t port) 13 | : m_user(user), 14 | m_ip(ip), 15 | m_port(port) 16 | { 17 | 18 | } 19 | 20 | 21 | SshWrapper::~SshWrapper() 22 | { 23 | 24 | } 25 | 26 | 27 | std::string SshWrapper::m_getpass(const char *prompt, bool show_asterisk) 28 | { 29 | const char BACKSPACE = 8; 30 | const char RETURN = 13; 31 | 32 | std::string password; 33 | unsigned char ch = 0; 34 | 35 | std::cout << prompt << std::endl; 36 | 37 | DWORD con_mode; 38 | DWORD dwRead; 39 | 40 | HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE); 41 | 42 | GetConsoleMode(hIn, &con_mode); 43 | SetConsoleMode(hIn, con_mode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT)); 44 | 45 | while (ReadConsoleA(hIn, &ch, 1, &dwRead, NULL) && ch != RETURN) 46 | { 47 | if (ch == BACKSPACE) 48 | { 49 | if (password.length() != 0) 50 | { 51 | if (show_asterisk) 52 | std::cout << "\b \b"; 53 | password.resize(password.length() - 1); 54 | } 55 | } 56 | else 57 | { 58 | password += ch; 59 | if (show_asterisk) 60 | std::cout << '*'; 61 | } 62 | } 63 | std::cout << std::endl; 64 | return password; 65 | } 66 | 67 | int SshWrapper::m_runCmd(std::string &cmd) 68 | { 69 | return -1; 70 | } 71 | 72 | void SshWrapper::playNote(float freq, float length) 73 | { 74 | std::string cmd = "beep"; 75 | cmd.append(" frequency=" + std::to_string(freq)); 76 | cmd.append(" length=" + std::to_string(length)); 77 | 78 | std::cout << cmd << std::endl; 79 | m_runCmd(cmd); 80 | } 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mikrotik SSH Piano 2 | 3 | That program allows you play on mikrotik beeper like on piano or one voice synth. All is works over SSH session. 4 | 5 | ## Requirements 6 | That project uses libssh for windows. So you need dll's from libssh. Precompiled dll's are available in Debug folder, also u can download Debug folder and enjoy using it! 7 | 8 | ## Arguments 9 | * First argument - username 10 | * Second argument - router ip 11 | * Third - ssh port on router 12 | * Example: **MikrotikSSHPiano.exe admin 192.168.0.1 22** 13 | 14 | ## Keyboard markdown 15 | Keyboard markdown same as in FL Studio. In FL you can use it, when you turn on feature "Typing keyboard to piano keyboard". 16 | http://flstudio.image-line.com/help/html/img_glob/qwerty_keyboard.jpg 17 | 18 | +--------------------------------------------------------------------------+ 19 | | Octave N+1 | 20 | | +---+ +---+ +---+ +---+ +---+ | 21 | | +---+ | 2 | +---+ | 3 | +---+ +---+ | 5 | +---+ | 6 | +---+ | 7 | +---+ | 22 | | ||Q|| +---+ ||W|| +---+ ||E|| ||R|| +---+ ||T|| +---+ ||Y|| +---+ ||U|| | 23 | | +---+ +---+ +---+ +---+ +---+ +---+ +---+ | 24 | | | 25 | +--------------------------------------------------------------------------+ 26 | | Octave N | 27 | | +---+ +---+ +---+ +---+ +---+ | 28 | | +---+ | S | +---+ | D | +---+ +---+ | G | +---+ | H | +---+ | J | +---+ | 29 | | ||Z|| +---+ ||X|| +---+ ||C|| ||V|| +---+ ||B|| +---+ ||N|| +---+ ||M|| | 30 | | +---+ +---+ +---+ +---+ +---+ +---+ +---+ | 31 | +--------------------------------------------------------------------------+ 32 | | | 33 | | +----------------------------------+ +-+-+ Current +-+-+ | 34 | | | Exit | | + | 4 | - | | 35 | | +----------------------------------+ +-+-+ +-+-+ | 36 | | | 37 | | Octave Up Octave Down | 38 | | | 39 | +--------------------------------------------------------------------------+ 40 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15.2) 2 | 3 | project(mikrotik-ssh-piano C CXX) 4 | 5 | IF(MSVC) 6 | set(PROJECT_NAME "${PROJECT_NAME}-win") 7 | ELSEIF(APPLE) 8 | set(PROJECT_NAME "${PROJECT_NAME}-osx") 9 | ELSEIF(UNIX) 10 | set(PROJECT_NAME "${PROJECT_NAME}-linux") 11 | ENDIF() 12 | 13 | set(REQUIRED_LIBSSH_VERSION 1.10.0) 14 | IF(MSVC) 15 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /MT") 16 | ELSE() 17 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -static-libstdc++") 18 | ENDIF(MSVC) 19 | 20 | IF(CMAKE_BUILD_TYPE MATCHES Debug) 21 | message("Debug build.") 22 | ELSEIF(CMAKE_BUILD_TYPE MATCHES Release) 23 | message("Release build.") 24 | ELSE() 25 | message(" ! ! ! Unknown build type.") 26 | ENDIF() 27 | 28 | message("CMAKEFLAGS DUMP: ${CMAKE_CXX_FLAGS}") 29 | 30 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}) 31 | list(APPEND CMAKE_PREFIX_PATH ${CMAKE_BINARY_DIR}) 32 | 33 | if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") 34 | message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") 35 | file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/v0.16.1/conan.cmake" 36 | "${CMAKE_BINARY_DIR}/conan.cmake" 37 | EXPECTED_HASH SHA256=396e16d0f5eabdc6a14afddbcfff62a54a7ee75c6da23f32f7a31bc85db23484 38 | TLS_VERIFY ON) 39 | endif() 40 | 41 | include(${CMAKE_BINARY_DIR}/conan.cmake) 42 | 43 | conan_cmake_configure(REQUIRES libssh2/${REQUIRED_LIBSSH_VERSION} 44 | GENERATORS cmake_find_package) 45 | 46 | conan_cmake_autodetect(settings) 47 | 48 | conan_cmake_install(PATH_OR_REFERENCE . 49 | BUILD missing 50 | REMOTE conancenter 51 | SETTINGS ${settings}) 52 | 53 | find_package(Libssh2 REQUIRED) 54 | 55 | include_directories("./") 56 | include_directories("./include") 57 | 58 | file(GLOB SOURCES 59 | "./include/ssh/SshSocket.hpp" 60 | "./include/ssh/SshSession.hpp" 61 | "./include/ssh/SshChannel.hpp" 62 | "./include/KeyToNote.hpp" 63 | "./include/SshWrapper.hpp" 64 | "./include/Mikrotik.hpp" 65 | "./src/ssh/SshSocket.cpp" 66 | "./src/ssh/SshSession.cpp" 67 | "./src/ssh/SshChannel.cpp" 68 | "./src/KeyToNote.cpp" 69 | "./src/SshWrapper.cpp" 70 | "./src/Mikrotik.cpp" 71 | "./src/mikrotik-ssh-piano.cpp" 72 | ) 73 | 74 | add_executable(${PROJECT_NAME} ${SOURCES}) 75 | 76 | IF(MSVC) 77 | set_property( 78 | TARGET 79 | ${PROJECT_NAME} 80 | PROPERTY 81 | MSVC_RUNTIME_LIBRARY 82 | "MultiThreaded$<$:>" 83 | ) 84 | ENDIF(MSVC) 85 | 86 | include_directories(${Libssh2_INCLUDE_DIRS}) 87 | target_link_libraries(${PROJECT_NAME} Libssh2::libssh2) 88 | -------------------------------------------------------------------------------- /src/KeyToNote.cpp: -------------------------------------------------------------------------------- 1 | #include "KeyToNote.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | const unsigned int notesInOctave = 12; 9 | 10 | enum Notes{ 11 | KEY_C, 12 | KEY_C_SHARP, 13 | KEY_D, 14 | KEY_Eb, 15 | KEY_E, 16 | KEY_F, 17 | KEY_F_SHARP, 18 | KEY_G, 19 | KEY_G_SHARP, 20 | KEY_A, 21 | KEY_Bb, 22 | KEY_B 23 | }; 24 | 25 | std::vector m_symbolicNotes = { 26 | "C 0", "C# 0", "D 0", "Eb 0", "E 0", "F 0", "F# 0", "G 0", "G# 0", "A 0", "Bb 0", "B 0", /* #0 */ 27 | "C 1", "C# 1", "D 1", "Eb 1", "E 1", "F 1", "F# 1", "G 1", "G# 1", "A 1", "Bb 1", "B 1", /* #1 */ 28 | "C 2", "C# 2", "D 2", "Eb 2", "E 2", "F 2", "F# 2", "G 2", "G# 2", "A 2", "Bb 2", "B 2", /* #2 */ 29 | "C 3", "C# 3", "D 3", "Eb 3", "E 3", "F 3", "F# 3", "G 3", "G# 3", "A 3", "Bb 3", "B 3", /* #3 */ 30 | "C 4", "C# 4", "D 4", "Eb 4", "E 4", "F 4", "F# 4", "G 4", "G# 4", "A 4", "Bb 4", "B 4", /* #4 */ 31 | "C 5", "C# 5", "D 5", "Eb 5", "E 5", "F 5", "F# 5", "G 5", "G# 5", "A 5", "Bb 5", "B 5", /* #5 */ 32 | "C 6", "C# 6", "D 6", "Eb 6", "E 6", "F 6", "F# 6", "G 6", "G# 6", "A 6", "Bb 6", "B 6", /* #6 */ 33 | "C 7", "C# 7", "D 7", "Eb 7", "E 7", "F 7", "F# 7", "G 7", "G# 7", "A 7", "Bb 7", "B 7", /* #7 */ 34 | "C 8", "C# 8", "D 8", "Eb 8", "E 8", "F 8", "F# 8", "G 8", "G# 8", "A 8", "Bb 8", "B 8", /* #8 */ 35 | "C 9", "C# 9", "D 9", "Eb 9", "E 9", "F 9", "F# 9", "G 9", "G# 9", "A 9", "Bb 9", "B 9" /* #9 */ 36 | }; 37 | 38 | std::vector m_freqNotes = { 39 | 8.18, 8.66, 9.18, 9.72, 10.30, 10.91, 11.56, 12.25, 12.98, 13.75, 14.57, 15.43, /* #0 */ 40 | 16.35, 17.32, 18.35, 19.45, 20.60, 21.83, 23.12, 24.50, 25.96, 27.50, 29.14, 30.87, /* #1 */ 41 | 32.70, 34.65, 36.71, 38.89, 41.20, 43.65, 46.25, 49.0, 51.91, 55.0, 58.27, 61.74, /* #2 */ 42 | 65.41, 69.30, 73.42, 77.78, 82.41, 87.31, 92.50, 98.0, 103.83, 110.0, 116.54, 123.47, /* #3 */ 43 | 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.0, 196.0, 207.65, 220.0, 233.08, 246.94, /* #4 */ 44 | 261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.0, 415.30, 440.0, 466.16, 493.88, /* #5 */ 45 | 523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 880.0, 932.33, 987.77, /* #6 */ 46 | 1046.5, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98, 1567.98, 1661.22, 1760.0, 1864.66, 1975.53, /* #7 */ 47 | 2093.0, 2217.46, 2349.32, 2489.02, 2637.02, 2793.83, 2959.96, 3135.96, 3322.44, 3520.0, 3729.31, 3951.07, /* #8 */ 48 | 4186.01, 4434.92, 4698.63, 4978.03, 5274.04, 5587.65, 5919.91, 6271.93, 6644.88, 7040.0, 7458.62, 7902.13 /* #9 */ 49 | }; 50 | 51 | std::map octaveMapLow = { 52 | { 122, KEY_C, }, // "z" btn 53 | { 115, KEY_C_SHARP, }, // "s" btn 54 | { 120, KEY_D, }, // "x" btn 55 | { 100, KEY_Eb, }, // "d" btn 56 | { 99, KEY_E, }, // "c" btn 57 | { 118, KEY_F, }, // "v" btn 58 | { 103, KEY_F_SHARP, }, // "g" btn 59 | { 98, KEY_G, }, // "b" btn 60 | { 104, KEY_G_SHARP, }, // "h" btn 61 | { 110, KEY_A, }, // "n" btn 62 | { 106, KEY_Bb, }, // "j" btn 63 | { 109, KEY_B }, // "m" btn 64 | }; 65 | 66 | std::map octaveMapHigh = { 67 | { 113, KEY_C, }, // "q" btn 68 | { 50, KEY_C_SHARP, }, // "2" btn 69 | { 119, KEY_D, }, // "w" btn 70 | { 51, KEY_Eb, }, // "3" btn 71 | { 101, KEY_E, }, // "e" btn 72 | { 114, KEY_F, }, // "r" btn 73 | { 53, KEY_F_SHARP, }, // "5" btn 74 | { 116, KEY_G, }, // "t" btn 75 | { 54, KEY_G_SHARP, }, // "6" btn 76 | { 121, KEY_A, }, // "y" btn 77 | { 55, KEY_Bb, }, // "7" btn 78 | { 117, KEY_B }, // "u" btn 79 | }; 80 | 81 | KeyToNote::KeyToNote() 82 | { 83 | } 84 | 85 | 86 | KeyToNote::~KeyToNote() 87 | { 88 | } 89 | 90 | float KeyToNote::keyToFreq(unsigned char key) 91 | { 92 | float freq = 0.0f; 93 | int noteKey = 0; 94 | int freqPos = 0; 95 | auto itOctLow = octaveMapLow.find(key); 96 | auto itOctHigh = octaveMapHigh.find(key); 97 | 98 | if (itOctLow == octaveMapLow.end() && itOctHigh == octaveMapHigh.end()) 99 | return 0.0f; 100 | 101 | if (itOctLow != octaveMapLow.end()) { 102 | noteKey = itOctLow->second; 103 | freqPos = noteKey + (notesInOctave * m_octave); 104 | } else if (itOctHigh != octaveMapHigh.end()) { 105 | noteKey = itOctHigh->second; 106 | freqPos = noteKey + (notesInOctave * (m_octave + 1)); 107 | } 108 | 109 | 110 | if(freqPos < m_freqNotes.size()) 111 | freq = m_freqNotes[freqPos]; 112 | 113 | return freq; 114 | } 115 | 116 | void KeyToNote::octaveUp() 117 | { 118 | if(m_octave < 9) 119 | m_octave++; 120 | } 121 | 122 | void KeyToNote::octaveDown() 123 | { 124 | if(m_octave > 1) 125 | m_octave--; 126 | } 127 | -------------------------------------------------------------------------------- /src/ssh/SshSession.cpp: -------------------------------------------------------------------------------- 1 | #include "ssh/SshSession.hpp" 2 | 3 | #include 4 | 5 | static int waitsocket(int socket_fd, LIBSSH2_SESSION *session) 6 | { 7 | struct timeval timeout; 8 | int rc; 9 | fd_set fd; 10 | fd_set *writefd = NULL; 11 | fd_set *readfd = NULL; 12 | int dir; 13 | 14 | timeout.tv_sec = 10; 15 | timeout.tv_usec = 0; 16 | FD_ZERO(&fd); 17 | FD_SET(socket_fd, &fd); 18 | 19 | /* now make sure we wait in the correct direction */ 20 | dir = libssh2_session_block_directions(session); 21 | if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) 22 | readfd = &fd; 23 | if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) 24 | writefd = &fd; 25 | rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout); 26 | return rc; 27 | } 28 | 29 | SshSession::SshSession(std::string hostname, const uint16_t port, std::string username, std::string password) 30 | { 31 | #ifdef WIN32 32 | WSADATA wsadata; 33 | int err = WSAStartup(MAKEWORD(2, 0), &wsadata); 34 | if(err != 0) 35 | throw std::runtime_error("WSAStartup failed"); 36 | #endif 37 | uint32_t hostaddr = inet_addr(hostname.data()); 38 | m_sock = socket(AF_INET, SOCK_STREAM, 0); 39 | 40 | struct sockaddr_in sin; 41 | sin.sin_family = AF_INET; 42 | sin.sin_port = htons(port); 43 | sin.sin_addr.s_addr = hostaddr; 44 | if(connect(m_sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) != 0) 45 | { 46 | throw std::runtime_error("Error failed to connect to socket"); 47 | } 48 | 49 | m_session = libssh2_session_init(); 50 | if(!m_session) 51 | throw std::runtime_error("Error libssh2_session_init"); 52 | libssh2_session_set_blocking(m_session, 0); 53 | int rc = 0; 54 | while((rc = libssh2_session_handshake(m_session, m_sock)) == LIBSSH2_ERROR_EAGAIN); 55 | if(rc) 56 | throw std::runtime_error("Failure establishing SSH session"); 57 | 58 | LIBSSH2_KNOWNHOSTS *nh = libssh2_knownhost_init(m_session); 59 | if(!nh) 60 | throw std::runtime_error("Failure libssh2_knownhost_init"); 61 | 62 | /* read all hosts from here */ 63 | libssh2_knownhost_readfile(nh, "known_hosts", LIBSSH2_KNOWNHOST_FILE_OPENSSH); 64 | 65 | /* store all known hosts to here */ 66 | libssh2_knownhost_writefile(nh, "dumpfile", LIBSSH2_KNOWNHOST_FILE_OPENSSH); 67 | 68 | int type; 69 | std::size_t len; 70 | const char *fingerprint = libssh2_session_hostkey(m_session, &len, &type); 71 | if(fingerprint) 72 | { 73 | struct libssh2_knownhost *host; 74 | int check = libssh2_knownhost_checkp( 75 | nh, 76 | hostname.data(), 77 | port, 78 | fingerprint, 79 | len, 80 | LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW, 81 | &host 82 | ); 83 | 84 | //fprintf(stderr, "Host check: %d, key: %s\n", check, 85 | // (check <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)? 86 | // host->key:""); 87 | 88 | /***** 89 | * At this point, we could verify that 'check' tells us the key is 90 | * fine or bail out. 91 | *****/ 92 | } 93 | else 94 | { 95 | throw std::runtime_error("Failure libssh2_session_hostkey"); 96 | } 97 | libssh2_knownhost_free(nh); 98 | 99 | if(password.size() != 0) { 100 | /* We could authenticate via password */ 101 | while((rc = libssh2_userauth_password(m_session, username.data(), password.data())) == 102 | LIBSSH2_ERROR_EAGAIN); 103 | if(rc) { 104 | throw std::runtime_error("Authentication by password failed"); 105 | } 106 | } 107 | else { 108 | /* Or by public key */ 109 | while((rc = libssh2_userauth_publickey_fromfile( 110 | m_session, username.data(), 111 | "/home/user/" 112 | ".ssh/id_rsa.pub", 113 | "/home/user/" 114 | ".ssh/id_rsa", 115 | password.data())) == LIBSSH2_ERROR_EAGAIN); 116 | if(rc) { 117 | throw std::runtime_error("Authentication by public key failed"); 118 | } 119 | } 120 | 121 | } 122 | 123 | SshSession::~SshSession() 124 | { 125 | libssh2_session_disconnect(m_session, "Normal Shutdown, Thank you for playing"); 126 | libssh2_session_free(m_session); 127 | #ifdef WIN32 128 | closesocket(m_sock); 129 | #else 130 | close(m_sock); 131 | #endif 132 | } 133 | 134 | int SshSession::runCmd(std::string cmd, std::string &response) 135 | { 136 | char *exitsignal = (char *)"none"; 137 | int exitcode = 0; 138 | LIBSSH2_CHANNEL *channel; 139 | while((channel = libssh2_channel_open_session(m_session)) == NULL && 140 | libssh2_session_last_error(m_session, NULL, NULL, 0) == 141 | LIBSSH2_ERROR_EAGAIN) { 142 | waitsocket(m_sock, m_session); 143 | } 144 | if(channel == NULL) { 145 | fprintf(stderr, "Error\n"); 146 | exit(1); 147 | } 148 | int rc = 0; 149 | while((rc = libssh2_channel_exec(channel, cmd.data())) == 150 | LIBSSH2_ERROR_EAGAIN) { 151 | waitsocket(m_sock, m_session); 152 | } 153 | if(rc != 0) { 154 | fprintf(stderr, "Error\n"); 155 | exit(1); 156 | } 157 | int bytecount = 0; 158 | for(;;) { 159 | /* loop until we block */ 160 | int rc; 161 | do { 162 | char buffer[0x4000]; 163 | rc = libssh2_channel_read(channel, buffer, sizeof(buffer) ); 164 | if(rc > 0) { 165 | int i; 166 | bytecount += rc; 167 | fprintf(stderr, "We read:\n"); 168 | for(i = 0; i < rc; ++i) 169 | fputc(buffer[i], stderr); 170 | fprintf(stderr, "\n"); 171 | } 172 | else { 173 | if(rc != LIBSSH2_ERROR_EAGAIN) 174 | /* no need to output this for the EAGAIN case */ 175 | fprintf(stderr, "libssh2_channel_read returned %d\n", rc); 176 | } 177 | } 178 | while(rc > 0); 179 | 180 | /* this is due to blocking that would occur otherwise so we loop on 181 | this condition */ 182 | if(rc == LIBSSH2_ERROR_EAGAIN) { 183 | waitsocket(m_sock, m_session); 184 | } 185 | else 186 | break; 187 | } 188 | exitcode = 127; 189 | while((rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN) 190 | waitsocket(m_sock, m_session); 191 | 192 | if(rc == 0) { 193 | exitcode = libssh2_channel_get_exit_status(channel); 194 | libssh2_channel_get_exit_signal(channel, &exitsignal, 195 | NULL, NULL, NULL, NULL, NULL); 196 | } 197 | 198 | if(exitsignal) 199 | fprintf(stderr, "\nGot signal: %s\n", exitsignal); 200 | else 201 | fprintf(stderr, "\nEXIT: %d bytecount: %d\n", exitcode, bytecount); 202 | 203 | libssh2_channel_free(channel); 204 | channel = NULL; 205 | return 0; 206 | } 207 | --------------------------------------------------------------------------------