How to get OpenSSL BIO_do_connect() failure reason? - c++

I'm using Ubuntu 18.04/gcc 7.3/OpenSSL 1.1.0g to make C++ app performing TLS/SSL connection with non-blocking BIO API.
When BIO_do_connect() fails connecting, e.g. if using wrong host name or port, there is no errors reported by OpenSSL. ERR_get_error() returns zero and ERR_print_errors_xx() doesn't print anything.
So the question is - how to get actual connection failure reason, e.g. 'Connection refused' or 'Host not resolved' etc?
Used code snippet below:
#include <cstdio>
#include <cstring>
#include <iostream>
#include "openssl/bio.h"
#include "openssl/err.h"
#include "openssl/ssl.h"
int main(int argc, char *argv[])
{
OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, nullptr);
std::cout << OpenSSL_version(OPENSSL_VERSION) << std::endl;
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
if (!ctx)
{
std::cerr << "Error creating SSL context:" << std::endl;
ERR_print_errors_fp(stderr);
return 1;
}
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
if(!SSL_CTX_load_verify_locations(ctx,
"/etc/ssl/certs/ca-certificates.crt",
nullptr))
{
std::cerr << "Error loading trust store into SSL context" << std::endl;
ERR_print_errors_fp(stderr);
return 1;
}
BIO* cbio = BIO_new_ssl_connect(ctx);
SSL* ssl = nullptr;
BIO_get_ssl(cbio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
BIO_set_conn_hostname(cbio, "not_actually_existing_host.com:https");
BIO_set_nbio(cbio, 1);
std::cout << "Start connecting" << std::endl;
next:
if (BIO_do_connect(cbio) <= 0)
{
if (!BIO_should_retry(cbio))
{
std::cerr << "Error attempting to connect:" << std::endl;
ERR_print_errors_fp(stderr); // <---- PRINTS NOTHING!!!
BIO_free_all(cbio);
SSL_CTX_free(ctx);
return 1;
}
else goto next;
}
std::cout << "Connected OK" << std::endl;
BIO_free_all(cbio);
SSL_CTX_free(ctx);
return 0;
}

This approach finally works for me:
const auto sysErrorCode = errno;
const auto sslErrorCode = ERR_get_error();
std::string errorDescription;
if (sslErrorCode != 0) errorDescription = ERR_error_string(sslErrorCode, nullptr);
if (sysErrorCode != 0)
{
if (!errorDescription.empty()) errorDescription += '\n';
errorDescription += "System error, code=" + std::to_string(sysErrorCode);
errorDescription += ", ";
errorDescription += strerror(sysErrorCode);
}

Related

WSA UDP socket can't be reused as it forcibly closes the connection

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

c++ libssh - ssh_channel_read() keeps returning 0 and ssh_channel_is_open() returns false

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

libev + non-blocking socket continuously invokes callback

