The branch, master has been updated via c01d49db3afcbc028d704d3048f96fabee5827b4 (commit) from ac2eeb5461e7f27cef3aaff705f690bf839bd8ce (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- commit c01d49db3afcbc028d704d3048f96fabee5827b4 Author: Oleksandr Shneyder <o.shneyder@phoca-gmbh.de> Date: Mon Nov 12 16:37:59 2012 +0100 implement class SshProcess. Add support for type SSH in X2GoBroker ----------------------------------------------------------------------- Summary of changes: sshconnection.cpp | 561 ++++++++++++++++++++++++++++++++++++++++++++++++++- sshconnection.h | 80 +++++--- sshprocess.cpp | 242 ++++++++++++++++++++++ sshprocess.h | 84 ++++++++ x2goapplication.cpp | 5 +- x2gobroker.cpp | 50 ++++- x2gobroker.h | 9 +- x2goclient2.pro | 2 + 8 files changed, 991 insertions(+), 42 deletions(-) create mode 100644 sshprocess.cpp create mode 100644 sshprocess.h The diff of changes is: diff --git a/sshconnection.cpp b/sshconnection.cpp index 878c66c..29f22cf 100644 --- a/sshconnection.cpp +++ b/sshconnection.cpp @@ -20,6 +20,7 @@ #include "sshconnection.h" #include "sshconnectionguiinteraction.h" +#include "sshprocess.h" #define PROXYTUNNELPORT 44444 @@ -68,6 +69,7 @@ SshConnection::SshConnection(QObject* parent, QString host, int port, bool accep sshProxyReady=false; nextPid=0; my_ssh_session=0; + connected=false; breakLoop=false; this->host=host; @@ -94,11 +96,71 @@ SshConnection::SshConnection(QObject* parent, QString host, int port, bool accep } } +//private constructor for reverse tunnel connections +SshConnection::SshConnection(QObject* parent, QString host, int port, bool acceptUnknownServers, + QString user, QString pass, QString key, bool autoLogin, bool krbLogin, + int remotePort, QString localHost, int localPort, SshProcess* creator, bool useProxy, + SshConnection::ProxyType proxyType, QString proxyServer, quint16 proxyPort, QString proxyLogin, + QString proxyPassword, QString proxyKey, bool proxyAutoLogin, int localProxyPort): QThread(parent) +{ +#if defined ( Q_OS_DARWIN ) + setStackSize (sizeof (char) * 1024 * 1024 * 2); +#endif + nextPid=0; + my_ssh_session=0; + tcpProxySocket = NULL; + tcpNetworkProxy = NULL; + sshProxy= NULL; + sshProxyReady=false; + breakLoop=false; + this->host=host; + this->port=port; + this->user=user; + this->pass=pass; + this->key=key; + this->autoLogin=autoLogin; + this->acceptUnknownServers=acceptUnknownServers; + this->useProxy=useProxy; + this->proxyServer=proxyServer; + this->proxyPort=proxyPort; + this->proxyLogin=proxyLogin; + this->proxyPassword=proxyPassword; + this->proxyType=proxyType; + this->proxyAutoLogin=proxyAutoLogin; + this->proxyKey=proxyKey; + this->localProxyPort=localProxyPort; + this->kerberos=krbLogin; + reverseTunnelLocalHost=localHost; + reverseTunnelLocalPort=localPort; + reverseTunnelCreator=creator; + reverseTunnel=true; + reverseTunnelRemotePort=remotePort; +#ifdef DEBUG + qDebug()<<"SshMasterConnection, instance "<<this<<" created (reverse tunnel)"; +#endif +} + + SshConnection::~SshConnection() { + disconnectFlagMutex.lock(); + disconnectSessionFlag=true; + disconnectFlagMutex.unlock(); +#ifdef DEBUG + qDebug()<<"SshConnection, instance "<<this<<" waiting for thread to finish"; +#endif + wait(); +#ifdef DEBUG + qDebug()<<"SshConnection, instance "<<this<<" thread finished"; +#endif + for(int i=processes.count()-1; i>=0; --i) + { + delete processes[i]; + } if(my_ssh_session) { ssh_free(my_ssh_session); + my_ssh_session=0; } qDebug()<<"ssh connection destructor"; } @@ -106,6 +168,7 @@ SshConnection::~SshConnection() void SshConnection::run() { disconnectSessionFlag=false; +#warning implement ssh proxy code if ( !isLibSshInited ) { if ( ssh_init() !=0 ) @@ -113,7 +176,7 @@ void SshConnection::run() QString err=tr ( "Can not initialize libssh" ); qDebug()<<err<<endl; guiInteractor->critical(err, MessageBox::OK); - emit signalError ( CONNECTION, err ); + emit signalError ( (int)CONNECTION, err ); return; } isLibSshInited=true; @@ -133,7 +196,7 @@ void SshConnection::run() QString err=tr ( "Can not create ssh session" ); qDebug()<<err<<endl; guiInteractor->critical(err, MessageBox::OK); - emit signalError ( CONNECTION, err); + emit signalError ( (int)CONNECTION, err); if ( reverseTunnel ) emit signalIoError ( reverseTunnelCreator, err, "" ); return; @@ -161,8 +224,9 @@ void SshConnection::run() QString message=tr ( "Can not connect to proxy server" ); qDebug()<<message<<endl; guiInteractor->critical(message, MessageBox::OK); - emit signalError ( CONNECTION, message ); + emit signalError ( (int)CONNECTION, message ); ssh_free ( my_ssh_session ); + my_ssh_session=0; return; } ssh_options_set( my_ssh_session, SSH_OPTIONS_FD, &proxysocket); @@ -176,10 +240,11 @@ void SshConnection::run() message+=" - "+err; qDebug()<<message; guiInteractor->critical(message, MessageBox::OK); - emit signalError (CONNECTION, message); + emit signalError ((int)CONNECTION, message); if ( reverseTunnel ) emit signalIoError ( reverseTunnelCreator, message, err); ssh_free ( my_ssh_session ); + my_ssh_session=0; return; } QString errMsg; @@ -187,9 +252,10 @@ void SshConnection::run() if ( state != SSH_SERVER_KNOWN_OK ) { guiInteractor->critical(errMsg, MessageBox::OK); - emit signalError (SERVERAUTH, errMsg); + emit signalError ((int)SERVERAUTH, errMsg); ssh_disconnect ( my_ssh_session ); ssh_free ( my_ssh_session ); + my_ssh_session=0; return; } @@ -201,6 +267,7 @@ void SshConnection::run() { qDebug()<<"SSH session connected"; emit signalConnectionOk(host); + connected=true; } else { @@ -211,11 +278,12 @@ void SshConnection::run() guiInteractor->critical(message, MessageBox::OK); message+=" - "+err+"\n"+authErrors.join ( "\n" ); qDebug()<<message; - emit signalError (USERAUTH, message ); + emit signalError ((int)USERAUTH, message ); if ( reverseTunnel ) emit signalIoError ( reverseTunnelCreator,message,err ); ssh_disconnect ( my_ssh_session ); ssh_free ( my_ssh_session ); + my_ssh_session=0; return; } @@ -241,6 +309,7 @@ void SshConnection::run() emit signalIoError ( reverseTunnelCreator, message, err ); ssh_disconnect ( my_ssh_session ); ssh_free ( my_ssh_session ); + my_ssh_session=0; return; } emit signalReverseListenOk ( reverseTunnelCreator ); @@ -303,7 +372,7 @@ int SshConnection::serverAuth(QString& errorMsg) return SSH_SERVER_ERROR; #ifdef DEBUG - x2goDebug<<"state: "<<state<<endl; + qDebug()<<"state: "<<state<<endl; #endif switch ( state ) @@ -466,12 +535,488 @@ bool SshConnection::userAuthWithPass() } + +void SshConnection::copy() +{ + for ( int i=copyRequests.size()-1; i>=0; --i ) + { + QStringList lst=copyRequests[i].dst.split ( "/" ); + QString dstFile=lst.last(); + lst.removeLast(); + QString dstPath=lst.join ( "/" ); +#ifdef DEBUG + qDebug()<<"dst path:"<<dstPath<<" file:"<<dstFile<<endl; +#endif + ssh_scp scp=ssh_scp_new ( my_ssh_session, SSH_SCP_WRITE|SSH_SCP_RECURSIVE, dstPath.toAscii() ); + if ( scp == NULL ) + { + qDebug()<<"Error allocating scp session: "<< ssh_get_error ( my_ssh_session ) <<endl; + return; + } + int rc = ssh_scp_init ( scp ); + if ( rc != SSH_OK ) + { + qDebug()<<"Error initializing scp session: "<< ssh_get_error ( my_ssh_session ) <<endl; + ssh_scp_free ( scp ); + return; + } + QFile file ( copyRequests[i].src ); + if ( !file.open ( QIODevice::ReadOnly ) ) + { + QString errMsg=tr ( "Can not open file " ) +copyRequests[i].src; + emit signalCopyError ( copyRequests[i].creator, errMsg, "" ); + copyRequests.removeAt ( i ); + ssh_scp_close ( scp ); + ssh_scp_free ( scp ); + continue; + } + QByteArray arr=file.readAll(); + file.close(); + rc=ssh_scp_push_file ( scp,dstFile.toAscii(),arr.size(), 0600 ); + if ( rc != SSH_OK ) + { + QString errMsg=tr ( "Can not create remote file " ) +copyRequests[i].dst; + QString serr=ssh_get_error ( my_ssh_session ); + qDebug()<<errMsg<<" - "<<serr<<endl; + emit signalCopyError ( copyRequests[i].creator, errMsg, serr ); + copyRequests.removeAt ( i ); + ssh_scp_close ( scp ); + ssh_scp_free ( scp ); + continue; + } + rc=ssh_scp_write ( scp,arr.data(),arr.size() ); + if ( rc != SSH_OK ) + { + QString serr=ssh_get_error ( my_ssh_session ); + QString errMsg=tr ( "Can not write to remote file " ) +copyRequests[i].dst; + qDebug()<<errMsg<<" - "<<serr<<endl; + emit signalCopyError ( copyRequests[i].creator, errMsg, serr ); + copyRequests.removeAt ( i ); + ssh_scp_close ( scp ); + ssh_scp_free ( scp ); + continue; + } + emit signalCopyOk ( copyRequests[i].creator ); +#ifdef DEBUG + qDebug()<<"scp ok: "<<copyRequests[i].src<<" -> "<<user<<"@"<<host<<":"<<copyRequests[i].dst<<endl; +#endif + copyRequests.removeAt ( i ); + ssh_scp_close ( scp ); + ssh_scp_free ( scp ); + } +} + + void SshConnection::channelLoop() { + forever + { + copyRequestMutex.lock(); + if ( copyRequests.size() >0 ) + copy(); + copyRequestMutex.unlock(); + if ( reverseTunnel ) + { + ssh_channel newChan=channel_forward_accept ( my_ssh_session,0 ); + if ( newChan ) + { +#ifdef DEBUG + qDebug()<<"new forward connection"<<endl; +#endif + int sock=socket ( AF_INET, SOCK_STREAM,0 ); +#ifndef Q_OS_WIN + const int y=1; +#else + const char y=1; +#endif + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,&y, sizeof(int)); + + struct sockaddr_in address; + address.sin_family=AF_INET; + address.sin_port=htons ( reverseTunnelLocalPort ); +#ifdef DEBUG + qDebug()<<"connecting to "<<reverseTunnelLocalHost<<":"<<reverseTunnelLocalPort<<endl; +#endif +#ifndef Q_OS_WIN + inet_aton ( reverseTunnelLocalHost.toAscii(), &address.sin_addr ); +#else + address.sin_addr.s_addr=inet_addr ( + reverseTunnelLocalHost.toAscii() ); +#endif + + if ( ::connect ( sock, ( struct sockaddr * ) &address,sizeof ( address ) ) !=0 ) + { + QString errMsg=tr ( "can not connect to " ) + + reverseTunnelLocalHost+":"+QString::number ( reverseTunnelLocalPort ); + qDebug()<<errMsg<<endl; + emit signalIoError ( reverseTunnelCreator, errMsg, "" ); + continue; + } +#ifdef DEBUG + qDebug()<<"creating new channel connection"<<endl; +#endif + ChannelConnection con; + con.channel=newChan; + con.sock=sock; + con.creator=reverseTunnelCreator; + channelConnectionsMutex.lock(); + channelConnections<<con; + channelConnectionsMutex.unlock(); + } + } + + char buffer[1024*512]; //512K buffer + int nbytes; + fd_set rfds; + + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 500; + + int retval; + int maxsock=-1; + + disconnectFlagMutex.lock(); + bool disconnect=disconnectSessionFlag; + disconnectFlagMutex.unlock(); + + if ( disconnect ) + { + + if (useProxy && proxyType==PROXYSSH && sshProxy) + { + delete sshProxy; + sshProxy=0; + } + +#ifdef DEBUG + if ( !reverseTunnel ) + qDebug()<<"Disconnecting..."<<endl; +#endif + reverseTunnelConnectionsMutex.lock(); + for ( int i=reverseTunnelConnections.size()-1; i>=0; --i) + { + delete reverseTunnelConnections[i]; + } + reverseTunnelConnectionsMutex.unlock(); + + channelConnectionsMutex.lock(); + for ( int i=0; i<channelConnections.size(); ++i ) + { + finalize ( i ); + } + channelConnectionsMutex.unlock(); + ssh_disconnect ( my_ssh_session ); + ssh_free ( my_ssh_session ); + my_ssh_session=0; + + if (tcpProxySocket != NULL) + delete tcpProxySocket; + if (tcpNetworkProxy != NULL) + delete tcpNetworkProxy; +#ifdef DEBUG + if ( !reverseTunnel ) + qDebug()<<"All channels closed, session disconnected, quiting session loop"<<endl; +#endif + return; + } + + channelConnectionsMutex.lock(); + if ( channelConnections.size() <=0 ) + { + channelConnectionsMutex.unlock(); + usleep ( 500 ); + continue; + } + ssh_channel* read_chan=new ssh_channel[channelConnections.size() +1]; + ssh_channel* out_chan=new ssh_channel[channelConnections.size() +1]; + read_chan[channelConnections.size() ]=NULL; + + FD_ZERO ( &rfds ); + + for ( int i=0; i<channelConnections.size(); ++i ) + { + int tcpSocket=channelConnections.at ( i ).sock; + if ( tcpSocket>0 ) + FD_SET ( tcpSocket, &rfds ); + if ( channelConnections.at ( i ).channel==0l ) + { +#ifdef DEBUG + qDebug()<<"creating new channel"<<endl; +#endif + ssh_channel channel=channel_new ( my_ssh_session ); +#ifdef DEBUG + qDebug()<<"new channel:"<<channel<<endl; +#endif + channelConnections[i].channel=channel; + if ( tcpSocket>0 ) + { +#ifdef DEBUG + qDebug()<<"forwarding new channel, local port: "<<channelConnections.at ( i ).localPort<<endl; +#endif + if ( channel_open_forward ( channel, + channelConnections.at ( i ).forwardHost.toAscii(), + channelConnections.at ( i ).forwardPort, + channelConnections.at ( i ).localHost.toAscii(), + channelConnections.at ( i ).localPort ) != SSH_OK ) + { + QString err=ssh_get_error ( my_ssh_session ); + QString errorMsg=tr ( "channel_open_forward failed" ); + emit signalIoError ( channelConnections[i].creator, errorMsg, err ); + qDebug()<<errorMsg<<": "<<err<<endl; + } +#ifdef DEBUG + else + { + qDebug()<<" new channel forwarded"<<endl; + } +#endif + } + else + { +#ifdef DEBUG + qDebug()<<"executing remote: "<<channelConnections.at ( i ).command<<endl; +#endif + if ( channel_open_session ( channel ) !=SSH_OK ) + { + QString err=ssh_get_error ( my_ssh_session ); + QString errorMsg=tr ( "channel_open_session failed" ); + emit signalIoError ( channelConnections[i].creator, errorMsg, err ); + qDebug()<<errorMsg<<": "<<err<<endl; + } + else if ( channel_request_exec ( channel, channelConnections[i].command.toAscii() ) != SSH_OK ) + { + QString err=ssh_get_error ( my_ssh_session ); + QString errorMsg=tr ( "channel_request_exec failed" ); + emit signalIoError ( channelConnections[i].creator, errorMsg, err ); + qDebug()<<errorMsg<<": "<<err<<endl; + } +#ifdef DEBUG + else + { + qDebug()<<" new exec channel created"<<endl; + } +#endif + } + } + read_chan[i]=channelConnections.at ( i ).channel; + if ( tcpSocket>maxsock ) + maxsock=tcpSocket; + } + channelConnectionsMutex.unlock(); + retval=ssh_select ( read_chan,out_chan,maxsock+1,&rfds,&tv ); + delete [] read_chan; + delete [] out_chan; + + if ( retval == -1 ) + { + qDebug()<<"select error\n"; + continue; + } + + channelConnectionsMutex.lock(); + for ( int i=channelConnections.size()-1; i>=0; --i ) + { + int tcpSocket=channelConnections.at ( i ).sock; + ssh_channel channel=channelConnections.at ( i ).channel; + if ( channel==0l ) + continue; + if ( channel_poll ( channel,1 ) >0 ) + { + nbytes = channel_read ( channel, buffer, sizeof ( buffer )-1, 1 ); + emit signalStdErr ( channelConnections[i].creator, QByteArray ( buffer,nbytes ) ); + } + int rez=channel_poll ( channel,0 ); + if ( rez==SSH_EOF ) + { +#ifdef DEBUG + qDebug()<<"EOF ON CHANNEL "<<channel<<endl; +#endif + //////Finished//////// + finalize ( i ); + continue; + } + if ( rez>0 ) + { + nbytes = channel_read ( channel, buffer, sizeof ( buffer )-1, 0 ); + if ( nbytes > 0 ) + { + if ( tcpSocket>0 ) + { + if ( send ( tcpSocket,buffer, nbytes,0 ) != nbytes ) + { + QString errMsg=tr ( "error writing to socket" ); + qDebug()<<"error writing "<<nbytes<<" to tcp socket"<<tcpSocket<<endl; + emit signalIoError ( channelConnections[i].creator,errMsg,"" ); + finalize ( i ); + continue; + } + } + else + { + emit signalStdOut ( channelConnections[i].creator, QByteArray ( buffer,nbytes ) ); + } + } + + if ( nbytes < 0 ) + { + //////ERROR!!!!!//////// + QString err=ssh_get_error ( my_ssh_session ); + QString errorMsg=tr ( "error reading channel" ); + emit signalIoError ( channelConnections[i].creator, errorMsg, err ); + qDebug()<<errorMsg<<" - "<<err<<endl; + finalize ( i ); + continue; + } + + if ( channel_is_eof ( channel ) ) + { +#ifdef DEBUG + qDebug()<<"EOF ON CHANNEL "<<channel<<endl; +#endif + //////Finished//////// + finalize ( i ); + continue; + } + } + if ( tcpSocket<=0 ) + { + continue; + } + if ( FD_ISSET ( tcpSocket,&rfds ) ) + { + nbytes = recv ( tcpSocket, buffer, sizeof ( buffer )-1,0 ); + if ( nbytes > 0 ) + { + if ( channel_write ( channel, buffer, nbytes ) !=nbytes ) + { + QString err=ssh_get_error ( my_ssh_session ); + QString errorMsg=tr ( "channel_write failed" ); + emit signalIoError ( channelConnections[i].creator, errorMsg, err ); + qDebug()<<errorMsg<<" - "<<err<<endl; + finalize ( i ); + continue; + } + } + if ( nbytes < 0 ) + { + //////ERROR!!!!!//////// + QString err=""; + QString errorMsg=tr ( "error reading tcp socket" ); + emit signalIoError ( channelConnections[i].creator, errorMsg, err ); + qDebug()<<errorMsg<<" - "<<err<<endl; + finalize ( i ); + continue; + } + if ( nbytes==0 ) + { +#ifdef DEBUG + qDebug()<<"socket closed "<<tcpSocket<<endl; +#endif + finalize ( i ); + continue; + } + } + } + channelConnectionsMutex.unlock(); + } +} + +void SshConnection::finalize(int item) +{ + int tcpSocket=channelConnections.at ( item ).sock; + ssh_channel channel=channelConnections.at ( item ).channel; + if ( channel ) + { + channel_send_eof ( channel ); +#ifdef DEBUG + qDebug()<<"eof sent\n"; +#endif + channel_close ( channel ); +#ifdef DEBUG + qDebug()<<"channel closed\n"; +#endif + channel_free ( channel ); + } + if ( tcpSocket>0 ) + { +#ifndef Q_OS_WIN + shutdown(tcpSocket, SHUT_RDWR); +#endif + close ( tcpSocket ); + } + SshProcess* proc=channelConnections[item].creator; + channelConnections.removeAt ( item ); + emit signalChannelClosed ( proc ); +} +void SshConnection::addChannelConnection(SshProcess* creator, QString cmd) +{ + ChannelConnection con; + con.channel=NULL; + con.sock=-1; + con.creator=creator; + con.command=cmd; + + channelConnectionsMutex.lock(); + channelConnections<<con; + channelConnectionsMutex.unlock(); +} + +void SshConnection::addChannelConnection(SshProcess* creator, int sock, QString forwardHost, int forwardPort, QString localHost, int localPort, void* channel) +{ + ChannelConnection con; + con.channel= ( ssh_channel ) channel; + con.sock=sock; + con.creator=creator; + con.forwardHost=forwardHost; + con.forwardPort=forwardPort; + con.localHost=localHost; + con.localPort=localPort; + + + channelConnectionsMutex.lock(); + channelConnections<<con; + channelConnectionsMutex.unlock(); } -void SshConnection::finalize(int arg1) +void SshConnection::addCopyRequest(SshProcess* creator, QString src, QString dst) { + CopyRequest req; + req.src=src; + req.dst=dst; + req.creator=creator; + copyRequestMutex.lock(); + copyRequests<<req; + copyRequestMutex.unlock(); +} +SshConnection* SshConnection::reverseTunnelConnection(SshProcess* creator, int remotePort, QString localHost, int localPort) +{ + SshConnection* con=new SshConnection (this, host, port, acceptUnknownServers, user, pass, + key, autoLogin, kerberos, remotePort,localHost, + localPort, creator, useProxy, proxyType, proxyServer, proxyPort, proxyLogin, + proxyPassword, proxyKey, proxyAutoLogin, localProxyPort ); + + connect(con, SIGNAL(signalIoError(SshProcess*, QString, QString)), this, SIGNAL(signalIoError(SshProcess*, QString, QString))); + connect(con, SIGNAL(signalStdErr(SshProcess*, QByteArray)), this, SIGNAL ( signalStdErr(SshProcess*, QByteArray))); + connect(con, SIGNAL(signalReverseListenOk(SshProcess*)), this, SIGNAL(signalReverseListenOk(SshProcess*))); + con->start(); + reverseTunnelConnectionsMutex.lock(); + reverseTunnelConnections.append ( con ); + reverseTunnelConnectionsMutex.unlock(); + return con; +} + +int SshConnection::executeCommand(const QString& command, QObject* receiver, const char* slotFinished) +{ + SshProcess* proc=new SshProcess(this, nextPid++); + if(receiver && slotFinished) + { + connect(proc, SIGNAL(signalSshFinished(bool,QString,int)), receiver, slotFinished); + } + proc->startCommand(command); + processes<<proc; + return proc->pid; } diff --git a/sshconnection.h b/sshconnection.h index abb08e7..81171ab 100644 --- a/sshconnection.h +++ b/sshconnection.h @@ -35,32 +35,10 @@ class SshProcess; class SshConnectionGuiInteraction; -struct ChannelConnection -{ - ssh_channel channel; - int sock; - SshProcess* creator; - int forwardPort; - int localPort; - QString forwardHost; - QString localHost; - QString command; - bool operator==(ChannelConnection& t) - { - return (channel==t.channel); - } -}; - -struct CopyRequest -{ - SshProcess* creator; - QString src; - QString dst; -}; - class SshConnection: public QThread { Q_OBJECT + X2GO_RO_PROPERTY(bool, connected) public: enum ProxyType {PROXYSSH, PROXYHTTP}; enum Error {CONNECTION, SERVERAUTH, USERAUTH}; @@ -71,7 +49,39 @@ public: bool proxyAutoLogin=false); ~SshConnection(); static void finalizeLibSsh(); + void addChannelConnection(SshProcess* creator, QString cmd); + void addChannelConnection(SshProcess* creator, int sock, QString forwardHost, + int forwardPort, QString localHost, int localPort, void* channel=0l); + + void addCopyRequest(SshProcess* creator, QString src, QString dst); + SshConnection* reverseTunnelConnection(SshProcess* creator, int remotePort, + QString localHost, int localPort); + int executeCommand(const QString& command, QObject* receiver=0, const char* slotFinished=0); + + + private: + struct ChannelConnection + { + ssh_channel channel; + int sock; + SshProcess* creator; + int forwardPort; + int localPort; + QString forwardHost; + QString localHost; + QString command; + bool operator==(ChannelConnection& t) + { + return (channel==t.channel); + } + }; + struct CopyRequest + { + SshProcess* creator; + QString src; + QString dst; + }; ssh_session my_ssh_session; QList<ChannelConnection> channelConnections; QList<CopyRequest> copyRequests; @@ -110,20 +120,40 @@ private: bool breakLoop; static bool isLibSshInited; static SshConnectionGuiInteraction* guiInteractor; + + QMutex channelConnectionsMutex; + QMutex copyRequestMutex; + QMutex disconnectFlagMutex; + QMutex reverseTunnelConnectionsMutex; + private: + SshConnection(QObject* parent, QString host, int port, bool acceptUnknownServers, QString user, + QString pass, QString key, bool autoLogin, bool krbLogin, int remotePort, QString localHost, + int localPort, SshProcess* creator, + bool useProxy=false, ProxyType proxyType=PROXYSSH, QString proxyServer=QString::null, quint16 proxyPort=0, + QString proxyLogin=QString::null, QString proxyPassword=QString::null, QString proxyKey=QString::null, + bool proxyAutoLogin=false, int localProxyPort=0); + bool sshConnect(); bool userAuthWithPass(); bool userAuthAuto(); bool userAuthWithKey(); bool userAuth(); void channelLoop(); - void finalize(int arg1); + void finalize(int item); int serverAuth(QString& errorMsg); + void copy(); protected: void run(); signals: - void signalError(Error, QString); + void signalError(int, QString); void signalIoError(SshProcess* caller, QString error, QString lastSessionError); + void signalCopyError(SshProcess* caller, QString error, QString lastSessionError); + void signalCopyOk(SshProcess* caller); + void signalChannelClosed(SshProcess* caller); + + void signalStdErr(SshProcess* caller, QByteArray data); + void signalStdOut(SshProcess* caller, QByteArray data); void signalConnectionOk( QString host); void signalReverseListenOk(SshProcess* creator); diff --git a/sshprocess.cpp b/sshprocess.cpp new file mode 100644 index 0000000..f382f64 --- /dev/null +++ b/sshprocess.cpp @@ -0,0 +1,242 @@ +/************************************************************************** +* Copyright (C) 2005-2012 by Oleksandr Shneyder * +* o.shneyder@phoca-gmbh.de * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + + +#include "sshprocess.h" +#include <QTimer> +#include <QDebug> + +#ifndef Q_OS_WIN +#include <arpa/inet.h> +#include <netinet/tcp.h> +#endif + +#undef DEBUG +// #define DEBUG + + +SshProcess::SshProcess(SshConnection* masterConnection, int pid): QObject() +{ + this->masterConnection=masterConnection; + serverSocket=0; + this->pid=pid; + connect(masterConnection, SIGNAL(signalStdErr(SshProcess*,QByteArray)),this,SLOT(slotStdErr(SshProcess*,QByteArray))); + connect(masterConnection, SIGNAL(signalIoError(SshProcess*,QString,QString)), this,SLOT(slotIOerr(SshProcess*,QString,QString))); + tunnel=false; + normalExited=true; + +} + +SshProcess::~SshProcess() +{ +#ifdef DEBUG + qDebug()<<"ssh process destructor"; +#endif + if (serverSocket>0) + { +#ifdef Q_OS_WIN + closesocket(serverSocket); + WSACleanup(); + +#else + close(serverSocket); +#endif + } +} + + +void SshProcess::startCommand(const QString& cmd) +{ + QString shcmd = "sh -c \""+cmd+"\""; + masterConnection->addChannelConnection(this, shcmd); + connect(masterConnection,SIGNAL(signalStdOut(SshProcess*,QByteArray)),this,SLOT(slotStdOut(SshProcess*,QByteArray))); + connect(masterConnection,SIGNAL(signalChannelClosed(SshProcess*)), this,SLOT(slotChannelClosed(SshProcess*))); +} + +void SshProcess::startCopy(QString src, QString dst) +{ + connect(masterConnection, SIGNAL(signalCopyError(SshProcess*,QString,QString)), this, + SLOT(slotCopyErr(SshProcess*,QString,QString))); + connect(masterConnection, SIGNAL(signalCopyOk(SshProcess*)), this,SLOT(slotCopyOk(SshProcess*))); + scpSource=src; + masterConnection->addCopyRequest(this,src,dst); +} + +void SshProcess::startTunnel(const QString& forwardHost, uint forwardPort, const QString& localHost, uint localPort, bool reverse) +{ + this->forwardHost=forwardHost; + this->forwardPort=forwardPort; + this->localHost=localHost; + this->localPort=localPort; + tunnel=true; + if (!reverse) + tunnelLoop(); + else + { + connect(masterConnection, SIGNAL(signalReverseListenOk(SshProcess*)), this, SLOT(slotReverseTunnelOk(SshProcess*))); + tunnelConnection=masterConnection->reverseTunnelConnection(this, forwardPort, localHost, localPort); + } +} + +void SshProcess::slotChannelClosed(SshProcess* creator) +{ + if (creator!=this) + return; + QString output; + if (!normalExited) + { + output=abortString; + } + else + { + if ( stdOutString.length()<=0 && stdErrString.length() >0 ) + { + normalExited=false; + output=stdErrString; +#ifdef DEBUG + qDebug()<<"have only stderr, something must be wrong"<<endl; +#endif + } + else + output=stdOutString; + } +#ifdef DEBUG + qDebug()<<"ssh finished:"<<normalExited<<" - "<<output<<endl; +#endif + emit signalSshFinished(normalExited, output, pid); +} + +void SshProcess::slotCheckNewConnection() +{ + fd_set rfds; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(serverSocket, &rfds); + + if (select(serverSocket+1,&rfds,NULL,NULL,&tv)<=0) + return; + +#ifdef DEBUG + qDebug()<<"new tcp connection\n"; +#endif + int tcpSocket=accept(serverSocket, (struct sockaddr*)&address,&addrlen); + +#ifdef DEBUG + qDebug()<<"new socket:"<<tcpSocket<<endl; +#endif + masterConnection->addChannelConnection(this, tcpSocket, forwardHost, forwardPort, localHost, + ntohs(address.sin_port)); +} + +void SshProcess::slotCopyErr(SshProcess* creator, QString message, QString sshSessionErr) +{ + if (creator!=this) + return; + emit signalSshFinished(false, message+" - "+sshSessionErr, pid); + +} + +void SshProcess::slotCopyOk(SshProcess* creator) +{ + if (creator!=this) + return; + emit signalSshFinished(true,"", pid); +} + +void SshProcess::slotIOerr(SshProcess* creator, QString message, QString sshSessionErr) +{ + if (creator!=this) + return; +#ifdef DEBUG + qDebug()<<"io error:"<<message<<" - "<<sshSessionErr<<endl; +#endif + normalExited=false; + abortString=message+" - "+sshSessionErr; +} + +void SshProcess::slotReverseTunnelOk(SshProcess* creator) +{ + if (creator==this) + emit signalSshTunnelOk(pid); + +} + +void SshProcess::slotStdErr(SshProcess* creator, QByteArray data) +{ + if (creator!=this) + return; +#ifdef DEBUG + qDebug()<<"new err data:"<<data<<endl; +#endif + stdErrString+=data; +} + +void SshProcess::slotStdOut(SshProcess* creator, QByteArray data) +{ + if (creator!=this) + return; +#ifdef DEBUG + qDebug()<<"new data"<<data<<endl; +#endif + stdOutString+=data; +} + +void SshProcess::tunnelLoop() +{ + serverSocket=socket(AF_INET, SOCK_STREAM, 0); + if (serverSocket<=0) + { + QString err=tr("Error creating socket"); + qDebug()<<err<<endl; + emit signalSshFinished(false,err,pid); + return; + } +#ifndef Q_OS_WIN + const int y=1; +#else + const char y=1; +#endif + setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR,&y, sizeof(int)); + setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY,&y, sizeof(int)); + + address.sin_family=AF_INET; + address.sin_addr.s_addr=INADDR_ANY; + address.sin_port=htons(localPort); + if (bind(serverSocket,(struct sockaddr*) &address,sizeof(address))!=0) + { + QString err=tr("Error binding ")+localHost+":"+QString::number(localPort); + qDebug()<<err<<endl; + emit signalSshFinished(false,err,pid); + return; + } + listen(serverSocket,5); + addrlen=sizeof(struct sockaddr_in); + QTimer* timer=new QTimer(); + connect(timer,SIGNAL(timeout()),this,SLOT(slotCheckNewConnection())); + timer->start(100); + emit signalSshTunnelOk(pid); +#ifdef DEBUG + qDebug()<<"Direct tunnel: waiting for connections on "<<localHost<<":"<<localPort<<endl; +#endif +} diff --git a/sshprocess.h b/sshprocess.h new file mode 100644 index 0000000..5603f0c --- /dev/null +++ b/sshprocess.h @@ -0,0 +1,84 @@ +/************************************************************************** +* Copyright (C) 2005-2012 by Oleksandr Shneyder * +* o.shneyder@phoca-gmbh.de * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + + +#ifndef SSHPROCESS_H +#define SSHPROCESS_H + +#include <QObject> + +#ifndef Q_OS_WIN +#include <netinet/in.h> +#endif + +#include "sshconnection.h" + + +class SshProcess: public QObject +{ + Q_OBJECT + friend class SshConnection; +private: + explicit SshProcess(SshConnection* masterConnection, int pid); + virtual ~SshProcess(); + void startCommand(const QString& cmd); + void startTunnel(const QString& forwardHost, uint forwardPort, const QString& localHost, + uint localPort, bool reverse=false); + void startCopy(QString src, QString dst); + void tunnelLoop(); + +private: + SshConnection* masterConnection; + SshConnection* tunnelConnection; + int pid; + QString forwardHost; + QString localHost; + QString command; + QString scpSource; + quint16 forwardPort; + quint16 localPort; + uint serverSocket; + struct sockaddr_in address; +#ifndef Q_OS_WIN + socklen_t addrlen; +#else + int addrlen; +#endif + QString stdOutString; + QString stdErrString; + QString abortString; + bool tunnel; + bool normalExited; + +private slots: + void slotCheckNewConnection(); + void slotStdErr(SshProcess* creator, QByteArray data); + void slotStdOut(SshProcess* creator, QByteArray data); + void slotIOerr(SshProcess* creator,QString message, QString sshSessionErr); + void slotChannelClosed(SshProcess* creator); + void slotReverseTunnelOk(SshProcess* creator); + void slotCopyOk(SshProcess* creator); + void slotCopyErr(SshProcess* creator,QString message, QString sshSessionErr); +signals: + void signalSshFinished ( bool result, QString output, int processId); + void signalSshTunnelOk(int processId); +}; + +#endif // SSHPROCESS_H diff --git a/x2goapplication.cpp b/x2goapplication.cpp index 817f571..7ff0050 100644 --- a/x2goapplication.cpp +++ b/x2goapplication.cpp @@ -78,18 +78,21 @@ void X2GoApplication::slotInitApplication() { QMessageBox::critical(0,tr("Error"), tr("Error parsing command line arguments")); exit(-1); + return; } if(clientConfig->get_showHelp().get_value().toBool()) { HelpDialog dialog(0); dialog.showHelp(); exit(-1); + return; } if(clientConfig->get_showHelpPack().get_value().toBool()) { HelpDialog dialog(0); dialog.showPacks(); exit(-1); + return; } if(clientConfig->get_brokerUrl().get_value().toString()==QString::null) @@ -106,7 +109,7 @@ void X2GoApplication::slotInitApplication() if(broker->get_brokerAbort()) return; connect(broker, SIGNAL(signalSessionsLoaded()), this, SLOT(slotBrokerSessionsConfig())); - broker->getUserSessions(); + broker->slotGetUserSessions(); } if(clientConfig->get_disallowSessionEdit().get_value().toBool()) { diff --git a/x2gobroker.cpp b/x2gobroker.cpp index cca32a7..ba34837 100644 --- a/x2gobroker.cpp +++ b/x2gobroker.cpp @@ -22,6 +22,7 @@ #include <QFile> #include <QDir> #include <QDebug> +#include <QTimer> #include "x2gobroker.h" #include "x2goapplication.h" #include "x2goclientconfig.h" @@ -39,7 +40,7 @@ X2GoBroker::X2GoBroker(QObject* parent): QObject(parent) url = QUrl( urlString); if(url.userName().length()>0) user=url.userName(); - if(!cfg->get_defaultNoBrockerAuth().get_value().toBool()) + if(!cfg->get_defaultNoBrockerAuth().get_value().toBool() && brokerType==HTTP) { getLoginData(); } @@ -47,10 +48,14 @@ X2GoBroker::X2GoBroker(QObject* parent): QObject(parent) { brokerType=SSH; #warning search for default key hier - SshConnection* con=new SshConnection(this, url.host(), url.port(), cfg->get_autoAddToKnownHosts().get_value().toBool(), - user, pass, QString::null, - cfg->get_defaultAutoLogin().get_value().toBool()); - con->start(); + + sshConnection=new SshConnection(this, url.host(), url.port(), cfg->get_autoAddToKnownHosts().get_value().toBool(), + user, pass, cfg->get_brokerSSHKey().get_value().toString(), + cfg->get_defaultBrockerAutoLogin().get_value().toBool()); + connect(sshConnection, SIGNAL(signalConnectionOk(QString)), this, SLOT(slotSshConnectionOk())); + connect(sshConnection, SIGNAL(signalError(int,QString)), this, + SLOT(slotSshConnectionError(int,QString))); + sshConnection->start(); } else { @@ -89,6 +94,7 @@ X2GoBroker::~X2GoBroker() void X2GoBroker::getLoginData() { + X2GoClientConfig* cfg=X2GoApplication::instance()->get_clientConfig(); if(url.userName().length()<=0) { MessageBox::Buttons res=MessageBox::input(tr("Enter user name for authentication on broker"),tr("User:"),user); @@ -98,6 +104,13 @@ void X2GoBroker::getLoginData() X2GoApplication::exit(-1); } } + if(brokerType==SSH) + { + if(cfg->get_defaultBrockerAutoLogin().get_value().toBool()) + return; + if(cfg->get_brokerSSHKey().get_value().toString().length()>0) + return; + } pass=QString::null; MessageBox::Buttons res=MessageBox::input(tr("Enter password for authentication on broker"),tr("Password:"),pass, QLineEdit::Password); if(res!=MessageBox::OK) @@ -108,7 +121,7 @@ void X2GoBroker::getLoginData() } -void X2GoBroker::getUserSessions() +void X2GoBroker::slotGetUserSessions() { if(brokerType==HTTP) { @@ -122,6 +135,19 @@ void X2GoBroker::getUserSessions() httpSessionAnswer.setData ( 0,0 ); sessionsRequest=http->post ( url.path(),req.toUtf8(),&httpSessionAnswer ); } + if(brokerType==SSH) + { + if(!sshConnection->get_connected()) + { + QTimer::singleShot(100, this, SLOT(slotGetUserSessions())); + return; + } + sshConnection->executeCommand ( url.path() +" --authid "+ + X2GoApplication::instance()->get_clientConfig()->get_authId().get_value().toString()+ + " --task listsessions", + this, SLOT ( slotListSessions ( bool, QString,int ) )); + + } } void X2GoBroker::slotRequestFinished(int id, bool error) @@ -155,7 +181,7 @@ void X2GoBroker::slotListSessions(bool success, QString answer, int) if(!checkAccess(answer)) { getLoginData(); - getUserSessions(); + slotGetUserSessions(); return; } createIniFile(answer); @@ -311,4 +337,14 @@ QString X2GoBroker::getHexVal(const QByteArray& ba) } +void X2GoBroker::slotSshConnectionError(int, QString message) +{ + X2GoApplication::exit(-1); +} + +void X2GoBroker::slotSshConnectionOk() +{ + +} + diff --git a/x2gobroker.h b/x2gobroker.h index 49e3248..a101f40 100644 --- a/x2gobroker.h +++ b/x2gobroker.h @@ -28,6 +28,7 @@ #include <QSslError> #include <QTime> #include "x2goapplication.h" +#include "sshconnection.h" class QHttp; class X2GoBroker : public QObject @@ -38,7 +39,6 @@ class X2GoBroker : public QObject public: explicit X2GoBroker(QObject* parent = 0); virtual ~X2GoBroker(); - void getUserSessions(); private: enum {SSH,HTTP} brokerType; QString user; @@ -51,6 +51,7 @@ private: int sessionsRequest; int selSessRequest; QTime requestTime; + SshConnection* sshConnection; private: void getLoginData(); QString getHexVal ( const QByteArray& ba ); @@ -61,6 +62,12 @@ private slots: void slotRequestFinished ( int id, bool error ); void slotSslErrors ( const QList<QSslError> & errors ) ; void slotListSessions ( bool success, QString answer, int pid); + void slotSshConnectionError ( int error, QString message); + void slotSshConnectionOk (); + +public slots: + void slotGetUserSessions(); + signals: void signalSessionsLoaded(); }; diff --git a/x2goclient2.pro b/x2goclient2.pro index 5f02ec9..6962eac 100755 --- a/x2goclient2.pro +++ b/x2goclient2.pro @@ -58,6 +58,7 @@ SOURCES += main.cpp \ x2gobroker.cpp \ sshconnection.cpp \ sshconnectionguiinteraction.cpp \ + sshprocess.cpp \ profile.cpp HEADERS += mainwindow.h \ @@ -88,6 +89,7 @@ HEADERS += mainwindow.h \ x2gobroker.h \ sshconnection.h \ sshconnectionguiinteraction.h \ + sshprocess.h \ profile.h LIBS += -lssh hooks/post-receive -- x2goclient2.git (X2Go Client 2 (rewrite of x2goclient.git)) This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "x2goclient2.git" (X2Go Client 2 (rewrite of x2goclient.git)).