diff --git a/.gitignore b/.gitignore index 1d9d07c0..4d43eccc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # User settings *.user macOSPackage/ +*.dmg # Qt-es /.qmake.cache diff --git a/client/client.pro b/client/client.pro index 76cf33d8..5d79929b 100644 --- a/client/client.pro +++ b/client/client.pro @@ -7,21 +7,36 @@ TEMPLATE = app DEFINES += QT_DEPRECATED_WARNINGS HEADERS += \ + communicator.h \ core/router.h \ debug.h \ defines.h \ + localclient.h \ + managementserver.h \ + message.h \ + openvpnprotocol.h \ runguard.h \ ui/Controls/SlidingStackedWidget.h \ ui/mainwindow.h \ + utils.h \ + vpnconnection.h \ + vpnprotocol.h \ SOURCES += \ + communicator.cpp \ core/router.cpp \ debug.cpp \ + localclient.cpp \ main.cpp \ + managementserver.cpp \ + message.cpp \ + openvpnprotocol.cpp \ runguard.cpp \ ui/Controls/SlidingStackedWidget.cpp \ ui/mainwindow.cpp \ - + utils.cpp \ + vpnconnection.cpp \ + vpnprotocol.cpp \ FORMS += ui/mainwindow.ui @@ -29,8 +44,7 @@ RESOURCES += \ resources.qrc TRANSLATIONS = \ - translations/amneziavpn.en.ts \ - translations/amneziavpn.ru.ts + translations/amneziavpn_ru.ts CONFIG(release, debug|release) { DESTDIR = $$PWD/../../AmneziaVPN-build/client/release @@ -46,7 +60,7 @@ win32 { HEADERS += SOURCES += - VERSION = 1.1.1.1 + VERSION = 1.0.0.0 QMAKE_TARGET_COMPANY = "AmneziaVPN" QMAKE_TARGET_PRODUCT = "AmneziaVPN" diff --git a/client/communicator.cpp b/client/communicator.cpp new file mode 100644 index 00000000..45710e29 --- /dev/null +++ b/client/communicator.cpp @@ -0,0 +1,77 @@ +#include "communicator.h" +#include "defines.h" +#include "localclient.h" +#include "utils.h" + +Communicator::Communicator(QObject* parent) : QObject(parent), + m_localClient(nullptr) +{ + connectToServer(); +} + +Communicator::~Communicator() +{ + +} + +void Communicator::connectToServer() +{ + if (m_localClient) { + delete m_localClient; + } + + qDebug().noquote() << QString("Connect to local server '%1'").arg(SERVICE_NAME); + + m_localClient = new LocalClient(this); + connect(m_localClient, &LocalClient::connected, this, &Communicator::onConnected); + connect(m_localClient, &LocalClient::lineAvailable, this, &Communicator::onLineAvailable); + m_localClient->connectToServer(SERVICE_NAME); +} + +void Communicator::onConnected() +{ + qDebug().noquote() << QString("Connected to local server '%1'").arg(m_localClient->serverName()); + Message message(Message::State::Initialize, QStringList({"Ping"})); + sendMessage(message); +} + +void Communicator::onLineAvailable(const QString& line) +{ + Message message(line); + if (!message.isValid()) { + qDebug() << "Message is not valid"; + return; + } + + emit messageReceived(message); +} + +bool Communicator::connected() const +{ + if (!m_localClient) { + return false; + } + + return m_localClient->connectedState(); +} + +QString Communicator::readData() +{ + return QString(); +} + +bool Communicator::writeData(const QString& data) +{ + return m_localClient->write(data.toLocal8Bit()); +} + +void Communicator::sendMessage(const Message& message) +{ + if (!connected()) { + return; + } + const QString data = message.toString(); + bool status = writeData(data + "\n"); + + qDebug().noquote() << QString("Send message '%1', status '%2'").arg(data).arg(Utils::toString(status)); +} diff --git a/client/communicator.h b/client/communicator.h new file mode 100644 index 00000000..27a7eac0 --- /dev/null +++ b/client/communicator.h @@ -0,0 +1,41 @@ +#ifndef COMMUNICATOR_H +#define COMMUNICATOR_H + +#include +#include + +#include "message.h" + +class LocalClient; + +class Communicator : public QObject +{ + Q_OBJECT + +public: + explicit Communicator(QObject* parent = nullptr); + ~Communicator(); + + void sendMessage(const Message& message); + +signals: + void messageReceived(const Message& message); + + void comminicatorConnected(); + void comminicatorDisconnected(); + +protected slots: + void onConnected(); + void onLineAvailable(const QString& line); + +protected: + QString readData(); + bool connected() const; + bool writeData(const QString& data); + void connectToServer(); + + LocalClient* m_localClient; +}; + + +#endif // COMMUNICATOR_H diff --git a/client/debug.cpp b/client/debug.cpp index 3103b947..29a418f8 100644 --- a/client/debug.cpp +++ b/client/debug.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include "debug.h" #include "defines.h" @@ -23,6 +25,8 @@ void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, cons } Debug::m_textStream << qFormatLogMessage(type, context, msg) << endl << flush; + + std::cout << qFormatLogMessage(type, context, msg).toStdString() << std::endl << std::flush; } bool Debug::init() @@ -35,7 +39,7 @@ bool Debug::init() m_logFileName = QString("%1.log").arg(APPLICATION_NAME); - qSetMessagePattern("[%{time}|%{type}] %{message}"); + qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} %{type} %{message}"); m_file.setFileName(appDir.filePath(m_logFileName)); if (!m_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { @@ -66,3 +70,8 @@ bool Debug::openLogsFolder() } return true; } + +QString Debug::appLogFileNamePath() +{ + return m_file.fileName(); +} diff --git a/client/debug.h b/client/debug.h index aabbea4d..7a89c8ca 100644 --- a/client/debug.h +++ b/client/debug.h @@ -1,10 +1,11 @@ #ifndef DEBUG_H #define DEBUG_H -#include -#include -#include +#include #include +#include +#include +#include class Debug { @@ -12,6 +13,7 @@ public: static QString logsDir(); static bool init(); static bool openLogsFolder(); + static QString appLogFileNamePath(); private: static QFile m_file; diff --git a/client/defines.h b/client/defines.h index edf60b10..4f244d90 100644 --- a/client/defines.h +++ b/client/defines.h @@ -4,5 +4,6 @@ #define APPLICATION_NAME "AmneziaVPN" #define SERVICE_NAME "AmneziaVPN-service" #define ORGANIZATION_NAME "AmneziaVPN.ORG" +#define APP_VERSION "1.0.0.0" #endif // DEFINES_H diff --git a/client/localclient.cpp b/client/localclient.cpp new file mode 100644 index 00000000..223c6f1e --- /dev/null +++ b/client/localclient.cpp @@ -0,0 +1,64 @@ +#include +#include + +#include "localclient.h" + +LocalClient::LocalClient(QObject *parent) : QObject(parent), + m_socket(new QLocalSocket(this)) +{ + m_in.setDevice(m_socket); + m_in.setVersion(QDataStream::Qt_5_10); + + connect(m_socket, &QLocalSocket::readyRead, this, &LocalClient::onReadyRead); + connect(m_socket, &QLocalSocket::connected, this, &LocalClient::onConnected); + connect(m_socket, QOverload::of(&QLocalSocket::error), this, &LocalClient::displayError); +} + +void LocalClient::connectToServer(const QString& name) +{ + m_blockSize = 0; + m_socket->abort(); + m_socket->connectToServer(name); +} + +QString LocalClient::serverName() const +{ + return m_socket->serverName(); +} + +void LocalClient::onConnected() +{ + emit connected(); +} + +bool LocalClient::connectedState() const +{ + return (m_socket->state() == QLocalSocket::ConnectedState); +} + +quint64 LocalClient::write(const QByteArray& data) +{ + return m_socket->write(data); +} + +void LocalClient::onReadyRead() +{ + qDebug() << "On ready read"; + + if (m_socket->canReadLine()) { + char buf[1024]; + qint64 lineLength = m_socket->readLine(buf, sizeof(buf)); + if (lineLength != -1) { + QString line = buf; + line = line.simplified(); + qDebug().noquote() << QString("Readed line: '%1'").arg(line); + emit lineAvailable(line); + } + } +} + +void LocalClient::displayError(QLocalSocket::LocalSocketError socketError) +{ + Q_UNUSED(socketError) + qDebug() << QString("The following error occurred: %1.").arg(m_socket->errorString()); +} diff --git a/client/localclient.h b/client/localclient.h new file mode 100644 index 00000000..cd66af7a --- /dev/null +++ b/client/localclient.h @@ -0,0 +1,34 @@ +#ifndef LOCALCLIENT_H +#define LOCALCLIENT_H + +#include +#include + +class LocalClient : public QObject +{ + Q_OBJECT + +public: + explicit LocalClient(QObject *parent = nullptr); + + QString serverName() const; + bool connectedState() const; + quint64 write(const QByteArray& data); + void connectToServer(const QString& name); + +signals: + void connected(); + void lineAvailable(const QString& line); + +private slots: + void displayError(QLocalSocket::LocalSocketError socketError); + void onConnected(); + void onReadyRead(); + +private: + QLocalSocket* m_socket; + QDataStream m_in; + quint32 m_blockSize; +}; + +#endif // LOCALCLIENT_H diff --git a/client/main.cpp b/client/main.cpp index 840ada2e..319124b4 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -10,6 +10,13 @@ #include "ui/mainwindow.h" +static void loadTranslator() +{ + QTranslator* translator = new QTranslator; + if (translator->load(QLocale(), QString("amneziavpn"), QLatin1String("_"), QLatin1String(":/translations"))) { + qApp->installTranslator(translator); + } +} int main(int argc, char *argv[]) { @@ -17,6 +24,7 @@ int main(int argc, char *argv[]) RunGuard::instance(APPLICATION_NAME).activate(); QApplication app(argc, argv); + loadTranslator(); if (! RunGuard::instance().tryToRun()) { qDebug() << "Tried to run second instance. Exiting..."; @@ -35,32 +43,17 @@ int main(int argc, char *argv[]) QFontDatabase::addApplicationFont(":/fonts/Lato-Thin.ttf"); QFontDatabase::addApplicationFont(":/fonts/Lato-ThinItalic.ttf"); - { - QTranslator *translator = new QTranslator; - QLocale ru(QLocale("ru_RU")); - QLocale::setDefault(ru); - if (translator->load(QLocale(), "amneziavpn", ".", QLatin1String(":/translations"))) { - bool ok = qApp->installTranslator(translator); - qDebug().noquote() << "Main: Installing translator for locale" << ru.name() << ok; - } - else { - qDebug().noquote() << "Main: Failed to install translator for locale" << ru.name(); - } - } - app.setApplicationName(APPLICATION_NAME); app.setOrganizationName(ORGANIZATION_NAME); app.setApplicationDisplayName(APPLICATION_NAME); - //app.setQuitOnLastWindowClosed(false); - QCommandLineParser parser; parser.setApplicationDescription(APPLICATION_NAME); parser.addHelpOption(); parser.addVersionOption(); if (!Debug::init()) { - qCritical() << "Initialization of debug subsystem failed"; + qWarning() << "Initialization of debug subsystem failed"; } QFont f("Lato Regular", 10); diff --git a/client/managementserver.cpp b/client/managementserver.cpp new file mode 100644 index 00000000..9330f046 --- /dev/null +++ b/client/managementserver.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +#include "managementserver.h" + +ManagementServer::ManagementServer(QObject *parent) : QObject(parent), + m_tcpServer(nullptr) +{ + +} + +ManagementServer::~ManagementServer() +{ + +} + +bool ManagementServer::isOpen() const +{ + return (m_socket && m_socket->isOpen()); +} + +void ManagementServer::stop() +{ + m_tcpServer->close(); +} + +void ManagementServer::onAcceptError(QAbstractSocket::SocketError socketError) +{ + qDebug().noquote() << QString("Accept error: %1").arg(socketError); +} + +qint64 ManagementServer::writeCommand(const QString& message) +{ + if (!isOpen()) { + return 0; + } + + const QString command = message + "\n"; + return m_socket->write(command.toStdString().c_str()); +} + +void ManagementServer::onNewConnection() +{ + qDebug() << "New incoming connection"; + + m_socket = m_tcpServer->nextPendingConnection(); + m_tcpServer->close(); + + QObject::connect(m_socket, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected())); + QObject::connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError))); + QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); +} + +void ManagementServer::onSocketError(QAbstractSocket::SocketError socketError) +{ + Q_UNUSED(socketError); + + qDebug().noquote() << QString("Mananement server error: %1").arg(m_socket->errorString()); +} + +void ManagementServer::onSocketDisconnected() +{ + m_socket->deleteLater(); +} + +QTcpSocket* ManagementServer::socket() const +{ + if (!isOpen()) { + return nullptr; + } + return m_socket; +} + +void ManagementServer::onReadyRead() +{ + emit readyRead(); +} + +bool ManagementServer::start(const QString& host, unsigned int port) +{ + if (m_tcpServer) { + delete m_tcpServer; + } + + m_tcpServer = new QTcpServer(this); + m_tcpServer->setMaxPendingConnections(1); + + connect(m_tcpServer, SIGNAL(acceptError(QAbstractSocket::SocketError)), this, SLOT(onAcceptError(QAbstractSocket::SocketError))); + connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection())); + + if (m_tcpServer->listen(QHostAddress(host), port)) { + emit serverStarted(); + return true; + } + + qDebug().noquote() << QString("Can't start TCP server, %1,%2") + .arg(m_tcpServer->serverError()) + .arg(m_tcpServer->errorString()); + return false; +} + +QString ManagementServer::readLine() +{ + if (!isOpen()) { + qDebug() << "Socket is not opened"; + return QString(); + } + + return m_socket->readLine(); +} diff --git a/client/managementserver.h b/client/managementserver.h new file mode 100644 index 00000000..bb91c3aa --- /dev/null +++ b/client/managementserver.h @@ -0,0 +1,43 @@ +#ifndef MANAGEMENTSERVER_H +#define MANAGEMENTSERVER_H + +#include +#include + +class QTcpServer; +class QTcpSocket; + +class ManagementServer : public QObject +{ + Q_OBJECT + +public: + explicit ManagementServer(QObject *parent = nullptr); + ~ManagementServer(); + + bool start(const QString& host, unsigned int port); + void stop(); + bool isOpen() const; + + QString readLine(); + qint64 writeCommand(const QString& message); + + QTcpSocket* socket() const; + +signals: + void readyRead(); + void serverStarted(); + +protected slots: + void onAcceptError(QAbstractSocket::SocketError socketError); + void onNewConnection(); + void onReadyRead(); + void onSocketDisconnected(); + void onSocketError(QAbstractSocket::SocketError socketError); + +protected: + QTcpServer* m_tcpServer; + QTcpSocket* m_socket; +}; + +#endif // MANAGEMENTSERVER_H diff --git a/client/message.cpp b/client/message.cpp new file mode 100644 index 00000000..bc63392b --- /dev/null +++ b/client/message.cpp @@ -0,0 +1,95 @@ +#include "message.h" + +Message::Message(State state, const QStringList& args) : + m_state(state), + m_args(args), + m_valid(true) +{ + +} + +bool Message::isValid() const +{ + return m_valid; +} + +QString Message::textState() const +{ + switch (m_state) { + case State::Unknown: return "Unknown"; + case State::Initialize: return "Initialize"; + case State::StartRequest: return "StartRequest"; + case State::Started: return "Started"; + case State::FinishRequest: return "FinishRequest"; + case State::Finished: return "Finished"; + default: + ; + } + return QString(); +} + +Message::State Message::state() const +{ + return m_state; +} + +QString Message::toString() const +{ + if (!isValid()) { + return QString(); + } + + return QString("%1%2%3") + .arg(textState()) + .arg(m_dataSeparator) + .arg(argsToString()); +} + +QString Message::argAtIndex(int index) const +{ + if ((index + 1) > args().size()) { + return QString(); + } + + return args().at(index); +} + +QStringList Message::args() const +{ + return m_args; +} + +QString Message::argsToString() const +{ + return m_args.join(m_argSeparator); +} + +Message::Message(const QString& data) +{ + m_valid = false; + if (data.isEmpty()) { + return; + } + + QStringList dataList = data.split(m_dataSeparator); + if ((dataList.size() != 2)) { + return; + } + + bool stateFound = false; + for (int i = static_cast(State::Unknown); i <= static_cast(State::Finished); i++ ) { + m_state = static_cast(i); + if (textState() == dataList.at(0)) { + stateFound = true; + break; + } + } + + if (!stateFound) { + return; + } + + m_args = dataList.at(1).split(m_argSeparator); + m_valid = true; +} + diff --git a/client/message.h b/client/message.h new file mode 100644 index 00000000..46eed2a5 --- /dev/null +++ b/client/message.h @@ -0,0 +1,31 @@ +#ifndef MESSAGE_H +#define MESSAGE_H + +#include + +class Message { + +public: + enum class State {Unknown, Initialize, StartRequest, Started, FinishRequest, Finished}; + Message(State state, const QStringList& args); + Message(const QString& data); + + QString argAtIndex(int index) const; + QString argsToString() const; + QString toString() const; + QStringList args() const; + State state() const; + bool isValid() const; + +protected: + QString textState() const; + + const QString m_argSeparator = ","; + const QString m_dataSeparator = "|"; + + bool m_valid; + State m_state; + QStringList m_args; +}; + +#endif // MESSAGE_H diff --git a/client/openvpnprotocol.cpp b/client/openvpnprotocol.cpp new file mode 100644 index 00000000..a1f0e40f --- /dev/null +++ b/client/openvpnprotocol.cpp @@ -0,0 +1,238 @@ +#include +#include + +#include "communicator.h" +#include "debug.h" +#include "openvpnprotocol.h" +#include "utils.h" + + +OpenVpnProtocol::OpenVpnProtocol(const QString& args, QObject* parent) : + VpnProtocol(args, parent), + m_requestFromUserToStop(false) +{ + setConfigFile(args); + connect(m_communicator, &Communicator::messageReceived, this, &OpenVpnProtocol::onMessageReceived); + connect(&m_managementServer, &ManagementServer::readyRead, this, &OpenVpnProtocol::onReadyReadDataFromManagementServer); +} + +OpenVpnProtocol::~OpenVpnProtocol() +{ + stop(); +} + +void OpenVpnProtocol::onMessageReceived(const Message& message) +{ + if (!message.isValid()) { + qWarning().noquote() << QString("Message received: '%1', but it is not valid").arg(message.toString()); + return; + } + + switch (message.state()) { + case Message::State::Started: + qDebug().noquote() << QString("OpenVPN process started"); + break; + case Message::State::Finished: + qDebug().noquote() << QString("OpenVPN process finished with status %1").arg(message.argAtIndex(1)); + onOpenVpnProcessFinished(message.argAtIndex(1).toInt()); + break; + default: + qDebug().noquote() << QString("Message received: '%1'").arg(message.toString()); + ; + } +} + +void OpenVpnProtocol::stop() +{ + if ((m_connectionState == VpnProtocol::ConnectionState::Preparing) || + (m_connectionState == VpnProtocol::ConnectionState::Connecting) || + (m_connectionState == VpnProtocol::ConnectionState::Connected)) { + if (!sendTermSignal()) { + killOpenVpnProcess(); + } + setConnectionState(VpnProtocol::ConnectionState::Disconnecting); + } +} + +void OpenVpnProtocol::killOpenVpnProcess() +{ + // send command to kill openvpn process. +} + +bool OpenVpnProtocol::setConfigFile(const QString& configFileNamePath) +{ + m_configFileName = configFileNamePath; + QFileInfo file(m_configFileName); + + if (file.fileName().isEmpty()) { + m_configFileName = Utils::systemConfigPath() + "/" + QCoreApplication::applicationName() + ".ovpn"; + } + + if (m_configFileName.isEmpty()) { + return false; + } + + qDebug() << "Set config file:" << configPath(); + + return false; +} + +bool OpenVpnProtocol::openVpnProcessIsRunning() const +{ + return Utils::processIsRunning("openvpn"); +} + +void OpenVpnProtocol::disconnectFromManagementServer() +{ + m_managementServer.stop(); +} + +QString OpenVpnProtocol::configPath() const +{ + return m_configFileName; +} + +void OpenVpnProtocol::writeCommand(const QString& command) +{ + QTextStream stream(reinterpret_cast(m_managementServer.socket())); + stream << command << endl; +} + +QString OpenVpnProtocol::openVpnExecPath() const +{ +#ifdef Q_OS_WIN + return Utils::executable(QString("openvpn/%1/openvpn").arg(QSysInfo::buildCpuArchitecture()), true); +#else + return Utils::executable(QString("/openvpn"), true); +#endif +} + +bool OpenVpnProtocol::start() +{ + qDebug() << "Start OpenVPN connection" << openVpnExecPath(); + + m_requestFromUserToStop = false; + m_openVpnStateSigTermHandlerTimer.stop(); + stop(); + + if (!QFileInfo::exists(openVpnExecPath())) { + qCritical() << "OpeVPN executable does not exist!"; + return false; + } + + if (!QFileInfo::exists(configPath())) { + qCritical() << "OpeVPN config file does not exist!"; + return false; + } + + QString vpnLogFileNamePath = Utils::systemLogPath() + "/openvpn.log"; + Utils::createEmptyFile(vpnLogFileNamePath); + + QStringList args({openVpnExecPath(), + "--config" , configPath(), + "--management", m_managementHost, QString::number(m_managementPort), + "--management-client", + "--log-append", vpnLogFileNamePath + }); + + if (!m_managementServer.start(m_managementHost, m_managementPort)) { + return false; + } + + setConnectionState(ConnectionState::Connecting); + + qDebug().noquote() << "Start OpenVPN process with args: " << args; + m_communicator->sendMessage(Message(Message::State::StartRequest, args)); + + startTimeoutTimer(); + + return true; +} + +void OpenVpnProtocol::openVpnStateSigTermHandlerTimerEvent() +{ + bool processStatus = openVpnProcessIsRunning(); + if (processStatus) { + killOpenVpnProcess(); + } + onOpenVpnProcessFinished(0); +} + +void OpenVpnProtocol::openVpnStateSigTermHandler() +{ + m_openVpnStateSigTermHandlerTimer.start(5000); +} + +bool OpenVpnProtocol::sendTermSignal() +{ + return m_managementServer.writeCommand("signal SIGTERM"); +} + +void OpenVpnProtocol::sendByteCount() +{ + m_managementServer.writeCommand("bytecount 1"); +} + +void OpenVpnProtocol::sendInitialData() +{ + m_managementServer.writeCommand("state on"); + m_managementServer.writeCommand("log on"); +} + +void OpenVpnProtocol::onReadyReadDataFromManagementServer() +{ + for (;;) { + QString line = m_managementServer.readLine().simplified(); + + if (line.isEmpty()) { + return; + } + + if (!line.contains(">BYTECOUNT")) { + qDebug().noquote() << line; + } + + if (line.contains(">INFO:OpenVPN Management Interface")) { + sendInitialData(); + } else if (line.startsWith(">STATE")) { + if (line.contains("CONNECTED,SUCCESS")) { + sendByteCount(); + stopTimeoutTimer(); + setConnectionState(VpnProtocol::ConnectionState::Connected); + continue; + } else if (line.contains("EXITING,SIGTER")) { + openVpnStateSigTermHandler(); + continue; + } + } + + QByteArray data(line.toStdString().c_str()); + if (data.contains(">BYTECOUNT:")) { + int beg = data.lastIndexOf(">BYTECOUNT:"); + int end = data.indexOf("\n", beg); + + beg += sizeof(">BYTECOUNT:") - 1; + QList count = data.mid(beg, end - beg + 1).split(','); + + quint64 r = static_cast(count.at(0).trimmed().toULongLong()); + quint64 s = static_cast(count.at(1).trimmed().toULongLong()); + + setBytesChanged(r, s); + } + } +} + +void OpenVpnProtocol::onOpenVpnProcessFinished(int exitCode) +{ + m_openVpnStateSigTermHandlerTimer.stop(); + if (m_connectionState == VpnProtocol::ConnectionState::Disconnected) { + qDebug() << "Already in disconnected state"; + return; + } + + qDebug().noquote() << QString("Process finished with code: %1").arg(exitCode); + + setConnectionState(VpnProtocol::ConnectionState::Disconnected); +} + + diff --git a/client/openvpnprotocol.h b/client/openvpnprotocol.h new file mode 100644 index 00000000..956a3b4d --- /dev/null +++ b/client/openvpnprotocol.h @@ -0,0 +1,51 @@ +#ifndef OPENVPNPROTOCOL_H +#define OPENVPNPROTOCOL_H + +#include +#include +#include + +#include "managementserver.h" +#include "message.h" +#include "vpnprotocol.h" + +class OpenVpnProtocol : public VpnProtocol +{ + Q_OBJECT + +public: + explicit OpenVpnProtocol(const QString& args = QString(), QObject* parent = nullptr); + ~OpenVpnProtocol(); + + bool start() override; + void stop() override; + +protected slots: + void onMessageReceived(const Message& message); + void onOpenVpnProcessFinished(int exitCode); + void onReadyReadDataFromManagementServer(); + +protected: + QString configPath() const; + QString openVpnExecPath() const; + bool openVpnProcessIsRunning() const; + bool sendTermSignal(); + bool setConfigFile(const QString& configFileNamePath); + void disconnectFromManagementServer(); + void killOpenVpnProcess(); + void openVpnStateSigTermHandler(); + void openVpnStateSigTermHandlerTimerEvent(); + void sendByteCount(); + void sendInitialData(); + void writeCommand(const QString& command); + + const QString m_managementHost = "127.0.0.1"; + const unsigned int m_managementPort = 57775; + + ManagementServer m_managementServer; + QString m_configFileName; + QTimer m_openVpnStateSigTermHandlerTimer; + bool m_requestFromUserToStop; +}; + +#endif // OPENVPNPROTOCOL_H diff --git a/client/resources.qrc b/client/resources.qrc index 851b4007..acc656f5 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -1,4 +1,8 @@ + + translations/amneziavpn_ru.qm + + images/close.png images/settings.png diff --git a/client/translations/amneziavpn_ru.qm b/client/translations/amneziavpn_ru.qm new file mode 100644 index 00000000..66b197e1 Binary files /dev/null and b/client/translations/amneziavpn_ru.qm differ diff --git a/client/translations/amneziavpn_ru.ts b/client/translations/amneziavpn_ru.ts new file mode 100644 index 00000000..b0a85cbd --- /dev/null +++ b/client/translations/amneziavpn_ru.ts @@ -0,0 +1,197 @@ + + + + + MainWindow + + + Подключиться к уже + созданному серверу VPN + + + + + Код для подключения + + + + + + Подключение... + + + + + + Подключиться + + + + + Настроить собственный сервер + + + + + Подключите ваш сервер, + чтобы использовать VPN + + + + + IP-адрес сервера + + + + + Логин для подключения по SSH + + + + + Пароль + + + + + 51.83.180.158 + + + + + root + + + + + qazqazwsxwsx + + + + + Где взять данные для подключения → + + + + + + 0 Mbps + + + + + Add site + Добавить сайт + + + + Connected + Подключено + + + + How to use VPN + Как использовать VPN + + + + For all connections + Для всех соединений + + + + For selected sites + Для выбранных сайтов + + + + List of most popular prohibited sites + + + + + Эти сайты будут открываться через VPN + + + + + Например, rutor.org или 17.21.111.8 + + + + + + + + + + + + Удалить выбранный + + + + + Адрес сайта или ip-адрес + + + + + Server settings + Настройки сервера + + + + Share connection + Поделиться подключением + + + + Connection string + Строка подключения + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Consolas'; font-size:22px; font-weight:600; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:20pt;">vpn:\\aosdiufhafafsuhgqejghuserhglaidhgauhgalgadg</span></p></body></html> + + + + + Copy + Копировать + + + + Тот, кто зайдёт с этим кодом, будет иметь те же права на испольтование VPN, что и вы. Чтобы создать новый код смените логин и/или пароль для подлючения в настройках вашего сервера. + + + + + Cannot open logs folder! + Невозможно открыть папку с логами! + + + + QObject + + + Notify + Уведомление + + + + AmneziaVPN is already running. + Приложение AmneziaVPN уже запущено. + + + + VpnConnection + + + Mbps + + + + diff --git a/client/ui/mainwindow.cpp b/client/ui/mainwindow.cpp index 4ce6bdf5..a11b8b59 100644 --- a/client/ui/mainwindow.cpp +++ b/client/ui/mainwindow.cpp @@ -1,12 +1,20 @@ #include #include +#include +#include +#include "communicator.h" #include "debug.h" #include "defines.h" #include "mainwindow.h" #include "ui_mainwindow.h" +#include "utils.h" +#include "vpnconnection.h" -MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow), + m_vpnConnection(nullptr) { ui->setupUi(this); @@ -15,7 +23,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi ui->stackedWidget_main->setCurrentIndex(2); connect(ui->pushButton_blocked_list, SIGNAL(clicked(bool)), this, SLOT(onPushButtonBlockedListClicked(bool))); - connect(ui->pushButton_connect, SIGNAL(clicked(bool)), this, SLOT(onPushButtonConnectClicked(bool))); + connect(ui->pushButton_connect, SIGNAL(toggled(bool)), this, SLOT(onPushButtonConnectToggled(bool))); connect(ui->pushButton_settings, SIGNAL(clicked(bool)), this, SLOT(onPushButtonSettingsClicked(bool))); connect(ui->pushButton_back_from_sites, SIGNAL(clicked(bool)), this, SLOT(onPushButtonBackFromSitesClicked(bool))); @@ -23,14 +31,37 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi setFixedSize(width(),height()); - qDebug() << APPLICATION_NAME; - qDebug() << "Started"; + qInfo().noquote() << QString("Started %1 version %2").arg(APPLICATION_NAME).arg(APP_VERSION); + qInfo().noquote() << QString("%1 (%2)").arg(QSysInfo::prettyProductName()).arg(QSysInfo::currentCpuArchitecture()); + + + QDir dir; + QString configPath = Utils::systemConfigPath(); + if (!dir.mkpath(configPath)) { + qWarning() << "Cannot initialize config path:" << configPath; + } + + m_vpnConnection = new VpnConnection; + connect(m_vpnConnection, SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); + connect(m_vpnConnection, SIGNAL(connectionStateChanged(VpnProtocol::ConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::ConnectionState))); + + onConnectionStateChanged(VpnProtocol::ConnectionState::Disconnected); } MainWindow::~MainWindow() { delete ui; + hide(); + + m_vpnConnection->disconnectFromVpn(); + for (int i = 0; i < 50; i++) { + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + QThread::msleep(100); + if (m_vpnConnection->disconnected()) { + break; + } + } qDebug() << "Closed"; } @@ -42,16 +73,22 @@ void MainWindow::goToIndex(int index) void MainWindow::keyPressEvent(QKeyEvent *event) { switch (event->key()) { - case Qt::Key_L: - if (!Debug::openLogsFolder()) { - QMessageBox::warning(this, APPLICATION_NAME, tr("Cannot open logs folder!")); - } + case Qt::Key_L: + if (!Debug::openLogsFolder()) { + QMessageBox::warning(this, APPLICATION_NAME, tr("Cannot open logs folder!")); + } break; - default: + default: ; } } +void MainWindow::onBytesChanged(quint64 receivedData, quint64 sentData) +{ + ui->label_speed_received->setText(VpnConnection::bytesToText(receivedData)); + ui->label_speed_sent->setText(VpnConnection::bytesToText(sentData)); +} + void MainWindow::onPushButtonBackFromSettingsClicked(bool) { goToIndex(2); @@ -72,9 +109,52 @@ void MainWindow::onPushButtonSettingsClicked(bool) goToIndex(4); } -void MainWindow::onPushButtonConnectClicked(bool) +void MainWindow::onConnectionStateChanged(VpnProtocol::ConnectionState state) { - qDebug() << "onPushButtonConnectClicked"; + bool pushButtonConnectEnabled = false; + ui->label_state->setText(VpnProtocol::textConnectionState(state)); + + switch (state) { + case VpnProtocol::ConnectionState::Disconnected: + onBytesChanged(0,0); + ui->pushButton_connect->setChecked(false); + pushButtonConnectEnabled = true; + break; + case VpnProtocol::ConnectionState::Preparing: + pushButtonConnectEnabled = false; + break; + case VpnProtocol::ConnectionState::Connecting: + pushButtonConnectEnabled = false; + break; + case VpnProtocol::ConnectionState::Connected: + pushButtonConnectEnabled = true; + break; + case VpnProtocol::ConnectionState::Disconnecting: + pushButtonConnectEnabled = false; + break; + case VpnProtocol::ConnectionState::TunnelReconnecting: + pushButtonConnectEnabled = false; + break; + case VpnProtocol::ConnectionState::Error: + pushButtonConnectEnabled = true; + break; + case VpnProtocol::ConnectionState::Unknown: + default: + pushButtonConnectEnabled = true; + ; + } + + ui->pushButton_connect->setEnabled(pushButtonConnectEnabled); +} + +void MainWindow::onPushButtonConnectToggled(bool checked) +{ + if (checked) { + m_vpnConnection->connectToVpn(); + ui->pushButton_connect->setEnabled(false); + } else { + m_vpnConnection->disconnectFromVpn(); + } } diff --git a/client/ui/mainwindow.h b/client/ui/mainwindow.h index 7d247d42..369b6ff9 100644 --- a/client/ui/mainwindow.h +++ b/client/ui/mainwindow.h @@ -3,6 +3,10 @@ #include +#include "vpnprotocol.h" + +class VpnConnection; + namespace Ui { class MainWindow; } @@ -23,12 +27,15 @@ public slots: private slots: void onPushButtonBlockedListClicked(bool clicked); - void onPushButtonConnectClicked(bool clicked); + void onPushButtonConnectToggled(bool checked); void onPushButtonSettingsClicked(bool clicked); void onPushButtonBackFromSettingsClicked(bool clicked); void onPushButtonBackFromSitesClicked(bool clicked); + void onBytesChanged(quint64 receivedBytes, quint64 sentBytes); + void onConnectionStateChanged(VpnProtocol::ConnectionState state); + protected: void keyPressEvent(QKeyEvent* event); @@ -36,6 +43,7 @@ private: void goToIndex(int index); Ui::MainWindow *ui; + VpnConnection* m_vpnConnection; }; #endif // MAINWINDOW_H diff --git a/client/ui/mainwindow.ui b/client/ui/mainwindow.ui index 2990f1e1..d2a39b91 100644 --- a/client/ui/mainwindow.ui +++ b/client/ui/mainwindow.ui @@ -861,7 +861,7 @@ QPushButton:hover { - + 260 @@ -890,7 +890,7 @@ font: 63 12pt "Lato"; Qt::AlignCenter - + 0 @@ -951,7 +951,7 @@ line-height: 21px; - Добавить сайт + Add site @@ -1001,7 +1001,7 @@ image: url(:/images/connect_button_connected.png); false - + 0 @@ -1021,7 +1021,7 @@ color: #181922; - Подключено + Connected Qt::AlignCenter @@ -1043,7 +1043,7 @@ color: #181922; - + 20 @@ -1064,7 +1064,7 @@ color: #181922; - Как использовать VPN + How to use VPN Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter @@ -1083,7 +1083,7 @@ color: #181922; - Для всех соеднинений + For all connections @@ -1099,7 +1099,7 @@ color: #181922; - Для определённых сайтов + For selected sites true diff --git a/client/utils.cpp b/client/utils.cpp new file mode 100644 index 00000000..21bc6f6b --- /dev/null +++ b/client/utils.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +#include "defines.h" +#include "utils.h" + +QString Utils::toString(bool value) +{ + return value ? "true" : "false"; +} + +QString Utils::systemLogPath() +{ + return systemDataLocationPath() + "/log"; +} + +QString Utils::systemConfigPath() +{ + return systemDataLocationPath() + "/config"; +} + +bool Utils::createEmptyFile(const QString& path) +{ + QFile f(path); + return f.open(QIODevice::WriteOnly | QIODevice::Truncate); +} + +QString Utils::systemDataLocationPath() +{ + QStringList locationList = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); + QString primaryLocation; +#ifdef Q_OS_WIN + primaryLocation = "ProgramData"; +#elif defined Q_OS_MAC + primaryLocation = "Users"; +#endif + + foreach (const QString& location, locationList) { + if (location.contains(primaryLocation)) { + return QString("%1/%2").arg(location).arg(APPLICATION_NAME); + } + } + + return QString(); +} + +QString Utils::executable(const QString& baseName, bool absPath) +{ + QString ext; +#ifdef Q_OS_WIN + ext = ".exe"; +#endif + const QString fileName = baseName + ext; + if (!absPath) { + return fileName; + } + return QCoreApplication::applicationDirPath() + "/" + fileName; +} + +bool Utils::processIsRunning(const QString& fileName) +{ +#ifdef Q_OS_WIN + QProcess process; + process.setReadChannel(QProcess::StandardOutput); + process.setReadChannelMode(QProcess::MergedChannels); + process.start(QString("wmic.exe /OUTPUT:STDOUT PROCESS get %1").arg("Caption")); + process.waitForStarted(); + process.waitForFinished(); + QString processData(process.readAll()); + QStringList processList = processData.split(QRegExp("[\r\n]"),QString::SkipEmptyParts); + foreach (const QString& rawLine, processList) { + const QString line = rawLine.simplified(); + if (line.isEmpty()) { + continue; + } + + if (line == fileName) { + return true; + } + + } + return false; +#else + QProcess process; + process.setProcessChannelMode(QProcess::MergedChannels); + process.start("pgrep", QStringList({fileName})); + process.waitForFinished(); + if (process.exitStatus() == QProcess::NormalExit) { + return (process.readAll().toUInt() > 0); + } + return false; +#endif +} + diff --git a/client/utils.h b/client/utils.h new file mode 100644 index 00000000..d5c05dd2 --- /dev/null +++ b/client/utils.h @@ -0,0 +1,18 @@ +#ifndef UTILS_H +#define UTILS_H + +#include + +class Utils { + +public: + static QString executable(const QString& baseName, bool absPath); + static QString systemConfigPath(); + static QString systemDataLocationPath(); + static QString systemLogPath(); + static QString toString(bool value); + static bool createEmptyFile(const QString& path); + static bool processIsRunning(const QString& fileName); +}; + +#endif // UTILS_H diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp new file mode 100644 index 00000000..8b3b08ce --- /dev/null +++ b/client/vpnconnection.cpp @@ -0,0 +1,78 @@ +#include + +#include "openvpnprotocol.h" +#include "vpnconnection.h" + +VpnConnection::VpnConnection(QObject* parent) : QObject(parent) +{ + +} + +VpnConnection::~VpnConnection() +{ + +} + +void VpnConnection::onBytesChanged(quint64 receivedBytes, quint64 sentBytes) +{ + emit bytesChanged(receivedBytes, sentBytes); +} + +void VpnConnection::onConnectionStateChanged(VpnProtocol::ConnectionState state) +{ + emit connectionStateChanged(state); +} + +void VpnConnection::connectToVpn(Protocol protocol) +{ + qDebug() << "Connect to VPN"; + + switch (protocol) { + case Protocol::OpenVpn: + m_vpnProtocol.reset(new OpenVpnProtocol()); + break; + ; + default: + // TODO, add later + return; + ; + } + + connect(m_vpnProtocol.data(), SIGNAL(connectionStateChanged(VpnProtocol::ConnectionState)), this, SLOT(onConnectionStateChanged(VpnProtocol::ConnectionState))); + connect(m_vpnProtocol.data(), SIGNAL(bytesChanged(quint64, quint64)), this, SLOT(onBytesChanged(quint64, quint64))); + + m_vpnProtocol.data()->start(); +} + +QString VpnConnection::bytesToText(quint64 bytes) +{ + return QString("%1 %2").arg((bytes * 8) / 1024).arg(tr("Mbps")); +} + +void VpnConnection::disconnectFromVpn() +{ + qDebug() << "Disconnect from VPN"; + + if (!m_vpnProtocol.data()) { + return; + } + m_vpnProtocol.data()->stop(); +} + +bool VpnConnection::connected() const +{ + if (!m_vpnProtocol.data()) { + return false; + } + + return m_vpnProtocol.data()->connected(); +} + +bool VpnConnection::disconnected() const +{ + if (!m_vpnProtocol.data()) { + return true; + } + + return m_vpnProtocol.data()->disconnected(); +} diff --git a/client/vpnconnection.h b/client/vpnconnection.h new file mode 100644 index 00000000..5f1423dc --- /dev/null +++ b/client/vpnconnection.h @@ -0,0 +1,40 @@ +#ifndef VPNCONNECTION_H +#define VPNCONNECTION_H + +#include +#include +#include + +#include "vpnprotocol.h" + +class VpnConnection : public QObject +{ + Q_OBJECT + +public: + explicit VpnConnection(QObject* parent = nullptr); + ~VpnConnection(); + + enum class Protocol{OpenVpn}; + void connectToVpn(Protocol protocol = Protocol::OpenVpn); + + bool connected() const; + bool disconnected() const; + void disconnectFromVpn(); + + static QString bytesToText(quint64 bytes); + +signals: + void bytesChanged(quint64 receivedBytes, quint64 sentBytes); + void connectionStateChanged(VpnProtocol::ConnectionState state); + +protected slots: + void onBytesChanged(quint64 receivedBytes, quint64 sentBytes); + void onConnectionStateChanged(VpnProtocol::ConnectionState state); + +protected: + + QScopedPointer m_vpnProtocol; +}; + +#endif // VPNCONNECTION_H diff --git a/client/vpnprotocol.cpp b/client/vpnprotocol.cpp new file mode 100644 index 00000000..2ac462f3 --- /dev/null +++ b/client/vpnprotocol.cpp @@ -0,0 +1,93 @@ +#include +#include + +#include "communicator.h" +#include "vpnprotocol.h" + +VpnProtocol::VpnProtocol(const QString& args, QObject* parent) + : QObject(parent), + m_connectionState(ConnectionState::Unknown), + m_communicator(new Communicator), + m_timeoutTimer(new QTimer(this)) +{ + m_timeoutTimer->setSingleShot(true); + connect(m_timeoutTimer, &QTimer::timeout, this, &VpnProtocol::onTimeout); + + Q_UNUSED(args) +} + +VpnProtocol::~VpnProtocol() +{ + +} + +void VpnProtocol::onTimeout() +{ + qDebug() << "Timeout"; + + emit timeoutTimerEvent(); + stop(); +} + +void VpnProtocol::startTimeoutTimer() +{ + m_timeoutTimer->start(30000); +} + +void VpnProtocol::stopTimeoutTimer() +{ + m_timeoutTimer->stop(); +} + +VpnProtocol::ConnectionState VpnProtocol::connectionState() const +{ + return m_connectionState; +} + +void VpnProtocol::setBytesChanged(quint64 receivedBytes, quint64 sentBytes) +{ + emit bytesChanged(receivedBytes, sentBytes); +} + +void VpnProtocol::setConnectionState(VpnProtocol::ConnectionState state) +{ + if (m_connectionState == state) { + return; + } + + m_connectionState = state; + emit connectionStateChanged(m_connectionState); +} + +QString VpnProtocol::textConnectionState(ConnectionState connectionState) +{ + switch (connectionState) { + case ConnectionState::Unknown: return "Unknown"; + case ConnectionState::Disconnected: return "Disconnected"; + case ConnectionState::Preparing: return "Preparing"; + case ConnectionState::Connecting: return "Connecting"; + case ConnectionState::Connected: return "Connected"; + case ConnectionState::Disconnecting: return "Disconnecting"; + case ConnectionState::TunnelReconnecting: return "TunnelReconnecting"; + case ConnectionState::Error: return "Error"; + default: + ; + } + + return QString(); +} + +QString VpnProtocol::textConnectionState() const +{ + return textConnectionState(m_connectionState); +} + +bool VpnProtocol::connected() const +{ + return m_connectionState == ConnectionState::Connected; +} + +bool VpnProtocol::disconnected() const +{ + return m_connectionState == ConnectionState::Disconnected; +} diff --git a/client/vpnprotocol.h b/client/vpnprotocol.h new file mode 100644 index 00000000..39adb8c9 --- /dev/null +++ b/client/vpnprotocol.h @@ -0,0 +1,48 @@ +#ifndef VPNPROTOCOL_H +#define VPNPROTOCOL_H + +#include +#include + +class QTimer; +class Communicator; + +class VpnProtocol : public QObject +{ + Q_OBJECT + +public: + explicit VpnProtocol(const QString& args = QString(), QObject* parent = nullptr); + ~VpnProtocol(); + + enum class ConnectionState {Unknown, Disconnected, Preparing, Connecting, Connected, Disconnecting, TunnelReconnecting, Error}; + static QString textConnectionState(ConnectionState connectionState); + + ConnectionState connectionState() const; + QString textConnectionState() const; + virtual bool connected() const; + virtual bool disconnected() const; + virtual bool start() = 0; + virtual void stop() = 0; + +signals: + void bytesChanged(quint64 receivedBytes, quint64 sentBytes); + void connectionStateChanged(VpnProtocol::ConnectionState state); + void timeoutTimerEvent(); + +protected slots: + virtual void onTimeout(); + +protected: + void startTimeoutTimer(); + void stopTimeoutTimer(); + + virtual void setBytesChanged(quint64 receivedBytes, quint64 sentBytes); + virtual void setConnectionState(VpnProtocol::ConnectionState state); + + Communicator* m_communicator; + ConnectionState m_connectionState; + QTimer* m_timeoutTimer; +}; + +#endif // VPNPROTOCOL_H diff --git a/deploy/data/windows/openvpn/libcrypto-1_1.dll b/deploy/data/windows/openvpn/i386/libcrypto-1_1.dll similarity index 100% rename from deploy/data/windows/openvpn/libcrypto-1_1.dll rename to deploy/data/windows/openvpn/i386/libcrypto-1_1.dll diff --git a/deploy/data/windows/openvpn/liblzo2-2.dll b/deploy/data/windows/openvpn/i386/liblzo2-2.dll similarity index 100% rename from deploy/data/windows/openvpn/liblzo2-2.dll rename to deploy/data/windows/openvpn/i386/liblzo2-2.dll diff --git a/deploy/data/windows/openvpn/libpkcs11-helper-1.dll b/deploy/data/windows/openvpn/i386/libpkcs11-helper-1.dll similarity index 100% rename from deploy/data/windows/openvpn/libpkcs11-helper-1.dll rename to deploy/data/windows/openvpn/i386/libpkcs11-helper-1.dll diff --git a/deploy/data/windows/openvpn/libssl-1_1.dll b/deploy/data/windows/openvpn/i386/libssl-1_1.dll similarity index 100% rename from deploy/data/windows/openvpn/libssl-1_1.dll rename to deploy/data/windows/openvpn/i386/libssl-1_1.dll diff --git a/deploy/data/windows/openvpn/i386/license.txt b/deploy/data/windows/openvpn/i386/license.txt new file mode 100644 index 00000000..68661860 --- /dev/null +++ b/deploy/data/windows/openvpn/i386/license.txt @@ -0,0 +1,555 @@ +OpenVPN (TM) -- An Open Source VPN daemon + +Copyright (C) 2002-2018 OpenVPN Inc + +This distribution contains multiple components, some +of which fall under different licenses. By using OpenVPN +or any of the bundled components enumerated below, you +agree to be bound by the conditions of the license for +each respective component. + +OpenVPN trademark +----------------- + + "OpenVPN" is a trademark of OpenVPN Inc + + +OpenVPN license: +---------------- + + OpenVPN is distributed under the GPL license version 2 (see Below). + + Special exception for linking OpenVPN with OpenSSL: + + In addition, as a special exception, OpenVPN Inc gives + permission to link the code of this program with the OpenSSL + library (or with modified versions of OpenSSL that use the same + license as OpenSSL), and distribute linked combinations including + the two. You must obey the GNU General Public License in all + respects for all of the code used other than OpenSSL. If you modify + this file, you may extend this exception to your version of the + file, but you are not obligated to do so. If you do not wish to + do so, delete this exception statement from your version. + +LZO license: +------------ + + LZO is Copyright (C) Markus F.X.J. Oberhumer, + and is licensed under the GPL. + + Special exception for linking OpenVPN with both OpenSSL and LZO: + + Hereby I grant a special exception to the OpenVPN project + (http://openvpn.net/) to link the LZO library with + the OpenSSL library (http://www.openssl.org). + + Markus F.X.J. Oberhumer + +TAP-Win32/TAP-Win64 Driver license: +----------------------------------- + + This device driver was inspired by the CIPE-Win32 driver by + Damion K. Wilson. + + The source and object code of the TAP-Win32/TAP-Win64 driver + is Copyright (C) 2002-2018 OpenVPN Inc, and is released under + the GPL version 2. + +Windows DDK Samples: +-------------------- + + The Windows binary distribution includes devcon.exe, a + Microsoft DDK sample which is redistributed under the terms + of the DDK EULA. + +NSIS License: +------------- + + Copyright (C) 2002-2003 Joost Verburg + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute + it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; + you must not claim that you wrote the original software. + If you use this software in a product, an acknowledgment in the + product documentation would be appreciated but is not required. + 2. Altered versions must be plainly marked as such, + and must not be misrepresented as being the original software. + 3. This notice may not be removed or altered from any distribution. + +OpenSSL License: +---------------- + + The OpenSSL toolkit stays under a dual license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. Actually both licenses are BSD-style + Open Source licenses. In case of any license issues related to OpenSSL + please contact openssl-core@openssl.org. + +/* ==================================================================== + * Copyright (c) 1998-2003 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +GNU Public License (GPL) +------------------------ + + OpenVPN, LZO, and the TAP-Win32 distributions are + licensed under the GPL version 2 (see COPYRIGHT.GPL). + + In the Windows binary distribution of OpenVPN, the + GPL is reproduced below. + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/deploy/data/windows/openvpn/openvpn.exe b/deploy/data/windows/openvpn/i386/openvpn.exe similarity index 100% rename from deploy/data/windows/openvpn/openvpn.exe rename to deploy/data/windows/openvpn/i386/openvpn.exe diff --git a/deploy/data/windows/tap/x32/OemVista.inf b/deploy/data/windows/tap/i386/OemVista.inf similarity index 100% rename from deploy/data/windows/tap/x32/OemVista.inf rename to deploy/data/windows/tap/i386/OemVista.inf diff --git a/deploy/data/windows/tap/x32/tap0901.cat b/deploy/data/windows/tap/i386/tap0901.cat similarity index 100% rename from deploy/data/windows/tap/x32/tap0901.cat rename to deploy/data/windows/tap/i386/tap0901.cat diff --git a/deploy/data/windows/tap/x32/tap0901.sys b/deploy/data/windows/tap/i386/tap0901.sys similarity index 100% rename from deploy/data/windows/tap/x32/tap0901.sys rename to deploy/data/windows/tap/i386/tap0901.sys diff --git a/deploy/data/windows/tap/i386/tapinstall.exe b/deploy/data/windows/tap/i386/tapinstall.exe new file mode 100644 index 00000000..6ffd2eb4 Binary files /dev/null and b/deploy/data/windows/tap/i386/tapinstall.exe differ diff --git a/deploy/data/windows/tap/x64/OemVista.inf b/deploy/data/windows/tap/x86_64/OemVista.inf similarity index 100% rename from deploy/data/windows/tap/x64/OemVista.inf rename to deploy/data/windows/tap/x86_64/OemVista.inf diff --git a/deploy/data/windows/tap/x64/tap0901.cat b/deploy/data/windows/tap/x86_64/tap0901.cat similarity index 100% rename from deploy/data/windows/tap/x64/tap0901.cat rename to deploy/data/windows/tap/x86_64/tap0901.cat diff --git a/deploy/data/windows/tap/x64/tap0901.sys b/deploy/data/windows/tap/x86_64/tap0901.sys similarity index 100% rename from deploy/data/windows/tap/x64/tap0901.sys rename to deploy/data/windows/tap/x86_64/tap0901.sys diff --git a/deploy/data/windows/tap/x86_64/tapinstall.exe b/deploy/data/windows/tap/x86_64/tapinstall.exe new file mode 100644 index 00000000..6e26987f Binary files /dev/null and b/deploy/data/windows/tap/x86_64/tapinstall.exe differ diff --git a/service/server/localserver.cpp b/service/server/localserver.cpp new file mode 100644 index 00000000..33274ccc --- /dev/null +++ b/service/server/localserver.cpp @@ -0,0 +1,168 @@ +#include +#include +#include +#include + +#include "localserver.h" +#include "utils.h" + +LocalServer::LocalServer(const QString& name, QObject *parent) : QObject(parent), + m_clientConnected(false), + m_clientConnection(nullptr) +{ + m_server = new QLocalServer(this); + m_server->setSocketOptions(QLocalServer::WorldAccessOption); + + if (!m_server->listen(name)) { + qDebug() << QString("Unable to start the server: %1.").arg(m_server->errorString()); + return; + } + + connect(m_server, &QLocalServer::newConnection, this, &LocalServer::onNewConnection); + + qDebug() << "Local server started"; +} + +LocalServer::~LocalServer() +{ + m_clientConnected = false; + m_server->disconnect(); + + qDebug() << "Local server stopped"; +} + +bool LocalServer::isRunning() const +{ + return m_server->isListening(); +} + +void LocalServer::onNewConnection() +{ + if (m_clientConnection) { + m_clientConnection->deleteLater(); + } + + m_clientConnection = m_server->nextPendingConnection(); + connect(m_clientConnection, &QLocalSocket::disconnected, this, &LocalServer::onDisconnected); + m_clientConnected = true; + + qDebug() << "On new connection"; + + for(;;) { + qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + if (!m_clientConnected || !m_clientConnection) { + break; + } + + if (m_clientConnection->waitForReadyRead() && m_clientConnection->canReadLine()) { + char buf[1024]; + qint64 lineLength = m_clientConnection->readLine(buf, sizeof(buf)); + if (lineLength != -1) { + QString line = buf; + line = line.simplified(); + qDebug().noquote() << QString("Readed line: '%1'").arg(line); + Message icomingMessage(line); + if (!icomingMessage.isValid()) { + qWarning().noquote() << "Message is not valid!"; + continue; + } + + switch (icomingMessage.state()) { + case Message::State::Initialize: + sendMessage(Message(Message::State::Initialize, QStringList({"Pong"}))); + break; + case Message::State::StartRequest: + startProcess(icomingMessage.args()); + break; + case Message::State::FinishRequest: + finishProcess(icomingMessage.args()); + break; + default: + ; + } + } + } + } + + qDebug() << "Released"; +} + +void LocalServer::finishProcess(const QStringList& args) +{ + Q_UNUSED(args) +} + +void LocalServer::startProcess(const QStringList& messageArgs) +{ + if (messageArgs.size() < 1) { + return; + } + + QProcess* process = new QProcess(); + connect(process, SIGNAL(started()), this, SLOT(onStarted())); + connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onFinished(int, QProcess::ExitStatus))); + + const QString program = messageArgs.at(0); + QStringList args; + for (int i = 1; i < messageArgs.size(); i++) { + args.append(messageArgs.at(i)); + } + + QFileInfo fi(program); + const QString baseName = fi.baseName(); + if (!fi.exists()) { + qWarning() << "This program does not exist"; + sendMessage(Message(Message::State::Started, QStringList({baseName}))); + sendMessage(Message(Message::State::Finished, QStringList({baseName, QString::number(-1)}))); + return; + } + + process->setObjectName(baseName); + + qDebug().noquote() << QString("Start process '%1' - '%2' with args '%3'") + .arg(baseName).arg(program).arg(args.join(",")); + + process->start(program, args); + m_processList.append(process); +} + +void LocalServer::onFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + Q_UNUSED(exitStatus) + + QProcess* process = (QProcess*)sender(); + sendMessage(Message(Message::State::Finished, QStringList({process->objectName(), QString::number(exitCode)}))); +} + +void LocalServer::onStarted() +{ + QProcess* process = (QProcess*)sender(); + sendMessage(Message(Message::State::Started, QStringList({process->objectName()}))); +} + +void LocalServer::onDisconnected() +{ + if (!m_clientConnected) { + return; + } + + m_clientConnected = false; + QLocalSocket* clientConnection = (QLocalSocket*)sender(); + clientConnection->deleteLater(); + + qDebug() << "Diconnected"; +} + +void LocalServer::sendMessage(const Message& message) +{ + if (!m_clientConnection || !m_clientConnected) { + qDebug()<< "Cannot send data, remote peer is not connected"; + return; + } + + const QString data = message.toString(); + bool status = m_clientConnection->write(QString(data + "\n").toLocal8Bit()); + + qDebug().noquote() << QString("Send message '%1', status '%2'").arg(data).arg(Utils::toString(status)); +} + diff --git a/service/server/localserver.h b/service/server/localserver.h new file mode 100644 index 00000000..9a929bc0 --- /dev/null +++ b/service/server/localserver.h @@ -0,0 +1,43 @@ +#ifndef LOCALSERVER_H +#define LOCALSERVER_H + +#include +#include +#include +#include + +#include "message.h" + +class QLocalServer; +class QLocalSocket; +class QProcess; + +class LocalServer : public QObject +{ + Q_OBJECT + +public: + explicit LocalServer(const QString& name, QObject* parent = nullptr); + ~LocalServer(); + + bool isRunning() const; + +protected slots: + void onDisconnected(); + void onNewConnection(); + + void onFinished(int exitCode, QProcess::ExitStatus exitStatus); + void onStarted(); + +private: + void finishProcess(const QStringList& messageArgs); + void sendMessage(const Message& message); + void startProcess(const QStringList& messageArgs); + + QLocalServer* m_server; + QLocalSocket* m_clientConnection; + QVector m_processList; + bool m_clientConnected; +}; + +#endif // LOCALSERVER_H diff --git a/service/server/log.cpp b/service/server/log.cpp new file mode 100644 index 00000000..9d0d3a72 --- /dev/null +++ b/service/server/log.cpp @@ -0,0 +1,53 @@ +#include +#include + +#include + +#include "log.h" +#include "defines.h" +#include "utils.h" + +QFile Log::m_file; +QTextStream Log::m_textStream; +QString Log::m_logFileName; + +void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) +{ + if (msg.simplified().isEmpty()) { + return; + } + + Log::m_textStream << qFormatLogMessage(type, context, msg) << endl << flush; + + std::cout << qFormatLogMessage(type, context, msg).toStdString() << std::endl << std::flush; +} + +bool Log::initialize() +{ + QString path = Utils::systemLogPath(); + QDir appDir(path); + if (!appDir.mkpath(path)) { + return false; + } + + m_logFileName = QString("%1.log").arg(SERVICE_NAME); + + qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} %{type} %{message}"); + + m_file.setFileName(appDir.filePath(m_logFileName)); + if (!m_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + qWarning() << "Cannot open log file:" << m_logFileName; + return false; + } + m_file.setTextModeEnabled(true); + m_textStream.setDevice(&m_file); + qInstallMessageHandler(debugMessageHandler); + + return true; +} + +QString Log::serviceLogFileNamePath() +{ + return m_file.fileName(); +} + diff --git a/service/server/log.h b/service/server/log.h new file mode 100644 index 00000000..1c385f7a --- /dev/null +++ b/service/server/log.h @@ -0,0 +1,23 @@ +#ifndef LOG_H +#define LOG_H + +#include +#include +#include +#include + +class Log +{ +public: + static bool initialize(); + static QString serviceLogFileNamePath(); + +private: + friend void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg); + + static QFile m_file; + static QString m_logFileName; + static QTextStream m_textStream; +}; + +#endif // LOG_H diff --git a/service/server/main.cpp b/service/server/main.cpp index 480c80a9..7cadeda9 100644 --- a/service/server/main.cpp +++ b/service/server/main.cpp @@ -1,7 +1,10 @@ #include #include -#include "server.h" +#include "systemservice.h" +#include "log.h" +#include "defines.h" +#include "localserver.h" int main(int argc, char **argv) { @@ -11,6 +14,20 @@ int main(int argc, char **argv) QSettings::setPath(QSettings::NativeFormat, QSettings::SystemScope, QDir::tempPath()); qWarning("(Example uses dummy settings file: %s/QtSoftware.conf)", QDir::tempPath().toLatin1().constData()); #endif - HttpService service(argc, argv); - return service.exec(); + + Log::initialize(); + + if (argc == 2) { + qInfo() << "Started as console application"; + QCoreApplication app(argc,argv); + LocalServer localServer(SERVICE_NAME); + if (!localServer.isRunning()) { + return -1; + } + return app.exec(); + } else { + qInfo() << "Started as system service"; + SystemService systemService(argc, argv); + return systemService.exec(); + } } diff --git a/service/server/server.cpp b/service/server/server.cpp deleted file mode 100644 index 6b1ff170..00000000 --- a/service/server/server.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include -#include -#include - -#include "server.h" - -HttpDaemon::HttpDaemon(quint16 port, QObject* parent) - : QTcpServer(parent), disabled(false) -{ - listen(QHostAddress::Any, port); - qDebug() << "Listen on port: " << port; - - connect(this, &QTcpServer::newConnection, this, &HttpDaemon::sendFortune); -} - -void HttpDaemon::sendFortune() -{ - qDebug() << "New connection: "; - - QTcpSocket *clientConnection = this->nextPendingConnection(); - connect(clientConnection, &QAbstractSocket::disconnected, - clientConnection, &QObject::deleteLater); - - - connect(clientConnection, SIGNAL(readyRead()), this, SLOT(readClient())); - connect(clientConnection, SIGNAL(disconnected()), this, SLOT(discardClient())); - //->setSocketDescriptor(socket); -} - -void HttpDaemon::pause() -{ - disabled = true; -} - -void HttpDaemon::resume() -{ - disabled = false; -} - -void HttpDaemon::readClient() -{ - qDebug() << "readClient"; - - // if (disabled) - // return; - // - // This slot is called when the client sent data to the server. The - // server looks if it was a get request and sends a very simple HTML - // document back. - QTcpSocket* socket = (QTcpSocket*)sender(); - if (socket->canReadLine()) { - QStringList tokens = QString(socket->readLine()).split(QRegExp("[ \r\n][ \r\n]*")); - if (tokens[0] == "GET") { - QTextStream os(socket); - os.setAutoDetectUnicode(true); - os << "HTTP/1.0 200 Ok\r\n" - "Content-Type: text/html; charset=\"utf-8\"\r\n" - "\r\n" - "