I'm using libev + non-blocking sockets to send a request to a server. I'm using Keep Alive because I need to send future requests to the destination over this same connection.
Behavior
Run the program and it fetches the URL and logs to console, as expected.
After doing this, wait and don't push ctrl+c to exit the program.
Expected
App should stay open because event loop is waiting for future responses but should not console log anything after the initial response.
Actual
Leave the app running. After 30+ seconds, it will start to console log the same response over and over and over again without end.
Question
Why is libev calling my callback (example_cb) repeatedly when no new request was sent and no new response data was received? How can I fix this?
#include <ev.h>
#include <stdio.h>
#include <iostream>
#include <ctype.h>
#include <cstring>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sstream>
#include <fstream>
#include <string>
using namespace std;
void sendRequest(int sockfd)
{
puts("------");
puts("sendRequest() was called");
stringstream ss;
ss << "GET /posts/11 HTTP/1.1\r\n"
<< "Host: jsonplaceholder.typicode.com\r\n"
<< "Accept: application/json\r\n"
<< "\r\n";
string request = ss.str();
if (send(sockfd, request.c_str(), request.length(), 0) != (int)request.length()) {
cout << "Error sending request." << endl;
exit(1);
}
cout << "Request sent. No err occured." << endl;
}
static void delay_cb(EV_P_ ev_timer *w, int revents)
{
puts("------");
puts("delay_cb() was called");
sendRequest(3);
}
static void example_cb(EV_P_ ev_io *w, int revents)
{
puts("------");
puts("example_cb() was called");
int sockfd = 3;
size_t len = 80*1024, nparsed; // response must be <= 80 Kb
char buf[len];
ssize_t recved;
recved = recv(sockfd, &buf, len, 0);
if (recved < 0) {
perror("recved was <1");
}
// don't process keep alives
if (buf[0] != '\0') {
std::cout << buf << std::endl;
}
// clear buf
buf[0] = '\0';
std::cout << "buf after clear attempt: " << buf << std::endl;
}
int example_request()
{
std::string hostname = "jsonplaceholder.typicode.com";
int PORT = 80;
struct sockaddr_in client;
struct hostent * host = gethostbyname(hostname.c_str());
if (host == NULL || host->h_addr == NULL) {
cout << "Error retrieving DNS information." << endl;
exit(1);
}
bzero(&client, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons( PORT );
memcpy(&client.sin_addr, host->h_addr, host->h_length);
// create a socket
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
cout << "Error creating socket." << endl;
exit(1);
}
cout << "Socket created" << endl;
// enable keep alive
int val = 1;
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof val);
if (connect(sockfd, (struct sockaddr *)&client, sizeof(client)) < 0) {
close(sockfd);
cout << "Could not connect" << endl;
exit(1);
}
cout << "Socket connected" << endl;
// make non-blocking
int status = fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
if (status == -1) {
perror("ERROR making socket non-blocking");
}
std::cout << "Socket set to non-blocking" << std::endl;
std::cout << "Sockfd is: " << sockfd << std::endl;
return sockfd;
}
int main(void)
{
// establish socket connection
int sockfd = example_request();
struct ev_loop *loop = EV_DEFAULT;
ev_io example_watcher;
ev_io_init(&example_watcher, example_cb, sockfd, EV_READ);
ev_io_start(loop, &example_watcher);
// used to send the request 2 sec later
ev_timer delay_watcher;
ev_timer_init(&delay_watcher, delay_cb, 2, 0.0);
ev_timer_start(loop, &delay_watcher);
ev_run(loop, 0);
return 0;
}
Edit: Code updated with suggestions from comments
The source of the problem is that you do not check recved == 0 condition which corresponds to the other side closing the connection. When that happens the OS sets the socket into "closed mode" which (at least under linux) is always ready for reading and subsequent calls to recv will always return 0.
So what you need to do is to check for that condition, call close(fd); on the file descriptor (possibly with shutdown before) and ev_io_stop on the associated watcher. If you wish to continue at that point then you have to open a new socket and eo_io_start new watcher.

How do I receive data from NTP server?

