I am trying to create a simplified SSH connection between client and server with libssh library in C++. My code is as displayed below:
ssh_session session;
session = ssh_new();
if(session == NULL) {
cout << "Session cannot be created. Exiting.." << endl;
exit(EXIT_FAILURE);
}
int verbosity = SSH_LOG_FUNCTIONS;
ssh_options_set(session, SSH_OPTIONS_HOST, "127.0.0.1");
ssh_options_set(session, SSH_OPTIONS_PORT, "22");
ssh_options_set(session, SSH_OPTIONS_USER, "box")
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
int rc;
// ---- This is where it starts
rc = ssh_connect(session); // <--- CONNECTION IS CREATED, but THEN IT IS REFUSED BY SERVER!
if(rc != SSH_OK) {
cout << "rc != SSH_OK" << endl; // <--- GETS PRINTED AFTER REFUSAL OF CONNECTION
exit(EXIT_FAILURE); // <--- CODE EXITS HERE!
}
enum ssh_known_hosts_e state;
unsigned char *hash = NULL;
ssh_key srvpubkey = NULL;
size_t hashlen;
rc = ssh_get_server_publickey(session, &srvpubkey);
if(rc < 0) {
return -1;
}
rc = ssh_get_publickey_hash(srvpubkey, SSH_PUBLICKEY_HASH_SHA256, &hash, &hashlen);
ssh_key_free(srvpubkey);
if(rc < 0) {
cout << "Public Key Hash #ERROR" << endl;
return -1;
}
switch(state) {
case SSH_KNOWN_HOSTS_OK: {
// ok, no problem!
cout << "All good!" << endl;
break;
}
case SSH_KNOWN_HOSTS_CHANGED: {
ssh_clean_pubkey_hash(&hash);
cout << "Hash: " << hash << endl << "Length: " << hashlen << endl;
return -1;
}
case SSH_KNOWN_HOSTS_OTHER: {
ssh_clean_pubkey_hash(&hash);
return -1;
}
case SSH_KNOWN_HOSTS_NOT_FOUND: {
ssh_clean_pubkey_hash(&hash);
return -1;
}
case SSH_KNOWN_HOSTS_UNKNOWN: {
ssh_clean_pubkey_hash(&hash);
return -1;
}
case SSH_KNOWN_HOSTS_ERROR: {
ssh_clean_pubkey_hash(&hash);
return -1;
}
default: {
ssh_clean_pubkey_hash(&hash);
return -1;
}
}
ssh_clean_pubkey_hash(&hash);
ssh_disconnect(session);
ssh_free(session);
When executing this code, I am greeted with Connection Refused error. Please note that my SSH server is up and running, when I attempt to connect through my command terminal it is working OK. I am able to connect and do all I need, however when executed through this code; as previously stated, the connection is being refused. Any tips, hints or assistance is greatly appreciated.
PS. I've enabled maximum verbosity to learn more about the error but what I said is all that was provided. It gave no further information when debugging.
This has been solved. Thanks to tcpdump I was able to learn that the connection was being made to a different port. What immediately came to my attention then is to check how the port was being set and the mistake was right here:
ssh_options_set(session, SSH_OPTIONS_PORT, "22");
Fix:
int port = 22;
ssh_options_set(session, SSH_OPTIONS_PORT, &port);
Because SSH_OPTIONS_PORT takes reference.
Related
I need to close and then reuse the same socket in my app. The first time the socket connects it's able to connect properly, but a second time it's tried to be used, client gets a wsaerror 10054 (existing connection was forcibly closed by the remote host) from the server, and I see that server does not receive the "syn" data from the client. What seems to be wrong here? The client that has connected before is able to connect to a server again, but a server that has received a connection before is unable to accept a new connection as it somehow causes a 10054.
connectionmanager.hpp
#pragma once
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <iostream>
#include <string>
#pragma comment (lib, "Ws2_32.lib")
#define DEFAULT_PORT 27015
#define DEFAULT_BUFFER_LENGTH 64
class ConnectionManager {
private:
fd_set fdset;
struct timeval client_wait_timeout;
struct timeval server_wait_timeout;
SOCKET sock = INVALID_SOCKET;
// This is where we'll be setting up connection parameters or where we'll be storing the parameters for a connection that's made.
SOCKADDR_IN connection_data;
int connection_data_len = sizeof(connection_data);
char receive_buffer[DEFAULT_BUFFER_LENGTH] = { 0 }; // The object where the recieved data will be placed on.
public:
std::wstring server_ipv4;
bool is_connected = false;
std::string type = "none";
ConnectionManager();
void init(std::string connection_type);
void reset();
bool establish_first_connection();
bool await_first_connection();
std::string receive_data();
std::string send_data(std::string data);
};
connectionmanager.cpp
#include "connection_manager.hpp"
ConnectionManager::ConnectionManager() {
WSADATA wsadata;
int result;
// Initialize Windows Sockets library, version 2.2.
result = WSAStartup(MAKEWORD(2, 2), &wsadata);
if (result != 0)
std::cerr << "WSAStartup failed, error: " << result << "\n";
connection_data.sin_family = AF_INET; // Using IPv4
connection_data.sin_port = htons(DEFAULT_PORT);
}
void ConnectionManager::init(std::string connection_type) {
int result = 0;
if (connection_type == "server") {
connection_data.sin_addr.s_addr = INADDR_ANY; // Bind the socket to all available interfaces - or in other words, accept connections from any IPv4 address. We'll change this after we establish our first connection with the client.
// Create a socket for the server to listen from client for data / send data to client.
sock = socket(connection_data.sin_family, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
std::cerr << "Error occured while creating server socket: " << WSAGetLastError() << "\n";
WSACleanup();
}
// Bind the listening socket.
result = bind(sock, (SOCKADDR*)&connection_data, connection_data_len);
if (result == SOCKET_ERROR) {
std::cerr << "Listening socket bind failed with error: " << WSAGetLastError() << "\n";
closesocket(sock);
WSACleanup();
}
std::cout << "Awaiting connection..." << "\n";
if (!await_first_connection())
std::cerr << "Either no one connnected during the 60 second period, or there was a problem with the server. Last WSA error:" << WSAGetLastError() << "\n";
else {
std::cout << "Connected successfully!" << "\n";
is_connected = true;
}
}
else if (connection_type == "client") {
InetPton(connection_data.sin_family, (PCWSTR)(server_ipv4.c_str()), &connection_data.sin_addr.s_addr); // Set the IP address to connect to on the connection_data structure.
// Create a socket for sending data to server.
sock = socket(connection_data.sin_family, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) {
std::cerr << "Error occured while creating client socket: " << WSAGetLastError() << "\n";
WSACleanup();
}
std::wcout << "Attempting to connect to " << server_ipv4 << "..." << "\n";
if (!establish_first_connection())
std::cerr << "There was a problem connecting the server. Last WSA error: " << WSAGetLastError() << "\n";
else {
std::wcout << "Successfully connected to " << server_ipv4 << "!" << "\n";
is_connected = true;
}
}
// Put the socket in non-blocking mode.
unsigned long mode = 1;
if (ioctlsocket(sock, FIONBIO, (unsigned long*)&mode) == SOCKET_ERROR) {
std::cerr << "Error while putting the socket into non-blocking mode: " << WSAGetLastError() << "\n";
}
}
void ConnectionManager::reset() {
is_connected = false;
closesocket(sock);
}
/*
Functions "establish_first_connection" and "await_first_connection" do something that's quite similar to the three-way handshake method of a TCP connection.
*/
bool ConnectionManager::establish_first_connection() { // This will be used by the client.
// Set up the file descriptor set.
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
int send_result = INT32_MAX;
std::string syn_message = "SYN";
send_result = sendto(sock, syn_message.c_str(), syn_message.length(), 0, (SOCKADDR*)&connection_data, connection_data_len);
if (send_result == SOCKET_ERROR) {
std::cerr << "Error occured while attempting to send SYN to server: " << WSAGetLastError() << "\n";
}
else {
int result = 0;
int receive_result = 0;
// Set up the timeval struct for the timeout.
// We'll wait for 10 seconds for the server to respond, or else we'll call the connection off.
client_wait_timeout.tv_sec = 10; // seconds
client_wait_timeout.tv_usec = 0; // microseconds
// Wait until the timeout or until we receive data.
result = select(sock, &fdset, NULL, NULL, &client_wait_timeout);
if (result == 0)
std::cout << "Timeout." << "\n"; // todo
else if (result == -1)
std::cerr << "Error occured while awaiting first connection data from server. Last WSA error:" << WSAGetLastError() << "\n";
receive_result = recvfrom(sock, receive_buffer, DEFAULT_BUFFER_LENGTH, 0, (SOCKADDR*)&connection_data, &connection_data_len);
if (receive_result > 0) { // If we received any data before the timeout, return true.
std::string client_ack_message = "ACK";
std::cout << receive_buffer << "\n";
sendto(sock, client_ack_message.c_str(), client_ack_message.length(), 0, (SOCKADDR*)&connection_data, connection_data_len);
return true;
}
}
return false;
}
bool ConnectionManager::await_first_connection() { // This will be used by the server.
int result = 0;
int receive_result = 0;
int send_result = 0;
// Set up the file descriptor set.
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
// Set up the timeval struct for the timeout.
// We'll wait for 60 seconds for someone to connect and if someone doesn't connect, we'll cancel the server.
server_wait_timeout.tv_sec = 60; // seconds
server_wait_timeout.tv_usec = 0; // microseconds
// Wait until the timeout or until we receive data.
result = select(sock, &fdset, NULL, NULL, &server_wait_timeout);
if (result == 0) {
std::cout << "Timeout." << "\n";
return false;
}
else if (result == -1)
std::cerr << "Error occured while awaiting first connection data from client. Last WSA error: " << WSAGetLastError() << "\n";
receive_result = recvfrom(sock, receive_buffer, DEFAULT_BUFFER_LENGTH, 0, (SOCKADDR*)&connection_data, &connection_data_len); // We set the first connected client as the only suitable connector from now on here.
if (receive_result > 0) { // If we received any data before the timeout, let the client know that we acknowledge their request and return true.
std::string ack_message = "ACK";
send_result = sendto(sock, ack_message.c_str(), ack_message.length(), 0, (SOCKADDR*)&connection_data, connection_data_len); // Let the client know that we received their message.
if (send_result != SOCKET_ERROR)
return true;
}
return false;
}
std::string ConnectionManager::receive_data() {
ZeroMemory(receive_buffer, DEFAULT_BUFFER_LENGTH); // Clean the receive buffer of any possibly remaining data.
int receive_result = 42;
u_long ioctl_result = 123;
while (true) { // When ioctl with FIONREAD results 0, that means there's no datagram pending in the receive queue. We'll use this to grab only the last received package.
receive_result = recvfrom(sock, receive_buffer, DEFAULT_BUFFER_LENGTH, 0, (SOCKADDR*)&connection_data, &connection_data_len);
ioctlsocket(sock, FIONREAD, &ioctl_result);
if (ioctl_result == 0)
break;
}
// Handle errors.
if (receive_result > 0) {
return std::string(receive_buffer, receive_result); // Using the built-in method of casting char to std::string.
}
else if (receive_result == 0)
return "RECEIVEDNOTHING";
else if (receive_result == SOCKET_ERROR)
switch (WSAGetLastError()) {
case WSAEWOULDBLOCK:
return "WOULDBLOCK";
break;
case WSAECONNRESET:
return "CONNRESET";
break;
case NTE_OP_OK:
break;
default:
std::cerr << "Unhandled error while receiving data: " << WSAGetLastError() << "\n";
}
return "NONE";
}
std::string ConnectionManager::send_data(std::string data) {
int send_result = 666;
send_result = sendto(sock, data.c_str(), data.length(), 0, (SOCKADDR*)&connection_data, connection_data_len);
// Handle errors.
if (send_result == SOCKET_ERROR) {
std::cerr << "Error while sending data: " << WSAGetLastError() << "\n";
return std::string("FAIL");
}
else
return std::string("OK");
}
main.cpp
#include <iostream>
#include <string>
#include "connectionmanager.hpp"
int main() {
ConnectionManager connection_manager;
std::string connection_type;
std::cout << "server or client?" << "\n";
std::cin >> connection_type;
if (connection_type == "client") {
std::wstring ipv4_addr;
std::cout << "ip address?" << "\n";
std::wcin >> ipv4_addr;
connection_manager.server_ipv4 = ipv4_addr;
}
connection_manager.type = connection_type;
connection_manager.init(); // this works fine
connection_manager.reset();
connection_manager.init(); // client returns wsaerror 10054, server receives no data
}
I was able to solve this issue by moving the sin_family and sin_port initialization to ConnectionManager::init() from the constructor and by editing the ConnectionManager::reset() to look like this:
void ConnectionManager::reset() {
puts("reset!");
is_connected = false;
closesocket(sock);
sock = INVALID_SOCKET;
memset(&connection_data, 0, sizeof(connection_data)); // Get rid of the data from the previous connection.
memset(&receive_buffer, 0, sizeof(receive_buffer));
}
This is the first time I am implementing ssh programmatically and I am baffled about why my code does not work -- to be more specific, ssh_channel_read() keeps returning 0 bytes read. I don't know what I am doing wrong! I have been following the API instructions step by step but I am obviously omitting something inadvertently.
I am trying to connect to my Pi with a user name + password. Here is the complete code, you can just copy paste this and compile it with:
g++ main.cpp -lssh -o myapp
After the code, you can see the output I am getting. Please don't be harsh, like I said, this is the first time I am dealing with SSH:
#include <iostream>
#include <string>
#include <libssh/libsshpp.hpp>
int main(int argc, const char **argv)
{
int vbs = SSH_LOG_RARE;
int timeout_ms = 1000;
ssh_session session = ssh_new();
ssh_channel channel;
char buffer[256];
int bytes_red;
if (session == NULL)
{
std::cout << "Failed to create ssh session." << std::endl;
exit(-1);
}
ssh_set_blocking(session, 1);
std::cout << "Created SSH session..." << std::endl;
ssh_options_set(session, SSH_OPTIONS_HOST, "192.168.1.5");
ssh_options_set(session, SSH_OPTIONS_PORT_STR, "22");
ssh_options_set(session, SSH_OPTIONS_USER, "pi#192.168.1.5");
ssh_options_set(session,SSH_OPTIONS_LOG_VERBOSITY, &vbs);
int con_result = ssh_connect(session);
int auth_result = ssh_userauth_password(session, "pi", "1234");
std::cout << "Connecton Result is: " << con_result << std::endl;
std::cout << "Auth Result is: " << auth_result << std::endl;
///////////////////////////////////////////
// Did we create the session successfully?
///////////////////////////////////////////
if (con_result != SSH_OK)
{
std::cout << "SSH connection failed. Error code is: " << con_result << std::endl;
ssh_free(session);
return con_result;
}
///////////////////////////////////////////
// Did we authenticate?
///////////////////////////////////////////
if (auth_result != SSH_AUTH_SUCCESS)
{
std::cout << "SSH authentication failed. Error code is: " << auth_result << std::endl;
ssh_free(session);
return auth_result;
}
///////////////////////////////////////////
// Create a new ssh_channel
///////////////////////////////////////////
channel = ssh_channel_new(session);
if (channel == NULL)
{
std::cout << "Failed to create SSH channel." << std::endl;
ssh_free(session);
return SSH_ERROR;
}
if (ssh_channel_is_open(channel))
std::cout << "Channel is open" << std::endl;
else
std::cout << "Channel is closed" << std::endl;
while(!ssh_channel_is_eof(channel))
{
bytes_red = ssh_channel_read_timeout(channel, buffer, sizeof(buffer), 0, timeout_ms);
// if (bytes_red)
std::cout << "Bytes read: " << bytes_red << std::endl;
}
std::cout << "Exiting ..." << std::endl;
ssh_channel_close(channel);
ssh_channel_free(channel);
ssh_free(session);
return 0;
}
and here is the output I am getting when running it:
$./myapp
Created SSH session...
[2018/05/19 14:57:14.246759, 1] socket_callback_connected: Socket connection callback: 1 (0)
[2018/05/19 14:57:14.301270, 1] ssh_client_connection_callback: SSH server banner: SSH-2.0-OpenSSH_7.4p1 Raspbian-10+deb9u1
[2018/05/19 14:57:14.301321, 1] ssh_analyze_banner: Analyzing banner: SSH-2.0-OpenSSH_7.4p1 Raspbian-10+deb9u1
[2018/05/19 14:57:14.301337, 1] ssh_analyze_banner: We are talking to an OpenSSH client version: 7.4 (70400)
Connecton Result is: 0
Auth Result is: 0
Channel is closed
[2018/05/19 14:57:14.669298, 1] ssh_packet_process: Couldn't do anything with packet type 80
Bytes read: 0
Bytes read: 0
Bytes read: 0
Bytes read: 0
Bytes read: 0
^C
$
I can see the error, "Channel is closed" but why? What am I doing wrong?
After this, I also want to send data to the server and obviously get the feedback. From what I have read, ssh_channel_write() is the function to use.
I haven't dealt with SSH programmatically before and I am learning this as I write this.
All your help is very much appreciated.
Update
Thank to Jarra, I have solved this! Here is the final code that works!
#include <iostream>
#include <string>
#include <libssh/libsshpp.hpp>
int main(int argc, const char **argv)
{
int vbs = SSH_LOG_RARE;
int timeout_ms = 1000;
ssh_session session = ssh_new();
ssh_channel channel;
char buffer[256];
int bytes_red;
if (session == NULL)
{
std::cout << "Failed to create ssh session." << std::endl;
exit(-1);
}
ssh_set_blocking(session, 1);
std::cout << "Created SSH session..." << std::endl;
ssh_options_set(session, SSH_OPTIONS_HOST, "192.168.1.5");
ssh_options_set(session, SSH_OPTIONS_PORT_STR, "22");
ssh_options_set(session, SSH_OPTIONS_USER, "pi#192.168.1.5");
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &vbs);
int con_result = ssh_connect(session);
int auth_result = ssh_userauth_password(session, "pi", "1234");
std::cout << "Connecton Result is: " << con_result << std::endl;
std::cout << "Auth Result is: " << auth_result << std::endl;
///////////////////////////////////////////
// Did we create the session successfully?
///////////////////////////////////////////
if (con_result != SSH_OK)
{
std::cout << "SSH connection failed. Error code is: " << con_result << std::endl;
ssh_free(session);
return con_result;
}
///////////////////////////////////////////
// Did we authenticate?
///////////////////////////////////////////
if (auth_result != SSH_AUTH_SUCCESS)
{
std::cout << "SSH authentication failed. Error code is: " << auth_result << std::endl;
ssh_free(session);
return auth_result;
}
///////////////////////////////////////////
// Create a new ssh_channel
///////////////////////////////////////////
channel = ssh_channel_new(session);
if (channel == NULL)
{
std::cout << "Failed to create SSH channel." << std::endl;
ssh_free(session);
return SSH_ERROR;
}
ssh_channel_open_session(channel);
if (ssh_channel_is_open(channel))
std::cout << "Channel is open" << std::endl;
else
std::cout << "Channel is closed" << std::endl;
int rc = ssh_channel_request_exec(channel, "ls");
while(!ssh_channel_is_eof(channel))
{
bytes_red = ssh_channel_read_timeout(channel, buffer, sizeof(buffer), 0, timeout_ms);
// if (bytes_red)
// std::cout << "Bytes read: " << bytes_red << std::endl;
std::cout << buffer << std::endl;
}
std::cout << "Exiting ..." << std::endl;
ssh_channel_close(channel);
ssh_channel_free(channel);
ssh_free(session);
return 0;
}
To compile: g++ main.cpp -lssh -o myapp and here is what you get when I run it:
./myapp
Created SSH session...
[2018/05/19 16:01:41.830861, 1] socket_callback_connected: Socket connection callback: 1 (0)
[2018/05/19 16:01:41.884875, 1] ssh_client_connection_callback: SSH server banner: SSH-2.0-OpenSSH_7.4p1 Raspbian-10+deb9u1
[2018/05/19 16:01:41.884929, 1] ssh_analyze_banner: Analyzing banner: SSH-2.0-OpenSSH_7.4p1 Raspbian-10+deb9u1
[2018/05/19 16:01:41.884945, 1] ssh_analyze_banner: We are talking to an OpenSSH client version: 7.4 (70400)
Connecton Result is: 0
Auth Result is: 0
[2018/05/19 16:01:42.258668, 1] ssh_packet_process: Couldn't do anything with packet type 80
Channel is open
Desktop
Documents
Downloads
Music
Pictures
Public
python_games
Templates
Videos
����s
Exiting ...
I just need to work on that last bit with the funny chars. This is straight out of my source code editor when I just got it to work, so the code isn't perfect.
ssh_channel_new allocated the resources for a new channel. It does not open it.
Depending on what you are trying to achieve you should then call an appropriate ssh_channel_open_XXXX function on that channel.
A simple example can be found here: https://github.com/substack/libssh/blob/c073979235eb0d0587ac9cb3c192e91e32d34b06/examples/exec.c
First ssh_channel_open_session is called to open a session (shell) channel, and then ssh_channel_request_exec is called to execute the lsof command.
How/when you will write to the channel depends on the type of channel you have opened. An example of writing to a session channel (after calling cat > /dev/null on the host to pipe written data to /dev/null) can be seen here: https://github.com/substack/libssh/blob/c073979235eb0d0587ac9cb3c192e91e32d34b06/examples/senddata.c
I'm starting a simple socket server on a separate thread, and trying to connect to it using netcat on the main thread. This is the code:
#include <cstdlib>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
void* callback(void*);
bool isServerReady = false;
int main()
{
pthread_t thread;
int result = pthread_create(&thread, 0, callback, 0);
if (result != 0) {
cout << "[ERROR] Unable to create thread" << endl;
}
while (!isServerReady) {
}
int returnValue = system("echo TEST | netcat localhost 1234");
cout << "Return value: " << returnValue << endl;
return 0;
}
void* callback(void* threadId)
{
int serverSocketId = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocketId == -1) {
cout << "[SERVER ERROR] Unable to create server socket." << endl;
return (void*)-1;
}
int serverPort = 1234;
struct sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(serverPort);
serverAddress.sin_addr.s_addr = INADDR_ANY;
int bindReturn = bind(serverSocketId, (struct sockaddr*)&serverAddress, sizeof(serverAddress));
if (bindReturn == -1) {
cout << "[SERVER ERROR] Unable to bind server socket." << endl;
return (void*) -1;
}
isServerReady = true;
int waitQueueSize = 5;
listen(serverSocketId, waitQueueSize);
struct sockaddr_in clientAddress;
socklen_t clientAddressSize = sizeof(clientAddress);
int clientSocketId = accept(serverSocketId, (struct sockaddr*)&clientAddress, &clientAddressSize);
if (clientSocketId == -1) {
cout << "[SERVER ERROR] Unable to create client socket." << endl;
return (void*)-1;
}
char clientBuffer[256];
bzero(clientBuffer, 256);
int charsRead = read(clientSocketId, clientBuffer, 255);
if (charsRead == -1) {
cout << "[SERVER ERROR] Unable to read client socket." << endl;
return (void*)-1;
}
string serverMessage = "You wrote: ";
serverMessage.append(clientBuffer);
int charsWritten = write(clientSocketId, (char*)serverMessage.c_str(), (int)serverMessage.length());
if (charsWritten == -1) {
cout << "[SERVER ERRROR] Error writing to client socket." << endl;
return (void*)-1;
}
int closeReturn = close(serverSocketId);
if (closeReturn == -1) {
cout << "[SERVER ERROR] Error closing server socket." << endl;
return (void*)-1;
}
return (void*)0;
}
However, the system function never returns, making the program hang: the server's response "You wrote: TEST" is correctly printed on the console, but after that the program just hangs, without reaching the line that prints the return value. If I extract the same server code into another program, launch it, and use the same netcat call from the command line, I get the same response message, and then netcat exits without me being required to do anything.
This is the compilation command:
g++ -std=c++11 test.cpp -o test -lpthread && ./test
and compiler version:
g++ (Ubuntu 4.8.4-2ubuntu1~14.04.1) 4.8.4
Why do you think netcat is not returning when called from inside system()?
Thanks to #kfsone, the problem was just that my server never closed the client socket, thus preventing the client netcat to realize that the server wasn't listening any more, and making it wait for no reason indefinitely. To solve this:
// ...
string serverMessage = "You wrote: ";
serverMessage.append(clientBuffer);
int charsWritten = write(clientSocketId, (char*)serverMessage.c_str(), (int)serverMessage.length());
if (charsWritten == -1) {
cout << "[SERVER ERRROR] Error writing to client socket." << endl;
return (void*)-1;
}
// ADDED THIS:
int closeClientReturn = close(clientSocketId);
if (closeClientReturn == -1) {
cout << "[SERVER ERROR] Error closing client socket." << endl;
return (void*)-1;
}
int closeReturn = close(serverSocketId);
if (closeReturn == -1) {
cout << "[SERVER ERROR] Error closing server socket." << endl;
return (void*)-1;
}
return (void*)0;
At the time we are trying to create an interface for serial communication, to be able to communicate with a microprocessor.
Actually - everything works fine. Almost!
To be able to communicate with our controller, we need to sync up with it. To do this, we write a string: "?0{SY}13!", and the controller should then reply with "!0{SY}F5?" to accept the request for sync.
To do this, we use a writeData function (that works - we know that by using echo), and after that we use a readData to read the answer.
The problem is that, for some reason, it will not read anything. Though it returns 1 for success, the chars it reads is constanly " " (nothing).
Now comes the weird part - if we use an external terminal program to initialize the port (like putty), and then close the program, then everything works fine. It accepts the sync request, answers (and we can read it), and then we can do all that we want. But unless we use an external program to initialize the port, it doesn't work.
The constructor for the initializing the interface looks like this:
SerialIF::SerialIF(int baud, int byteSize, int stopBits, char* parity, int debug)
{
string coutport = getPort();
wstring wideport;
debug_ = debug; //Debuglevel
sync = false; //sync starts with false
error = false; //Error false as beginnging
//this is just for converting to the right type
for (int i = 0; i < coutport.length(); i++)
{
wideport += wchar_t(coutport[i]);
}
const wchar_t* port = wideport.c_str();
SerialIF::hserial = CreateFile(port,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (hserial == INVALID_HANDLE_VALUE)
{
if (GetLastError() == ERROR_FILE_NOT_FOUND)
{
if (debug_ != LOW)
{
cout << "[-] Port " << coutport << "doesn't exist." << endl;
}
}
if (debug_ != LOW)
{
cout << "[-] Handle error - is there another terminal active?" << endl;
}
error = true;
}
DCB dcbParms = { 0 };
dcbParms.DCBlength = sizeof(dcbParms);
if (!GetCommState(hserial, &dcbParms))
{
if (debug_ != LOW)
{
cout << "[-] Couldn't get status from port " << coutport << endl;
}
error = true;
}
if (!error)
{
setBaud(dcbParms, baud);
setParity(dcbParms, parity);
setByteSize(dcbParms, byteSize);
setStopbits(dcbParms, stopBits);
if (debug_ == HIGH)
{
cout << "[+] Serial port " << coutport << " has been activated. \nBaud-rate: " << baud << "\nParity: "
<< parity << "\nStop bits: " << stopBits << endl;
}
}
else if (debug_ != LOW)
{
cout << "[-] Port not initialized" << endl;
}
}
This should work - I really don't know why it shouldn't. It returns no errors, I've tried A LOT of error searching the last couple of days, I tried timeouts, I tried other ways of building it, but it all boils down to the same problem.
Why wont this initialize the port?
EDIT:
The output when trying to sync:
Can't post pictures due to lack of reputation. though it outputs as follows:
[+] Serial port COM1 has been activated.
Baud-rate: 9600
Parity: NONE
Stop bits: 1
[+] -> ?0{SY}13! is written to the port.
((And this is where it goes in to the infinite loop reading " "))
EDIT: code for read:
const int bytesToRead = 1; //I byte pr læsning
char buffer[bytesToRead + 1] = { 0 }; //Bufferen til data
DWORD dwBytesRead = 0; //Antal bytes læst
string store; //Store - den vi gemmer den samlede streng i
bool end = false; //Kontrolvariabel til whileloop.
while (end == false)
{
if (ReadFile(hserial, buffer, bytesToRead, &dwBytesRead, NULL))
/*Readfile læser fra interfacet vha. hserial som vi oprettede i constructoren*/
{
if (buffer[0] == '?') //Da protokollen slutter en modtaget streng med "?", sætter vi end til true
{ //Hvis denne læses.
end = true;
}
store += buffer[0];
}
else
{
if (debug_ != LOW)
{
cout << "[-] Read fail" << endl; //Hvis readfile returnerer false, så er der sket en fejl.
}
end = true;
}
}
if (debug_ == HIGH)
{
cout << "[+] Recieved: " << store << endl; //I forbindelse med debug, er det muligt at få udsrkevet det man fik ind.
}
recentIn = store; //RecentIN brugES i andre funktioner
if (verify()) //Som f.eks. her, hvor vi verificerer dataen
{
if (debug_ == HIGH)
{
cout << "[+] Verification success!" << endl;
}
return convertRecData(store);
}
else
{
if (debug_ != LOW)
{
cout << "[-] Verification failed." << endl;
}
vector <string> null; //Returnerer en string uden data i, hvis der er sket en fejl.
return null;
}
You never call SetCommState.
I'm not sure where your functions setBaud,setParity etc. come from, but I can't see how they can actually modify the serial port, as they don't have access to the comm device's handle.
ReadFile() can return success even when zero bytes are read. Use dwBytesRead to find the actual number of received characters.
while (ReadFile(hserial, buffer, 1, &dwBytesRead, NULL))
{
if (dwBytesRead != 0)
{
store += buffer[0];
if (buffer[0] == '?')
{
end = true;
break;
}
}
}
Had a similar problem between a PC and an arduino nano clone including a CH340. This post was the only one which discribes my problem very good.
I solved it by switching off DTR (data-terminal-ready) and RTS (request-to-send) flow control, which is normaly activated after (re)start the PC or plugging in the arduino. I found a descrition of this parameters in the documentation of DCB
I know that shis post is very old but maybe i can help somebody else with this idea/solution.
I'm new to Qt and I would like to implement FTP and SFTP support for my software.
As I googled I discovered that there doesn't exist a sftp library for Qt but it should be possible with QNetworkAccessManager.
I tried then to discover on how to build a custom protocol or something like that but didn't figure out how to do it.
Does someone know how I could do that?
Thanks,
Michael
There is no support for SFTP in Qt SDK but Qt Creator implements SFTP.
I have isolated the library that contains SSH and SFTP and I have created a new project named QSsh in Github. The aim of the project is to provide SSH and SFTP support for any Qt Application.
I have written an example on how to upload a file using SFTP. Take a look at examples/SecureUploader/
I hope it might be helpful
You need a custom implementation for each protocol. But we can create a class like QHttp which will do that. There are several protocols that has similar semantic, but not all. So, if you want write it, tell me and I help you.
There's no current SSH wrapper implementation in the Qt SDK. You have 3 choices here:
Roll your own custom SSH/SFTP client implementation using the IETF RFC and standard drafts like RFC4253. This might not be what you're looking for.
Use any of the ssh implementation libraries like openssh/libssh directly or writing your own Qt/C++ wrapper for future-reuse. Any reasonably decent project with ssh needs usually links to a an already established ssh library and uses it programatically. Like Qt Creator does, if you dig inside it long enough you'll find what user Paglian mentioned earlier. Relying on a library is less risky and more future-proof then rolling your own.
Use openssh tooling at the command line interface directly, using QProcess just like you'd use it at the shell. This is is the fastest method if you're working on a proof-of-concept project and don't need any complex ftp operations, as it might get a bit difficult to devise a robust wrapper around the CLI tooling.
I do this using libssh. Very straight forward.
https://api.libssh.org/stable/libssh_tutor_sftp.html
Don't forget to add your sftp server into known hosts in your system.
ssh-keyscan -H mysftpserver.com >> ~/.ssh/known_hosts
Example code:
#include "sftpuploader.h"
#include <QtDebug>
#include <QFileInfo>
#include <libssh/libssh.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <libssh/sftp.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <QFile>
int verify_knownhost(ssh_session session)
{
int state, hlen;
unsigned char *hash = NULL;
char *hexa;
char buf[10];
state = ssh_is_server_known(session);
hlen = ssh_get_pubkey_hash(session, &hash);
if (hlen < 0)
return -1;
switch (state)
{
case SSH_SERVER_KNOWN_OK:
break; /* ok */
case SSH_SERVER_KNOWN_CHANGED:
fprintf(stderr, "Host key for server changed: it is now:\n");
ssh_print_hexa("Public key hash", hash, hlen);
fprintf(stderr, "For security reasons, connection will be stopped\n");
free(hash);
return -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");
free(hash);
return -1;
case SSH_SERVER_FILE_NOT_FOUND:
fprintf(stderr, "Could not find known host file.\n");
fprintf(stderr, "If you accept the host key here, 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);
if (fgets(buf, sizeof(buf), stdin) == NULL)
{
free(hash);
return -1;
}
if (strncasecmp(buf, "yes", 3) != 0)
{
free(hash);
return -1;
}
if (ssh_write_knownhost(session) < 0)
{
fprintf(stderr, "Error %s\n", strerror(errno));
free(hash);
return -1;
}
break;
case SSH_SERVER_ERROR:
fprintf(stderr, "Error %s", ssh_get_error(session));
free(hash);
return -1;
}
free(hash);
return 0;
}
bool upload(const QString &localFile,
const QString &dest,
const QString &host,
const QString &username,
const QString &passwd)
{
bool retVal = false;
QFileInfo info(localFile);
m_localFilename = info.canonicalFilePath();
m_remoteFilename = dest + "/" + info.fileName();
int verbosity = SSH_LOG_PROTOCOL;
int port = 22;
int rc;
sftp_session sftp;
sftp_file file;
int access_type;
int nwritten;
QByteArray dataToWrite;
ssh_session my_ssh_session;
QFile myfile(m_localFilename);
if(!myfile.exists())
{
qDebug() << "SFTPUploader: File doesn't exist " << m_localFilename;
return retVal;
}
my_ssh_session = ssh_new();
if(my_ssh_session == NULL)
{
return retVal;
}
ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, host.toUtf8());
ssh_options_set(my_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(my_ssh_session, SSH_OPTIONS_PORT, &port);
rc = ssh_connect(my_ssh_session);
if (rc != SSH_OK)
{
qDebug() << "SFTPUploader: Error connecting to localhost: " << ssh_get_error(my_ssh_session);
ssh_free(my_ssh_session);
return retVal;
}
else
{
qDebug() << "SFTPUploader: SSH connected";
}
// Verify the server's identity
// For the source code of verify_knowhost(), check previous example
if (verify_knownhost(my_ssh_session) < 0)
{
ssh_disconnect(my_ssh_session);
ssh_free(my_ssh_session);
qDebug() << "SFTPUploader: verify_knownhost failed";
return retVal;
}
rc = ssh_userauth_password(my_ssh_session, username.toUtf8(), passwd.toUtf8());
if (rc != SSH_AUTH_SUCCESS)
{
qDebug() << "SFTPUploader: Error authenticating with password: " << ssh_get_error(my_ssh_session);
ssh_disconnect(my_ssh_session);
ssh_free(my_ssh_session);
return retVal;
}
else
{
qDebug() << "SFTPUploader: Authentication sucess";
}
sftp = sftp_new(my_ssh_session);
if (sftp == NULL)
{
qDebug() << "SFTPUploader: Error allocating SFTP session:" << ssh_get_error(my_ssh_session);
ssh_disconnect(my_ssh_session);
ssh_free(my_ssh_session);
return retVal;
}
rc = sftp_init(sftp);
if (rc != SSH_OK)
{
qDebug() << "SFTPUploader: Error initializing SFTP session:", sftp_get_error(sftp);
sftp_free(sftp);
ssh_disconnect(my_ssh_session);
ssh_free(my_ssh_session);
return retVal;
}
access_type = O_WRONLY | O_CREAT | O_TRUNC;
file = sftp_open(sftp, dest.toUtf8(), access_type, S_IRWXU);
if (file == NULL)
{
qDebug() << "SFTPUploader: Can't open file for writing:", ssh_get_error(my_ssh_session);
sftp_free(sftp);
ssh_disconnect(my_ssh_session);
ssh_free(my_ssh_session);
return retVal;
}
if(myfile.open(QFile::ReadOnly))
{
dataToWrite = myfile.readAll();
}
nwritten = sftp_write(file, dataToWrite, dataToWrite.size());
if (nwritten != dataToWrite.size())
{
qDebug() << "SFTPUploader: Can't write data to file: ", ssh_get_error(my_ssh_session);
sftp_close(file);
sftp_free(sftp);
ssh_disconnect(my_ssh_session);
ssh_free(my_ssh_session);
return retVal;
}
rc = sftp_close(file);
if (rc != SSH_OK)
{
qDebug() << "SFTPUploader: Can't close the written file:" << ssh_get_error(my_ssh_session);
sftp_free(sftp);
ssh_disconnect(my_ssh_session);
ssh_free(my_ssh_session);
return retVal;
}
else
{
qDebug() << "SFTPUploader: Success";
retVal = true;
}
return retVal;
}