I try to set SSL connection on Windows with OpenSSL. My steps follow:
Create TCP socket BIO.
Connect to server with TCP.
Add this BIO to SSL instance.
Upgrade connection to SSL.
However, when I try to call SSL_connect with BIO that for sure recently connected to TCP socket, I receive SSL_ERROR_SYSCALL with WSAENOTCONN on Windows.
My code follows.
this->TcpSocket = BIO_new(BIO_s_connect());
BIO_set_nbio(this->TcpSocket, 1);
BIO_set_conn_hostname(this->TcpSocket, hostname);
BIO_set_conn_port(this->TcpSocket, port);
int connectionResult;
while ((connectionResult = BIO_do_connect(this->TcpSocket)) <= 0 && BIO_should_retry(this->TcpSocket))
{
auto retryType = BIO_retry_type(this->TcpSocket);
if (retryType & BIO_FLAGS_READ != 0
|| retryType & BIO_FLAGS_WRITE != 0)
{
auto handle = BIO_get_fd(this->TcpSocket, NULL);
fd_set handles;
handles.fd_count = 1;
handles.fd_array[0] = handle;
timeval timeout;
timeout.tv_sec = seconds;
timeout.tv_usec = 0;
if (retryType & BIO_FLAGS_READ != 0)
select(handle + 1, &handles, NULL, NULL, &timeout);
else
select(handle + 1, NULL, &handles, NULL, &timeout);
}
else
Thread::Sleep(50);
}
this->SslContext = SSL_CTX_new(SSLv23_client_method());
SSL_CTX_set_verify(this->SslContext, SSL_VERIFY_NONE, NULL);
this->SslSocket = SSL_new(this->SslContext);
SSL_set_bio(this->SslSocket, this->TcpSocket, this->TcpSocket);
int sslConnectResult;
while ((sslConnectResult = SSL_connect(this->SslSocket)) == -1)
{
auto now = time(NULL);
int sslConnectErrorCode = SSL_get_error(this->SslSocket, sslConnectResult);
switch (sslConnectErrorCode)
{
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
if (now >= deadline)
throw SocketTimeoutException();
else
this->WaitForTcpSocket(deadline - now);
break;
case SSL_ERROR_SYSCALL:
{
auto err = GetLastError();
this->RaiseOpenSSLException();
}
break;
default:
this->RaiseOpenSSLException();
}
}
What is reason of the error? I understand that it means that client is disconnected from server. But I don't understand why. I have good Internet connection, and server also is stable, so it is unlikely that the reason in network connectivity.
Your TCP connect loop is not taking into account if BIO_do_connect() fails and BIO_should_retry() returns false. Your loop will stop in that condition and you will not have a connection, but you attempt to activate SSL anyway, which could cause the WSAENOTCONN error.
Try something more like this instead:
do
{
connectionResult = BIO_do_connect(this->TcpSocket);
if (connectionResult > 0)
break;
if (!BIO_should_retry(this->TcpSocket))
throw SocketException();
auto retryType = BIO_retry_type(this->TcpSocket);
if (retryType & (BIO_FLAGS_READ | BIO_FLAGS_WRITE))
{
auto handle = BIO_get_fd(this->TcpSocket, NULL);
fd_set handles;
FD_ZERO(&handles);
FD_SET(handle, &handles);
timeval timeout;
timeout.tv_sec = seconds;
timeout.tv_usec = 0;
if (retryType & BIO_FLAGS_READ)
selectResult = select(handle + 1, &handles, NULL, NULL, &timeout);
else
selectResult = select select(handle + 1, NULL, &handles, NULL, &timeout);
if (selectResult < 0)
throw SocketException();
if (selectResult == 0)
throw SocketTimeoutException();
}
else
Thread::Sleep(50);
}
while (true);
Related
I read from socket using recv function. I have problem when no data available for reading. My programm just stops. I found that I can set timeout using select function. But looks that timeout affects select function itself and recv that goes after select still waits uncontinuously.
fd_set set;
struct timeval timeout;
FD_ZERO(&set); /* clear the set */
FD_SET(s, &set); /* add our file descriptor to the set */
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC;
timeout.tv_usec = 0;
int rv = select(s, &set, NULL, NULL, &timeout);
if((recv_size = recv(s , rx_tmp , bufSize ,0)) == SOCKET_ERROR)
{
...
}
How to ask recv function return after some timout?
Another way to set a timeout on recv() itself without using select() is to use setsockopt() to set the socket's SO_RCVTIMEO option (on platforms that support it).
On Windows, the code would look like this:
DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
//...
recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAETIMEDOUT)
//...
}
On other platforms, the code would look like this instead:
struct timeval timeout;
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC;
timeout.tv_usec = 0;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
//...
recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == -1)
{
if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
//...
}
You should check return value of select. select will return 0 in case timeout expired, so you should check for error and call recv only if select returned positive value:
On success, select() and pselect() return the number of file descriptors contained in the three returned descriptor sets (that is, the total number of bits that are set in readfds, writefds, exceptfds) which may be zero if the timeout expires before anything interesting happens.
int rv = select(s + 1, &set, NULL, NULL, &timeout);
if (rv == SOCKET_ERROR)
{
// select error...
}
else if (rv == 0)
{
// timeout, socket does not have anything to read
}
else
{
// socket has something to read
recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == SOCKET_ERROR)
{
// read failed...
}
else if (recv_size == 0)
{
// peer disconnected...
}
else
{
// read successful...
}
}
use the FD_ISSET() macro to test whether there is data to read. If it returns false, don't do the read.
http://linux.die.net/man/3/fd_set
Trying to write a client which will try to receive data till 3 seconds. I have implemented the connect method using select by below code.
//socket creation
m_hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
m_stAddress.sin_family = AF_INET;
m_stAddress.sin_addr.S_un.S_addr = inet_addr(pchIP);
m_stAddress.sin_port = htons(iPort);
m_stTimeout.tv_sec = SOCK_TIMEOUT_SECONDS;
m_stTimeout.tv_usec = 0;
//connecting to server
long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
connect(m_hSocket, (struct sockaddr *)&m_stAddress, sizeof(m_stAddress));
long iMode = 0;
iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
fd_set stWrite;
FD_ZERO(&stWrite);
FD_SET(m_hSocket, &stWrite);
iResult = select(0, NULL, &stWrite, NULL, &m_stTimeout);
if((iResult > 0) && (FD_ISSET(m_hSocket, &stWrite)))
return true;
But I cannot figure out what I am missing at receiving timeout with below code? It doesn't wait if the server connection got disconnected. It just returns instantly from select method.
Also how can I write a non blocking socket call with timeout for socket send.
long iMode = 1;
int iResult = ioctlsocket(m_hSocket, FIONBIO, &iMode);
fd_set stRead;
FD_ZERO(&stRead);
FD_SET(m_hSocket, &stRead);
int iRet = select(0, &stRead, NULL, NULL, &m_stTimeout);
if ((iRet > 0) && (FD_ISSET(m_hSocket, &stRead)))
{
while ((iBuffLen-1) > 0)
{
int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
if (iRcvLen == SOCKET_ERROR)
{
return false;
}
else if (iRcvLen == 0)
{
break;
}
pchBuff += iRcvLen;
iBuffLen -= iRcvLen;
}
}
The first parameter to select should not be 0.
Correct usage of select can be found here :
http://developerweb.net/viewtopic.php?id=2933
the first parameter should be the max value of your socket +1 and take interrupted system calls into account if it is non blocking:
/* Call select() */
do {
FD_ZERO(&readset);
FD_SET(socket_fd, &readset);
result = select(socket_fd + 1, &readset, NULL, NULL, NULL);
} while (result == -1 && errno == EINTR);
This is just example code you probably need the timeout parameter as well.
If you can get EINTR this will complicate your required logic, because if you get EINTR you have to do the same call again, but with the remaining time to wait for.
I think for non blocking mode one needs to check the recv() failure along with a timeout value. That mean first select() will return whether the socket is ready to receive data or not. If yes it will go forward else it will sleep until timeout elapses on the select() method call line. But if the receive fails due to some uncertain situations while inside read loop there we need to manually check for socket error and maximum timeout value. If the socket error continues and timeout elapses we need to break it.
I'm done with my receive timeout logic with non blocking mode.
Please correct me if I am wrong.
bool bReturn = true;
SetNonBlockingMode(true);
//check whether the socket is ready to receive
fd_set stRead;
FD_ZERO(&stRead);
FD_SET(m_hSocket, &stRead);
int iRet = select(0, &stRead, NULL, NULL, &m_stTimeout);
DWORD dwStartTime = GetTickCount();
DWORD dwCurrentTime = 0;
//if socket is not ready this line will be hit after 3 sec timeout and go to the end
//if it is ready control will go inside the read loop and reads data until data ends or
//socket error is getting triggered continuously for more than 3 secs.
if ((iRet > 0) && (FD_ISSET(m_hSocket, &stRead)))
{
while ((iBuffLen-1) > 0)
{
int iRcvLen = recv(m_hSocket, pchBuff, iBuffLen-1, 0);
dwCurrentTime = GetTickCount();
if ((iRcvLen == SOCKET_ERROR) && ((dwCurrentTime - dwStartTime) >= SOCK_TIMEOUT_SECONDS * 1000))
{
bReturn = false;
break;
}
else if (iRcvLen == 0)
{
break;
}
pchBuff += iRcvLen;
iBuffLen -= iRcvLen;
}
}
SetNonBlockingMode(false);
return bReturn;
I'm writing a TCP server (blocking socket model).
I'm having trouble implementing a valid normal program exit when the server is waiting (blocking) for new connection attempts on Accept (I use WSAccept).
The code for the server's listening socket is something like this (I omitted error handling and other irrelevant code):
int ErrCode = WSAStartup(MAKEWORD(2,2), &m_wsaData) ;
// Create a new socket to listen and accept new connection attempts
struct addrinfo hints, *res = NULL, *ptr = NULL ;
int rc, count = 0 ;
memset(&hints, 0, sizeof(hints)) ;
hints.ai_family = AF_UNSPEC ;
hints.ai_socktype = SOCK_STREAM ;
hints.ai_protocol = IPPROTO_TCP ;
hints.ai_flags = AI_PASSIVE ;
CString strPort ;
strPort.Format("%d", Port) ;
getaddrinfo(pLocalIp, strPort.GetBuffer(), &hints, &res) ;
strPort.ReleaseBuffer() ;
ptr = res ;
if ((m_Socket = WSASocket(res->ai_family, res->ai_socktype, res->ai_protocol, NULL, 0, 0)) == INVALID_SOCKET)
{
// some error
}
if(bind(m_Socket, (SOCKADDR *)res->ai_addr, res->ai_addrlen) == SOCKET_ERROR)
{
// some error
}
if (listen(m_Socket, SOMAXCONN) == SOCKET_ERROR)
{
// some error
}
So far so good... Then I implemented the WSAccept call inside a thread like this:
SOCKADDR_IN ClientAddr ;
int ClientAddrLen = sizeof(ClientAddr) ;
SOCKET TempS = WSAAccept(m_Socket, (SOCKADDR*) &ClientAddr, &ClientAddrLen, NULL, NULL);
Of course the WSAccept blocks until a new connection attempt is made but if I wish to exit
the program then i need some way to cause WSAccept to exit. I have tried several different approaches:
Attempt to call shutdown and/or closesocket with m_Socket from within another thread failed (program just hangs).
using WSAEventSelect indeed solves this issue but then WSAccept delivers only non-blocking sockets - which is not my intention. (Is there a way to make the sockets blocking?)
I Read about APC and tried to use something like QueueUserAPC(MyAPCProc, m_hThread, 1)) but it didn't work either.
What am I doing wrong ?
Is there a better way to cause this blocking WSAccept to exit ?
Use select() with a timeout to detect when a client connection is actually pending before then calling WSAAccept() to accept it. It works with blocking sockets without putting them into non-blocking mode. That will give your code more opportunities to check if the app is shutting down.
Go with non-blocking accepting socket (WSAEventSelect as you mentioned) and use non-blocking WSAccept. You can make a non-blocking socket that WSAccept returns into blocking socket with ioctlsocket (see msdn).
Do all the other stuff you absoultely have to on shutdown, (maybe you have DB connections to close, or files to flush?), and then call ExitProcess(0). That will stop your listening thread, no problem.
See log4cplus source for my take on this issue. I basically wait on two event objects, one is signaled when connection is being accepted (using WSAEventSelect()) and another is there to interrupt the waiting. The most relevant parts of the source is below. See ServerSocket::accept().
namespace {
static
bool
setSocketBlocking (SOCKET_TYPE s)
{
u_long val = 0;
int ret = ioctlsocket (to_os_socket (s), FIONBIO, &val);
if (ret == SOCKET_ERROR)
{
set_last_socket_error (WSAGetLastError ());
return false;
}
else
return true;
}
static
bool
removeSocketEvents (SOCKET_TYPE s, HANDLE ev)
{
// Clean up socket events handling.
int ret = WSAEventSelect (to_os_socket (s), ev, 0);
if (ret == SOCKET_ERROR)
{
set_last_socket_error (WSAGetLastError ());
return false;
}
else
return true;
}
static
bool
socketEventHandlingCleanup (SOCKET_TYPE s, HANDLE ev)
{
bool ret = removeSocketEvents (s, ev);
ret = setSocketBlocking (s) && ret;
ret = WSACloseEvent (ev) && ret;
return ret;
}
} // namespace
ServerSocket::ServerSocket(unsigned short port)
{
sock = openSocket (port, state);
if (sock == INVALID_SOCKET_VALUE)
{
err = get_last_socket_error ();
return;
}
HANDLE ev = WSACreateEvent ();
if (ev == WSA_INVALID_EVENT)
{
err = WSAGetLastError ();
closeSocket (sock);
sock = INVALID_SOCKET_VALUE;
}
else
{
assert (sizeof (std::ptrdiff_t) >= sizeof (HANDLE));
interruptHandles[0] = reinterpret_cast<std::ptrdiff_t>(ev);
}
}
Socket
ServerSocket::accept ()
{
int const N_EVENTS = 2;
HANDLE events[N_EVENTS] = {
reinterpret_cast<HANDLE>(interruptHandles[0]) };
HANDLE & accept_ev = events[1];
int ret;
// Create event and prime socket to set the event on FD_ACCEPT.
accept_ev = WSACreateEvent ();
if (accept_ev == WSA_INVALID_EVENT)
{
set_last_socket_error (WSAGetLastError ());
goto error;
}
ret = WSAEventSelect (to_os_socket (sock), accept_ev, FD_ACCEPT);
if (ret == SOCKET_ERROR)
{
set_last_socket_error (WSAGetLastError ());
goto error;
}
do
{
// Wait either for interrupt event or actual connection coming in.
DWORD wsawfme = WSAWaitForMultipleEvents (N_EVENTS, events, FALSE,
WSA_INFINITE, TRUE);
switch (wsawfme)
{
case WSA_WAIT_TIMEOUT:
case WSA_WAIT_IO_COMPLETION:
// Retry after timeout or APC.
continue;
// This is interrupt signal/event.
case WSA_WAIT_EVENT_0:
{
// Reset the interrupt event back to non-signalled state.
ret = WSAResetEvent (reinterpret_cast<HANDLE>(interruptHandles[0]));
// Clean up socket events handling.
ret = socketEventHandlingCleanup (sock, accept_ev);
// Return Socket with state set to accept_interrupted.
return Socket (INVALID_SOCKET_VALUE, accept_interrupted, 0);
}
// This is accept_ev.
case WSA_WAIT_EVENT_0 + 1:
{
// Clean up socket events handling.
ret = socketEventHandlingCleanup (sock, accept_ev);
// Finally, call accept().
SocketState st = not_opened;
SOCKET_TYPE clientSock = acceptSocket (sock, st);
int eno = 0;
if (clientSock == INVALID_SOCKET_VALUE)
eno = get_last_socket_error ();
return Socket (clientSock, st, eno);
}
case WSA_WAIT_FAILED:
default:
set_last_socket_error (WSAGetLastError ());
goto error;
}
}
while (true);
error:;
DWORD eno = get_last_socket_error ();
// Clean up socket events handling.
if (sock != INVALID_SOCKET_VALUE)
{
(void) removeSocketEvents (sock, accept_ev);
(void) setSocketBlocking (sock);
}
if (accept_ev != WSA_INVALID_EVENT)
WSACloseEvent (accept_ev);
set_last_socket_error (eno);
return Socket (INVALID_SOCKET_VALUE, not_opened, eno);
}
void
ServerSocket::interruptAccept ()
{
(void) WSASetEvent (reinterpret_cast<HANDLE>(interruptHandles[0]));
}
A not so neat way of solving this problem is by issuing a dummy WSAConnect request from the thread that needs to do the shutdown. If the dummy connect fails, you might resort to ExitProcess as suggested by Martin.
void Drain()
{
if (InterlockedIncrement(&drain) == 1)
{
// Make a dummy connection to unblock wsaaccept
SOCKET ConnectSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
if (ConnectSocket != INVALID_SOCKET) {
int iResult = WSAConnect(ConnectSocket, result->ai_addr, result->ai_addrlen, 0, 0, 0, 0);
if (iResult != 0) {
printf("Unable to connect to server! %d\n", WSAGetLastError());
}
else
{
closesocket(ConnectSocket);
}
}
}
}
I have a function to write data to the serial port with a certain protocol. When the function writes one frame, it waits for one answer of receiver. If no answer is received it has to resend data during 3 timeouts and in the end of 3 timeouts with no success, close the communication...
I have this function:
int serial_write(int fd, unsigned char* send, size_t send_size) {
......
int received_counter = 0;
while (!RECEIVED) {
Timeout.tv_usec = 0; // milliseconds
Timeout.tv_sec = timeout; // seconds
FD_SET(fd, &readfs);
//set testing for source 1
res = select(fd + 1, &readfs, NULL, NULL, &Timeout);
//timeout occurred.
if (received_counter == 3) {
printf(
"Connection maybe turned off! Number of resends exceeded!\n");
exit(-1);
}
if (res == 0) {
printf("Timeout occured\n");
write(fd, (&I[0]), I.size());
numTimeOuts++;
received_counter++;
} else {
RECEIVED = true;
break;
}
}
......
}
I have verified that this function, when it goes into timeout, does not resend the data. Why?
I'm looking for an example of how to use libssh2 to setup ssh port forwarding. I've looked at the API, but there is very little in the way of documentation in the area of port forwarding.
For instance, when using PuTTY's plink there is the remote port to listen on, but also the local port that traffic should be sent to. Is it the developers responsibility to set this up? Can someone give an example of how to do this?
Also, an example where remote port is brought to a local port would be useful. Do I use libssh2_channel_direct_tcpip_ex()?
I'm willing to put up a bounty if need be to get a couple of working examples of this.
The key to making libssh2 port forwarding work was discovering that it basically just gives you the data that came in to that port. You have to actually send the data onto a local port that you open:
(Note, this code is not yet complete, there is no error checking, and the thread yielding isn't correct, but it gives a general outline of how to accomplish this.)
void reverse_port_forward(CMainDlg* dlg, addrinfo * hubaddr, std::string username, std::string password, int port)
{
int iretval;
unsigned long mode = 1;
int last_socket_err = 0;
int other_port = 0;
fd_set read_set, write_set;
SOCKET sshsock = socket(AF_INET, SOCK_STREAM, 0);
iretval = connect(sshsock, hubaddr->ai_addr, hubaddr->ai_addrlen);
if (iretval != 0)
::PostQuitMessage(0);
LIBSSH2_SESSION * session = NULL;
session = libssh2_session_init();
iretval = libssh2_session_startup(session, sshsock);
if (iretval)
::PostQuitMessage(0);
iretval = libssh2_userauth_password(session, username.c_str(), password.c_str());
dlg->m_track_status(dlg, 1, 0, "Authorized");
LIBSSH2_LISTENER* listener = NULL;
listener = libssh2_channel_forward_listen_ex(session, "127.0.0.1", port, &other_port, 1);
if (!listener)
::PostQuitMessage(0);
LIBSSH2_CHANNEL* channel = NULL;
ioctlsocket(sshsock, FIONBIO, &mode);
libssh2_session_set_blocking(session, 0); // non-blocking
int err = LIBSSH2_ERROR_EAGAIN;
while (err == LIBSSH2_ERROR_EAGAIN)
{
channel = libssh2_channel_forward_accept(listener);
if (channel) break;
err = libssh2_session_last_errno(session);
boost::this_thread::yield();
}
if (channel)
{
char buf[MAX_BUF_LEN];
char* chunk;
long bytes_read = 0;
long bytes_written = 0;
int total_set = 0;
timeval wait;
wait.tv_sec = 0;
wait.tv_usec = 2000;
sockaddr_in localhost;
localhost.sin_family = AF_INET;
localhost.sin_addr.s_addr = inet_addr("127.0.0.1");
localhost.sin_port = htons(5900);
SOCKET local_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ioctlsocket(local_sock, FIONBIO, &mode);
iretval = connect(local_sock, (sockaddr*) &localhost, sizeof(localhost) );
if (iretval == SOCKET_ERROR)
iretval = WSAGetLastError();
while (1)
{
bytes_read = libssh2_channel_read(channel, buf, MAX_BUF_LEN);
if (bytes_read >= 0){
FD_ZERO(&read_set);
FD_ZERO(&write_set);
FD_SET(local_sock, &write_set);
// wait until the socket can be written to
while (select(0, &read_set, &write_set, NULL, &wait) < 1)
boost::this_thread::yield();
if (FD_ISSET(local_sock, &write_set))
{
FD_CLR(local_sock, &write_set);
chunk = buf;
// everything may not get written in this call because we're non blocking. So
// keep writing more data until we've emptied the buffer pointer.
while ((bytes_written = send(local_sock, chunk, bytes_read, 0)) < bytes_read)
{
// if it couldn't write anything because the buffer is full, bytes_written
// will be negative which won't help our pointer math much
if (bytes_written > 0)
{
chunk = buf + bytes_written;
bytes_read -= bytes_written;
if (bytes_read == 0)
break;
}
FD_ZERO(&read_set);
FD_ZERO(&write_set);
FD_SET(local_sock, &write_set);
// wait until the socket can be written to
while (select(0, &read_set, &write_set, NULL, &wait) < 1)
boost::this_thread::yield();
}
}
}
FD_ZERO(&read_set);
FD_ZERO(&write_set);
FD_SET(local_sock, &read_set);
select(0, &read_set, &write_set, NULL, &wait);
if (FD_ISSET(local_sock, &read_set))
{
FD_CLR(local_sock, &read_set);
bytes_read = recv(local_sock, buf, MAX_BUF_LEN, 0);
if (bytes_read >= 0)
{
while ((bytes_written = libssh2_channel_write_ex(channel, 0, buf, bytes_read)) == LIBSSH2_ERROR_EAGAIN)
boost::this_thread::yield();
}
}
boost::this_thread::yield();
} // while
} // if channel
}
P.S. To make this work requires the latest SVN builds of libssh2. There were bugs in prior versions that kept port forwarding from being usable.
The libssh2 source code includes since a few years a direct_tcpip.c example which demonstrates how to create direct-tcpip SSH channels, and since last week a forward-tcpip.c example which demonstrates how to create forward-tcpip SSH channels.
direct-tcpip is what ssh -L uses, and forward-tcpip is what ssh -R uses.
It is always the responsibility of libssh2 users to deal with the actual data. libssh2 takes care of SSH channels and nothing else. You can benefit significantly from studying the SSH RFCs, in particular RFC 4254, to find more about what exactly each channel type promises you, and thus what you can expect from libssh2.