mirror of
https://github.com/levinsv/pgadmin3.git
synced 2026-05-15 14:15:49 -06:00
632 lines
No EOL
17 KiB
C++
632 lines
No EOL
17 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// pgAdmin III - PostgreSQL Tools
|
|
//
|
|
// Copyright (C) 2002 - 2016, The pgAdmin Development Team
|
|
// This software is released under the PostgreSQL Licence
|
|
//
|
|
// sshTunnel.cpp - Used to create SSH Tunnels
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
// App headers
|
|
#include "pgAdmin3.h"
|
|
#include <wx/dynlib.h>
|
|
|
|
#undef ssize_t
|
|
#define ssize_t long
|
|
#include "libssh2.h"
|
|
#include "utils/sshTunnel.h"
|
|
#include "frm/frmMain.h"
|
|
|
|
#pragma comment (lib, "Ws2_32.lib")
|
|
#if defined(HAVE_OPENSSL_CRYPTO) || defined(HAVE_GCRYPT)
|
|
|
|
typedef const char *(*inet_ntop_t) (int af, const void *src, char *dst, socklen_t size);
|
|
|
|
#ifdef WIN32
|
|
const char *custom_inet_ntop(int af, const void *src, char *dst, int cnt)
|
|
{
|
|
struct sockaddr_in srcaddr;
|
|
|
|
memset(&srcaddr, 0, sizeof(struct sockaddr_in));
|
|
memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr));
|
|
|
|
srcaddr.sin_family = af;
|
|
if (WSAAddressToStringA((struct sockaddr *) &srcaddr, sizeof(struct sockaddr_in), 0, dst, (LPDWORD) &cnt) != 0)
|
|
{
|
|
|
|
wxLogInfo(wxT("SSH error: WSAAddressToStringA failed with error code %d"), WSAGetLastError());
|
|
return NULL;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
static inet_ntop_t gs_fnPtr_inet_ntop = &custom_inet_ntop;
|
|
#else
|
|
static inet_ntop_t gs_fnPtr_inet_ntop = &inet_ntop;
|
|
#endif
|
|
|
|
char CSSHTunnelThread::m_keyboard_interactive_pwd[SSH_MAX_PASSWORD_LEN];
|
|
|
|
CSSHTunnelThread::CSSHTunnelThread(const wxString tunnelhost, const wxString remote_desthost, const unsigned int remote_destport,
|
|
const wxString username, const wxString password, const wxString publickey, const wxString privatekey,
|
|
const enAuthenticationMethod &enAuthMethod, const unsigned int tunnelPort)
|
|
: m_tunnelhost(tunnelhost), m_remote_desthost(remote_desthost), m_remote_destport(remote_destport), m_username(username),
|
|
m_password(password), m_publickey(publickey), m_privatekey(privatekey), m_enAuthMethod(enAuthMethod), m_tunnelPort(tunnelPort)
|
|
{
|
|
m_local_listenip = wxEmptyString;
|
|
m_local_listenport = 0;
|
|
m_listensock = -1, m_sock = -1;
|
|
m_session = NULL;
|
|
|
|
memset(m_keyboard_interactive_pwd, 0 , strlen(m_keyboard_interactive_pwd));
|
|
strncpy(m_keyboard_interactive_pwd, (const char *)password.mb_str(wxConvUTF8), password.Length());
|
|
}
|
|
|
|
CSSHTunnelThread::~CSSHTunnelThread(void)
|
|
{
|
|
}
|
|
|
|
bool CSSHTunnelThread::Initialize()
|
|
{
|
|
int rc, auth = AUTH_NONE;
|
|
const char *fingerprint;
|
|
char *userauthlist;
|
|
|
|
#ifdef WIN32
|
|
char sockopt;
|
|
WSADATA wsadata;
|
|
int err;
|
|
|
|
err = WSAStartup(MAKEWORD(2, 0), &wsadata);
|
|
if(err != 0)
|
|
{
|
|
wxLogInfo(wxT("WSAStartup failed with error: %d"), err);
|
|
return false;
|
|
}
|
|
#else
|
|
int sockopt;
|
|
#endif
|
|
|
|
wxArrayString arrTunnelHostIP;
|
|
|
|
if (resolveDNS(m_tunnelhost.mb_str(), arrTunnelHostIP))
|
|
{
|
|
rc = libssh2_init (0);
|
|
|
|
if (rc != 0)
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("libssh2 initialization failed with error code %d"), rc), GetId());
|
|
return false;
|
|
}
|
|
|
|
/* Connect to SSH server */
|
|
m_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
m_sin.sin_family = AF_INET;
|
|
if (INADDR_NONE == (m_sin.sin_addr.s_addr = inet_addr(arrTunnelHostIP.Item(0).mb_str())))
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: Error in inet address with error code %d"), wxSysErrorCode()), GetId());
|
|
return false;
|
|
}
|
|
m_sin.sin_port = htons(m_tunnelPort);
|
|
if (connect(m_sock, (struct sockaddr *)(&m_sin),
|
|
sizeof(struct sockaddr_in)) != 0)
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: Could not connect to socket with error code %d"), wxSysErrorCode()), GetId());
|
|
return false;
|
|
}
|
|
|
|
/* Create a session instance */
|
|
m_session = libssh2_session_init();
|
|
|
|
if (!m_session)
|
|
{
|
|
LogSSHTunnelErrors(_("SSH error: Could not initialize SSH session!"), GetId());
|
|
return false;
|
|
}
|
|
|
|
/* ... start it up. This will trade welcome banners, exchange keys,
|
|
* and setup crypto, compression, and MAC layers
|
|
*/
|
|
rc = libssh2_session_handshake(m_session, m_sock);
|
|
if (rc)
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: Error when starting up SSH session with error code %d"), rc), GetId(), m_session);
|
|
return false;
|
|
}
|
|
|
|
/* At this point we havn't yet authenticated. The first thing to do
|
|
* is check the hostkey's fingerprint against our known hosts Your app
|
|
* may have it hard coded, may go to a file, may present it to the
|
|
* user, that's your call
|
|
*/
|
|
fingerprint = libssh2_hostkey_hash(m_session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
|
wxString newHostKey = wxEmptyString;
|
|
for(int i = 0; i < 20; i++)
|
|
{
|
|
newHostKey += wxString::Format(wxT("%02X "), (unsigned char)fingerprint[i]);
|
|
}
|
|
|
|
// Check if the SSH Host Key is verified
|
|
if(!IsHostKeyVerified(newHostKey))
|
|
{
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
|
|
|
|
/* check what authentication methods are available */
|
|
userauthlist = libssh2_userauth_list(m_session, m_username.mb_str(), strlen(m_username.mb_str()));
|
|
|
|
if (strstr(userauthlist, "password"))
|
|
auth |= AUTH_PASSWORD;
|
|
if(strstr(userauthlist, "keyboard-interactive"))
|
|
auth |= AUTH_KEYBOARD_INTERACTIVE;
|
|
if (strstr(userauthlist, "publickey"))
|
|
auth |= AUTH_PUBLICKEY;
|
|
|
|
if ((auth & AUTH_PASSWORD) && (m_enAuthMethod == AUTH_PASSWORD))
|
|
auth = AUTH_PASSWORD;
|
|
else if ((auth & AUTH_KEYBOARD_INTERACTIVE) && (m_enAuthMethod == AUTH_PASSWORD))
|
|
auth = AUTH_KEYBOARD_INTERACTIVE;
|
|
if ((auth & AUTH_PUBLICKEY) && (m_enAuthMethod == AUTH_PUBLICKEY))
|
|
auth = AUTH_PUBLICKEY;
|
|
|
|
if (auth & AUTH_PASSWORD)
|
|
{
|
|
rc = libssh2_userauth_password(m_session, m_username.mb_str(), m_password.mb_str());
|
|
if (rc)
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: Authentication by password failed with error code %d"), rc), GetId(), m_session);
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
}
|
|
else if (auth & AUTH_KEYBOARD_INTERACTIVE)
|
|
{
|
|
rc = libssh2_userauth_keyboard_interactive(m_session, m_username.mb_str(), &CSSHTunnelThread::keyboard_interactive);
|
|
if (rc)
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: Authentication by password failed with error code %d"), rc), GetId(), m_session);
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
}
|
|
else if (auth & AUTH_PUBLICKEY)
|
|
{
|
|
#ifdef HAVE_GCRYPT
|
|
rc = libssh2_userauth_publickey_fromfile(m_session, m_username.mb_str(), m_publickey.mb_str(), m_privatekey.mb_str(), m_password.mb_str());
|
|
#else
|
|
rc = libssh2_userauth_publickey_fromfile(m_session, m_username.mb_str(), NULL, m_privatekey.mb_str(), m_password.mb_str());
|
|
#endif
|
|
if (rc)
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: Authentication by identity file failed with error code %d"), rc), GetId(), m_session);
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogSSHTunnelErrors(_("SSH error: No supported authentication methods found!"), GetId());
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
|
|
// Get the IP Address of local machine
|
|
wxArrayString arrLocalIP;
|
|
if(resolveDNS("localhost", arrLocalIP))
|
|
{
|
|
m_listensock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
memset(&m_sin, 0 , sizeof(m_sin));
|
|
m_sin.sin_family = AF_INET;
|
|
|
|
// Give port no to 0 so that bind will automatically select the available port.
|
|
m_sin.sin_port = htons(0);
|
|
if (INADDR_NONE == (m_sin.sin_addr.s_addr = inet_addr(arrLocalIP.Item(0).mb_str())))
|
|
{
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
|
|
sockopt = 1;
|
|
setsockopt(m_listensock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt));
|
|
m_sinlen = sizeof(m_sin);
|
|
if (-1 == bind(m_listensock, (struct sockaddr *)&m_sin, m_sinlen))
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: bind failed with error code %d"), wxSysErrorCode()), GetId());
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
|
|
if (getsockname(m_listensock, (struct sockaddr *)&m_sin, &m_sinlen) == -1)
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: getsockname() failed with error code %d"), wxSysErrorCode()), GetId());
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
|
|
if (-1 == listen(m_listensock, 2))
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: listen failed with error code %d"), wxSysErrorCode()), GetId());
|
|
Cleanup();
|
|
return false;
|
|
}
|
|
|
|
m_local_listenip = wxString(inet_ntoa(m_sin.sin_addr), wxConvLibc);
|
|
m_local_listenport = ntohs(m_sin.sin_port);
|
|
|
|
wxLogInfo(wxT("Waiting for TCP connection on %s:%d..."), m_local_listenip.c_str(), m_local_listenport);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
LogSSHTunnelErrors(_("SSH error: Unable to resolve localhost"), GetId());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: Unable to resolve host: %s"), m_tunnelhost.c_str()), GetId());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void *CSSHTunnelThread::Entry()
|
|
{
|
|
while (1)
|
|
{
|
|
int forwardsock = accept(m_listensock, (struct sockaddr *) & m_sin, &m_sinlen);
|
|
if (-1 == forwardsock)
|
|
{
|
|
#ifdef WIN32
|
|
if(wxSysErrorCode() != WSAEINTR && wxSysErrorCode() != WSAEBADF)
|
|
#else
|
|
if(wxSysErrorCode() != EINTR && wxSysErrorCode() != EBADF && wxSysErrorCode() != ECONNABORTED)
|
|
#endif
|
|
{
|
|
LogSSHTunnelErrors(wxString::Format(_("SSH error: accept failed with error code %d"), wxSysErrorCode()), GetId());
|
|
Cleanup();
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Create thread for read/write.
|
|
CSubThread *subThread = new CSubThread(m_sin, m_remote_desthost, m_remote_destport, m_session, forwardsock);
|
|
if ( subThread->Create() != wxTHREAD_NO_ERROR )
|
|
{
|
|
delete subThread;
|
|
subThread = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (subThread->Run() != wxTHREAD_NO_ERROR )
|
|
{
|
|
delete subThread;
|
|
subThread = NULL;
|
|
}
|
|
else
|
|
{
|
|
g_SSHThreadMutex.Lock();
|
|
g_setSocketDescriptor.insert(forwardsock);
|
|
g_SSHThreadMutex.Unlock();
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CSSHTunnelThread::Cleanup()
|
|
{
|
|
// Close all the sockets
|
|
g_SSHThreadMutex.Lock();
|
|
subThreadSDSet::iterator it;
|
|
for( it = g_setSocketDescriptor.begin(); it != g_setSocketDescriptor.end(); ++it )
|
|
{
|
|
int socketDescriptor = *it;
|
|
#ifdef WIN32
|
|
closesocket(socketDescriptor);
|
|
#else
|
|
close(socketDescriptor);
|
|
#endif
|
|
}
|
|
g_SSHThreadMutex.Unlock();
|
|
|
|
Sleep(1000);
|
|
|
|
if(m_session)
|
|
{
|
|
libssh2_session_disconnect(m_session, "Client disconnecting normally");
|
|
libssh2_session_free(m_session);
|
|
m_session = NULL;
|
|
}
|
|
|
|
if (m_listensock)
|
|
{
|
|
#ifdef WIN32
|
|
closesocket(m_listensock);
|
|
#else
|
|
close(m_listensock);
|
|
#endif
|
|
m_listensock = 0;
|
|
}
|
|
|
|
if (m_sock)
|
|
{
|
|
#ifdef WIN32
|
|
closesocket(m_sock);
|
|
#else
|
|
close(m_sock);
|
|
#endif
|
|
m_sock = 0;
|
|
}
|
|
|
|
libssh2_exit();
|
|
}
|
|
|
|
bool CSSHTunnelThread::resolveDNS(const char *host, wxArrayString &arrIPAddress)
|
|
{
|
|
struct addrinfo hints, *res, *p;
|
|
int status;
|
|
char ipstr[INET6_ADDRSTRLEN];
|
|
#ifndef AI_ADDRCONFIG
|
|
#define AI_ADDRCONFIG 0
|
|
#endif
|
|
memset(&hints, 0, sizeof hints);
|
|
memset(&ipstr, 0, sizeof ipstr);
|
|
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE;
|
|
|
|
if ((status = getaddrinfo(host, NULL, &hints, &res)) != 0)
|
|
{
|
|
wxLogInfo(wxT("getaddrinfo failed with error code: %d"), status);
|
|
return false;
|
|
}
|
|
|
|
for(p = res; p != NULL; p = p->ai_next)
|
|
{
|
|
void *addr;
|
|
if (p->ai_family == AF_INET)
|
|
{
|
|
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
|
|
addr = &(ipv4->sin_addr);
|
|
|
|
/* convert the IP to a string*/
|
|
if(NULL != gs_fnPtr_inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr))
|
|
arrIPAddress.Add(wxString(ipstr, wxConvLocal));
|
|
}
|
|
}
|
|
|
|
if(res)
|
|
{
|
|
freeaddrinfo(res); // free the linked list
|
|
}
|
|
|
|
if(arrIPAddress.Count() > 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CSSHTunnelThread::keyboard_interactive(const char *name, int name_len, const char *instr, int instr_len,
|
|
int num_prompts, const struct _LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE *res,
|
|
void **abstract)
|
|
{
|
|
if (num_prompts == 1)
|
|
{
|
|
res[0].text = strdup(m_keyboard_interactive_pwd);
|
|
res[0].length = strlen(m_keyboard_interactive_pwd);
|
|
}
|
|
}
|
|
|
|
bool CSSHTunnelThread::IsHostKeyVerified(const wxString &newHostKey)
|
|
{
|
|
bool bIsVerified = false;
|
|
wxString cachedHostKey = settings->Read(wxT("HostKeys/") + m_tunnelhost, wxEmptyString);
|
|
|
|
// If cached host key is empty then ask user to accept or reject
|
|
if (cachedHostKey == wxEmptyString)
|
|
{
|
|
// Prompt User to accept or reject
|
|
wxString msg = wxString::Format(wxT("Host key received for the SSH server \"%s\" is \n\n%s\n\nWould you like to accept it and continue with the connection?"), m_tunnelhost.c_str(), newHostKey.c_str());
|
|
int answer = wxMessageBox(msg, wxT("Host key verification"), wxYES_NO | wxNO_DEFAULT);
|
|
if (answer == wxYES)
|
|
{
|
|
// Write the host key with respect to tunnel host
|
|
settings->Write(wxT("HostKeys/") + m_tunnelhost, newHostKey);
|
|
bIsVerified = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Compare the cached host key with the new key
|
|
if (cachedHostKey.compare(newHostKey) == 0)
|
|
bIsVerified = true;
|
|
else
|
|
{
|
|
// Prompt user to accept or reject the new host key received for the tunnel host
|
|
wxString msg = wxString::Format(wxT("The host key received from the server \"%s\" is\n\n%s\n\nbut the stored key is\n\n%s\n\nThis may indicate that this is not the same server that was previously used.\n\nDo you wish to accept and store the new key, and continue with the connection?"), m_tunnelhost.c_str(), newHostKey.c_str(), cachedHostKey.c_str());
|
|
int answer = wxMessageBox(msg, wxT("Host key verification - WARNING"), wxYES_NO | wxNO_DEFAULT);
|
|
if (answer == wxYES)
|
|
{
|
|
// Write the host key with respect to tunnel host
|
|
settings->Write(wxT("HostKeys/") + m_tunnelhost, newHostKey);
|
|
bIsVerified = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bIsVerified;
|
|
}
|
|
|
|
CSubThread::CSubThread(const struct sockaddr_in sin, const wxString remote_desthost, const unsigned int remote_destport,
|
|
LIBSSH2_SESSION *session, int forwardsock)
|
|
: m_sin(sin), m_remote_desthost(remote_desthost), m_remote_destport(remote_destport),
|
|
m_subThreadSession(session), m_forwardsock(forwardsock)
|
|
{
|
|
}
|
|
|
|
CSubThread::~CSubThread(void)
|
|
{
|
|
g_SSHThreadMutex.Lock();
|
|
g_setSocketDescriptor.erase(m_forwardsock);
|
|
g_SSHThreadMutex.Unlock();
|
|
}
|
|
|
|
void *
|
|
CSubThread::Entry()
|
|
{
|
|
fd_set fds;
|
|
struct timeval tv;
|
|
ssize_t len, wr;
|
|
char buf[20480];
|
|
int rc, i = 0;
|
|
|
|
const char *shost = inet_ntoa(m_sin.sin_addr);
|
|
unsigned int sport = ntohs(m_sin.sin_port);
|
|
|
|
wxLogInfo(wxT("Forwarding connection from %s:%d to %s:%d"), wxString(inet_ntoa(m_sin.sin_addr), wxConvLibc).c_str(),
|
|
sport, m_remote_desthost.c_str(), m_remote_destport);
|
|
|
|
/* Must use blocking here to avoid connect errors */
|
|
//libssh2_session_set_blocking(m_subThreadSession, 1);
|
|
|
|
while((m_channel = libssh2_channel_direct_tcpip_ex(m_subThreadSession, m_remote_desthost.mb_str(),
|
|
m_remote_destport, shost, sport)) == NULL)
|
|
{
|
|
rc = libssh2_session_last_error(m_subThreadSession, NULL, NULL, 0);
|
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
|
Sleep(10);
|
|
else
|
|
break;
|
|
}
|
|
|
|
/* Must use non-blocking IO hereafter due to the current libssh2 API */
|
|
libssh2_session_set_blocking(m_subThreadSession, 0);
|
|
|
|
if (!m_channel)
|
|
{
|
|
wxLogInfo(_("SSH error: Could not open a direct-tcpip channel!"));
|
|
goto shutdown;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
FD_ZERO(&fds);
|
|
FD_SET(m_forwardsock, &fds);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 100000;
|
|
rc = select(m_forwardsock + 1, &fds, NULL, NULL, &tv);
|
|
memset(buf, 0, sizeof(buf));
|
|
if (-1 == rc)
|
|
{
|
|
wxLogInfo(_("SSH error: select failed with error code %d"), wxSysErrorCode());
|
|
goto shutdown;
|
|
}
|
|
if (rc && FD_ISSET(m_forwardsock, &fds))
|
|
{
|
|
len = recv(m_forwardsock, buf, sizeof(buf), 0);
|
|
if (len < 0)
|
|
{
|
|
wxLogInfo(_("SSH error: read failed with error code %d"), wxSysErrorCode());
|
|
goto shutdown;
|
|
}
|
|
else if (0 == len)
|
|
{
|
|
wxLogInfo(_("The client at %s:%d disconnected!"), wxString(inet_ntoa(m_sin.sin_addr), wxConvLibc).c_str(), sport);
|
|
goto shutdown;
|
|
}
|
|
wr = 0;
|
|
do
|
|
{
|
|
i = libssh2_channel_write(m_channel, buf, len);
|
|
|
|
if (i < 0)
|
|
{
|
|
wxLogInfo(_("SSH error: libssh2_channel_write with error code %d"), i);
|
|
goto shutdown;
|
|
}
|
|
wr += i;
|
|
}
|
|
while (i > 0 && wr < len);
|
|
}
|
|
while (1)
|
|
{
|
|
len = libssh2_channel_read(m_channel, buf, sizeof(buf));
|
|
|
|
if (LIBSSH2_ERROR_EAGAIN == len)
|
|
break;
|
|
else if (len < 0)
|
|
{
|
|
wxLogInfo(_("SSH error: libssh2_channel_read with error code %d"), (int)len);
|
|
goto shutdown;
|
|
}
|
|
wr = 0;
|
|
while (wr < len)
|
|
{
|
|
i = send(m_forwardsock, buf + wr, len - wr, 0);
|
|
if (i <= 0)
|
|
{
|
|
wxLogInfo(_("SSH error: write failed with error code %d"), wxSysErrorCode());
|
|
goto shutdown;
|
|
}
|
|
wr += i;
|
|
}
|
|
if (libssh2_channel_eof(m_channel))
|
|
{
|
|
wxLogInfo(_("Connection at %s:%d disconnected by server"),
|
|
wxString(inet_ntoa(m_sin.sin_addr), wxConvLibc).c_str(), sport);
|
|
goto shutdown;
|
|
}
|
|
}
|
|
}
|
|
shutdown:
|
|
#ifdef WIN32
|
|
closesocket(m_forwardsock);
|
|
#else
|
|
close(m_forwardsock);
|
|
#endif
|
|
|
|
if (m_channel)
|
|
{
|
|
libssh2_channel_close(m_channel);
|
|
libssh2_channel_free(m_channel);
|
|
m_channel = NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void LogSSHTunnelErrors(const wxString &msg, const int &id, struct _LIBSSH2_SESSION *session)
|
|
{
|
|
g_SSHThreadMutex.TryLock();
|
|
|
|
wxString errorMsg = msg;
|
|
// If session is not NULL then fetch the last error on that session
|
|
if (session)
|
|
{
|
|
char *errmsg;
|
|
int errmsg_len;
|
|
libssh2_session_last_error(session, &errmsg, &errmsg_len, 0);
|
|
if (errmsg_len > 0)
|
|
{
|
|
wxString errmsg_s(errmsg, wxConvLibc);
|
|
errorMsg += wxString::Format(_(" [%s]"), errmsg_s.c_str());
|
|
}
|
|
}
|
|
|
|
wxCommandEvent event(SSH_TUNNEL_ERROR_EVENT, id);
|
|
// Give it some contents
|
|
event.SetString(errorMsg);
|
|
|
|
// Do send it
|
|
wxPostEvent(winMain, event);
|
|
|
|
g_SSHThreadMutex.Unlock();
|
|
}
|
|
#endif |