Nothing to see here

\n" - << QDateTime::currentDateTime().toString() << "\n"; - socket->close(); - - QtServiceBase::instance()->logMessage("Wrote to client"); - - if (socket->state() == QTcpSocket::UnconnectedState) { - delete socket; - QtServiceBase::instance()->logMessage("Connection closed"); - } - } - } -} - -void HttpDaemon::discardClient() -{ - QTcpSocket* socket = (QTcpSocket*)sender(); - socket->deleteLater(); - - QtServiceBase::instance()->logMessage("Connection closed"); -} - - - - - - -HttpService::HttpService(int argc, char **argv) - : QtService(argc, argv, "Qt HTTP Daemon") -{ - setServiceDescription("A dummy HTTP service implemented with Qt"); - setServiceFlags(QtServiceBase::CanBeSuspended); -} - -void HttpService::start() -{ - QCoreApplication *app = application(); - daemon = new HttpDaemon(8989, app); - - if (!daemon->isListening()) { - logMessage(QString("Failed to bind to port %1").arg(daemon->serverPort()), QtServiceBase::Error); - app->quit(); - } -} - -void HttpService::pause() -{ - daemon->pause(); -} - -void HttpService::resume() -{ - daemon->resume(); -} diff --git a/service/server/server.h b/service/server/server.h deleted file mode 100644 index c741afb2..00000000 --- a/service/server/server.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef SERVER_H -#define SERVER_H - -#include -#include - -#include "qtservice.h" - - -class HttpDaemon : public QTcpServer -{ - Q_OBJECT -public: - HttpDaemon(quint16 port, QObject* parent = 0); - void sendFortune(); - - void pause(); - void resume(); - -private slots: - void readClient(); - void discardClient(); -private: - bool disabled; -}; - - -class HttpService : public QtService -{ -public: - HttpService(int argc, char **argv); - -protected: - void pause(); - void resume(); - void start(); - -private: - HttpDaemon *daemon; -}; - - -#endif // SERVER_H diff --git a/service/server/server.pro b/service/server/server.pro index 1e31d93d..9b35d7af 100644 --- a/service/server/server.pro +++ b/service/server/server.pro @@ -1,13 +1,22 @@ TARGET = AmneziaVPN-service TEMPLATE = app -CONFIG += console qt -QT = core network +CONFIG += console qt no_batch +QT += core network HEADERS = \ - server.h + ../../client/message.h \ + ../../client/utils.h \ + localserver.h \ + log.h \ + systemservice.h + SOURCES = \ - server.cpp \ - main.cpp + ../../client/message.cpp \ + ../../client/utils.cpp \ + localserver.cpp \ + log.cpp \ + main.cpp \ + systemservice.cpp include(../src/qtservice.pri) @@ -17,3 +26,5 @@ CONFIG(release, debug|release) { OBJECTS_DIR = $$DESTDIR RCC_DIR = $$DESTDIR } + +INCLUDEPATH += "$$PWD/../../client" diff --git a/service/server/systemservice.cpp b/service/server/systemservice.cpp new file mode 100644 index 00000000..4afe71e4 --- /dev/null +++ b/service/server/systemservice.cpp @@ -0,0 +1,24 @@ +#include "defines.h" +#include "localserver.h" +#include "systemservice.h" + +SystemService::SystemService(int argc, char **argv) + : QtService(argc, argv, SERVICE_NAME) +{ + setServiceDescription("Service for AmneziaVPN"); +} + +void SystemService::start() +{ + QCoreApplication* app = application(); + m_localServer = new LocalServer(SERVICE_NAME); + + if (!m_localServer->isRunning()) { + app->quit(); + } +} + +void SystemService::stop() +{ + delete m_localServer; +} diff --git a/service/server/systemservice.h b/service/server/systemservice.h new file mode 100644 index 00000000..26ffde69 --- /dev/null +++ b/service/server/systemservice.h @@ -0,0 +1,24 @@ +#ifndef SYSTEMSERVICE_H +#define SYSTEMSERVICE_H + +#include + +#include "qtservice.h" + +class LocalServer; + +class SystemService : public QtService +{ + +public: + SystemService(int argc, char** argv); + +protected: + void start() override; + void stop() override; + +private: + LocalServer* m_localServer; +}; + +#endif // SYSTEMSERVICE_H