/*************************************************************************** * Copyright (C) 2006 by Michael Kaufmann * * michael@enlighter.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. * ***************************************************************************/ /* TODO: Error 14 with mSocket if openvpn is not startet. */ #include "openvpnmanager.h" #include "authmanager.h" // #include #include #include #include #include #include #include using namespace std; int openVPNManager::mCounter = 0; openVPNManager::openVPNManager( const QString & id, const QString & host, const QString & port, const bool saveMIPassword, const bool saveAccount, const bool savePassphrase, const bool reconnect, const bool autoconnect, const bool disconnectOnExit ) { mId = id; /* I want to know who I am before I load the config */ mHost = host; mPort = port; mSaveMIPassword = saveMIPassword; mSaveAccount = saveAccount; mSavePassphrase = savePassphrase; mAutoconnect = autoconnect; mDisconnectOnExit = disconnectOnExit; mReconnect = reconnect; mInstance = mCounter++; mInterval = 2000; mUptimeRequestCounter = 0; debug( "openVPNManager", QString( "openVPNManager started" ) ); debug( "openVPNManager", QString( "id : %1" ).arg( mId ) ); debug( "openVPNManager", QString( "host : %1" ).arg( mHost ) ); debug( "openVPNManager", QString( "port : %1" ).arg( mPort ) ); debug( "openVPNManager", QString( "saveMIPassword : %1" ).arg( mSaveMIPassword ) ); debug( "openVPNManager", QString( "saveAccount : %1" ).arg( mSaveAccount ) ); debug( "openVPNManager", QString( "savePassphrase : %1" ).arg( mSavePassphrase ) ); debug( "openVPNManager", QString( "reconnect : %1" ).arg( mReconnect ) ); debug( "openVPNManager", QString( "autoconnect : %1" ).arg( mAutoconnect ) ); debug( "openVPNManager", QString( "disconnectOnExit : %1" ).arg( mDisconnectOnExit ) ); /* Initialization of all important variabled */ mStatus.state = UNKNOWN; mStatus.uptime = 0; mStatus.localIP = ""; mStatus.remoteIP = ""; mLastStatus.state = UNKNOWN; mLastStatus.uptime = 0; mLastStatus.localIP = ""; mLastStatus.remoteIP = ""; mWaitingForHoldRelease = false; mWaitingForAuth = false; mWaitingForPass = false; mWaitingForMIPass = false; mMIAuthFailed = false; mConnectionWanted = false; mDisconnectForced = false; mInitialStatusReported = false; mForce = false; mSocket = new bufferedSocket; mSocketTimer = new QTimer( this ); mSocketTimer->start( mInterval, true ); mOvpnTimer = new QTimer( this ); mOvpnConnectionStart = new QDateTime(); mAuthManager = authManager::manager(); mSocket->enableRead( true ); mSocket->setTimeout( 1000 ); /* wait 1000 ms. */ QObject::connect( mSocketTimer, SIGNAL ( timeout() ), this, SLOT ( socketConnect() ) ); QObject::connect( mOvpnTimer, SIGNAL ( timeout() ), this, SLOT ( ovpnTimer() ) ); QObject::connect( mSocket, SIGNAL ( readyRead() ), this, SLOT ( socketReadyRead() ) ); QObject::connect( mSocket, SIGNAL ( gotError( int ) ), this, SLOT ( socketGotError( int ) ) ); QObject::connect( mSocket, SIGNAL ( stateChanged( int ) ), this, SLOT ( socketStateChanged( int ) ) ); } openVPNManager::~openVPNManager() { debug( "~openVPNManager" ); /* If I can control OpenVPN and if the user want's it, I disconnect if kovpn is closed. */ if ( ! mWaitingForMIPass && mDisconnectOnExit ) { debug( "~openVPNManager", "Disconnect on exit" ); mSocket->setOutputBuffering( false ); mSocket->setInputBuffering( false ); mSocket->setBlocking( true ); ovpnDisconnect(); } else { debug( "~openVPNManager", "Stay connected on disconnect" ); } socketDisconnect(); debug( "~openVPNManager", QString( "openVPNManager for %1 is beeing deleted. Good night!" ).arg( mId ) ); mCounter--; } bool openVPNManager::socketConnect() { debug( "socketConnect", QString( "Attempting to onnecting to %1:%2" ).arg( mHost ).arg( mPort ) ); socketDisconnect(); debug( "socketConnect", QString( "Set interval to %1" ).arg( mInterval ) ); // mSocketTimer->start( mInterval, true ); return mSocket->connect( mHost, mPort ); } void openVPNManager::socketDisconnect() { mSocket->close(); } void openVPNManager::socketWrite() { if ( mSocketStatus == KNetwork::KClientSocketBase::Open ) { job * writeJob = mWriteQueue.dequeue(); /* Copy the type so that I can delete writeJob at the end of this method. */ requests * type = new requests; *type = writeJob->type; mReadQueue.enqueue( type ); debug( "socketWrite", QString( "[%1] %2" ).arg( writeJob->type ).arg( writeJob->command ) ); mSocket->writeBlock( writeJob->command, writeJob->command.length() ); delete writeJob; /* TODO: Can I delete this here? */ } else { debug( "socketWrite", "cannot write, because socket is not up" ); } } /* Configures the management interface so that it suites my needs */ void openVPNManager::ovpnInit() { debug( "ovpnInit", "Initializing OpenVPN and its management interface" ); // command( "hold on\n" ); /* Wait for me until I tell you to connect */ command( "auth-retry interact\n" ); /* Don't kill yourself if authentication failed, just ask again. */ command( "state on\n" ); /* Enable real-time notification of status changes */ command( "log on\n" ); /* Enable real-time notification of log messages */ command( "state\n", STATE ); /* Identify last state. */ // command( "log all\n", LOG ); /* Identify the last network connected */ } /* Resets openVPN, e.g. makes it holding until slotOvpnHold() sends a hold release. */ void openVPNManager::ovpnConnect() { mDisconnectForced = false; mConnectionWanted = true; if ( mWaitingForHoldRelease ) { command( "hold release\n" ); mWaitingForHoldRelease = false; } if ( mWaitingForAuth ) { ovpnPassword( "Need 'Auth' username/password" ); } if ( mWaitingForPass ) { ovpnPassword( "Need 'Private Key' password" ); } } /* Resets openVPN, e.g. makes it holding until slotOvpnHold() sends a hold release. */ void openVPNManager::ovpnDisconnect() { mDisconnectForced = true; mConnectionWanted = false; mForce = false; command( "hold on\n" ); /* OpenVPN does imediatelly reconnect without the hold release. */ command( "signal SIGHUP\n" ); } void openVPNManager::ovpnReconnect() { mForce = true; command( "signal SIGUSR1\n" ); } /* Forces to connect to openVPN. Only used for user interaction when the user want's to connect after he has explicitly told to disconnect. */ void openVPNManager::ovpnForceConnect() { debug( "ovpnForceConnect" ); mForce = true; ovpnHold( QString::null ); } /* Authenticates agains the remote openVPN server */ void openVPNManager::ovpnAuthenticate( const QString & type, const QString & username, const QString & password ) { debug( "ovpnAuthenticate", "requesting username and password" ); command ( QString( "username \"%1\" " ).arg( type ) + QString( username ).replace( "\\", "\\\\" ).replace( "\"", "\\\"" ) + "\n", AUTHENTICATE ); command ( QString( "password \"%1\" " ).arg( type ) + QString( password ).replace( "\\", "\\\\" ).replace( "\"", "\\\"" ) + "\n" , AUTHENTICATE ); // command( "username \"%1\" " + QString( username ).replace( "\\", "\\\\" ).replace( "\"", "\\\"" ) + "\npassword \"Auth\" \"" + QString( password ).replace( "\\", "\\\\" ).replace( "\"", "\\\"" ) + "\"\r\n" , AUTHENTICATE ); } /* Decrypt the local private key */ void openVPNManager::ovpnPKPassphrase( const QString & type, const QString & passphrase ) { debug( "ovpnPKPassphrase", "requesting private key passphrase" ); command ( QString( "password \"%1\" " ).arg( type ) + QString( passphrase ).replace( "\\", "\\\\" ).replace( "\"", "\\\"" ) + "\n", PRIVATEKEY ); // command( "password \"Private Key\" \"" + QString( passphrase ).replace( "\\", "\\\\" ).replace( "\"", "\\\"" ) + "\"\r\n", PRIVATEKEY ); } /* Gain access to the management interface */ void openVPNManager::ovpnMIPassword( const QString & password ) { /* The management interface password must not be encoded in any special way */ debug( "ovpnMIPassword", "requesting management interface password" ); command( password + "\r\n", MIPASSWORD ); } /* Manages the HOLD real time notifications from openVPN, e.g. sends a hold release if neccessary */ void openVPNManager::ovpnHold( QString what ) { debug ( "ovpnHold", what ); if ( what == "Waiting for hold release" ) { mWaitingForHoldRelease = true; } /* Send hold release if openvpn Requests for it and autoconnect is configures (c_autoConnect) */ debug( "ovpnHold", QString( "mAutoconnect : %1" ).arg( mAutoconnect ) ); debug( "ovpnHold", QString( "mReconnect : %1" ).arg( mReconnect ) ); debug( "ovpnHold", QString( "mForce : %1" ).arg( mForce ) ); debug( "ovpnHold", QString( "mConnectionWanted : %1" ).arg( mConnectionWanted ) ); debug( "ovpnHold", QString( "mDisconnectForced : %1" ).arg( mDisconnectForced ) ); debug( "ovpnHold", QString( "Reconnect : %1" ).arg( ( what == "Waiting for hold release" && ( ( ( mAutoconnect && ! mConnectionWanted && ! mDisconnectForced ) || ( mReconnect && ! mDisconnectForced && mConnectionWanted ) ) ) ) || mForce ) ); /* Connect if: * 1. I am forced to do so no matter what is my current state (mForce) * 2. If I should automatically connect at startup of kovpn (mautoconnect) * 3. If I should reconnect and there has already been a connection beforce (mReconnect, mConnectionWanted) * A connection is wanted, if the user requested one (in any of the above ways). */ // if ( ( what == "Waiting for hold release" && ( ( mAutoconnect || ( mReconnect && mConnectionWanted ) ) && ! mDisconnectForced ) ) || mForce || ( mConnectionWanted && ! mDisconnectForced ) ) { // if ( ( what == "Waiting for hold release" && ( ( ( mAutoconnect && mConnectionWanted ) || mReconnect ) && ! mDisconnectForced ) ) || mForce || ( mConnectionWanted && ! mDisconnectForced ) ) { if ( ( what == "Waiting for hold release" && ( ( ( mAutoconnect && ! mConnectionWanted && ! mDisconnectForced ) || ( mReconnect && ! mDisconnectForced && mConnectionWanted ) ) ) ) || mForce ) { mConnectionWanted = true; debug( "ovpnHold", "sending hold release" ); mWaitingForHoldRelease = false; command( "hold release\n" ); /* released the hold state -> openvpn establishes a connection */ } else { /* If I am not allowed to reconnect change the status to HOLD. OpenVPN itself would say that it is reconnecting, but I don't think that is what the user expects, because it actually doesn't reconnect until the hold release is sent again. */ debug( "ovpnHold", "setting state to disconnected" ); ovpnState( ",DISCONNECTED,DISCONNECTED," ); ovpnStopTimer(); } } /* Manages the STATE notifications from openVPN. */ void openVPNManager::ovpnState( QString what ) { debug( "ovpnState", what ); /* The output format consists of 4 comma-separated parameters: (a) the integer unix date/time, (b) the state name, (c) optional descriptive string (used mostly on RECONNECTING and EXITING to show the reason for the disconnect), and (d) optional TUN/TAP local IP address (shown for ASSIGN_IP and CONNECTED). Real-time state notifications will have a ">STATE:" prefix prepended to them. */ // QRegExp rt( "^([^,]*),([^,]*),([^,]*),([^,]*)$" ); /* Detects realtime state changes */ QRegExp rt( "^([^,]*),([^,]*),([^,]*),([^,]*)" ); /* Detects realtime state changes */ if ( rt.search ( what ) != -1 ) { if ( rt.cap ( 2 ) == "CONNECTED" ) { ovpnStartTimer(); mStatus.localIP = rt.cap ( 4 ); /* A IP Address is only reported on CONNECTED or ASSIGN_IP. But CONNECTED is enough */ /* Calculate time difference between kovpn startup/kovpn iniiated connection and start of the connection. */ mOvpnConnectionStart->setTime_t( rt.cap( 1 ).toInt() ); debug( "ovpnState", QString( "Up since %1" ).arg( KGlobal::locale() ->formatDateTime( *mOvpnConnectionStart ) ) ); } else { ovpnStopTimer(); } mLastStatus = mStatus; mStatus.state = toStatus( rt.cap ( 2 ) ); /* OpenVPN reports that it is connecting, when it waits for on of these things to come (e.g. hold release). * I don't think that this is what the user expexts, because it is disconnected and waits until the user tells * it to connect. So I translate this always to DISCONNECTED. */ debug( "ovpnState", QString( "mWaitingForHoldRelease : %1" ).arg( mWaitingForHoldRelease ) ); debug( "ovpnState", QString( "mWaitingForAuth : %1" ).arg( mWaitingForAuth ) ); debug( "ovpnState", QString( "mWaitingForPass : %1" ).arg( mWaitingForPass ) ); debug( "ovpnState", QString( "mStatus (before) : %1" ).arg( mStatus.state ) ); if ( mWaitingForHoldRelease || mWaitingForAuth || mWaitingForPass ) { mStatus.state = DISCONNECTED; } debug( "ovpnState", QString( "mStatus (after) : %1" ).arg( mStatus.state ) ); } if ( mLastStatus.state != mStatus.state || ! mInitialStatusReported ) { /* Only make noise if there is something new. */ debug( "ovpnState", QString( "Status Changed from %1 to %2" ).arg( mLastStatus.state ).arg( mStatus.state ) ); mInitialStatusReported = true; emit signalOvpnStatusChanged( mId, mStatus ); emit signalOvpnStatusChanged( mId, mStatus.state ); } /* Must be after "emit signalOvpnStatusChanged", because after beeinf not ready there should be not * further state change - You cannot goto sleep if you are dead! */ if ( mStatus.state == EXITING ) { emit signalOvpnNotReady( mId ); } // if ( mLastStatus.localIP != mStatus.localIP ) { emit signalOvpnIPChanged( mId, mStatus ); emit signalOvpnIPChanged( mId, mStatus.localIP ); // } } /* Manages LOG messages from openvpn. */ void openVPNManager::ovpnLog( QString what ) { debug( "ovpnLog", what ); /* Real-time log messages begin with the ">LOG:" prefix followed by the following comma-separated fields: (a) unix integer date/time, (b) zero or more message flags in a single string: I -- informational F -- fatal error N -- non-fatal error W -- warning D -- debug, and (c) message text. */ QRegExp rt_network( "^([^,]*),I,\\[([^,]*)\\].* ([^,]*)$" ); /* Detects network logs. */ if ( rt_network.search ( what ) != -1 ) { debug( "ovpnLog", "REMOTE NAME FOUND" ); mStatus.remoteName = rt_network.cap ( 2 ); emit signalOvpnServerChanged( mId, mStatus ); emit signalOvpnServerChanged( mId, mStatus.remoteName ); } } /* Manages the PASSWORD real time notifications from openVPN, e.g. sends a request causing the GUI to request a username and a password from the user */ void openVPNManager::ovpnPassword( QString what ) { debug( "ovpnPassword", what ); QString username, password, passphrase; static bool failure = false; /* for failures with username/password and pk password */ QRegExp authMsg( "^Need \'(.*)\' username/password" ); QRegExp pkMsg( "^Need \'(.*)\' password" ); QRegExp failedMsg( "^Verification Failed: \'(.*)\'" ); QString typeText = QString::null; /* Make it human readable */ /* Username/Password */ if ( authMsg.search( what ) != -1 ) { if ( authMsg.cap( 1 ) == "Auth" ) { typeText = i18n( "an authentification" ); } else { typeText = QString( "a %1" ).arg( authMsg.cap( 1 ) ); } debug( "ovpnPassword", QString( "requesting \"%1\" username and password" ).arg( authMsg.cap( 1 ) ) ); if ( mAuthManager->authRequest( mId, authMsg.cap( 1 ) + "_UP", username, password, failure, i18n( "