I have no idea why send data is 48 bytes 010,0,0..., someone can explain? the problem is buffer for data received, I don't know how big he should be, and even if I receive data, how to make normal time from it?
Here's the code:
#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_MEAN_AND_LEAN
#include <winsock2.h>
#include <windows.h>
#include <time.h>
using namespace std;
class HRException
{
public:
HRException() :
m_pMessage("") {}
virtual ~HRException() {}
HRException(const char *pMessage) :
m_pMessage(pMessage) {}
const char * what() { return m_pMessage; }
private:
const char *m_pMessage;
};
const int REQ_WINSOCK_VER = 2; // Minimum winsock version required
const char DEF_SERVER_NAME[] = "0.pl.pool.ntp.org";
const int SERVER_PORT = 123;
const int TEMP_BUFFER_SIZE = 128;
const char msg[48] = { 010,0,0,0,0,0,0,0,0 };
// IP number typedef for IPv4
typedef unsigned long IPNumber;
IPNumber FindHostIP(const char *pServerName)
{
HOSTENT *pHostent;
// Get hostent structure for hostname:
if (!(pHostent = gethostbyname(pServerName)))
throw HRException("could not resolve hostname.");
// Extract primary IP address from hostent structure:
if (pHostent->h_addr_list && pHostent->h_addr_list[0])
return *reinterpret_cast<IPNumber*>(pHostent->h_addr_list[0]);
return 0;
}
void FillSockAddr(sockaddr_in *pSockAddr, const char *pServerName, int portNumber)
{
// Set family, port and find IP
pSockAddr->sin_family = AF_INET;
pSockAddr->sin_port = htons(portNumber);
pSockAddr->sin_addr.S_un.S_addr = FindHostIP(pServerName);
}
bool RequestHeaders(const char *pServername)
{
SOCKET hSocket = INVALID_SOCKET;
char tempBuffer[TEMP_BUFFER_SIZE];
sockaddr_in sockAddr = { 0 };
bool bSuccess = true;
try
{
// Lookup hostname and fill sockaddr_in structure:
cout << "Looking up hostname " << pServername << "... ";
FillSockAddr(&sockAddr, pServername, SERVER_PORT);
cout << "found.\n";
// Create socket
cout << "Creating socket... ";
if ((hSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
throw HRException("could not create socket.");
cout << "created.\n";
// Connect to server
cout << "Attempting to connect to " << inet_ntoa(sockAddr.sin_addr)
<< ":" << SERVER_PORT << "... ";
if (connect(hSocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) != 0)
throw HRException("could not connect.");
cout << "connected.\n";
cout << "Sending request... ";
// send request part 1
if (send(hSocket, msg, sizeof(msg) , 0) == SOCKET_ERROR)
throw HRException("failed to send data.");
cout << "request sent.\n";
cout << "Dumping received data...\n\n";
// Loop to print all data
recv(hSocket, tempBuffer, sizeof(tempBuffer), 0); // <-- the problem
///
//part where we take time out of tempBuffer
///
}
catch (HRException e)
{
cerr << "\nError: " << e.what() << endl;
bSuccess = false;
}
if (hSocket != INVALID_SOCKET)
{
closesocket(hSocket);
}
return bSuccess;
}
int main(int argc, char* argv[])
{
int iRet = 1;
WSADATA wsaData;
cout << "Initializing winsock... ";
if (WSAStartup(MAKEWORD(REQ_WINSOCK_VER, 0), &wsaData) == 0)
{
// Check if major version is at least REQ_WINSOCK_VER
if (LOBYTE(wsaData.wVersion) >= REQ_WINSOCK_VER)
{
cout << "initialized.\n";
// Set default hostname:
const char *pHostname = DEF_SERVER_NAME;
// Set custom hostname if given on the commandline:
if (argc > 1)
pHostname = argv[1];
iRet = !RequestHeaders(pHostname);
}
else
{
cerr << "required version not supported!";
}
cout << "Cleaning up winsock... ";
// Cleanup winsock
if (WSACleanup() != 0)
{
cerr << "cleanup failed!\n";
iRet = 1;
}
cout << "done.\n";
}
else
{
cerr << "startup failed!\n";
}
int x;
cin >> x;
return iRet;
}
Most part of code is from madwizard.org
Ok it works, main part of code:
const char msg[48] = { 010,0,0,0,0,0,0,0,0 };
if (send(hSocket, msg, sizeof(msg) , 0) == SOCKET_ERROR)
throw HRException("failed to send data.");
cout << "request sent.\n";
cout << "Dumping received data...\n\n";
char tempBuffer[1024];
int bytes = recv(hSocket, tempBuffer, sizeof(tempBuffer), 0);
cout << "bytes received: " << bytes << endl;
time_t tmit;
tmit = ntohl(((time_t*)tempBuffer)[4]);
tmit -= 2208988800U;
cout << ctime(&tmit);
No idea why data that we send is
msg[48] = { 010,0,0,0,0,0,0,0,0 };
and why received data contains many numbers? for example if change code to
tmit = ntohl(((time_t*)tempBuffer)[6]);
I will get date 2008y, why?
Guys why so many minuses?, still waiting for an explanation :D
Here's whole code http://pastebin.com/Sv3ERGfV , dont forget to link ws2_32.lib
Similar to my issue when trying to query the time from a self-hostet Windows-NTP-Server with the C++ library NTPClient which uses boost for the network tasks, msg[48] = { 010,0,0,0,0,0,0,0,0 }; configures the ntp.flags.mode. After comparing the network traffic of w32tm /stripchart /computer:10.159.96.65 using Wireshark, flag 27 or 11 seem to be the choices for my usecase:
Comparison of NTP network packages
tmit = ntohl(((time_t*)tempBuffer)[6]); extracts the data from the received package. It looks like
4 yields the reference time (last sync with timeserver I assume),
8 the time when server received request and
10 the transmit time (which should be almost equal).

c++ system() hangs on using netcat to connect to socket in different thread

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;