1 /*
2 * Copyright 2015-2021 The Regents of the University of California
3 * All rights reserved.
4 *
5 * This file is part of Spoofer.
6 *
7 * Spoofer is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Spoofer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Spoofer. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifndef SPOOFER_MANAGER_COMMON_COMMON_H
22 #define SPOOFER_MANAGER_COMMON_COMMON_H
23
24 #include "../../config.h"
25 #include <typeinfo>
26 #include <ctime>
27
28 #include "spoof_qt.h"
29
30 #include <QDataStream>
31 #include <QCoreApplication>
32 #include <QFile>
33 #include <QTextStream>
34 //#include <QHostAddress>
35 #include <QDateTime>
36 #include <QSettings>
37 #include <QList>
38
39 #include "port.h"
40
41 #ifdef UPGRADE_KEY
42 #define AUTOUPGRADE_ENABLED
43 // UPGRADE_CMD is a C++ string literal that will be executed in a shell
44 // script. $1 is the package file name.
45 #ifndef UPGRADE_CMD
46 #if defined(Q_OS_MACOS)
47 #define UPGRADE_CMD "/usr/sbin/installer -package \"$1\" -target /"
48 #elif defined(Q_OS_WIN32)
49 // UPGRADE_CMD is not used on Windows
50 #endif
51 #endif
52 #endif
53
54 // forward declarations
55 QT_BEGIN_NAMESPACE
56 class QCommandLineParser;
57 QT_END_NAMESPACE
58
59 #define EVERYONE_IS_PRIVILEGED 1
60
61 // scheduler messages
62 enum sc_msg_type {
63 SC_ERROR,
64 SC_TEXT,
65 SC_SCHEDULED,
66 SC_PROBER_STARTED,
67 SC_PROBER_FINISHED,
68 SC_PROBER_ERROR,
69 SC_PAUSED,
70 SC_RESUMED,
71 SC_DONE_CMD,
72 SC_CONFIG_CHANGED,
73 SC_NEED_CONFIG,
74 SC_CONFIGED,
75 SC_HELLO,
76 SC_UPGRADE_AVAILABLE,
77 SC_UPGRADE_PROGRESS,
78 SC_UPGRADE_ERROR,
79 SC_UPGRADE_INSTALLING
80 };
81
82 struct sc_msg_text {
83 QString text;
textsc_msg_text84 sc_msg_text(const QString &_text = QString()) : text(_text) {}
85 };
86
87 inline QDataStream &operator<<(QDataStream &out, const sc_msg_text &msg) {
88 return out << msg.text;
89 }
90
91 inline QDataStream &operator>>(QDataStream &in, sc_msg_text &msg) {
92 return in >> msg.text;
93 }
94
95 struct sc_msg_scheduled {
96 // QHostAddress addr;
97 qint32 when;
sc_msg_scheduledsc_msg_scheduled98 sc_msg_scheduled() : /*addr(),*/ when(0) {}
99 };
100
101 inline QDataStream &operator<<(QDataStream &out, const sc_msg_scheduled &msg) {
102 return out /* << msg.addr */ << msg.when;
103 }
104
105 inline QDataStream &operator>>(QDataStream &in, sc_msg_scheduled &msg) {
106 return in /* >> msg.addr */ >> msg.when;
107 }
108
109 struct sc_msg_upgrade_available {
110 int autoTime;
111 bool mandatory;
112 int32_t vnum;
113 QString vstr;
114 QString file;
115 QString warning;
116 sc_msg_upgrade_available(bool wantAuto = false, bool _mandatory = false,
117 int32_t _vnum = 0,
118 const QString &_vstr = QString(), const QString &_file = QString());
119 };
120
121 inline QDataStream &operator<<(QDataStream &out, const sc_msg_upgrade_available &msg) {
122 return out << msg.autoTime << msg.mandatory << msg.vnum << msg.vstr << msg.file << msg.warning;
123 }
124
125 inline QDataStream &operator>>(QDataStream &in, sc_msg_upgrade_available &msg) {
126 return in >> msg.autoTime >> msg.mandatory >> msg.vnum >> msg.vstr >> msg.file >> msg.warning;
127 }
128
129 #if defined(Q_OS_UNIX)
130 typedef int error_t;
getLastErr()131 inline error_t getLastErr() { return errno; }
132 #elif defined(Q_OS_WIN32)
133 typedef unsigned long error_t;
134 error_t getLastErr();
135 #endif
136
137 QString getErrmsg(error_t err);
getLastErrmsg()138 inline QString getLastErrmsg() { return getErrmsg(getLastErr()); }
139 bool getProcessName(qint64 pid, char *buf, unsigned len);
140
141 // base class for Spoofer applications
142 class SpooferBase {
143 public:
144 // A write-only QIODevice that wraps another but doesn't open it until
145 // needed, and can be switched to a different device at any time.
146 class OnDemandDevice Q_DECL_FINAL : public QIODevice {
147 private:
148 QIODevice *dev, *newdev, *fallback;
149 QString newname;
150 bool timestampEnabled;
151 OnDemandDevice(OnDemandDevice &) NO_METHOD; // no copy-ctor
152 OnDemandDevice operator=(OnDemandDevice &) NO_METHOD; // no copy-assign
153 public:
OnDemandDevice(FILE * file)154 OnDemandDevice(FILE *file) :
155 QIODevice(), dev(), newdev(), fallback(), newname(), timestampEnabled()
156 {
157 QFile *qfile = new QFile();
158 if (qfile) qfile->open(file, WriteOnly|Unbuffered);
159 dev = qfile;
160 this->open(WriteOnly);
161 }
readData(char * data,qint64 maxSize)162 qint64 readData(char *data, qint64 maxSize) OVERRIDE
163 { Q_UNUSED(data); Q_UNUSED(maxSize); return -1; }
164 qint64 writeData(const char *data, qint64 maxSize) OVERRIDE;
setDevice(QIODevice * device,const QString & name)165 void setDevice(QIODevice *device, const QString &name) {
166 newname = name;
167 newdev = device;
168 }
setDevice(QFileDevice * device)169 void setDevice(QFileDevice *device) {
170 setDevice(device, device->fileName());
171 }
setFallbackDevice(QFileDevice * device)172 void setFallbackDevice(QFileDevice *device) {
173 fallback = device;
174 }
close()175 void close() OVERRIDE {
176 QIODevice::close();
177 if (dev) { delete dev; dev = nullptr; }
178 if (newdev) { delete newdev; newdev = nullptr; }
179 if (fallback) { delete fallback; fallback = nullptr; }
180 }
type()181 const std::type_info& type() { return dev ? typeid(*dev) : typeid(0); }
setTimestampEnabled(bool flag)182 void setTimestampEnabled(bool flag) { timestampEnabled = flag; }
getTimestampEnabled()183 bool getTimestampEnabled() const { return timestampEnabled; }
184 };
185
186 class Config {
187 bool forWriting;
188 ATR_UNUSED_MEMBER uint8_t unused_padding[3];
189 public:
190 struct MemberBase {
191 const QString key;
192 QVariant defaultVal;
193 bool required;
194 const QString desc;
195 MemberBase(QString _key, QVariant _defaultVal, QString _desc = QString(), bool _hidden = false) :
keyMemberBase196 key(_key), defaultVal(_defaultVal), required(false), desc(_desc)
197 {
198 if (_hidden || desc.isNull()) return;
199 #ifdef EVERYONE_IS_PRIVILEGED
200 if (_key.startsWith(QSL("unpriv"))) return;
201 #endif
202 members.push_back(this);
203 }
~MemberBaseMemberBase204 virtual ~MemberBase() {}
variantMemberBase205 QVariant variant() const
206 { return settings ? settings->value(key, defaultVal) : defaultVal; }
207 virtual bool setFromString(QString value, QString &errmsg) = 0;
removeMemberBase208 void remove() // remove: config->foo.remove()
209 { if (settings) settings->remove(key); }
setDefaultMemberBase210 void setDefault(QVariant d) { defaultVal = d; }
isSetMemberBase211 bool isSet() const
212 { return settings && settings->contains(key); }
213 virtual QString optionHelpString() = 0;
enforceMemberBase214 virtual void enforce() {}
validateMemberBase215 virtual bool validate(QVariant var, QString &errmsg)
216 { Q_UNUSED(var); Q_UNUSED(errmsg); return true; }
217 };
218 template <class T> struct Member : public MemberBase {
219 Member(QString _key, T _defaultVal = T(), QString _desc = QString(), bool _hidden = false) :
MemberBaseMember220 MemberBase(_key, QVariant(_defaultVal), _desc, _hidden) {}
~MemberMember221 ~Member() {}
operatorMember222 T operator()() const // get: config->foo()
223 { return variant().template value<T>(); }
operatorMember224 void operator()(T value) // set: config->foo(value)
225 { if (settings) settings->setValue(key, QVariant(value)); }
setFromStringMember226 bool setFromString(QString value, QString &errmsg) OVERRIDE {
227 if (!settings) return false;
228 QVariant var(value);
229 int qtypeid = qMetaTypeId<T>();
230 if (!var.convert(qtypeid)) {
231 errmsg = QSL("%1: can not convert \"%2\" to %3.").
232 arg(key).arg(value).
233 arg(QString::fromLocal8Bit(QMetaType::typeName(qtypeid)));
234 qDebug() << errmsg;
235 return false;
236 }
237 if (!validate(var, errmsg)) return false;
238 settings->setValue(key, var);
239 return true;
240 }
optionHelpStringMember241 QString optionHelpString() OVERRIDE
242 { return optionHelpString(*this); }
243 private:
optionHelpStringMember244 template <typename U> QString optionHelpString(const Member<U> &member) {
245 Q_UNUSED(member);
246 return QSL("%1 from now on [\"%2\" setting or \"%3\"].").
247 arg(desc).arg(key).arg(defaultVal.toString());
248 }
249 // C++ doesn't allow an explicit template specialization inside a
250 // class, but does allow an overload. (The unused parameter is
251 // just for overload resolution.)
optionHelpStringMember252 QString optionHelpString(const Member<bool> &member) {
253 Q_UNUSED(member);
254 return QSL("%1 from now on (yes/no) [\"%2\" setting or %3].").
255 arg(desc).arg(key).arg(defaultVal.toInt());
256 }
257 };
258 struct MemberInt Q_DECL_FINAL : public Member<int> {
259 int minVal, maxVal;
260 MemberInt(QString _key, int _defaultVal, int _minVal, int _maxVal,
261 QString _desc = QString(), bool _hidden = false) :
MemberQ_DECL_FINAL262 Member(_key, _defaultVal, _desc, _hidden),
263 minVal(_minVal), maxVal(_maxVal)
264 {}
265 private:
validateQ_DECL_FINAL266 bool validate(QVariant var, QString &errmsg) OVERRIDE {
267 int val = var.value<int>();
268 if (val >= minVal && val <= maxVal) return true;
269 errmsg = QSL("%1: value %2 out of range [%3, %4]").
270 arg(key).arg(val).arg(minVal).arg(maxVal);
271 qDebug() << errmsg;
272 return false;
273 }
enforceQ_DECL_FINAL274 void enforce() OVERRIDE {
275 if (!settings) return;
276 int val = variant().value<int>();
277 if (val < minVal)
278 settings->setValue(key, QVariant(minVal));
279 if (val > maxVal)
280 settings->setValue(key, QVariant(maxVal));
281 }
282 };
283 static QSettings *settings;
284 public:
285 static QList<MemberBase*> members;
286
287 Member<QString> dataDir;
288 Member<QString> schedulerSocketName;
289 Member<bool> paused;
290 #if DEBUG
291 Member<bool> useDevServer;
292 MemberInt spooferProtocolVersion;
293 Member<bool> pretendMode;
294 Member<bool> standaloneMode;
295 Member<bool> installerAddTaint;
296 Member<bool> installerVerifySig;
297 Member<bool> installerKeep;
298 #else // DEBUG
useDevServer()299 bool useDevServer() { return false; }
pretendMode()300 bool pretendMode() { return false; }
standaloneMode()301 bool standaloneMode() { return false; }
installerAddTaint()302 bool installerAddTaint() { return true; }
installerVerifySig()303 bool installerVerifySig() { return true; }
installerKeep()304 bool installerKeep() { return false; }
305 #endif // DEBUG
306
307 #ifdef AUTOUPGRADE_ENABLED
308 Member<bool> autoUpgrade;
309 #endif
310 Member<bool> enableIPv4;
311 Member<bool> enableIPv6;
312 MemberInt keepLogs;
313 Member<bool> sharePublic;
314 Member<bool> shareRemedy;
315 Member<bool> enableTLS;
316 MemberInt netPollInterval;
317 MemberInt delayInterval;
318 MemberInt proberInterval;
319 MemberInt proberRetryInterval;
320 MemberInt maxRetries;
321 Member<bool> unprivView;
322 Member<bool> unprivTest;
323 Member<bool> unprivPref;
324
325 Config();
326 void initSettings(bool forWriting = false, bool debug = false);
~Config()327 ~Config() { if (settings) delete settings; settings = nullptr; }
328
329 bool error(const char *label);
330 void logError(const char *label, QString msg, QString msg2 = QString());
331 QString lockFileName();
332 void remove();
333
fileName()334 QString fileName() {
335 return settings ? settings->fileName() : QString();
336 }
337
isFile()338 bool isFile() {
339 if (!settings) return false;
340 #ifdef Q_OS_WIN32
341 if (settings->format() == QSettings::NativeFormat)
342 return false; // windows registry
343 #endif
344 return true;
345 }
346
sync()347 bool sync() {
348 if (!settings) return false;
349 settings->sync();
350 return !error("Config sync:");
351 }
352
find(const QString & key)353 MemberBase *find(const QString &key) {
354 for (int i = 0; i < members.size(); i++) {
355 if (key.compare(members[i]->key, Qt::CaseInsensitive) == 0)
356 return members[i];
357 }
358 return nullptr;
359 }
360
hasRequiredSettings()361 bool hasRequiredSettings() {
362 for (auto m : members) {
363 if (!m->isSet() && m->required)
364 return false;
365 }
366 return true;
367 }
368
enforce()369 void enforce() {
370 for (auto m : members) {
371 m->enforce();
372 }
373 }
374 };
375
376 QString appDir;
377 QString appFile;
378 static const QStringList *args; // alternate command line arguments
379 static QString optSettings;
380 static Config *config;
381 static OnDemandDevice outdev;
382 static OnDemandDevice errdev;
383 static QTextStream spout;
384 static QTextStream sperr;
385 static const QString proberLogFtime;
386 static const QString proberLogGlob;
387 static const QString proberLogRegex;
388
389 SpooferBase();
390
~SpooferBase()391 virtual ~SpooferBase() {
392 if (config) delete config;
393 }
394
395 static void logHandler(QtMsgType type,
396 const QMessageLogContext &ctx, const QString &msg);
397 static bool parseCommandLine(QCommandLineParser &clp, QString desc);
398 static QString ftime_zone(const QString &fmt, const time_t *tp, const Qt::TimeSpec &spec);
399 static QString ftime(const QString &fmt = QString(), const time_t *tp = nullptr)
400 { return ftime_zone(fmt, tp, Qt::LocalTime); }
401 static QString ftime_utc(const QString &fmt = QString(), const time_t *tp = nullptr)
402 { return ftime_zone(fmt, tp, Qt::UTC); }
403
findDefaultSettings(bool debug)404 static QSettings *findDefaultSettings(bool debug)
405 {
406 QSettings::Scope scope = debug ? QSettings::UserScope :
407 QSettings::SystemScope;
408 // Mac expects a domain here where others expect a name.
409 // QSettings() with no parameters would correctly auto-pick
410 // the domain or name, but doesn't let us specify the scope.
411 return new QSettings(scope,
412 #ifdef Q_OS_MACOS
413 QCoreApplication::organizationDomain(),
414 #else
415 QCoreApplication::organizationName(),
416 #endif
417 QCoreApplication::applicationName(), nullptr);
418 }
419
420 bool initConfig(bool _forWriting = false) {
421 config->initSettings(_forWriting);
422 if (config->error("Config")) // XXX ???
423 return false;
424 if (!_forWriting && config->dataDir().isEmpty()) {
425 // QSettings doesn't consider a missing file an error. But if
426 // we want read-only and dataDir is missing, something is wrong.
427 config->logError("Config", QSL("Missing \"dataDir\""),
428 QSL("Make sure the scheduler is running and using the same configuration."));
429 return false;
430 }
431 return true;
432 }
433 };
434
435 #endif // SPOOFER_MANAGER_COMMON_COMMON_H
436