commit 73cbd921218335d3e36f92dc9f64fcab0b04b0a2 Author: Syping Date: Sun Feb 2 00:44:30 2020 +0100 VPN Status diff --git a/Ping.cpp b/Ping.cpp new file mode 100644 index 0000000..576525c --- /dev/null +++ b/Ping.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include +#include +#include "Ping.h" + +extern "C" { +#include "oping.h" +} + +double Ping::ping(const QString &host, int tries, double timeout) +{ + double latency; + pingobj_t *pingObj; + pingobj_iter_t *pingIter; + if ((pingObj = ping_construct()) == NULL) { + QTextStream(stderr) << "Ping construction failed " << endl; + return -1; + } + if (ping_setopt(pingObj, PING_OPT_TIMEOUT, (void*)(&timeout)) < 0) { + QTextStream(stderr) << "Setting timeout to" << timeout << " have failed" << endl; + ping_destroy(pingObj); + return -1; + } + QHostAddress hostAddress(host); + if (QAbstractSocket::IPv4Protocol == hostAddress.protocol()) { + if (ping_host_add(pingObj, hostAddress.toString().toStdString().c_str()) < 0) { + ping_destroy(pingObj); + return -1; + } + } + else if (QAbstractSocket::IPv6Protocol == hostAddress.protocol()) { + if (ping_host_add(pingObj, hostAddress.toString().toStdString().c_str()) < 0) { + ping_destroy(pingObj); + return -1; + } + } + else { + QList hostAddresses = QHostInfo::fromName(host).addresses(); + if (hostAddresses.length() >= 1) { + QString ipStr = hostAddresses.at(0).toString(); + if (ping_host_add(pingObj, ipStr.toStdString().c_str()) < 0) { + ping_destroy(pingObj); + return -1; + } + } + else { + ping_destroy(pingObj); + return -1; + } + } + bool hostUp = false; + int curTry = 0; + while (!hostUp && curTry != tries) { + if (ping_send(pingObj) < 0) { + QTextStream(stderr) << "Pinging host " << host << " has failed" << endl; + ping_destroy(pingObj); + return -1; + } + bool pingSuccess = false; + for (pingIter = ping_iterator_get(pingObj); pingIter != NULL; pingIter = + ping_iterator_next(pingIter)) { + size_t len; + len = sizeof(double); + ping_iterator_get_info(pingIter, PING_INFO_LATENCY, &latency, &len); + pingSuccess = !(latency < 0); + } + if (pingSuccess) { + hostUp = true; + } + curTry++; + } + ping_destroy(pingObj); + if (hostUp) + return latency; + return -1; +} diff --git a/Ping.h b/Ping.h new file mode 100644 index 0000000..f352e4f --- /dev/null +++ b/Ping.h @@ -0,0 +1,12 @@ +#ifndef PING_H +#define PING_H + +#include + +class Ping +{ +public: + static double ping(const QString &host, int tries, double timeout = 2.5); +}; + +#endif // PING_H diff --git a/Runner.cpp b/Runner.cpp new file mode 100644 index 0000000..0b9e4ff --- /dev/null +++ b/Runner.cpp @@ -0,0 +1,69 @@ +#include +#include "Runner.h" +#include "Ping.h" + +Runner::Runner(QList pingHosts) : + pingHosts_p(pingHosts) +{ +} + +void Runner::refresh() +{ + QMutexLocker locker(&mutex); + refreshed_p = QDateTime::currentDateTimeUtc().toTime_t(); +} + +uint Runner::refreshed() +{ + QMutexLocker locker(&mutex); + return refreshed_p; +} + +const QString Runner::currentHost() +{ + QMutexLocker locker(&mutex); + return currentHost_p; +} + +const QMap Runner::pingData() +{ + QMutexLocker locker(&mutex); + return pingData_p; +} + +void Runner::setCurrentHost(const QString ¤tHost) +{ + QMutexLocker locker(&mutex); + currentHost_p = currentHost; +} + +void Runner::setPingData(const QMap &pingData) +{ + QMutexLocker locker(&mutex); + pingData_p = pingData; +} + +void Runner::run() +{ + // Ping data initialisation + QMap pingData_l; + for (const QString &host : pingHosts_p) { + pingData_l.insert(host, -1); + } + setPingData(pingData_l); + refresh(); + + forever { + if (QThread::currentThread()->isInterruptionRequested()) { + return; + } + for (const QString &host : pingHosts_p) { + setCurrentHost(host); + refresh(); + double ping = Ping::ping(host, 3); + pingData_l[host] = ping; + setPingData(pingData_l); + refresh(); + } + } +} diff --git a/Runner.h b/Runner.h new file mode 100644 index 0000000..588eb1e --- /dev/null +++ b/Runner.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_H +#define RUNNER_H + +#include +#include +#include +#include +#include + +class Runner : public QThread +{ + Q_OBJECT +public: + Runner(QList pingHosts); + void refresh(); + uint refreshed(); + const QString currentHost(); + const QMap pingData(); + void setCurrentHost(const QString ¤tHost); + void setPingData(const QMap &pingData); + +private: + QMap pingData_p; + QList pingHosts_p; + QString currentHost_p; + uint refreshed_p; + mutable QMutex mutex; + +protected: + void run(); +}; + +#endif // RUNNER_H diff --git a/Socket.cpp b/Socket.cpp new file mode 100644 index 0000000..fc216d8 --- /dev/null +++ b/Socket.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include "Socket.h" + +Socket::Socket(Runner *runner) : + runner(runner) +{ +} + +void Socket::incomingConnection(quintptr socketDescriptor) +{ + QLocalSocket *socket = new QLocalSocket; + if (!socket->setSocketDescriptor(socketDescriptor, QLocalSocket::ConnectedState, QLocalSocket::WriteOnly)) { + QTextStream(stderr) << "Socket failed to initialise." << endl; + delete socket; + return; + } + + QObject::connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater())); + + QJsonObject pingObject; + QMap pingData_l = runner->pingData(); + QMap::const_iterator it = pingData_l.constBegin(); + QMap::const_iterator end = pingData_l.constEnd(); + while (it != end) { + pingObject.insert(it.key(), it.value()); + it++; + } + + QJsonObject object; + object.insert("Current", runner->currentHost()); + object.insert("Ping", pingObject); + object.insert("Refreshed", QString::number(runner->refreshed(), 16)); + + QJsonDocument document; + document.setObject(object); + + QByteArray jsonData = document.toJson(QJsonDocument::Compact); + socket->write(jsonData + "\n"); + socket->disconnectFromServer(); +} diff --git a/Socket.h b/Socket.h new file mode 100644 index 0000000..eae7741 --- /dev/null +++ b/Socket.h @@ -0,0 +1,19 @@ +#ifndef SOCKET_H +#define SOCKET_H + +#include "Runner.h" +#include +#include + +class Socket : public QLocalServer +{ + Q_OBJECT +public: + Socket(Runner *runner); + void incomingConnection(quintptr socketDescriptor); + +private: + Runner *runner; +}; + +#endif // SOCKET_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..7667f2e --- /dev/null +++ b/main.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include +#include +#include +#include "Runner.h" +#include "Socket.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication::setSetuidAllowed(true); + QCoreApplication a(argc, argv); + + QCommandLineParser parser; + parser.addPositionalArgument("config", "Ping configuration"); + parser.addPositionalArgument("socket", "Unix socket"); + parser.process(a); + + const QStringList args = parser.positionalArguments(); + if (args.length() != 2) { + QTextStream(stderr) << "Not enough or too many arguments was given!" << endl; + return 1; + } + const QString pingConfig = args.at(0); + const QString unixSocket = args.at(1); + + QByteArray jsonContent; + QFile jsonFile(pingConfig); + if (jsonFile.open(QFile::ReadOnly)) { + jsonContent = jsonFile.readAll(); + jsonFile.close(); + } + else { + QTextStream(stderr) << "Failed to read Ping configuration!" << endl; + return 1; + } + + QList pingHosts; + QJsonDocument jsonDocument = QJsonDocument::fromJson(jsonContent); + QJsonObject jsonObject = jsonDocument.object(); + QJsonObject::const_iterator it = jsonObject.constBegin(); + QJsonObject::const_iterator end = jsonObject.constEnd(); + while (it != end) { + if (it.value().isArray()) { + QJsonArray array = it.value().toArray(); + QJsonArray::const_iterator it = array.constBegin(); + QJsonArray::const_iterator end = array.constEnd(); + while (it != end) { + const QString host = array.at(it.i).toString(); + if (!host.isEmpty() && host != "none" && !pingHosts.contains(host)) + pingHosts << host; + it++; + } + } + else if (it.value().isString()) { + const QString host = it.value().toString(); + if (!host.isEmpty() && host != "none" && !pingHosts.contains(host)) + pingHosts << host; + } + it++; + } + + Runner runner(pingHosts); + Socket socket(&runner); + socket.setSocketOptions(QLocalServer::WorldAccessOption); + if (!socket.listen(unixSocket)) { + QTextStream(stderr) << "Failed to initialise Unix socket!" << endl; + return 1; + } + + runner.start(); + + return a.exec(); +} diff --git a/vpnstatus.pro b/vpnstatus.pro new file mode 100644 index 0000000..1a56069 --- /dev/null +++ b/vpnstatus.pro @@ -0,0 +1,19 @@ +QT -= gui +QT += network + +CONFIG += c++11 console +CONFIG -= app_bundle + +unix: DEFINES += PRIVILEGE_DROP_REQUIRED +unix: LIBS += -loping + +SOURCES += \ + main.cpp \ + Ping.cpp \ + Runner.cpp \ + Socket.cpp + +HEADERS += \ + Ping.h \ + Runner.h \ + Socket.h