From 73cbd921218335d3e36f92dc9f64fcab0b04b0a2 Mon Sep 17 00:00:00 2001 From: Syping Date: Sun, 2 Feb 2020 00:44:30 +0100 Subject: [PATCH] VPN Status --- Ping.cpp | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ Ping.h | 12 ++++++++ Runner.cpp | 69 +++++++++++++++++++++++++++++++++++++++++++++ Runner.h | 33 ++++++++++++++++++++++ Socket.cpp | 42 +++++++++++++++++++++++++++ Socket.h | 19 +++++++++++++ main.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ vpnstatus.pro | 19 +++++++++++++ 8 files changed, 348 insertions(+) create mode 100644 Ping.cpp create mode 100644 Ping.h create mode 100644 Runner.cpp create mode 100644 Runner.h create mode 100644 Socket.cpp create mode 100644 Socket.h create mode 100644 main.cpp create mode 100644 vpnstatus.pro 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