-
Notifications
You must be signed in to change notification settings - Fork 152
Expand file tree
/
Copy pathcontroller.h
More file actions
330 lines (274 loc) · 10.4 KB
/
controller.h
File metadata and controls
330 lines (274 loc) · 10.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QDateTime>
#include <QList>
#include <QObject>
#include <QTimer>
#include "interfaceconfig.h"
#include "ipaddress.h"
#include "loghandler.h"
#include "models/server.h"
#include "models/serverdata.h"
#include "pinghelper.h"
class Controller;
class ControllerImpl;
class Controller : public QObject, public LogSerializer {
Q_OBJECT
Q_DISABLE_COPY_MOVE(Controller)
public:
// Note - these states are ordered from least to most active.
enum State {
// The initial state of the controller.
StateInitializing = 0,
// Initialization is blocked and the user must grant additional permissions
// before the controller can finish initialization.
StatePermissionRequired,
// The deactivated state of the controller. The VPN is disconnected and
// idle.
StateOff,
// A request to deactivate the controller is in progress, and is waiting on
// a disconnected() signal from the ControllerImpl.
StateDisconnecting,
// Requesting key to be regenerated due to version migration or expiration
// timeout. In case of successful connection, we continue to
// StateConnecting.
// In case of failure, we go to StateDisconnecting (via
// TaskControllerAction::eDeactivate)
StateRegeneratingKey,
// An error occurred during connection (i.e. server unavailable). The
// controller is idle.
// User action is required to recover from this state.
// User's network is forced down in this state to avoid traffic leaks.
StateConnectionError,
// One or more connections have been submitted to the ControllerImpl while
// activating the VPN. The controller is waiting for a connected() signal
// from the ControllerImpl before proceeding.
StateConnecting,
// All connections have been activated, and final connectivity checks are
// being performed.
StateConfirming,
// A major, user-facing configuration change is being applied, and one or
// more connections have been submitted to the ControllerImpl, and the
// controller is waiting for a connected() signal from the ControllerImpl
// before proceeding. This state is typically used to change locations.
StateSwitching,
// A minor configuration change is being applied which should not be visible
// to the user. One or more connections have been submitted to the
// ControllerImpl and the controller is waiting for a connected() signal
// before proceeding. This state is typically used for load-balancing and
// failover between servers.
StateSilentSwitching,
// The VPN is connected and offers partial connectivity to select network
// addresses.
StateOnPartial,
// The VPN is connected and is applying full device protection for all
// network traffic.
StateOn,
};
Q_ENUM(State)
enum Reason {
ReasonNone = 0,
ReasonSwitching,
ReasonConfirming,
ReasonUpdating
};
enum ErrorCode {
ErrorNone = 0,
ErrorFatal = 1,
ErrorSplitTunnelInit = 2,
ErrorSplitTunnelStart = 3,
ErrorSplitTunnelExclude = 4,
// Connection to server timed out during Confirming or Switching
ErrorServerTimeout = 5,
// Attempt to connect to a server marked as unavailable or empty server list
ErrorNoServerAvailable = 6,
};
Q_ENUM(ErrorCode);
/**
* @brief Who asked the Connection
* to be Initiated? A Webextension
* or a User Interaction inside the Client?
*/
enum ActivationPrincipal {
Null = 0,
ExtensionUser = 1,
ClientUser = 2,
};
Q_ENUM(ActivationPrincipal)
public:
qint64 connectionTimestamp() const;
void serverUnavailable();
void captivePortalPresent();
void captivePortalGone();
bool switchServers(const ServerData& serverData);
void updateRequired();
void deleteOSTunnelConfig();
void startHandshakeTimer();
bool isDeviceConnected() const { return m_isDeviceConnected; }
bool isInitialized() const { return m_state >= StateOff; }
bool shouldSuppressNextNotification();
Q_INVOKABLE bool isActive() const { return m_state > StateOff; }
const ServerData& currentServer() const { return m_serverData; }
bool enableDisconnectInConfirming() const {
return m_enableDisconnectInConfirming;
}
enum ServerSelectionPolicy {
RandomizeServerSelection,
DoNotRandomizeServerSelection,
};
int connectionRetry() const { return m_connectionRetry; }
State state() const;
ErrorCode error() const;
bool silentServerSwitchingSupported() const;
bool splitTunnelSupported() const;
void cleanupBackendLogs();
// LogSerializer interface
QString logName() const override { return "Mozilla VPN backend logs"; }
void logSerialize(QIODevice* device) override;
void getStatus(
std::function<void(const QString& serverIpv4Gateway,
const QString& deviceIpv4Address, uint64_t txBytes,
uint64_t rxBytes)>&& callback);
QString currentServerString() const;
public slots:
// These 2 methods activate/deactivate the VPN. Return true if a signal will
// be emitted at the end of the operation.
bool activate(
const ServerData& serverData, ActivationPrincipal = ClientUser,
ServerSelectionPolicy serverSelectionPolicy = RandomizeServerSelection);
bool deactivate(ActivationPrincipal = ClientUser);
Q_INVOKABLE void quit();
Q_INVOKABLE void forceDaemonCrash();
Q_INVOKABLE void forceDaemonSilentServerSwitch();
private:
Q_PROPERTY(State state READ state NOTIFY stateChanged)
Q_PROPERTY(ErrorCode error READ error NOTIFY errorChanged)
Q_PROPERTY(qint64 connectionTimestamp READ connectionTimestamp NOTIFY
timestampChanged)
Q_PROPERTY(
int connectionRetry READ connectionRetry NOTIFY connectionRetryChanged);
Q_PROPERTY(bool enableDisconnectInConfirming READ enableDisconnectInConfirming
NOTIFY enableDisconnectInConfirmingChanged);
Q_PROPERTY(bool silentServerSwitchingSupported READ
silentServerSwitchingSupported CONSTANT);
Q_PROPERTY(bool isDeviceConnected READ isDeviceConnected NOTIFY
isDeviceConnectedChanged);
Q_PROPERTY(bool splitTunnelSupported READ splitTunnelSupported CONSTANT);
// This is just for testing purposes.
Q_PROPERTY(QString currentServerString READ currentServerString NOTIFY
currentServerChanged);
private slots:
void handshakeTimeout();
void connected(const QString& pubkey);
void disconnected();
void statusUpdated(const QString& serverIpv4Gateway,
const QString& deviceIpv4Address, uint64_t txBytes,
uint64_t rxBytes);
void implInitialized(bool status, bool connected,
const QDateTime& connectionDate);
void implPermRequired();
void handleBackendFailure(ErrorCode code);
signals:
void stateChanged();
void errorChanged();
void timestampChanged();
void enableDisconnectInConfirmingChanged();
void connectionRetryChanged();
void recordConnectionStartTelemetry();
void recordConnectionEndTelemetry();
void recordDataTransferTelemetry();
void readyToQuit();
void readyToUpdate();
void readyToServerUnavailable(bool pingReceived);
void activationBlockedForCaptivePortal();
void isDeviceConnectedChanged();
void currentServerChanged();
public:
Controller();
~Controller();
void initialize();
struct IPAddressList {
QList<IPAddress> v6;
QList<IPAddress> v4;
QList<IPAddress> flatten() {
QList<IPAddress> list;
list.append(v6);
list.append(v4);
return list;
}
};
static QList<IPAddress> getAllowedIPAddressRanges(const Server& server);
enum ServerCoolDownPolicyForSilentSwitch {
eServerCoolDownNeeded,
eServerCoolDownNotNeeded,
};
bool silentSwitchServers(
ServerCoolDownPolicyForSilentSwitch serverCoolDownPolicy);
void startKeyRegeneration();
private:
enum NextStep {
None,
Quit,
Update,
Reconnect,
Disconnect,
};
NextStep m_nextStep = None;
enum DNSPortPolicy {
ForceDNSPort,
DoNotForceDNSPort,
};
void activateInternal(DNSPortPolicy dnsPort,
ServerSelectionPolicy serverSelectionPolicy,
ActivationPrincipal);
void clearRetryCounter();
void activateNext();
void setState(State state);
void setError(ErrorCode code);
void maybeEnableDisconnectInConfirming();
void serverDataChanged();
auto setupConfigs(DNSPortPolicy dnsPort,
ServerSelectionPolicy serverSelectionPolicy);
void maybeSendUpdatedConfig(const ServerData& serverData);
QString useLocalSocketPath() const;
private:
QTimer m_confirmingTimer;
QTimer m_handshakeTimer;
QDateTime m_connectedTimeInUTC;
State m_state = StateInitializing;
ActivationPrincipal m_initiator = Null;
bool m_enableDisconnectInConfirming = false;
QList<InterfaceConfig> m_activationQueue;
int m_connectionRetry = 0;
QScopedPointer<ControllerImpl> m_impl;
bool m_portalDetected = false;
bool m_isDeviceConnected = true;
ErrorCode m_errorCode = ErrorNone;
// Server data can change while the controller is busy completing an
// activation or a server switch because they are managed by the
// SettingsHolder object and exposed to user interaction, addons, and JS.
//
// But the controller needs to know the location to use for the entire
// duration of its tasks. When the client schedules a VPN activation,
// `m_serverData` is set as a copy of the current `MozillaVPN::serverData()`,
// ignoring further updates until the pending operations terminate. Instead,
// `m_nextServerData` is set when a server-switch request is scheduled while
// an activation operation is still in progress.
//
// At initialization time, these two member variables are set to
// MozillaVPN::serverData() to do not let not initialize.
//
// Please, do not use MozillaVPN::serverData() in the controller!
ServerData m_serverData;
ServerData m_nextServerData;
PingHelper m_pingCanary;
bool m_pingReceived = false;
QList<std::function<void(const QString& serverIpv4Gateway,
const QString& deviceIpv4Address, uint64_t txBytes,
uint64_t rxBytes)>>
m_getStatusCallbacks;
}; // namespace Controller
#endif // CONTROLLER_H