amnezia-client/client/protocols/openvpnprotocol.cpp
pokamest 0569c6411e
Release 1.1 (#5)
* Crash fix in management server

* Openvpn scripts fixes
some refactoring

* deploy fix

* Scripts fix for macos

* OpenVpn runtime error codes handling

* MacOS deploy script fix

* easyrsa scripts for MacOS

* Refactoring
Ui improvements
Bug fixes

* new server page fix

* Fix some warnings, fix installation scripts (macOS)

* Fix crash on fatal error, remove moc files from Windows installation

* ss files

* Fix issue with easyrsa

* ss files

* shadowsocks impl

* ss fix

* ui fix

* Macos doc icon

* travis scripts

* server scripts fix

* icon changed

* Server scripts fix

* travis fix

* Bug fixes:
- auto install tap
- share connectionState
- service crash fix

* travis release

* macos deploy
2021-01-27 00:57:02 +03:00

320 lines
9.2 KiB
C++

#include <QCoreApplication>
#include <QFileInfo>
#include <QProcess>
#include <QRegularExpression>
#include <QTcpSocket>
#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()
{
qDebug() << "OpenVpnProtocol::stop()";
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() << "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()
{
// TODO: need refactoring
// sendTermSignal() will evet return true while server connected
if ((m_connectionState == VpnProtocol::ConnectionState::Preparing) ||
(m_connectionState == VpnProtocol::ConnectionState::Connecting) ||
(m_connectionState == VpnProtocol::ConnectionState::Connected) ||
(m_connectionState == VpnProtocol::ConnectionState::TunnelReconnecting)) {
if (!sendTermSignal()) {
killOpenVpnProcess();
}
setConnectionState(VpnProtocol::ConnectionState::Disconnecting);
}
}
void OpenVpnProtocol::killOpenVpnProcess()
{
// send command to kill openvpn process (if any).
}
bool OpenVpnProtocol::setConfigFile(const QString& configFileNamePath)
{
m_configFileName = configFileNamePath;
QFileInfo file(m_configFileName);
if (file.fileName().isEmpty()) {
m_configFileName = Utils::defaultVpnConfigFileName();
}
if (m_configFileName.isEmpty()) {
return false;
}
qDebug().noquote() << QString("Set config file: '%1'").arg(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)
{
QIODevice *device = dynamic_cast<QIODevice*>(m_managementServer.socket().data());
if (device) {
QTextStream stream(device);
stream << command << endl;
}
}
void OpenVpnProtocol::updateRouteGateway(QString line)
{
line = line.split("ROUTE_GATEWAY", QString::SkipEmptyParts).at(1);
if (!line.contains("/")) return;
m_routeGateway = line.split("/", QString::SkipEmptyParts).first();
m_routeGateway.replace(" ", "");
qDebug() << "Set VPN route gateway" << m_routeGateway;
}
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
}
ErrorCode OpenVpnProtocol::start()
{
qDebug() << "Start OpenVPN connection";
m_requestFromUserToStop = false;
m_openVpnStateSigTermHandlerTimer.stop();
OpenVpnProtocol::stop();
if (communicator() && !communicator()->isConnected()) {
setLastError(ErrorCode::AmneziaServiceConnectionFailed);
return lastError();
}
if (!QFileInfo::exists(openVpnExecPath())) {
setLastError(ErrorCode::OpenVpnExecutableMissing);
return lastError();
}
if (!QFileInfo::exists(configPath())) {
setLastError(ErrorCode::OpenVpnConfigMissing);
return lastError();
}
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)) {
setLastError(ErrorCode::OpenVpnManagementServerError);
return lastError();
}
setConnectionState(ConnectionState::Connecting);
m_communicator->sendMessage(Message(Message::State::StartRequest, args));
startTimeoutTimer();
return ErrorCode::NoError;
}
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();
updateVpnGateway();
setConnectionState(VpnProtocol::ConnectionState::Connected);
continue;
} else if (line.contains("EXITING,SIGTER")) {
openVpnStateSigTermHandler();
continue;
} else if (line.contains("RECONNECTING")) {
setConnectionState(VpnProtocol::ConnectionState::TunnelReconnecting);
continue;
}
}
if (line.contains("ROUTE_GATEWAY")) {
updateRouteGateway(line);
}
if (line.contains("FATAL")) {
if (line.contains("tap-windows6 adapters on this system are currently in use or disabled")) {
emit protocolError(ErrorCode::OpenVpnAdaptersInUseError);
}
else {
emit protocolError(ErrorCode::OpenVpnUnknownError);
}
return;
}
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<QByteArray> count = data.mid(beg, end - beg + 1).split(',');
quint64 r = static_cast<quint64>(count.at(0).trimmed().toULongLong());
quint64 s = static_cast<quint64>(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);
}
void OpenVpnProtocol::updateVpnGateway()
{
QProcess ipconfig;
ipconfig.start("ipconfig", QStringList() << "/all");
ipconfig.waitForStarted();
ipconfig.waitForFinished();
QString d = ipconfig.readAll();
d.replace("\r", "");
//qDebug().noquote() << d;
QStringList adapters = d.split(":\n");
bool isTapV9Present = false;
QString tapV9;
for (int i = 0; i < adapters.size(); ++i) {
if (adapters.at(i).contains("TAP-Windows Adapter V9")) {
isTapV9Present = true;
tapV9 = adapters.at(i);
break;
}
}
if (!isTapV9Present) {
m_vpnGateway = "";
}
QStringList lines = tapV9.split("\n");
for (int i = 0; i < lines.size(); ++i) {
if (!lines.at(i).contains("DHCP")) continue;
QRegularExpression re("(: )([\\d\\.]+)($)");
QRegularExpressionMatch match = re.match(lines.at(i));
if (match.hasMatch()) {
qDebug().noquote() << "Current VPN Gateway IP Address: " << match.captured(0);
m_vpnGateway = match.captured(2);
return;
}
else continue;
}
m_vpnGateway = "";
}