You need to supply %1 username and a password.

" ).arg( typeText ), mSaveAccount ) ) { /* If user commited a username and a password I try to authenticate with them. */ failure = false; /* TODO: Is this line needed or even wrong? */ ovpnAuthenticate( authMsg.cap( 1 ), username, password ); /* Do not need them anymore, overwrite them for security reasons. I'm absolutely not sure, if this really deletes the contents from memory. There may be also other places there these data is stored. I'm going to search for it. */ username = ""; password = ""; mWaitingForAuth = false; /* because I gave it in this moment */ } else { /* If user didn't commit a username and/or password I disconnect, because I don't know what to do. */ // ovpnDisconnect(); mWaitingForAuth = true; } } /* else if ( failedMsg.search( what ) != -1 ) { debug( "ovpnPassword", QString( "username and password verification for \"%1\"failed" ).arg( failedMsg.cap( 1 ) ) ); failure = true; } else if ( what.contains( QRegExp ( "^AUTHENTICATE ERROR:.*$" ) ) ) { debug( "ovpnPassword", "username and password verification failed, some error occured." ); failure = true; ovpnPassword( "Need 'Auth' username/password" ); }*/ /* Private Key */ if ( pkMsg.search( what ) != -1 ) { if ( pkMsg.cap( 1 ) == "Private Key" ) { typeText = i18n( "a private key" ); } else { /* TODO: a, an must be set correct */ typeText = QString( "a %1" ).arg( pkMsg.cap( 1 ) ); } debug( "ovpnPassword", QString( "requesting \"%1\" password" ).arg( pkMsg.cap( 1 ) ) ); if ( mAuthManager->authRequest( mId, pkMsg.cap( 1 ) + "_P", passphrase, failure, i18n( "

You need to supply %1 password.

" ).arg( typeText ), mSavePassphrase ) ) { /* If user commited a username and a password I try to authenticate with them. */ failure = false; /* TODO: Is this line needed or even wrong? */ ovpnPKPassphrase( pkMsg.cap( 1 ), passphrase ); /* Do not need them anymore, overwrite them for security reasons. I'm absolutely not sure, if this really deletes the contents from memory. There may be also other places there these data is stored. I'm going to search for it. */ passphrase = ""; mWaitingForPass = false; /* because I gave it in this moment */ } else { /* If user didn't commit a username and/or password I disconnect, because I don't know what to do. */ // ovpnDisconnect(); mWaitingForPass = true; } } /* else if ( what == "Verification Failed: 'Private Key'" ) { debug( "ovpnPassword", "private key password verification failed" ); failure = true; } else if ( what.contains( QRegExp ( "^PRIVATE KEY ERROR:.*$" ) ) ) { debug( "ovpnPassword", "private key password verification failed, some error occured." ); failure = true; ovpnPassword( "Need 'Private Key' password" ); }*/ if ( failedMsg.search( what ) != -1 ) { debug( "ovpnPassword", QString( "username and/or password verification for \"%1\"failed" ).arg( failedMsg.cap( 1 ) ) ); failure = true; } /* Management Interface Password */ if ( what == "ENTER PASSWORD:" ) { /* TODO: AuthManager bekommt einen Fehler nicht mit, wenn er KWallet benutzen soll. */ debug( "ovpnPassword", "requesting management interface access password" ); if ( mAuthManager->authRequest( mId, "mi", password, mMIAuthFailed, i18n( "You need to supply a password for controlling the OpenVPN daemon for %1" ).arg( mId ), mSaveMIPassword ) ) { /* This is set to true so that I know if this function is called a second time. If everything went good then there is no second time. */ mMIAuthFailed = true; /* If user commited a password I try to authenticate with them. */ ovpnMIPassword( password ); /* Do not need them anymore, overwrite them for security reasons. I'm absolutely not sure, if this really deletes the contents from memory. There may be also other places there these data is stored. I'm going to search for it. */ password = ""; mWaitingForMIPass = false; /* because I gave it in this moment */ } else { mWaitingForMIPass = true; /* user didn't commit a password. Still waiting for it. */ } } // delete username; // delete password; // delete passphrase; } /* Manages all notifications from openVPN and passes them to the right methods to handle them. */ void openVPNManager::socketReadyRead() { debug( "socketReadReady" ); // ovpnNeedok( "Need 'token-insertion-request' confirmation MSG:Please insert your cryptographic token" ); /* I am not using a QByteArray because it's to expensive and hard to split it up into single lines */ // QByteArray readBuffer (socket()->bytesAvailable()); // socket()->readBlock(readBuffer.data(), readBuffer.size()); requests type; /* Remember the type so that I can pass it with the message signal */ QString readData; /* Stores the last line given from openvpn. */ static QString multilineData; /* Stores unfinished multiline Data such as answers on requested data. text available (which is indeed not all data). */ static QRegExp realTimeNotifications( "^>(.*):.*\\r\\n$" ); /* That's the general real time notification format of openvpn. */ static QRegExp realTimeStartup( "^>INFO:OpenVPN Management Interface Version 1.*$" ); /* Detects corrct startup message */ static QRegExp realTimeState( "^>STATE:(.*).*\\r\\n$" ); /* Detects state changes */ static QRegExp realTimeLog( "^>LOG:(.*).*\\r\\n$" ); /* Detects real-time LOG messages */ static QRegExp realTimeHold( "^>HOLD:(.*).*\\r\\n$" ); /* Detects corrct startup message */ static QRegExp realTimePassword( "^>PASSWORD:(.*).*\\r\\n$" ); /* Detects Auth requests */ static QRegExp realTimeFatal( "^>FATAL:(.*).*\\r\\n$" ); /* Detects Auth requests */ static QRegExp realTimeNeedok( "^>NEED-OK:(.*).*\\r\\n$" ); /* Detects Auth requests */ static QRegExp outputSuccess( "^SUCCESS:.*\\r\\n$" ); static QRegExp outputError( "^ERROR:.*\\r\\n$" ); static QRegExp singleLine( "^(.*)\\r\\n$" ); /* Cuts of the \r\n at the end of a line. */ /* The only Case in which there is something to read but it is no line is, when the * management interface requests an access password */ if ( ! mSocket->canReadLine() ) { QRegExp requestMIPassword( "^ENTER PASSWORD:$" ); /* Cuts of the \r\n at the end of a line. */ QByteArray readBuffer ( mSocket->bytesAvailable() ); mSocket->readBlock( readBuffer.data(), readBuffer.size() ); debug( "socketReadyRead", QString( "some Bytes : %1" ).arg( QString( readBuffer ) ) ); if ( requestMIPassword.search( QString( readBuffer ) ) != -1 ) { emit signalOvpnPreReady( mId ); ovpnPassword( QString( readBuffer ) ); } } while ( readData = mSocket->readLine() ) { if ( singleLine.search( readData ) != -1 ) { /* Cut off the line breaks, because they are not good when logging */ emit message( mId, singleLine.cap( 1 ) , MISC ); } else { /* If there are no line breaks, then just pass this message */ emit message( mId, readData , MISC ); } if ( realTimeLog.search( readData ) != -1 ) { ovpnLog( realTimeLog.cap ( 1 ) ); } else if ( realTimeState.search( readData ) != -1 ) { ovpnState( realTimeState.cap ( 1 ) ); } else if ( realTimePassword.search( readData ) != -1 ) { ovpnPassword( realTimePassword.cap( 1 ) ); } else if ( realTimeHold.search( readData ) != -1 ) { ovpnHold( realTimeHold.cap( 1 ) ); } else if ( realTimeFatal.search( readData ) != -1 ) { ovpnFatal( realTimeFatal.cap( 1 ) ); } else if ( realTimeNeedok.search( readData ) != -1 ) { ovpnNeedok( realTimeNeedok.cap( 1 ) ); } else if ( realTimeStartup.search( readData ) != -1 ) { mMIAuthFailed = false; /* This decission can at first be made here, because it's the "Success" notification of the login at the management interface */ emit signalOvpnReady( mId ); ovpnInit(); mSocketTimer->stop(); } else if ( realTimeNotifications.search ( readData ) != -1 ) { /* any yet unhandled real time notifications */ } // else if ( outputSuccess.search ( readData ) != -1 ) { /* unhandled SUCCESS notification */ // } else if ( outputError.search ( readData ) != -1 ) { /* unhandled ERROR notification */ // } else if ( singleLine.search( readData ) != -1 && mReadQueue.count() != 0 ) { // cout << mReadQueue.count() << ":" << *mReadQueue.head() << " [BEGIN] " << singleLine.cap( 1 ) << " [END] " << endl; // cout << " +++ " << mReadQueue.count() << " Commands enqueued." << endl; switch ( *mReadQueue.head() ) { case STATE: ovpnState( singleLine.cap( 1 ) ); break; case STATUS: break; case LOG: ovpnLog( singleLine.cap( 1 ) ); break; case ECHO: break; case AUTHENTICATE: /* I have to make this, because I must be able to decide if an error was from "Auth" oder "Private Key". OpenVPN itself sometimes only tells that there is an error (e.g. private key passphrase was empty). */ ovpnPassword( "AUTHENTICATE " + singleLine.cap( 1 ) ); break; case PRIVATEKEY: ovpnPassword( "PRIVATE KEY " + singleLine.cap( 1 ) ); break; case MISC: case DIRECT: default: /* There are some unhandled messages. I'm going to fix this if neccessary. But I don't think so. */ delete mReadQueue.dequeue(); break; } if ( singleLine.cap( 1 ) == "END" ) /* End of Output, delete command, process next one. */ { delete mReadQueue.dequeue(); } } else { debug ( "socketReadyRead", QString( "UNHANDLED: %1" ).arg( readData ) ); } } } void openVPNManager::socketGotError( int code ) { debug( "socketGotError", QString( "[%1] %2" ).arg( code ).arg( mSocket->errorString( ( KNetwork::KBufferedSocket::SocketError ) code ) ) ); switch ( ( KNetwork::KBufferedSocket::SocketError ) code ) { case KNetwork::KBufferedSocket::RemotelyDisconnected: case KNetwork::KBufferedSocket::ConnectionTimedOut: case KNetwork::KBufferedSocket::NotSupported: /* Why NotSupported if Link can't be established, because openvpn is down??? */ mSocketTimer->start( mInterval, true ); break; default: break; } } void openVPNManager::ovpnStartTimer() { debug( "ovpnStartTimer" ); if ( mUptimeRequestCounter != 0 ) { /* Only count if some module is interested in */ mOvpnTimer->start( 1000 ); } } void openVPNManager::ovpnStopTimer() { debug( "ovpnStopTimer" ); mOvpnTimer->stop(); mStatus.uptime = 0; emit signalOvpnUptimeChanged( mId, mStatus ); emit signalOvpnUptimeChanged( mId, mStatus.uptime ); } void openVPNManager::ovpnTimer() { debug( "ovpnTimer" ); if ( mStatus.state == CONNECTED ) { /* Uptime may be requested though offline, don't calc if not online */ mStatus.uptime = mOvpnConnectionStart->secsTo( QDateTime::currentDateTime() ); } else { mStatus.uptime = 0; } emit signalOvpnUptimeChanged( mId, mStatus ); emit signalOvpnUptimeChanged( mId, mStatus.uptime ); } void openVPNManager::socketStateChanged( int code ) { mSocketStatus = ( KNetwork::KClientSocketBase::SocketState ) code; switch ( code ) { case KNetwork::KClientSocketBase::Idle: debug( "socketStateChanged", "Idle" ); mInterval = QMIN( mInterval + ( mInterval / 2 ), 60000 ); /* increase interval to reduce cpu time. max 1 minute */ break; case KNetwork::KClientSocketBase::HostLookup: debug( "socketStateChanged", "Host Lookup" ); break; case KNetwork::KClientSocketBase::HostFound: debug( "socketStateChanged", "Host Found" ); break; case KNetwork::KClientSocketBase::Bound: debug( "socketStateChanged", "Bound" ); break; case KNetwork::KClientSocketBase::Connecting: debug( "socketStateChanged", "Connecting" ); break; case KNetwork::KClientSocketBase::Open: debug( "socketStateChanged", "Open" ); mInterval = 2000; /* If it is connected, reset interval, because next time it has to connect it should retry fast */ break; case KNetwork::KClientSocketBase::Closing: debug( "socketStateChanged", "Closing" ); /* I don't think that it is neccessary to emit this signal always exept "Open" because it may take some time to react to this signal and it's unlikely that it OpenVPN was ready and is not ready anymore without closing the socket or EXITING */ emit signalOvpnNotReady( mId ); break; default: debug( "socketStateChanged", QString( "Unknown (%1)" ).arg( code ) ); break; } } void openVPNManager::command( QString command, requests type ) { /* This is disabled, because It could print passwords to the console. */ debug( "command", QString( "[%1] %2" ).arg( type ).arg( QString::fromUtf8( command ).latin1() ) ); debug( "command" ); job * writeJob = new job; writeJob->type = type; // writeJob->command = QString::fromUtf8( command ).latin1(); writeJob->command = command.latin1(); mWriteQueue.enqueue( writeJob ); socketWrite(); } openVPNManager::states openVPNManager::toStatus( QString status ) { if ( status == "DISCONNECTED" ) return DISCONNECTED; else if ( status == "CONNECTING" ) return CONNECTING; else if ( status == "WAIT" ) return WAIT; else if ( status == "AUTH" ) return AUTH; else if ( status == "GET_CONFIG" ) return GET_CONFIG; else if ( status == "ASSIGN_IP" ) return ASSIGN_IP; else if ( status == "ADD_ROUTES" ) return ADD_ROUTES; else if ( status == "CONNECTED" ) return CONNECTED; else if ( status == "RECONNECTING" ) return RECONNECTING; else if ( status == "EXITING" ) return EXITING; else if ( status == "UNKNOWN" ) return UNKNOWN; else return DISCONNECTED; } // bool openVPNManager::reconnect( ) // { // return mReconnect; // } // void openVPNManager::setId( const QString & id ) // { // mId = id; // } void openVPNManager::setHost( const QString & host ) { if ( mHost != host ) { socketDisconnect(); mHost = host; socketConnect(); } } void openVPNManager::setPort( const QString & port ) { if ( mPort != port ) { socketDisconnect(); mPort = port; socketConnect(); } } void openVPNManager::setSaveMIPassword( const bool saveMIPassword ) { mSaveMIPassword = saveMIPassword; } void openVPNManager::setSaveAccount( const bool saveAccount ) { mSaveAccount = saveAccount; } void openVPNManager::setSavePassphrase( const bool savePassphrase ) { mSavePassphrase = savePassphrase; } void openVPNManager::setReconnect( const bool reconnect ) { mReconnect = reconnect; } void openVPNManager::setAutoconnect( const bool autoconnect ) { mAutoconnect = autoconnect; } /* If the user supplies 3x the wrong password openvpn closes it * management interface and the user has to click on "connect" */ void openVPNManager::connect( ) { debug( "connect" ); if ( mWaitingForMIPass ) { ovpnPassword( "ENTER PASSWORD:" ); } /* mWaitingForMIPass is set to false if the user supplied a password. * Otherwise is makes no sense to give the connect command. */ if ( ! mWaitingForMIPass ) { ovpnConnect(); } } /* If the user supplies 3x the wrong password openvpn closes it * management interface and the user has to click on "disconnect" */ void openVPNManager::disconnect( ) { debug( "disconnect" ); if ( mWaitingForMIPass ) { ovpnPassword( "ENTER PASSWORD:" ); } /* mWaitingForMIPass is set to false if the user supplied a password. * Otherwise is makes no sense to give the connect command. */ if ( ! mWaitingForMIPass ) { ovpnDisconnect(); } } // void openVPNManager::reconnect( ) // { // ovpnReconnect(); // } QString openVPNManager::id( ) { return mId; } QString openVPNManager::host( ) { return mHost; } QString openVPNManager::port( ) { return mPort; } bool openVPNManager::saveMIPassword( ) { return mSaveMIPassword; } bool openVPNManager::saveAccount( ) { return mSaveAccount; } bool openVPNManager::savePassphrase( ) { return mSavePassphrase; } bool openVPNManager::autoconnect( ) { return mAutoconnect; } bool openVPNManager::reconnect( ) { return mReconnect; } // openVPNManager::states openVPNManager::status( ) // { // return mStatus.state; // } openVPNManager::tStatus openVPNManager::status( ) { ovpnTimer(); /* Update Uptime */ return mStatus; } QString openVPNManager::ip( ) { return mStatus.localIP; } int openVPNManager::uptime( ) { ovpnTimer(); /* Update Uptime */ return mStatus.uptime; } void openVPNManager::debug( const QString & method, const QString & message ) { #ifdef DEBUG QString myMethod ( method ); static unsigned int maxLen = 0; maxLen = QMAX( myMethod.length(), maxLen ); cout << "openVPNManager[" << mId << "]::" << myMethod.leftJustify( maxLen ) << " => " << message << endl; #endif } void openVPNManager::ovpnFatal( QString what ) { emit error( what ); } void openVPNManager::ovpnNeedok( QString what ) { debug( "ovpnNeedok" ); QString topic = QString::null; QString body = QString::null; bool yesno = false; QRegExp rt( "^Need \'(.*)\' confirmation MSG:(.*)$" ); if ( rt.search( what ) != -1 ) { topic = rt.cap( 1 ); body = rt.cap( 2 ); /* Ask the user */ emit message( yesno, "OpenVPN Message", body ); if ( yesno ) { command( QString( "needok %1 ok\n" ).arg( topic ) ); } else { command( QString( "needok %1 cancel\n" ).arg( topic ) ); } } else { debug( "ovpnNeedok", what ); } } void openVPNManager::setDisconnectOnExit( const bool disconnectOnExit ) { mDisconnectOnExit = disconnectOnExit; } bool openVPNManager::disconnectOnExit( ) { return mDisconnectOnExit; } void openVPNManager::directCommand( const QString & cmd ) { command( QString( "%1\n\r" ).arg( cmd ), DIRECT ); } bool openVPNManager::startUptimeCounter( ) { mUptimeRequestCounter++; if ( mUptimeRequestCounter == 1 && mStatus.state == CONNECTED ) { debug( "startUptimeCounter", "timer started." ); mOvpnTimer->start( 1000 ); /* Only start, don't reset using ovpnStartTimer() */ } return true; } bool openVPNManager::stopUptimeCounter( ) { mUptimeRequestCounter--; if ( mUptimeRequestCounter == 0 ) { debug( "stopUptimeCounter", "timer stopped" ); mOvpnTimer->stop(); /* Only stop, don't reset using ovpnStopTimer() */ return true; } return false; } #include "openvpnmanager.moc"